1
/* Gearman server and library
2
* Copyright (C) 2008 Brian Aker, Eric Day
5
* Use and distribution licensed under the BSD license. See
6
* the COPYING file in the parent directory for full text.
11
* @brief HTTP Protocol Definitions
14
#include <libgearman-server/common.h>
16
#include <libgearman-server/plugins/protocol/http/protocol.h>
19
* @addtogroup gearmand::protocol::HTTPatic Static HTTP Protocol Definitions
20
* @ingroup gearman_protocol_http
27
#define GEARMAN_PROTOCOL_HTTP_DEFAULT_PORT "8080"
29
#pragma GCC diagnostic ignored "-Wold-style-cast"
31
/* Protocol callback functions. */
32
static gearmand_error_t _http_con_add(gearman_server_con_st *connection);
33
static void _http_free(gearman_server_con_st *connection, void *context);
34
static size_t _http_pack(const gearmand_packet_st *packet, gearman_server_con_st *connection,
35
void *data, size_t data_size,
36
gearmand_error_t *ret_ptr);
37
static size_t _http_unpack(gearmand_packet_st *packet, gearman_server_con_st *connection,
38
const void *data, size_t data_size,
39
gearmand_error_t *ret_ptr);
46
_method(gearmand::protocol::HTTP::TRACE),
50
command_line_options().add_options()
51
("http-port", boost::program_options::value(&global_port)->default_value(GEARMAN_PROTOCOL_HTTP_DEFAULT_PORT), "Port to listen on.");
58
gearmand_error_t HTTP::start(gearmand_st *gearmand)
60
gearmand_info("Initializing HTTP");
61
return gearmand_port_add(gearmand, global_port.c_str(), _http_con_add);
64
} // namespace protocol
65
} // namespace gearmand
67
/* Line parsing helper function. */
68
static const char *_http_line(const void *data, size_t data_size,
69
size_t *line_size, size_t *offset);
82
static gearmand_error_t _http_con_add(gearman_server_con_st *connection)
84
gearmand_info("HTTP connection made");
86
gearmand::protocol::HTTP *http= new (std::nothrow) gearmand::protocol::HTTP;
89
gearmand_error("new");
90
return GEARMAN_MEMORY_ALLOCATION_FAILURE;
93
gearmand_connection_set_protocol(connection, http, _http_free, _http_pack, _http_unpack);
95
return GEARMAN_SUCCESS;
98
static void _http_free(gearman_server_con_st *connection __attribute__ ((unused)),
101
gearmand_info("HTTP connection disconnected");
102
gearmand::protocol::HTTP *http= (gearmand::protocol::HTTP *)context;
106
static size_t _http_pack(const gearmand_packet_st *packet, gearman_server_con_st *connection,
107
void *data, size_t data_size,
108
gearmand_error_t *ret_ptr)
110
gearmand_info("Sending HTTP response");
114
gearmand::protocol::HTTP *http= (gearmand::protocol::HTTP *)gearmand_connection_protocol_context(connection);
116
if (packet->command != GEARMAN_COMMAND_WORK_COMPLETE and
117
packet->command != GEARMAN_COMMAND_WORK_FAIL and
118
packet->command != GEARMAN_COMMAND_ECHO_RES and
119
(http->background() == false or
120
packet->command != GEARMAN_COMMAND_JOB_CREATED))
122
gearmand_info("Sending HTTP told to ignore packet");
123
*ret_ptr= GEARMAN_IGNORE_PACKET;
127
if (http->method() == gearmand::protocol::HTTP::HEAD)
129
pack_size= (size_t)snprintf((char *)data, data_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"
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);
139
else if (http->method() == gearmand::protocol::HTTP::TRACE)
141
pack_size= (size_t)snprintf((char *)data, data_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"
150
pack_size= (size_t)snprintf((char *)data, data_size,
151
"HTTP/1.0 200 OK\r\n"
152
"X-Gearman-Job-Handle: %.*s\r\n"
153
"Content-Length: %"PRIu64"\r\n"
154
"Server: Gearman/" PACKAGE_VERSION "\r\n"
156
packet->command == GEARMAN_COMMAND_JOB_CREATED ? (int)packet->arg_size[0] : (int)packet->arg_size[0] - 1,
157
(const char *)packet->arg[0],
158
(uint64_t)packet->data_size);
161
if (pack_size > data_size)
163
gearmand_info("Sending HTTP had to flush");
164
*ret_ptr= GEARMAN_FLUSH_DATA;
168
if (! (http->keep_alive()))
170
gearman_io_set_option(&connection->con, GEARMAND_CON_CLOSE_AFTER_FLUSH, true);
173
*ret_ptr= GEARMAN_SUCCESS;
177
static size_t _http_unpack(gearmand_packet_st *packet, gearman_server_con_st *connection,
178
const void *data, size_t data_size,
179
gearmand_error_t *ret_ptr)
181
gearmand::protocol::HTTP *http;
185
ptrdiff_t method_size;
190
char content_length[11]; /* 11 bytes to fit max display length of uint32_t */
191
const char *unique= "-";
192
size_t unique_size= 2;
193
gearmand_job_priority_t priority= GEARMAND_JOB_PRIORITY_NORMAL;
195
gearmand_info("Receiving HTTP response");
197
/* Get the request line first. */
198
request= _http_line(data, data_size, &request_size, &offset);
199
if (request == NULL || request_size == 0)
201
gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "Zero length request made");
202
*ret_ptr= GEARMAN_IO_WAIT;
206
http= (gearmand::protocol::HTTP *)gearmand_connection_protocol_context(connection);
209
/* Parse out the method, URI, and HTTP version from the request line. */
210
const char *method= request;
211
const char *uri= (const char *)memchr(request, ' ', request_size);
214
gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad request line: %.*s", (uint32_t)request_size, request);
215
*ret_ptr= GEARMAN_INVALID_PACKET;
219
method_size= uri - request;
221
if (method_size == 3 and strncmp(method, "GET", 3) == 0)
223
http->set_method(gearmand::protocol::HTTP::GET);
225
else if (method_size == 3 and strncmp(method, "PUT", 3) == 0)
227
http->set_method(gearmand::protocol::HTTP::PUT);
229
else if (method_size == 4 and strncmp(method, "POST", 4) == 0)
231
http->set_method(gearmand::protocol::HTTP::POST);
233
else if (method_size == 4 and strncmp(method, "HEAD", 4) == 0)
235
http->set_method(gearmand::protocol::HTTP::HEAD);
237
else if (method_size == 5 and strncmp(method, "TRACE", 5) == 0)
239
http->set_method(gearmand::protocol::HTTP::TRACE);
243
gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad method: %.*s", (uint32_t)method_size, method);
244
*ret_ptr= GEARMAN_INVALID_PACKET;
258
const char *version= (const char *)memchr(uri, ' ', request_size - (size_t)(uri - request));
261
gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad request line: %.*s",
262
(uint32_t)request_size, request);
263
*ret_ptr= GEARMAN_INVALID_PACKET;
267
uri_size= version - uri;
268
switch (http->method())
270
case gearmand::protocol::HTTP::POST:
271
case gearmand::protocol::HTTP::PUT:
272
case gearmand::protocol::HTTP::GET:
275
gearmand_error("must give function name in URI");
276
*ret_ptr= GEARMAN_INVALID_PACKET;
280
case gearmand::protocol::HTTP::TRACE:
281
case gearmand::protocol::HTTP::HEAD:
285
while (*version == ' ')
290
version_size= request_size - (size_t)(version - request);
292
if (version_size == 8 && !strncasecmp(version, "HTTP/1.1", 8))
294
http->set_keep_alive(true);
296
else if (version_size != 8 || strncasecmp(version, "HTTP/1.0", 8))
298
gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad version: %.*s", (uint32_t)version_size, version);
299
*ret_ptr= GEARMAN_INVALID_PACKET;
303
/* Loop through all the headers looking for ones of interest. */
304
while ((header= _http_line(data, data_size, &header_size, &offset)) != NULL)
306
if (header_size == 0)
311
if (header_size > 16 && !strncasecmp(header, "Content-Length: ", 16))
313
if ((method_size == 4 && !strncasecmp(method, "POST", 4)) ||
314
(method_size == 3 && !strncasecmp(method, "PUT", 3)))
316
snprintf(content_length, 11, "%.*s", (int)header_size - 16,
318
packet->data_size= (size_t)atoi(content_length);
321
else if (header_size == 22 &&
322
!strncasecmp(header, "Connection: Keep-Alive", 22))
324
http->set_keep_alive(true);
326
else if (header_size > 18 && !strncasecmp(header, "X-Gearman-Unique: ", 18))
329
unique_size= header_size - 18;
331
else if (header_size == 26 &&
332
!strncasecmp(header, "X-Gearman-Background: true", 26))
334
http->set_background(true);
336
else if (header_size == 24 &&
337
!strncasecmp(header, "X-Gearman-Priority: high", 24))
339
priority= GEARMAND_JOB_PRIORITY_HIGH;
341
else if (header_size == 23 &&
342
!strncasecmp(header, "X-Gearman-Priority: low", 23))
344
priority= GEARMAND_JOB_PRIORITY_LOW;
348
/* Make sure we received the end of headers. */
351
gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "No headers were found");
352
*ret_ptr= GEARMAN_IO_WAIT;
356
/* Request and all headers complete, build a packet based on HTTP request. */
357
packet->magic= GEARMAN_MAGIC_REQUEST;
359
if (http->method() == gearmand::protocol::HTTP::TRACE)
361
packet->command= GEARMAN_COMMAND_ECHO_REQ;
363
*ret_ptr= gearmand_packet_pack_header(packet);
364
if (*ret_ptr != GEARMAN_SUCCESS)
369
packet->data_size= data_size;
370
packet->data= (const char*)data;
372
else if (http->method() == gearmand::protocol::HTTP::HEAD and uri_size == 0)
374
packet->command= GEARMAN_COMMAND_ECHO_REQ;
376
*ret_ptr= gearmand_packet_pack_header(packet);
377
if (*ret_ptr != GEARMAN_SUCCESS)
384
if (http->background())
386
if (priority == GEARMAND_JOB_PRIORITY_NORMAL)
387
packet->command= GEARMAN_COMMAND_SUBMIT_JOB_BG;
388
else if (priority == GEARMAND_JOB_PRIORITY_HIGH)
389
packet->command= GEARMAN_COMMAND_SUBMIT_JOB_HIGH_BG;
391
packet->command= GEARMAN_COMMAND_SUBMIT_JOB_LOW_BG;
395
if (priority == GEARMAND_JOB_PRIORITY_NORMAL)
396
packet->command= GEARMAN_COMMAND_SUBMIT_JOB;
397
else if (priority == GEARMAND_JOB_PRIORITY_HIGH)
398
packet->command= GEARMAN_COMMAND_SUBMIT_JOB_HIGH;
400
packet->command= GEARMAN_COMMAND_SUBMIT_JOB_LOW;
403
*ret_ptr= gearmand_packet_pack_header(packet);
404
if (*ret_ptr != GEARMAN_SUCCESS)
409
*ret_ptr= gearmand_packet_create(packet, uri, (size_t)uri_size + 1);
410
if (*ret_ptr != GEARMAN_SUCCESS)
415
*ret_ptr= gearmand_packet_create(packet, unique, unique_size + 1);
416
if (*ret_ptr != GEARMAN_SUCCESS)
421
/* Make sure function and unique are NULL terminated. */
422
packet->arg[0][uri_size]= 0;
423
packet->arg[1][unique_size]= 0;
425
*ret_ptr= GEARMAN_SUCCESS;
428
gearmand_info("Receiving HTTP response(finished)");
433
static const char *_http_line(const void *data, size_t data_size,
434
size_t *line_size, size_t *offset)
436
const char *start= (const char *)data + *offset;
439
end= (const char *)memchr(start, '\n', data_size - *offset);
443
*offset+= (size_t)(end - start) + 1;
445
if (end != start && *(end - 1) == '\r')
448
*line_size= (size_t)(end - start);