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
16
#include <libgearman/protocol_http.h>
19
* @addtogroup gearman_protocol_http HTTP Protocol Functions
20
* @ingroup gearman_protocol
27
#define GEARMAN_PROTOCOL_HTTP_DEFAULT_PORT 8080
30
* Private declarations
34
* Structure for HTTP specific data.
40
} gearman_protocol_http_st;
42
/* Protocol callback functions. */
43
static gearman_return_t _http_con_add(gearman_con_st *con);
44
static void _http_free(gearman_con_st *con , void *data);
45
static size_t _http_pack(gearman_packet_st *packet, gearman_con_st *con,
46
void *data, size_t data_size,
47
gearman_return_t *ret_ptr);
48
static size_t _http_unpack(gearman_packet_st *packet, gearman_con_st *con,
49
const void *data, size_t data_size,
50
gearman_return_t *ret_ptr);
52
/* Line parsing helper function. */
53
static const char *_http_line(const void *data, size_t data_size,
54
size_t *line_size, size_t *offset);
60
gearman_return_t gearman_protocol_http_conf(gearman_conf_st *conf)
62
gearman_conf_module_st *module;
64
module= gearman_conf_module_create(conf, NULL, "http");
66
return GEARMAN_MEMORY_ALLOCATION_FAILURE;
68
gearman_conf_module_add_option(module, "port", 0, "PORT",
69
"Port to listen on.");
71
return gearman_conf_return(conf);
74
gearman_return_t gearmand_protocol_http_init(gearmand_st *gearmand,
75
gearman_conf_st *conf)
77
in_port_t port= GEARMAN_PROTOCOL_HTTP_DEFAULT_PORT;
78
gearman_conf_module_st *module;
82
GEARMAN_INFO(gearmand, "Initializing http module")
84
/* Get module and parse the option values that were given. */
85
module= gearman_conf_module_find(conf, "http");
88
GEARMAN_FATAL(gearmand,
89
"gearman_protocol_http_init:gearman_conf_module_find:NULL")
90
return GEARMAN_QUEUE_ERROR;
93
while (gearman_conf_module_value(module, &name, &value))
95
if (!strcmp(name, "port"))
96
port= (in_port_t)atoi(value);
99
gearmand_protocol_http_deinit(gearmand);
100
GEARMAN_FATAL(gearmand, "gearman_protocol_http_init:Unknown argument: %s",
102
return GEARMAN_QUEUE_ERROR;
106
return gearmand_port_add(gearmand, port, _http_con_add);
109
gearman_return_t gearmand_protocol_http_deinit(gearmand_st *gearmand __attribute__ ((unused)))
111
return GEARMAN_SUCCESS;
115
* Private definitions
118
static gearman_return_t _http_con_add(gearman_con_st *con)
120
gearman_protocol_http_st *http;
122
http= malloc(sizeof(gearman_protocol_http_st));
125
GEARMAN_ERROR_SET(con->gearman, "_http_con_add", "malloc")
126
return GEARMAN_MEMORY_ALLOCATION_FAILURE;
129
http->background= false;
130
http->keep_alive= false;
132
gearman_con_set_protocol_data(con, http);
133
gearman_con_set_protocol_data_free_fn(con, _http_free);
134
gearman_con_set_packet_pack_fn(con, _http_pack);
135
gearman_con_set_packet_unpack_fn(con, _http_unpack);
137
return GEARMAN_SUCCESS;
140
static void _http_free(gearman_con_st *con __attribute__ ((unused)), void *data)
145
static size_t _http_pack(gearman_packet_st *packet, gearman_con_st *con,
146
void *data, size_t data_size,
147
gearman_return_t *ret_ptr)
150
gearman_protocol_http_st *http;
152
http= (gearman_protocol_http_st *)gearman_con_protocol_data(con);
154
if (packet->command != GEARMAN_COMMAND_WORK_COMPLETE &&
155
packet->command != GEARMAN_COMMAND_WORK_FAIL &&
156
(http->background == false ||
157
packet->command != GEARMAN_COMMAND_JOB_CREATED))
159
*ret_ptr= GEARMAN_IGNORE_PACKET;
163
pack_size= (size_t)snprintf((char *)data, data_size,
164
"HTTP/1.0 200 OK\r\n"
165
"X-Gearman-Job-Handle: %.*s\r\n"
166
"Content-Length: %"PRIu64"\r\n"
167
"Server: Gearman/" PACKAGE_VERSION "\r\n"
169
packet->command == GEARMAN_COMMAND_JOB_CREATED ?
170
(uint32_t)packet->arg_size[0] :
171
(uint32_t)packet->arg_size[0] - 1,
173
(uint64_t)packet->data_size);
175
if (pack_size > data_size)
177
*ret_ptr= GEARMAN_FLUSH_DATA;
181
if (!(http->keep_alive))
182
gearman_con_set_options(con, GEARMAN_CON_CLOSE_AFTER_FLUSH, 1);
184
*ret_ptr= GEARMAN_SUCCESS;
188
static size_t _http_unpack(gearman_packet_st *packet, gearman_con_st *con,
189
const void *data, size_t data_size,
190
gearman_return_t *ret_ptr)
192
gearman_protocol_http_st *http;
197
ptrdiff_t method_size;
204
char content_length[11]; /* 11 bytes to fit max display length of uint32_t */
205
const char *unique= "-";
206
size_t unique_size= 2;
207
gearman_job_priority_t priority= GEARMAN_JOB_PRIORITY_NORMAL;
209
/* Get the request line first. */
210
request= _http_line(data, data_size, &request_size, &offset);
211
if (request == NULL || request_size == 0)
213
*ret_ptr= GEARMAN_IO_WAIT;
217
http= (gearman_protocol_http_st *)gearman_con_protocol_data(con);
218
http->background= false;
219
http->keep_alive= false;
221
/* Parse out the method, URI, and HTTP version from the request line. */
223
uri= memchr(request, ' ', request_size);
226
GEARMAN_ERROR_SET(packet->gearman, "_http_unpack", "bad request line: %.*s",
227
(uint32_t)request_size, request);
228
*ret_ptr= GEARMAN_INVALID_PACKET;
232
method_size= uri - request;
233
if ((method_size != 3 ||
234
(strncasecmp(method, "GET", 3) && strncasecmp(method, "PUT", 3))) &&
235
(method_size != 4 || strncasecmp(method, "POST", 4)))
237
GEARMAN_ERROR_SET(packet->gearman, "_http_unpack", "bad method: %.*s",
238
(uint32_t)method_size, method);
239
*ret_ptr= GEARMAN_INVALID_PACKET;
249
version= memchr(uri, ' ', request_size - (size_t)(uri - request));
252
GEARMAN_ERROR_SET(packet->gearman, "_http_unpack", "bad request line: %.*s",
253
(uint32_t)request_size, request);
254
*ret_ptr= GEARMAN_INVALID_PACKET;
258
uri_size= version - uri;
261
GEARMAN_ERROR_SET(packet->gearman, "_http_unpack",
262
"must give function name in URI")
263
*ret_ptr= GEARMAN_INVALID_PACKET;
267
while (*version == ' ')
270
version_size= request_size - (size_t)(version - request);
272
if (version_size == 8 && !strncasecmp(version, "HTTP/1.1", 8))
273
http->keep_alive= true;
274
else if (version_size != 8 || strncasecmp(version, "HTTP/1.0", 8))
276
GEARMAN_ERROR_SET(packet->gearman, "_http_unpack", "bad version: %.*s",
277
(uint32_t)version_size, version);
278
*ret_ptr= GEARMAN_INVALID_PACKET;
282
/* Loop through all the headers looking for ones of interest. */
283
while ((header= _http_line(data, data_size, &header_size, &offset)) != NULL)
285
if (header_size == 0)
288
if (header_size > 16 && !strncasecmp(header, "Content-Length: ", 16))
290
if ((method_size == 4 && !strncasecmp(method, "POST", 4)) ||
291
(method_size == 3 && !strncasecmp(method, "PUT", 3)))
293
snprintf(content_length, 11, "%.*s", (uint32_t)header_size - 16,
295
packet->data_size= (size_t)atoi(content_length);
298
else if (header_size == 22 &&
299
!strncasecmp(header, "Connection: Keep-Alive", 22))
301
http->keep_alive= true;
303
else if (header_size > 18 && !strncasecmp(header, "X-Gearman-Unique: ", 18))
306
unique_size= header_size - 18;
308
else if (header_size == 26 &&
309
!strncasecmp(header, "X-Gearman-Background: true", 26))
311
http->background= true;
313
else if (header_size == 24 &&
314
!strncasecmp(header, "X-Gearman-Priority: high", 24))
316
priority= GEARMAN_JOB_PRIORITY_HIGH;
318
else if (header_size == 23 &&
319
!strncasecmp(header, "X-Gearman-Priority: low", 23))
321
priority= GEARMAN_JOB_PRIORITY_LOW;
325
/* Make sure we received the end of headers. */
328
*ret_ptr= GEARMAN_IO_WAIT;
332
/* Request and all headers complete, build a packet based on HTTP request. */
333
packet->magic= GEARMAN_MAGIC_REQUEST;
335
if (http->background)
337
if (priority == GEARMAN_JOB_PRIORITY_NORMAL)
338
packet->command= GEARMAN_COMMAND_SUBMIT_JOB_BG;
339
if (priority == GEARMAN_JOB_PRIORITY_HIGH)
340
packet->command= GEARMAN_COMMAND_SUBMIT_JOB_HIGH_BG;
342
packet->command= GEARMAN_COMMAND_SUBMIT_JOB_LOW_BG;
346
if (priority == GEARMAN_JOB_PRIORITY_NORMAL)
347
packet->command= GEARMAN_COMMAND_SUBMIT_JOB;
348
if (priority == GEARMAN_JOB_PRIORITY_HIGH)
349
packet->command= GEARMAN_COMMAND_SUBMIT_JOB_HIGH;
351
packet->command= GEARMAN_COMMAND_SUBMIT_JOB_LOW;
354
*ret_ptr= gearman_packet_pack_header(packet);
355
if (*ret_ptr != GEARMAN_SUCCESS)
358
*ret_ptr= gearman_packet_add_arg(packet, uri, (size_t)uri_size + 1);
359
if (*ret_ptr != GEARMAN_SUCCESS)
362
*ret_ptr= gearman_packet_add_arg(packet, unique, unique_size + 1);
363
if (*ret_ptr != GEARMAN_SUCCESS)
366
/* Make sure function and unique are NULL terminated. */
367
packet->arg[0][uri_size]= 0;
368
packet->arg[1][unique_size]= 0;
370
*ret_ptr= GEARMAN_SUCCESS;
374
static const char *_http_line(const void *data, size_t data_size,
375
size_t *line_size, size_t *offset)
377
const char *start= (const char *)data + *offset;
380
end= memchr(start, '\n', data_size - *offset);
384
*offset+= (size_t)(end - start) + 1;
386
if (end != start && *(end - 1) == '\r')
389
*line_size= (size_t)(end - start);