~clint-fewbar/ubuntu/precise/gearmand/drop-unneeded-patches

« back to all changes in this revision

Viewing changes to libgearman-server/plugins/protocol/http/protocol.cc

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2009-09-28 21:43:31 UTC
  • mto: (1.2.3 upstream) (6.1.1 sid)
  • mto: This revision was merged to the branch mainline in revision 5.
  • Revision ID: james.westby@ubuntu.com-20090928214331-9bku0d3v1b1ypgp4
ImportĀ upstreamĀ versionĀ 0.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Gearman server and library
2
 
 * Copyright (C) 2008 Brian Aker, Eric Day
3
 
 * All rights reserved.
4
 
 *
5
 
 * Use and distribution licensed under the BSD license.  See
6
 
 * the COPYING file in the parent directory for full text.
7
 
 */
8
 
 
9
 
/**
10
 
 * @file
11
 
 * @brief HTTP Protocol Definitions
12
 
 */
13
 
 
14
 
#include <libgearman-server/common.h>
15
 
 
16
 
#include <libgearman-server/plugins/protocol/http/protocol.h>
17
 
 
18
 
/**
19
 
 * @addtogroup gearmand::protocol::HTTPatic Static HTTP Protocol Definitions
20
 
 * @ingroup gearman_protocol_http
21
 
 * @{
22
 
 */
23
 
 
24
 
/**
25
 
 * Default values.
26
 
 */
27
 
#define GEARMAN_PROTOCOL_HTTP_DEFAULT_PORT "8080"
28
 
 
29
 
#pragma GCC diagnostic ignored "-Wold-style-cast"
30
 
 
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);
40
 
 
41
 
namespace gearmand {
42
 
namespace protocol {
43
 
 
44
 
HTTP::HTTP() :
45
 
  Plugin("HTTP"),
46
 
  _method(gearmand::protocol::HTTP::TRACE),
47
 
  _background(false),
48
 
  _keep_alive(false)
49
 
{
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.");
52
 
}
53
 
 
54
 
HTTP::~HTTP()
55
 
{
56
 
}
57
 
 
58
 
gearmand_error_t HTTP::start(gearmand_st *gearmand)
59
 
{
60
 
  gearmand_info("Initializing HTTP");
61
 
  return gearmand_port_add(gearmand, global_port.c_str(), _http_con_add);
62
 
}
63
 
 
64
 
} // namespace protocol
65
 
} // namespace gearmand
66
 
 
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);
70
 
 
71
 
/** @} */
72
 
 
73
 
/*
74
 
 * Public definitions
75
 
 */
76
 
 
77
 
 
78
 
/*
79
 
 * Static definitions
80
 
 */
81
 
 
82
 
static gearmand_error_t _http_con_add(gearman_server_con_st *connection)
83
 
{
84
 
  gearmand_info("HTTP connection made");
85
 
 
86
 
  gearmand::protocol::HTTP *http= new (std::nothrow) gearmand::protocol::HTTP;
87
 
  if (http == NULL)
88
 
  {
89
 
    gearmand_error("new");
90
 
    return GEARMAN_MEMORY_ALLOCATION_FAILURE;
91
 
  }
92
 
 
93
 
  gearmand_connection_set_protocol(connection, http, _http_free, _http_pack, _http_unpack);
94
 
 
95
 
  return GEARMAN_SUCCESS;
96
 
}
97
 
 
98
 
static void _http_free(gearman_server_con_st *connection __attribute__ ((unused)),
99
 
                       void *context)
100
 
{
101
 
  gearmand_info("HTTP connection disconnected");
102
 
  gearmand::protocol::HTTP *http= (gearmand::protocol::HTTP *)context;
103
 
  delete http;
104
 
}
105
 
 
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)
109
 
{
110
 
  gearmand_info("Sending HTTP response");
111
 
 
112
 
  size_t pack_size;
113
 
 
114
 
  gearmand::protocol::HTTP *http= (gearmand::protocol::HTTP *)gearmand_connection_protocol_context(connection);
115
 
 
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))
121
 
  {
122
 
    gearmand_info("Sending HTTP told to ignore packet");
123
 
    *ret_ptr= GEARMAN_IGNORE_PACKET;
124
 
    return 0;
125
 
  }
126
 
 
127
 
  if (http->method() == gearmand::protocol::HTTP::HEAD)
128
 
  {
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"
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 (http->method() == gearmand::protocol::HTTP::TRACE)
140
 
  {
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"
146
 
                                "\r\n");
147
 
  }
148
 
  else
149
 
  {
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"
155
 
                                "\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);
159
 
  }
160
 
 
161
 
  if (pack_size > data_size)
162
 
  {
163
 
    gearmand_info("Sending HTTP had to flush");
164
 
    *ret_ptr= GEARMAN_FLUSH_DATA;
165
 
    return 0;
166
 
  }
167
 
 
168
 
  if (! (http->keep_alive()))
169
 
  {
170
 
    gearman_io_set_option(&connection->con, GEARMAND_CON_CLOSE_AFTER_FLUSH, true);
171
 
  }
172
 
 
173
 
  *ret_ptr= GEARMAN_SUCCESS;
174
 
  return pack_size;
175
 
}
176
 
 
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)
180
 
{
181
 
  gearmand::protocol::HTTP *http;
182
 
  size_t offset= 0;
183
 
  const char *request;
184
 
  size_t request_size;
185
 
  ptrdiff_t method_size;
186
 
  ptrdiff_t uri_size;
187
 
  size_t version_size;
188
 
  const char *header;
189
 
  size_t header_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;
194
 
 
195
 
  gearmand_info("Receiving HTTP response");
196
 
 
197
 
  /* Get the request line first. */
198
 
  request= _http_line(data, data_size, &request_size, &offset);
199
 
  if (request == NULL || request_size == 0)
200
 
  {
201
 
    gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "Zero length request made");
202
 
    *ret_ptr= GEARMAN_IO_WAIT;
203
 
    return offset;
204
 
  }
205
 
 
206
 
  http= (gearmand::protocol::HTTP *)gearmand_connection_protocol_context(connection);
207
 
  http->reset();
208
 
 
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);
212
 
  if (uri == NULL)
213
 
  {
214
 
    gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad request line: %.*s", (uint32_t)request_size, request);
215
 
    *ret_ptr= GEARMAN_INVALID_PACKET;
216
 
    return 0;
217
 
  }
218
 
 
219
 
  method_size= uri - request;
220
 
 
221
 
  if (method_size == 3 and strncmp(method, "GET", 3) == 0)
222
 
  {
223
 
    http->set_method(gearmand::protocol::HTTP::GET);
224
 
  }
225
 
  else if (method_size == 3 and strncmp(method, "PUT", 3) == 0)
226
 
  {
227
 
    http->set_method(gearmand::protocol::HTTP::PUT);
228
 
  }
229
 
  else if (method_size == 4 and strncmp(method, "POST", 4) == 0)
230
 
  {
231
 
    http->set_method(gearmand::protocol::HTTP::POST);
232
 
  }
233
 
  else if (method_size == 4 and strncmp(method, "HEAD", 4) == 0)
234
 
  {
235
 
    http->set_method(gearmand::protocol::HTTP::HEAD);
236
 
  }
237
 
  else if (method_size == 5 and strncmp(method, "TRACE", 5) == 0)
238
 
  {
239
 
    http->set_method(gearmand::protocol::HTTP::TRACE);
240
 
  }
241
 
  else
242
 
  {
243
 
    gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad method: %.*s", (uint32_t)method_size, method);
244
 
    *ret_ptr= GEARMAN_INVALID_PACKET;
245
 
    return 0;
246
 
  }
247
 
 
248
 
  while (*uri == ' ')
249
 
  {
250
 
    uri++;
251
 
  }
252
 
 
253
 
  while (*uri == '/')
254
 
  {
255
 
    uri++;
256
 
  }
257
 
 
258
 
  const char *version= (const char *)memchr(uri, ' ', request_size - (size_t)(uri - request));
259
 
  if (version == NULL)
260
 
  {
261
 
    gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad request line: %.*s",
262
 
                      (uint32_t)request_size, request);
263
 
    *ret_ptr= GEARMAN_INVALID_PACKET;
264
 
    return 0;
265
 
  }
266
 
 
267
 
  uri_size= version - uri;
268
 
  switch (http->method())
269
 
  {
270
 
  case gearmand::protocol::HTTP::POST:
271
 
  case gearmand::protocol::HTTP::PUT:
272
 
  case gearmand::protocol::HTTP::GET:
273
 
    if (uri_size == 0)
274
 
    {
275
 
      gearmand_error("must give function name in URI");
276
 
      *ret_ptr= GEARMAN_INVALID_PACKET;
277
 
      return 0;
278
 
    }
279
 
 
280
 
  case gearmand::protocol::HTTP::TRACE:
281
 
  case gearmand::protocol::HTTP::HEAD:
282
 
    break;
283
 
  }
284
 
 
285
 
  while (*version == ' ')
286
 
  {
287
 
    version++;
288
 
  }
289
 
 
290
 
  version_size= request_size - (size_t)(version - request);
291
 
 
292
 
  if (version_size == 8 && !strncasecmp(version, "HTTP/1.1", 8))
293
 
  {
294
 
    http->set_keep_alive(true);
295
 
  }
296
 
  else if (version_size != 8 || strncasecmp(version, "HTTP/1.0", 8))
297
 
  {
298
 
    gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "bad version: %.*s", (uint32_t)version_size, version);
299
 
    *ret_ptr= GEARMAN_INVALID_PACKET;
300
 
    return 0;
301
 
  }
302
 
 
303
 
  /* Loop through all the headers looking for ones of interest. */
304
 
  while ((header= _http_line(data, data_size, &header_size, &offset)) != NULL)
305
 
  {
306
 
    if (header_size == 0)
307
 
    {
308
 
      break;
309
 
    }
310
 
 
311
 
    if (header_size > 16 && !strncasecmp(header, "Content-Length: ", 16))
312
 
    {
313
 
      if ((method_size == 4 && !strncasecmp(method, "POST", 4)) ||
314
 
          (method_size == 3 && !strncasecmp(method, "PUT", 3)))
315
 
      {
316
 
        snprintf(content_length, 11, "%.*s", (int)header_size - 16,
317
 
                 header + 16);
318
 
        packet->data_size= (size_t)atoi(content_length);
319
 
      }
320
 
    }
321
 
    else if (header_size == 22 &&
322
 
             !strncasecmp(header, "Connection: Keep-Alive", 22))
323
 
    {
324
 
      http->set_keep_alive(true);
325
 
    }
326
 
    else if (header_size > 18 && !strncasecmp(header, "X-Gearman-Unique: ", 18))
327
 
    {
328
 
      unique= header + 18;
329
 
      unique_size= header_size - 18;
330
 
    }
331
 
    else if (header_size == 26 &&
332
 
             !strncasecmp(header, "X-Gearman-Background: true", 26))
333
 
    {
334
 
      http->set_background(true);
335
 
    }
336
 
    else if (header_size == 24 &&
337
 
             !strncasecmp(header, "X-Gearman-Priority: high", 24))
338
 
    {
339
 
      priority= GEARMAND_JOB_PRIORITY_HIGH;
340
 
    }
341
 
    else if (header_size == 23 &&
342
 
             !strncasecmp(header, "X-Gearman-Priority: low", 23))
343
 
    {
344
 
      priority= GEARMAND_JOB_PRIORITY_LOW;
345
 
    }
346
 
  }
347
 
 
348
 
  /* Make sure we received the end of headers. */
349
 
  if (header == NULL)
350
 
  {
351
 
    gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "No headers were found");
352
 
    *ret_ptr= GEARMAN_IO_WAIT;
353
 
    return 0;
354
 
  }
355
 
 
356
 
  /* Request and all headers complete, build a packet based on HTTP request. */
357
 
  packet->magic= GEARMAN_MAGIC_REQUEST;
358
 
 
359
 
  if (http->method() == gearmand::protocol::HTTP::TRACE)
360
 
  {
361
 
    packet->command= GEARMAN_COMMAND_ECHO_REQ;
362
 
 
363
 
    *ret_ptr= gearmand_packet_pack_header(packet);
364
 
    if (*ret_ptr != GEARMAN_SUCCESS)
365
 
    {
366
 
      return 0;
367
 
    }
368
 
 
369
 
    packet->data_size= data_size;
370
 
    packet->data= (const char*)data;
371
 
  }
372
 
  else if (http->method() == gearmand::protocol::HTTP::HEAD and uri_size == 0)
373
 
  {
374
 
    packet->command= GEARMAN_COMMAND_ECHO_REQ;
375
 
 
376
 
    *ret_ptr= gearmand_packet_pack_header(packet);
377
 
    if (*ret_ptr != GEARMAN_SUCCESS)
378
 
    {
379
 
      return 0;
380
 
    }
381
 
  }
382
 
  else
383
 
  {
384
 
    if (http->background())
385
 
    {
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;
390
 
      else
391
 
        packet->command= GEARMAN_COMMAND_SUBMIT_JOB_LOW_BG;
392
 
    }
393
 
    else
394
 
    {
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;
399
 
      else
400
 
        packet->command= GEARMAN_COMMAND_SUBMIT_JOB_LOW;
401
 
    }
402
 
 
403
 
    *ret_ptr= gearmand_packet_pack_header(packet);
404
 
    if (*ret_ptr != GEARMAN_SUCCESS)
405
 
    {
406
 
      return 0;
407
 
    }
408
 
 
409
 
    *ret_ptr= gearmand_packet_create(packet, uri, (size_t)uri_size + 1);
410
 
    if (*ret_ptr != GEARMAN_SUCCESS)
411
 
    {
412
 
      return 0;
413
 
    }
414
 
 
415
 
    *ret_ptr= gearmand_packet_create(packet, unique, unique_size + 1);
416
 
    if (*ret_ptr != GEARMAN_SUCCESS)
417
 
    {
418
 
      return 0;
419
 
    }
420
 
 
421
 
    /* Make sure function and unique are NULL terminated. */
422
 
    packet->arg[0][uri_size]= 0;
423
 
    packet->arg[1][unique_size]= 0;
424
 
 
425
 
    *ret_ptr= GEARMAN_SUCCESS;
426
 
  }
427
 
 
428
 
  gearmand_info("Receiving HTTP response(finished)");
429
 
 
430
 
  return offset;
431
 
}
432
 
 
433
 
static const char *_http_line(const void *data, size_t data_size,
434
 
                              size_t *line_size, size_t *offset)
435
 
{
436
 
  const char *start= (const char *)data + *offset;
437
 
  const char *end;
438
 
 
439
 
  end= (const char *)memchr(start, '\n', data_size - *offset);
440
 
  if (end == NULL)
441
 
    return NULL;
442
 
 
443
 
  *offset+= (size_t)(end - start) + 1;
444
 
 
445
 
  if (end != start && *(end - 1) == '\r')
446
 
    end--;
447
 
 
448
 
  *line_size= (size_t)(end - start);
449
 
 
450
 
  return start;
451
 
}