protocol.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
  2. *
  3. * Gearmand client and server library.
  4. *
  5. * Copyright (C) 2011-2012 Data Differential, http://datadifferential.com/
  6. * Copyright (C) 2008 Brian Aker, Eric Day
  7. * All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions are
  11. * met:
  12. *
  13. * * Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. *
  16. * * Redistributions in binary form must reproduce the above
  17. * copyright notice, this list of conditions and the following disclaimer
  18. * in the documentation and/or other materials provided with the
  19. * distribution.
  20. *
  21. * * The names of its contributors may not be used to endorse or
  22. * promote products derived from this software without specific prior
  23. * written permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  28. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  29. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  30. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  31. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  32. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  35. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. */
  38. /**
  39. * @file
  40. * @brief HTTP Protocol Definitions
  41. */
  42. #include <config.h>
  43. #include <libgearman-server/common.h>
  44. #include <libgearman/strcommand.h>
  45. #include <cstdio>
  46. #include <cstdlib>
  47. #include <libgearman-server/plugins/protocol/http/protocol.h>
  48. /**
  49. * @addtogroup gearmand::protocol::HTTPatic Static HTTP Protocol Definitions
  50. * @ingroup gearman_protocol_http
  51. * @{
  52. */
  53. /**
  54. * Default values.
  55. */
  56. #define GEARMAN_PROTOCOL_HTTP_DEFAULT_PORT "8080"
  57. #pragma GCC diagnostic ignored "-Wold-style-cast"
  58. class HTTPtext;
  59. /* Protocol callback functions. */
  60. class HTTPtext : public gearmand::protocol::Context
  61. {
  62. public:
  63. HTTPtext() :
  64. _method(gearmand::protocol::httpd::TRACE),
  65. _sent_header(false),
  66. _background(false),
  67. _keep_alive(false),
  68. _http_response(gearmand::protocol::httpd::HTTP_OK)
  69. {
  70. }
  71. ~HTTPtext()
  72. { }
  73. void notify(gearman_server_con_st*)
  74. {
  75. gearmand_debug("HTTP connection disconnected");
  76. }
  77. size_t pack(const gearmand_packet_st *packet,
  78. gearman_server_con_st *connection,
  79. void *send_buffer, const size_t send_buffer_size,
  80. gearmand_error_t& ret_ptr)
  81. {
  82. switch (packet->command)
  83. {
  84. case GEARMAN_COMMAND_WORK_DATA:
  85. {
  86. for (const char *ptr= packet->data; ptr <= (packet->data +packet->data_size) -2; ptr++)
  87. {
  88. content.push_back(*ptr);
  89. }
  90. gearmand_log_debug(GEARMAN_DEFAULT_LOG_PARAM, "HTTP gearmand_command_t: GEARMAN_COMMAND_WORK_DATA length:%"PRIu64, uint64_t(content.size()));
  91. ret_ptr= GEARMAN_IGNORE_PACKET;
  92. return 0;
  93. }
  94. default:
  95. assert(0);
  96. case GEARMAN_COMMAND_WORK_FAIL:
  97. case GEARMAN_COMMAND_ECHO_RES:
  98. case GEARMAN_COMMAND_WORK_COMPLETE:
  99. break;
  100. case GEARMAN_COMMAND_JOB_CREATED:
  101. {
  102. gearmand_log_debug(GEARMAN_DEFAULT_LOG_PARAM,
  103. "Sending HTTP told to ignore packet: gearmand_command_t:%s",
  104. gearman_strcommand(packet->command));
  105. ret_ptr= GEARMAN_IGNORE_PACKET;
  106. return 0;
  107. }
  108. }
  109. gearmand_log_debug(GEARMAN_DEFAULT_LOG_PARAM,
  110. "Sending HTTP response: Content-length:%"PRIu64" data_size:%"PRIu64" gearmand_command_t:%s response:%s",
  111. uint64_t(content.size()),
  112. uint64_t(packet->data_size),
  113. gearman_strcommand(packet->command),
  114. gearmand::protocol::httpd::response(response()));
  115. size_t pack_size= 0;
  116. if (_sent_header == false)
  117. {
  118. if (response() != gearmand::protocol::httpd::HTTP_OK)
  119. {
  120. pack_size= (size_t)snprintf((char *)send_buffer, send_buffer_size,
  121. "HTTP/1.0 %u %s\r\n"
  122. "Server: Gearman/" PACKAGE_VERSION "\r\n"
  123. "Content-Length: 0\r\n"
  124. "\r\n",
  125. int(response()), gearmand::protocol::httpd::response(response()));
  126. }
  127. else if (method() == gearmand::protocol::httpd::HEAD)
  128. {
  129. pack_size= (size_t)snprintf((char *)send_buffer, send_buffer_size,
  130. "HTTP/1.0 200 OK\r\n"
  131. "X-Gearman-Job-Handle: %.*s\r\n"
  132. "Content-Length: %"PRIu64"\r\n"
  133. "Server: Gearman/" PACKAGE_VERSION "\r\n"
  134. "\r\n",
  135. packet->command == GEARMAN_COMMAND_JOB_CREATED ? (int)packet->arg_size[0] : (int)packet->arg_size[0] - 1,
  136. (const char *)packet->arg[0],
  137. (uint64_t)packet->data_size);
  138. }
  139. else if (method() == gearmand::protocol::httpd::TRACE)
  140. {
  141. pack_size= (size_t)snprintf((char *)send_buffer, send_buffer_size,
  142. "HTTP/1.0 200 OK\r\n"
  143. "Server: Gearman/" PACKAGE_VERSION "\r\n"
  144. "Connection: close\r\n"
  145. "Content-Type: message/http\r\n"
  146. "\r\n");
  147. }
  148. else if (method() == gearmand::protocol::httpd::POST)
  149. {
  150. pack_size= (size_t)snprintf((char *)send_buffer, send_buffer_size,
  151. "HTTP/1.0 200 OK\r\n"
  152. "X-Gearman-Job-Handle: %.*s\r\n"
  153. "X-Gearman-Command: %s\r\n"
  154. "Content-Length: %"PRIu64"\r\n"
  155. "Server: Gearman/" PACKAGE_VERSION "\r\n"
  156. "\r\n%.*s",
  157. packet->command == GEARMAN_COMMAND_JOB_CREATED ? int(packet->arg_size[0]) : int(packet->arg_size[0] - 1),
  158. (const char *)packet->arg[0],
  159. gearman_strcommand(packet->command),
  160. uint64_t(content.size()),
  161. int(content.size()), &content[0]);
  162. }
  163. else
  164. {
  165. pack_size= (size_t)snprintf((char *)send_buffer, send_buffer_size,
  166. "HTTP/1.0 200 OK\r\n"
  167. "X-Gearman-Job-Handle: %.*s\r\n"
  168. "X-Gearman-Command: %s\r\n"
  169. "Content-Length: %"PRIu64"\r\n"
  170. "Server: Gearman/" PACKAGE_VERSION "\r\n"
  171. "\r\n",
  172. packet->command == GEARMAN_COMMAND_JOB_CREATED ? int(packet->arg_size[0]) : int(packet->arg_size[0] - 1),
  173. (const char *)packet->arg[0],
  174. gearman_strcommand(packet->command),
  175. uint64_t(content.size()));
  176. }
  177. _sent_header= true;
  178. }
  179. if (pack_size > send_buffer_size)
  180. {
  181. gearmand_debug("Sending HTTP had to flush");
  182. ret_ptr= GEARMAN_FLUSH_DATA;
  183. return 0;
  184. }
  185. memcpy(send_buffer, &content[0], content.size());
  186. pack_size+= content.size();
  187. #if 0
  188. if (keep_alive() == false)
  189. #endif
  190. {
  191. gearman_io_set_option(&connection->con, GEARMAND_CON_CLOSE_AFTER_FLUSH, true);
  192. }
  193. ret_ptr= GEARMAN_SUCCESS;
  194. return pack_size;
  195. }
  196. size_t unpack(gearmand_packet_st *packet, gearman_server_con_st *connection,
  197. const void *data, const size_t data_size,
  198. gearmand_error_t& ret_ptr)
  199. {
  200. const char *unique= "-";
  201. size_t unique_size= 2;
  202. gearmand_job_priority_t priority= GEARMAND_JOB_PRIORITY_NORMAL;
  203. gearmand_info("Receiving HTTP response");
  204. /* Get the request line first. */
  205. size_t request_size;
  206. size_t offset= 0;
  207. const char *request= parse_line(data, data_size, request_size, offset);
  208. if (request == NULL or request_size == 0)
  209. {
  210. gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "Zero length request made");
  211. ret_ptr= GEARMAN_IO_WAIT;
  212. return offset;
  213. }
  214. reset();
  215. /* Parse out the method, URI, and HTTP version from the request line. */
  216. const char *uri= (const char *)memchr(request, ' ', request_size);
  217. if (uri == NULL)
  218. {
  219. gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad request line: %.*s", (uint32_t)request_size, request);
  220. set_response(gearmand::protocol::httpd::HTTP_NOT_FOUND);
  221. ret_ptr= GEARMAN_SUCCESS;
  222. return 0;
  223. }
  224. {
  225. const char *method_str= request;
  226. ptrdiff_t method_size= uri -request;
  227. if (method_size == 3 and
  228. strncmp(method_str, "GET", 3) == 0)
  229. {
  230. set_method(gearmand::protocol::httpd::GET);
  231. }
  232. else if (method_size == 3 and
  233. strncmp(method_str, "PUT", 3) == 0)
  234. {
  235. set_method(gearmand::protocol::httpd::PUT);
  236. }
  237. else if (method_size == 4 and
  238. strncmp(method_str, "POST", 4) == 0)
  239. {
  240. set_method(gearmand::protocol::httpd::POST);
  241. }
  242. else if (method_size == 4 and
  243. strncmp(method_str, "HEAD", 4) == 0)
  244. {
  245. set_method(gearmand::protocol::httpd::HEAD);
  246. }
  247. else if (method_size == 5 and
  248. strncmp(method_str, "TRACE", 5) == 0)
  249. {
  250. set_method(gearmand::protocol::httpd::TRACE);
  251. }
  252. else
  253. {
  254. gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad method: %.*s", (uint32_t)method_size, method_str);
  255. set_response(gearmand::protocol::httpd::HTTP_METHOD_NOT_ALLOWED);
  256. ret_ptr= GEARMAN_SUCCESS;
  257. return 0;
  258. }
  259. }
  260. gearmand_log_debug(GEARMAN_DEFAULT_LOG_PARAM, "HTTP METHOD: %s", str_method(method()));
  261. while (*uri == ' ')
  262. {
  263. uri++;
  264. }
  265. // Remove leading /
  266. while (*uri == '/')
  267. {
  268. uri++;
  269. }
  270. const char *version= (const char *)memchr(uri, ' ', request_size - (size_t)(uri - request));
  271. if (version == NULL)
  272. {
  273. gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad request line: %.*s",
  274. (uint32_t)request_size, request);
  275. ret_ptr= GEARMAN_INVALID_PACKET;
  276. return 0;
  277. }
  278. ptrdiff_t uri_size= version -uri;
  279. gearmand_log_debug(GEARMAN_DEFAULT_LOG_PARAM, "HTTP URI: \"%.*s\"", (int)uri_size, uri);
  280. switch (method())
  281. {
  282. case gearmand::protocol::httpd::POST:
  283. case gearmand::protocol::httpd::PUT:
  284. case gearmand::protocol::httpd::GET:
  285. if (uri_size == 0)
  286. {
  287. gearmand_error("must give function name in URI");
  288. set_response(gearmand::protocol::httpd::HTTP_NOT_FOUND);
  289. }
  290. case gearmand::protocol::httpd::TRACE:
  291. case gearmand::protocol::httpd::HEAD:
  292. break;
  293. }
  294. while (*version == ' ')
  295. {
  296. version++;
  297. }
  298. size_t version_size= request_size - size_t(version - request);
  299. if (version_size == 8 and
  300. strncmp(version, "HTTP/1.1", 8) == 0)
  301. {
  302. set_keep_alive(true);
  303. }
  304. else if (version_size == 8 and
  305. strncmp(version, "HTTP/1.0", 8) == 0)
  306. { }
  307. else
  308. {
  309. gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad version: %.*s", (uint32_t)version_size, version);
  310. ret_ptr= GEARMAN_INVALID_PACKET;
  311. return 0;
  312. }
  313. /* Loop through all the headers looking for ones of interest. */
  314. const char *header;
  315. size_t header_size;
  316. while ((header= parse_line(data, data_size, header_size, offset)) != NULL)
  317. {
  318. if (header_size == 0)
  319. {
  320. break;
  321. }
  322. if (header_size > 16 and
  323. strncasecmp(header, "Content-Length: ", 16) == 0)
  324. {
  325. if (method() == gearmand::protocol::httpd::PUT or
  326. method() == gearmand::protocol::httpd::POST)
  327. {
  328. char content_length[11]; /* 11 bytes to fit max display length of uint32_t */
  329. snprintf(content_length, sizeof(content_length), "%.*s", (int)header_size - 16,
  330. header + 16);
  331. packet->data_size= size_t(atoi(content_length));
  332. }
  333. }
  334. else if (header_size == 22 and
  335. strncasecmp(header, "Connection: Keep-Alive", 22) == 0)
  336. {
  337. set_keep_alive(true);
  338. }
  339. else if (header_size > 18 and
  340. strncasecmp(header, "X-Gearman-Unique: ", 18) == 0)
  341. {
  342. unique= header + 18;
  343. unique_size= header_size -18;
  344. }
  345. else if (header_size == 26 and
  346. strncasecmp(header, "X-Gearman-Background: true", 26) == 0)
  347. {
  348. set_background(true);
  349. }
  350. else if (header_size == 24 and
  351. strncasecmp(header, "X-Gearman-Priority: high", 24) == 0)
  352. {
  353. priority= GEARMAND_JOB_PRIORITY_HIGH;
  354. }
  355. else if (header_size == 23 and
  356. strncasecmp(header, "X-Gearman-Priority: low", 23) == 0)
  357. {
  358. priority= GEARMAND_JOB_PRIORITY_LOW;
  359. }
  360. }
  361. /* Make sure we received the end of headers. */
  362. if (header == NULL and response() == gearmand::protocol::httpd::HTTP_OK)
  363. {
  364. gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "No headers were found");
  365. ret_ptr= GEARMAN_IO_WAIT;
  366. return 0;
  367. }
  368. /* Request and all headers complete, build a packet based on HTTP request. */
  369. packet->magic= GEARMAN_MAGIC_REQUEST;
  370. if (response() != gearmand::protocol::httpd::HTTP_OK)
  371. {
  372. packet->command= GEARMAN_COMMAND_ECHO_REQ;
  373. if ((ret_ptr= gearmand_packet_pack_header(packet)) != GEARMAN_SUCCESS)
  374. {
  375. return 0;
  376. }
  377. packet->data_size= 0;
  378. packet->data= NULL;
  379. }
  380. else if (method() == gearmand::protocol::httpd::TRACE)
  381. {
  382. packet->command= GEARMAN_COMMAND_ECHO_REQ;
  383. if ((ret_ptr= gearmand_packet_pack_header(packet)) != GEARMAN_SUCCESS)
  384. {
  385. return 0;
  386. }
  387. packet->data_size= data_size;
  388. packet->data= (const char*)data;
  389. }
  390. else if (method() == gearmand::protocol::httpd::HEAD and uri_size == 0)
  391. {
  392. packet->command= GEARMAN_COMMAND_ECHO_REQ;
  393. if ((ret_ptr= gearmand_packet_pack_header(packet)) != GEARMAN_SUCCESS)
  394. {
  395. return 0;
  396. }
  397. }
  398. else
  399. {
  400. if (background())
  401. {
  402. if (priority == GEARMAND_JOB_PRIORITY_NORMAL)
  403. {
  404. packet->command= GEARMAN_COMMAND_SUBMIT_JOB_BG;
  405. }
  406. else if (priority == GEARMAND_JOB_PRIORITY_HIGH)
  407. {
  408. packet->command= GEARMAN_COMMAND_SUBMIT_JOB_HIGH_BG;
  409. }
  410. else
  411. {
  412. packet->command= GEARMAN_COMMAND_SUBMIT_JOB_LOW_BG;
  413. }
  414. }
  415. else
  416. {
  417. if (priority == GEARMAND_JOB_PRIORITY_NORMAL)
  418. {
  419. packet->command= GEARMAN_COMMAND_SUBMIT_JOB;
  420. }
  421. else if (priority == GEARMAND_JOB_PRIORITY_HIGH)
  422. {
  423. packet->command= GEARMAN_COMMAND_SUBMIT_JOB_HIGH;
  424. }
  425. else
  426. {
  427. packet->command= GEARMAN_COMMAND_SUBMIT_JOB_LOW;
  428. }
  429. }
  430. if ((ret_ptr= gearmand_packet_pack_header(packet)) != GEARMAN_SUCCESS)
  431. {
  432. return 0;
  433. }
  434. if ((ret_ptr= gearmand_packet_create(packet, uri, (size_t)uri_size +1)) != GEARMAN_SUCCESS)
  435. {
  436. return 0;
  437. }
  438. if ((ret_ptr= gearmand_packet_create(packet, unique, unique_size +1)) != GEARMAN_SUCCESS)
  439. {
  440. return 0;
  441. }
  442. /* Make sure function and unique are NULL terminated. */
  443. packet->arg[0][uri_size]= 0;
  444. packet->arg[1][unique_size]= 0;
  445. ret_ptr= GEARMAN_SUCCESS;
  446. }
  447. gearmand_info("Receiving HTTP response(finished)");
  448. return offset;
  449. }
  450. bool background()
  451. {
  452. return _background;
  453. }
  454. void set_background(bool arg)
  455. {
  456. _background= arg;
  457. }
  458. bool keep_alive()
  459. {
  460. return _keep_alive;
  461. }
  462. void set_keep_alive(bool arg)
  463. {
  464. _keep_alive= arg;
  465. }
  466. void set_response(gearmand::protocol::httpd::response_t arg)
  467. {
  468. _http_response= arg;
  469. }
  470. gearmand::protocol::httpd::response_t response() const
  471. {
  472. return _http_response;
  473. }
  474. gearmand::protocol::httpd::method_t method()
  475. {
  476. return _method;
  477. }
  478. void set_method(gearmand::protocol::httpd::method_t arg)
  479. {
  480. _method= arg;
  481. }
  482. void reset()
  483. {
  484. _sent_header= false;
  485. _background= false;
  486. _keep_alive= false;
  487. content.clear();
  488. _method= gearmand::protocol::httpd::TRACE;
  489. _http_response= gearmand::protocol::httpd::HTTP_OK;
  490. }
  491. const char *parse_line(const void *data, const size_t data_size,
  492. size_t& line_size, size_t& offset)
  493. {
  494. const char *start= (const char *)data +offset;
  495. const char *end= (const char *)memchr(start, '\n', data_size -offset);
  496. if (end == NULL)
  497. {
  498. return NULL;
  499. }
  500. offset+= size_t(end - start) +1;
  501. if (end != start && *(end - 1) == '\r')
  502. {
  503. end--;
  504. }
  505. line_size= size_t(end - start);
  506. return start;
  507. }
  508. private:
  509. gearmand::protocol::httpd::method_t _method;
  510. bool _sent_header;
  511. bool _background;
  512. bool _keep_alive;
  513. std::string global_port;
  514. gearmand::protocol::httpd::response_t _http_response;
  515. std::vector<char> content;
  516. };
  517. static gearmand_error_t _http_con_add(gearman_server_con_st *connection)
  518. {
  519. gearmand_info("HTTP connection made");
  520. HTTPtext *http= new (std::nothrow) HTTPtext;
  521. if (http == NULL)
  522. {
  523. gearmand_error("new");
  524. return GEARMAN_MEMORY_ALLOCATION_FAILURE;
  525. }
  526. gearmand_connection_set_protocol(connection, http);
  527. return GEARMAN_SUCCESS;
  528. }
  529. namespace gearmand {
  530. namespace protocol {
  531. HTTP::HTTP() :
  532. Plugin("HTTP")
  533. {
  534. command_line_options().add_options()
  535. ("http-port", boost::program_options::value(&_port)->default_value(GEARMAN_PROTOCOL_HTTP_DEFAULT_PORT), "Port to listen on.");
  536. }
  537. HTTP::~HTTP()
  538. {
  539. }
  540. gearmand_error_t HTTP::start(gearmand_st *gearmand)
  541. {
  542. gearmand_info("Initializing HTTP");
  543. return gearmand_port_add(gearmand, _port.c_str(), _http_con_add);
  544. }
  545. } // namespace protocol
  546. } // namespace gearmand
  547. /** @} */