root/src/noit_http.c

Revision ea766cfca8daf3326b219587338a3b25b87de8e4, 37.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

complete the request once we transition into PAYLOAD state

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34 #include "noit_http.h"
35 #include "utils/noit_str.h"
36
37 #include <errno.h>
38 #include <ctype.h>
39 #include <assert.h>
40 #include <zlib.h>
41 #include <libxml/tree.h>
42
43 #define REQ_PAT "\r\n\r\n"
44 #define REQ_PATSIZE 4
45 #define HEADER_CONTENT_LENGTH "content-length"
46 #define HEADER_EXPECT "expect"
47
48 static noit_log_stream_t http_debug = NULL;
49 static noit_log_stream_t http_io = NULL;
50 static noit_log_stream_t http_access = NULL;
51
52 #define CTX_ADD_HEADER(a,b) \
53     noit_hash_replace(&ctx->res.headers, \
54                       strdup(a), strlen(a), strdup(b), free, free)
55 static const char _hexchars[16] =
56   {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
57 static void inplace_urldecode(char *c) {
58   char *o = c;
59   while(*c) {
60     if(*c == '%') {
61       int i, ord = 0;
62       for(i = 0; i < 2; i++) {
63         if(c[i] >= '0' && c[i] <= '9') ord = (ord << 4) | (c[i] - '0');
64         else if (c[i] >= 'a' && c[i] <= 'f') ord = (ord << 4) | (c[i] - 'a');
65         else if (c[i] >= 'A' && c[i] <= 'F') ord = (ord << 4) | (c[i] - 'A');
66         else break;
67       }
68       if(i==2) {
69         *((unsigned char *)o++) = ord;
70         c+=3;
71         continue;
72       }
73     }
74     *o++ = *c++;
75   }
76   *o = '\0';
77 }
78
79 struct bchain *bchain_alloc(size_t size, int line) {
80   struct bchain *n;
81   n = malloc(size + (int)((char *)((struct bchain *)0)->buff));
82   /*noitL(noit_error, "bchain_alloc(%p) : %d\n", n, line);*/
83   if(!n) return NULL;
84   n->prev = n->next = NULL;
85   n->start = n->size = 0;
86   n->allocd = size;
87   return n;
88 }
89 void bchain_free(struct bchain *b, int line) {
90   /*noitL(noit_error, "bchain_free(%p) : %d\n", b, line);*/
91   free(b);
92 }
93 #define ALLOC_BCHAIN(s) bchain_alloc(s, __LINE__)
94 #define FREE_BCHAIN(a) bchain_free(a, __LINE__)
95 #define RELEASE_BCHAIN(a) do { \
96   while(a) { \
97     struct bchain *__b; \
98     __b = a; \
99     a = __b->next; \
100     bchain_free(__b, __LINE__); \
101   } \
102 } while(0)
103 struct bchain *bchain_from_data(const void *d, size_t size) {
104   struct bchain *n;
105   n = ALLOC_BCHAIN(size);
106   if(!n) return NULL;
107   memcpy(n->buff, d, size);
108   n->size = size;
109   return n;
110 }
111
112 static noit_http_method
113 _method_enum(const char *s) {
114   switch(*s) {
115    case 'G':
116     if(!strcasecmp(s, "GET")) return NOIT_HTTP_GET;
117     break;
118    case 'H':
119     if(!strcasecmp(s, "HEAD")) return NOIT_HTTP_HEAD;
120     break;
121    case 'P':
122     if(!strcasecmp(s, "POST")) return NOIT_HTTP_POST;
123     break;
124    default:
125     break;
126   }
127   return NOIT_HTTP_OTHER;
128 }
129 static noit_http_protocol
130 _protocol_enum(const char *s) {
131   if(!strcasecmp(s, "HTTP/1.1")) return NOIT_HTTP11;
132   if(!strcasecmp(s, "HTTP/1.0")) return NOIT_HTTP10;
133   return NOIT_HTTP09;
134 }
135 static noit_boolean
136 _fixup_bchain(struct bchain *b) {
137   /* make sure lines (CRLF terminated) don't cross chain boundaries */
138   while(b) {
139     struct bchain *f;
140     int start_in_b, end_in_f;
141     size_t new_size;
142     const char *str_in_f;
143
144     start_in_b = b->start;
145     if(b->size > 2) {
146       if(memcmp(b->buff + b->start + b->size - 2, "\r\n", 2) == 0) {
147         b = b->next;
148         continue;
149       }
150       start_in_b = b->start + b->size - 3; /* we already checked -2 */
151       while(start_in_b >= b->start) {
152         if(b->buff[start_in_b] == '\r' && b->buff[start_in_b+1] == '\n') {
153           start_in_b += 2;
154           break;
155         }
156         start_in_b--;
157       }
158     }
159
160     /* start_in_b points to the beginning of the string we need to build
161      * into a new buffer.
162      */
163     f = b->next;
164     if(!f) return noit_false; /* Nothing left, can't complete the line */
165     str_in_f = strnstrn("\r\n", 2, f->buff + f->start, f->size);
166     if(!str_in_f) return noit_false; /* nothing in next chain -- too long */
167     str_in_f += 2;
168     end_in_f = (str_in_f - f->buff - f->start);
169     new_size = end_in_f + (b->start + b->size - start_in_b);
170     if(new_size > DEFAULT_BCHAINSIZE) return noit_false; /* string too long */
171     f = ALLOC_BCHAIN(new_size);
172     f->prev = b;
173     f->next = b->next;
174     f->start = 0;
175     f->size = new_size;
176     memcpy(f->buff, b->buff + start_in_b, b->start + b->size - start_in_b);
177     memcpy(f->buff + b->start + b->size - start_in_b,
178            f->buff + f->start, end_in_f);
179     f->next->prev = f;
180     f->prev->next = f;
181     f->prev->size -= start_in_b - b->start;
182     f->next->size -= end_in_f;
183     f->next->start += end_in_f;
184     b = f->next; /* skip f, we know it is right */
185   }
186   return noit_true;
187 }
188 static noit_boolean
189 _extract_header(char *l, const char **n, const char **v) {
190   *n = NULL;
191   if(*l == ' ' || *l == '\t') {
192     while(*l == ' ' || *l == '\t') l++;
193     *v = l;
194     return noit_true;
195   }
196   *n = l;
197   while(*l != ':' && *l) { *l = tolower(*l); l++; }
198   if(!*l) return noit_false;
199   *v = l+1;
200   /* Right trim the name */
201   *l-- = '\0';
202   while(*l == ' ' || *l == '\t') *l-- = '\0';
203   while(**v == ' ' || **v == '\t') (*v)++;
204   return noit_true;
205 }
206 static void
207 noit_http_log_request(noit_http_session_ctx *ctx) {
208   char ip[64], timestr[64];
209   double time_ms;
210   struct tm *tm, tbuf;
211   time_t now;
212   struct timeval end_time, diff;
213
214   if(ctx->req.start_time.tv_sec == 0) return;
215   gettimeofday(&end_time, NULL);
216   now = end_time.tv_sec;
217   tm = gmtime_r(&now, &tbuf);
218   strftime(timestr, sizeof(timestr), "%d/%b/%Y:%H:%M:%S -0000", tm);
219   sub_timeval(end_time, ctx->req.start_time, &diff);
220   time_ms = diff.tv_sec * 1000 + diff.tv_usec / 1000;
221   noit_convert_sockaddr_to_buff(ip, sizeof(ip), &ctx->ac->remote.remote_addr);
222   noitL(http_access, "%s - - [%s] \"%s %s%s%s %s\" %d %llu %.3f\n",
223         ip, timestr,
224         ctx->req.method_str, ctx->req.uri_str,
225         ctx->req.orig_qs ? "?" : "", ctx->req.orig_qs ? ctx->req.orig_qs : "",
226         ctx->req.protocol_str,
227         ctx->res.status_code,
228         (long long unsigned)ctx->res.bytes_written,
229         time_ms);
230 }
231
232 static int
233 _http_perform_write(noit_http_session_ctx *ctx, int *mask) {
234   int len, tlen = 0;
235   struct bchain **head, *b;
236  choose_bucket:
237   head = ctx->res.leader ? &ctx->res.leader : &ctx->res.output_raw;
238   b = *head;
239
240   if(!ctx->conn.e) return 0;
241 #if 0
242   if(ctx->res.output_started == noit_false) return EVENTER_EXCEPTION;
243 #endif
244   if(!b) {
245     if(ctx->res.closed) ctx->res.complete = noit_true;
246     *mask = EVENTER_EXCEPTION;
247     return tlen;
248   }
249
250   if(ctx->res.output_raw_offset >= b->size) {
251     *head = b->next;
252     FREE_BCHAIN(b);
253     b = *head;
254     if(b) b->prev = NULL;
255     ctx->res.output_raw_offset = 0;
256     goto choose_bucket;
257   }
258
259   len = ctx->conn.e->opset->
260           write(ctx->conn.e->fd,
261                 b->buff + b->start + ctx->res.output_raw_offset,
262                 b->size - ctx->res.output_raw_offset,
263                 mask, ctx->conn.e);
264   if(len == -1 && errno == EAGAIN) {
265     *mask |= EVENTER_EXCEPTION;
266     return tlen;
267   }
268   if(len == -1) {
269     /* socket error */
270     ctx->res.complete = noit_true;
271     ctx->conn.needs_close = noit_true;
272     noit_http_log_request(ctx);
273     *mask |= EVENTER_EXCEPTION;
274     return -1;
275   }
276   noitL(http_io, " http_write(%d) => %d [\n%.*s\n]\n", ctx->conn.e->fd,
277         len, len, b->buff + b->start + ctx->res.output_raw_offset);
278   ctx->res.output_raw_offset += len;
279   ctx->res.bytes_written += len;
280   tlen += len;
281   goto choose_bucket;
282 }
283 static noit_boolean
284 noit_http_request_finalize_headers(noit_http_request *req, noit_boolean *err) {
285   int start;
286   void *vval;
287   const char *mstr, *last_name = NULL;
288   struct bchain *b;
289
290   if(req->state != NOIT_HTTP_REQ_HEADERS) return noit_false;
291   if(!req->current_input) req->current_input = req->first_input;
292   if(!req->current_input) return noit_false;
293   if(req->start_time.tv_sec == 0) gettimeofday(&req->start_time, NULL);
294  restart:
295   while(req->current_input->prev &&
296         (req->current_offset < (req->current_input->start + REQ_PATSIZE - 1))) {
297     int inset;
298     /* cross bucket */
299     if(req->current_input == req->last_input &&
300        req->current_offset >= (req->last_input->start + req->last_input->size))
301       return noit_false;
302     req->current_offset++;
303     inset = req->current_offset - req->current_input->start;
304     if(memcmp(req->current_input->buff + req->current_input->start,
305               REQ_PAT + (REQ_PATSIZE - inset), inset) == 0 &&
306        memcmp(req->current_input->prev->buff +
307                 req->current_input->prev->start +
308                 req->current_input->prev->size - REQ_PATSIZE + inset,
309               REQ_PAT + inset,
310               REQ_PATSIZE - inset) == 0) goto match;
311   }
312   start = MAX(req->current_offset - REQ_PATSIZE, req->current_input->start);
313   mstr = strnstrn(REQ_PAT, REQ_PATSIZE,
314                   req->current_input->buff + start,
315                   req->current_input->size -
316                     (start - req->current_input->start));
317   if(!mstr && req->current_input->next) {
318     req->current_input = req->current_input->next;
319     req->current_offset = req->current_input->start;
320     goto restart;
321   }
322   if(!mstr) return noit_false;
323   req->current_offset = mstr - req->current_input->buff + REQ_PATSIZE;
324  match:
325   req->current_request_chain = req->first_input;
326   noitL(http_debug, " noit_http_request_finalize : match(%d in %d)\n",
327         (int)(req->current_offset - req->current_input->start),
328         (int)req->current_input->size);
329   if(req->current_offset <
330      req->current_input->start + req->current_input->size) {
331     /* There are left-overs */
332     int lsize = req->current_input->size - req->current_offset;
333     noitL(http_debug, " noit_http_request_finalize -- leftovers: %d\n", lsize);
334     req->first_input = ALLOC_BCHAIN(lsize);
335     req->first_input->prev = NULL;
336     req->first_input->next = req->current_input->next;
337     req->first_input->start = 0;
338     req->first_input->size = lsize;
339     memcpy(req->first_input->buff,
340            req->current_input->buff + req->current_offset,
341            req->first_input->size);
342     req->current_input->size -= lsize;
343     if(req->last_input == req->current_input)
344       req->last_input = req->first_input;
345     else
346       FREE_BCHAIN(req->current_input);
347   }
348   else {
349     req->first_input = req->last_input = NULL;
350   }
351   req->current_input = NULL;
352   req->current_offset = 0;
353
354   /* Now we need to dissect the current_request_chain into an HTTP request */
355   /* First step: make sure that no line crosses a chain boundary by
356    * inserting new chains as necessary.
357    */
358   if(!_fixup_bchain(req->current_request_chain)) {
359     *err = noit_true;
360     return noit_false;
361   }
362   /* Second step is to parse out the request itself */
363   for(b = req->current_request_chain; b; b = b->next) {
364     char *curr_str, *next_str;
365     b->buff[b->start + b->size - 2] = '\0';
366     curr_str = b->buff + b->start;
367     do {
368       next_str = strstr(curr_str, "\r\n");
369       if(next_str) {
370         *((char *)next_str) = '\0';
371         next_str += 2;
372       }
373       if(req->method_str && *curr_str == '\0')
374         break; /* our CRLFCRLF... end of req */
375 #define FAIL do { *err = noit_true; return noit_false; } while(0)
376       if(!req->method_str) { /* request line */
377         req->method_str = (char *)curr_str;
378         req->uri_str = strchr(curr_str, ' ');
379         if(!req->uri_str) FAIL;
380         *(req->uri_str) = '\0';
381         req->uri_str++;
382         req->protocol_str = strchr(req->uri_str, ' ');
383         if(!req->protocol_str) FAIL;
384         *(req->protocol_str) = '\0';
385         req->protocol_str++;
386         req->method = _method_enum(req->method_str);
387         req->protocol = _protocol_enum(req->protocol_str);
388         req->opts |= NOIT_HTTP_CLOSE;
389         if(req->protocol == NOIT_HTTP11) req->opts |= NOIT_HTTP_CHUNKED;
390       }
391       else { /* request headers */
392         const char *name, *value;
393         if(_extract_header(curr_str, &name, &value) == noit_false) FAIL;
394         if(!name && !last_name) FAIL;
395         if(!strcmp(name ? name : last_name, "accept-encoding")) {
396           if(strstr(value, "gzip")) req->opts |= NOIT_HTTP_GZIP;
397           if(strstr(value, "deflate")) req->opts |= NOIT_HTTP_DEFLATE;
398         }
399         if(name)
400           noit_hash_replace(&req->headers, name, strlen(name), (void *)value,
401                             NULL, NULL);
402         else {
403           struct bchain *b;
404           const char *prefix = NULL;
405           int l1, l2;
406           noit_hash_retr_str(&req->headers, last_name, strlen(last_name),
407                              &prefix);
408           if(!prefix) FAIL;
409           l1 = strlen(prefix);
410           l2 = strlen(value);
411           b = ALLOC_BCHAIN(l1 + l2 + 2);
412           b->next = req->current_request_chain;
413           b->next->prev = b;
414           req->current_request_chain = b;
415           b->size = l1 + l2 + 2;
416           memcpy(b->buff, prefix, l1);
417           b->buff[l1] = ' ';
418           memcpy(b->buff + l1 + 1, value, l2);
419           b->buff[l1 + 1 + l2] = '\0';
420           noit_hash_replace(&req->headers, last_name, strlen(last_name),
421                             b->buff, NULL, NULL);
422         }
423         if(name) last_name = name;
424       }
425       curr_str = next_str;
426     } while(next_str);
427   }
428
429   /* headers are done... we could need to read a payload */
430   if(noit_hash_retrieve(&req->headers,
431                         HEADER_CONTENT_LENGTH,
432                         sizeof(HEADER_CONTENT_LENGTH)-1, &vval)) {
433     const char *val = vval;
434     req->has_payload = noit_true;
435     req->content_length = strtoll(val, NULL, 10);
436   }
437   if(noit_hash_retrieve(&req->headers, HEADER_EXPECT,
438                         sizeof(HEADER_EXPECT)-1, &vval)) {
439     const char *val = vval;
440     if(strncmp(val, "100-", 4) || /* Bad expect header */
441        req->has_payload == noit_false) /* expect, but no content length */
442       FAIL;
443     /* We need to tell the client to "go-ahead" -- HTTP sucks */
444     req->state = NOIT_HTTP_REQ_EXPECT;
445     return noit_false;
446   }
447   if(req->content_length > 0) {
448     /* switch modes... let's go read the payload */
449     req->state = NOIT_HTTP_REQ_PAYLOAD;
450     return noit_false;
451   }
452
453   req->complete = noit_true;
454   return noit_true;
455 }
456 void
457 noit_http_process_querystring(noit_http_request *req) {
458   char *cp, *interest, *brk;
459   cp = strchr(req->uri_str, '?');
460   if(!cp) return;
461   *cp++ = '\0';
462   req->orig_qs = strdup(cp);
463   for (interest = strtok_r(cp, "&", &brk);
464        interest;
465        interest = strtok_r(NULL, "&", &brk)) {
466     char *eq;
467     eq = strchr(interest, '=');
468     if(!eq) {
469       inplace_urldecode(interest);
470       noit_hash_store(&req->querystring, interest, strlen(interest), NULL);
471     }
472     else {
473       *eq++ = '\0';
474       inplace_urldecode(interest);
475       inplace_urldecode(eq);
476       noit_hash_store(&req->querystring, interest, strlen(interest), eq);
477     }
478   }
479 }
480 static noit_boolean
481 noit_http_request_finalize_payload(noit_http_request *req, noit_boolean *err) {
482   req->complete = noit_true;
483   return noit_true;
484 }
485 static noit_boolean
486 noit_http_request_finalize(noit_http_request *req, noit_boolean *err) {
487   if(req->state == NOIT_HTTP_REQ_HEADERS)
488     if(noit_http_request_finalize_headers(req, err)) return noit_true;
489   if(req->state == NOIT_HTTP_REQ_EXPECT) return noit_false;
490   if(req->state == NOIT_HTTP_REQ_PAYLOAD)
491     if(noit_http_request_finalize_payload(req, err)) return noit_true;
492   return noit_false;
493 }
494 static int
495 noit_http_complete_request(noit_http_session_ctx *ctx, int mask) {
496   struct bchain *in;
497   noit_boolean rv, err = noit_false;
498
499   if(mask & EVENTER_EXCEPTION) {
500    full_error:
501     ctx->conn.e->opset->close(ctx->conn.e->fd, &mask, ctx->conn.e);
502     ctx->conn.e = NULL;
503     return 0;
504   }
505   if(ctx->req.complete == noit_true) return EVENTER_EXCEPTION;
506
507   /* We could have a complete request in the tail of a previous request */
508   rv = noit_http_request_finalize(&ctx->req, &err);
509   if(rv == noit_true) return EVENTER_WRITE | EVENTER_EXCEPTION;
510   if(err == noit_true) goto full_error;
511
512   while(1) {
513     int len;
514
515     in = ctx->req.last_input;
516     if(!in) {
517       in = ctx->req.first_input = ctx->req.last_input =
518         ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
519       if(!in) goto full_error;
520     }
521     if(in->size > 0 && /* we've read something */
522        DEFAULT_BCHAINMINREAD > BCHAIN_SPACE(in) && /* we'd like read more */
523        DEFAULT_BCHAINMINREAD < DEFAULT_BCHAINSIZE) { /* and we can */
524       in->next = ctx->req.last_input =
525         ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
526       in->next->prev = in;
527       in = in->next;
528       if(!in) goto full_error;
529     }
530
531     len = ctx->conn.e->opset->read(ctx->conn.e->fd,
532                                    in->buff + in->start + in->size,
533                                    in->allocd - in->size - in->start,
534                                    &mask, ctx->conn.e);
535     noitL(http_debug, " noit_http -> read(%d) = %d\n", ctx->conn.e->fd, len);
536     noitL(http_io, " noit_http:read(%d) => %d [\n%.*s\n]\n", ctx->conn.e->fd, len, len, in->buff + in->start + in->size);
537     if(len == -1 && errno == EAGAIN) return mask;
538     if(len <= 0) goto full_error;
539     if(len > 0) in->size += len;
540     rv = noit_http_request_finalize(&ctx->req, &err);
541     if(len == -1 || err == noit_true) goto full_error;
542     if(ctx->req.state == NOIT_HTTP_REQ_EXPECT) {
543       const char *expect;
544       ctx->req.state = NOIT_HTTP_REQ_PAYLOAD;
545       assert(ctx->res.leader == NULL);
546       expect = "HTTP/1.1 100 Continue\r\n\r\n";
547       ctx->res.leader = bchain_from_data(expect, strlen(expect));
548       _http_perform_write(ctx, &mask);
549       ctx->req.complete = noit_true;
550       if(ctx->res.leader != NULL) return mask;
551     }
552     if(rv == noit_true) return mask | EVENTER_WRITE | EVENTER_EXCEPTION;
553   }
554   /* Not reached:
555    * return EVENTER_READ | EVENTER_EXCEPTION;
556    */
557 }
558 noit_boolean
559 noit_http_session_prime_input(noit_http_session_ctx *ctx,
560                               const void *data, size_t len) {
561   if(ctx->req.first_input != NULL) return noit_false;
562   if(len > DEFAULT_BCHAINSIZE) return noit_false;
563   ctx->req.first_input = ctx->req.last_input =
564       ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
565   memcpy(ctx->req.first_input->buff, data, len);
566   ctx->req.first_input->size = len;
567   return noit_true;
568 }
569
570 void
571 noit_http_request_release(noit_http_session_ctx *ctx) {
572   noit_hash_destroy(&ctx->req.querystring, NULL, NULL);
573   noit_hash_destroy(&ctx->req.headers, NULL, NULL);
574   /* If we expected a payload, we expect a trailing \r\n */
575   if(ctx->req.has_payload) {
576     int drained, mask;
577     ctx->drainage = ctx->req.content_length - ctx->req.content_length_read;
578     /* best effort, we'll drain it before the next request anyway */
579     drained = noit_http_session_req_consume(ctx, NULL, ctx->drainage, &mask);
580     ctx->drainage -= drained;
581   }
582   RELEASE_BCHAIN(ctx->req.current_request_chain);
583   if(ctx->req.orig_qs) free(ctx->req.orig_qs);
584   memset(&ctx->req.state, 0,
585          sizeof(ctx->req) - (unsigned long)&(((noit_http_request *)0)->state));
586 }
587 void
588 noit_http_response_release(noit_http_session_ctx *ctx) {
589   noit_hash_destroy(&ctx->res.headers, free, free);
590   if(ctx->res.status_reason) free(ctx->res.status_reason);
591   RELEASE_BCHAIN(ctx->res.leader);
592   RELEASE_BCHAIN(ctx->res.output);
593   RELEASE_BCHAIN(ctx->res.output_raw);
594   memset(&ctx->res, 0, sizeof(ctx->res));
595 }
596 void
597 noit_http_ctx_session_release(noit_http_session_ctx *ctx) {
598   if(noit_atomic_dec32(&ctx->ref_cnt) == 0) {
599     noit_http_request_release(ctx);
600     if(ctx->req.first_input) RELEASE_BCHAIN(ctx->req.first_input);
601     noit_http_response_release(ctx);
602     free(ctx);
603   }
604 }
605 void
606 noit_http_ctx_acceptor_free(void *v) {
607   noit_http_ctx_session_release((noit_http_session_ctx *)v);
608 }
609 int
610 noit_http_session_req_consume(noit_http_session_ctx *ctx,
611                               void *buf, size_t len, int *mask) {
612   size_t bytes_read = 0;
613   /* We attempt to consume from the first_input */
614   struct bchain *in, *tofree;
615   noitL(http_debug, " ... noit_http_session_req_consume(%d) %d of %d\n",
616         ctx->conn.e->fd, (int)len,
617         (int)(ctx->req.content_length - ctx->req.content_length_read));
618   len = MIN(len, ctx->req.content_length - ctx->req.content_length_read);
619   while(bytes_read < len) {
620     int crlen = 0;
621     in = ctx->req.first_input;
622     while(in && bytes_read < len) {
623       int partial_len = MIN(in->size, len - bytes_read);
624       if(buf) memcpy((char *)buf+bytes_read, in->buff+in->start, partial_len);
625       bytes_read += partial_len;
626       ctx->req.content_length_read += partial_len;
627       noitL(http_debug, " ... filling %d bytes (read through %d/%d)\n",
628             (int)bytes_read, (int)ctx->req.content_length_read,
629             (int)ctx->req.content_length);
630       in->start += partial_len;
631       in->size -= partial_len;
632       if(in->size == 0) {
633         tofree = in;
634         ctx->req.first_input = in = in->next;
635         tofree->next = NULL;
636         RELEASE_BCHAIN(tofree);
637         if(in == NULL) {
638           ctx->req.last_input = NULL;
639           noitL(http_debug, " ... noit_http_session_req_consume = %d\n",
640                 (int)bytes_read);
641           return bytes_read;
642         }
643       }
644     }
645     while(bytes_read + crlen < len) {
646       int rlen;
647       in = ctx->req.last_input;
648       if(!in)
649         in = ctx->req.first_input = ctx->req.last_input =
650             ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
651       else if(in->start + in->size >= in->allocd) {
652         in->next = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
653         in = ctx->req.last_input = in->next;
654       }
655       /* pull next chunk */
656       rlen = ctx->conn.e->opset->read(ctx->conn.e->fd,
657                                       in->buff + in->start + in->size,
658                                       in->allocd - in->size - in->start,
659                                       mask, ctx->conn.e);
660       noitL(http_debug, " noit_http -> read(%d) = %d\n", ctx->conn.e->fd, rlen);
661     noitL(http_io, " noit_http:read(%d) => %d [\n%.*s\n]\n", ctx->conn.e->fd, rlen, rlen, in->buff + in->start + in->size);
662       if(rlen == -1 && errno == EAGAIN) {
663         /* We'd block to read more, but we have data,
664          * so do a short read */
665         if(ctx->req.first_input && ctx->req.first_input->size) break;
666         /* We've got nothing... */
667         noitL(http_debug, " ... noit_http_session_req_consume = -1 (EAGAIN)\n");
668         return -1;
669       }
670       if(rlen <= 0) {
671         noitL(http_debug, " ... noit_http_session_req_consume = -1 (error)\n");
672         return -1;
673       }
674       in->size += rlen;
675       crlen += rlen;
676     }
677   }
678   /* NOT REACHED */
679   return bytes_read;
680 }
681
682 int
683 noit_http_session_drive(eventer_t e, int origmask, void *closure,
684                         struct timeval *now, int *done) {
685   noit_http_session_ctx *ctx = closure;
686   int rv = 0;
687   int mask = origmask;
688
689   if(origmask & EVENTER_EXCEPTION)
690     goto abort_drive;
691
692   /* Drainage -- this is as nasty as it sounds
693    * The last request could have unread upload content, we would have
694    * noted that in noit_http_request_release.
695    */
696   noitL(http_debug, " -> noit_http_session_drive(%d) [%x]\n", e->fd, origmask);
697   while(ctx->drainage > 0) {
698     int len;
699     noitL(http_debug, "   ... draining last request(%d)\n", e->fd);
700     len = noit_http_session_req_consume(ctx, NULL, ctx->drainage, &mask);
701     if(len == -1 && errno == EAGAIN) {
702       noitL(http_debug, " <- noit_http_session_drive(%d) [%x]\n", e->fd, mask);
703       return mask;
704     }
705     if(len <= 0) goto abort_drive;
706     ctx->drainage -= len;
707   }
708
709  next_req:
710   if(ctx->req.complete != noit_true) {
711     int maybe_write_mask;
712     noitL(http_debug, "   -> noit_http_complete_request(%d)\n", e->fd);
713     mask = noit_http_complete_request(ctx, origmask);
714     noitL(http_debug, "   <- noit_http_complete_request(%d) = %d\n",
715           e->fd, mask);
716     _http_perform_write(ctx, &maybe_write_mask);
717     if(ctx->conn.e == NULL) goto release;
718     if(ctx->req.complete != noit_true) {
719       noitL(http_debug, " <- noit_http_session_drive(%d) [%x]\n", e->fd,
720             mask|maybe_write_mask);
721       return mask | maybe_write_mask;
722     }
723     noitL(http_debug, "HTTP start request (%s)\n", ctx->req.uri_str);
724     noit_http_process_querystring(&ctx->req);
725   }
726
727   /* only dispatch if the response is not closed */
728   if(ctx->res.closed == noit_false) {
729     noitL(http_debug, "   -> dispatch(%d)\n", e->fd);
730     rv = ctx->dispatcher(ctx);
731     noitL(http_debug, "   <- dispatch(%d) = %d\n", e->fd, rv);
732   }
733
734   _http_perform_write(ctx, &mask);
735   if(ctx->res.complete == noit_true &&
736      ctx->conn.e &&
737      ctx->conn.needs_close == noit_true) {
738    abort_drive:
739     noit_http_log_request(ctx);
740     if(ctx->conn.e) {
741       ctx->conn.e->opset->close(ctx->conn.e->fd, &mask, ctx->conn.e);
742       ctx->conn.e = NULL;
743     }
744     goto release;
745   }
746   if(ctx->res.complete == noit_true) {
747     noit_http_log_request(ctx);
748     noit_http_request_release(ctx);
749     noit_http_response_release(ctx);
750   }
751   if(ctx->req.complete == noit_false) goto next_req;
752   if(ctx->conn.e) {
753     noitL(http_debug, " <- noit_http_session_drive(%d) [%x]\n", e->fd, mask|rv);
754     return mask | rv;
755   }
756   noitL(http_debug, " <- noit_http_session_drive(%d) [%x]\n", e->fd, 0);
757   goto abort_drive;
758
759  release:
760   *done = 1;
761   /* We're about to release, unhook us from the acceptor_closure so we
762    * don't get double freed */
763   if(ctx->ac->service_ctx == ctx) ctx->ac->service_ctx = NULL;
764   noit_http_ctx_session_release(ctx);
765   noitL(http_debug, " <- noit_http_session_drive(%d) [%x]\n", e->fd, 0);
766   return 0;
767 }
768
769 noit_http_session_ctx *
770 noit_http_session_ctx_new(noit_http_dispatch_func f, void *c, eventer_t e,
771                           acceptor_closure_t *ac) {
772   noit_http_session_ctx *ctx;
773   ctx = calloc(1, sizeof(*ctx));
774   ctx->ref_cnt = 1;
775   ctx->req.complete = noit_false;
776   ctx->conn.e = e;
777   ctx->dispatcher = f;
778   ctx->dispatcher_closure = c;
779   ctx->ac = ac;
780   return ctx;
781 }
782
783 noit_boolean
784 noit_http_response_status_set(noit_http_session_ctx *ctx,
785                               int code, const char *reason) {
786   if(ctx->res.output_started == noit_true) return noit_false;
787   ctx->res.protocol = ctx->req.protocol;
788   if(code < 100 || code > 999) return noit_false;
789   ctx->res.status_code = code;
790   if(ctx->res.status_reason) free(ctx->res.status_reason);
791   ctx->res.status_reason = strdup(reason);
792   return noit_true;
793 }
794 noit_boolean
795 noit_http_response_header_set(noit_http_session_ctx *ctx,
796                               const char *name, const char *value) {
797   if(ctx->res.output_started == noit_true) return noit_false;
798   noit_hash_replace(&ctx->res.headers, strdup(name), strlen(name),
799                     strdup(value), free, free);
800   return noit_true;
801 }
802 noit_boolean
803 noit_http_response_option_set(noit_http_session_ctx *ctx, u_int32_t opt) {
804   if(ctx->res.output_started == noit_true) return noit_false;
805   /* transfer and content encodings only allowed in HTTP/1.1 */
806   if(ctx->res.protocol != NOIT_HTTP11 &&
807      (opt & NOIT_HTTP_CHUNKED))
808     return noit_false;
809   if(ctx->res.protocol != NOIT_HTTP11 &&
810      (opt & (NOIT_HTTP_GZIP | NOIT_HTTP_DEFLATE)))
811     return noit_false;
812   if(((ctx->res.output_options | opt) &
813       (NOIT_HTTP_GZIP | NOIT_HTTP_DEFLATE)) ==
814         (NOIT_HTTP_GZIP | NOIT_HTTP_DEFLATE))
815     return noit_false;
816
817   /* Check out "accept" set */
818   if(!(opt & ctx->req.opts)) return noit_false;
819
820   ctx->res.output_options |= opt;
821   if(ctx->res.output_options & NOIT_HTTP_CHUNKED)
822     CTX_ADD_HEADER("Transfer-Encoding", "chunked");
823   if(ctx->res.output_options & (NOIT_HTTP_GZIP | NOIT_HTTP_DEFLATE)) {
824     CTX_ADD_HEADER("Vary", "Accept-Encoding");
825     if(ctx->res.output_options & NOIT_HTTP_GZIP)
826       CTX_ADD_HEADER("Content-Encoding", "gzip");
827     else if(ctx->res.output_options & NOIT_HTTP_DEFLATE)
828       CTX_ADD_HEADER("Content-Encoding", "deflate");
829   }
830   if(ctx->res.output_options & NOIT_HTTP_CLOSE) {
831     CTX_ADD_HEADER("Connection", "close");
832     ctx->conn.needs_close = noit_true;
833   }
834   return noit_true;
835 }
836 noit_boolean
837 noit_http_response_append(noit_http_session_ctx *ctx,
838                           const void *b, size_t l) {
839   struct bchain *o;
840   int boff = 0;
841   if(ctx->res.closed == noit_true) return noit_false;
842   if(ctx->res.output_started == noit_true &&
843      !(ctx->res.output_options & (NOIT_HTTP_CLOSE | NOIT_HTTP_CHUNKED)))
844     return noit_false;
845   if(!ctx->res.output)
846     assert(ctx->res.output = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE));
847   o = ctx->res.output;
848   while(o->next) o = o->next;
849   while(l > 0) {
850     if(o->allocd == o->start + o->size) {
851       /* Filled up, need another */
852       o->next = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
853       o->next->prev = o->next;
854       o = o->next;
855     }
856     if(o->allocd > o->start + o->size) {
857       int tocopy = MIN(l, o->allocd - o->start - o->size);
858       memcpy(o->buff + o->start + o->size, (const char *)b + boff, tocopy);
859       o->size += tocopy;
860       boff += tocopy;
861       l -= tocopy;
862     }
863   }
864   return noit_true;
865 }
866 noit_boolean
867 noit_http_response_append_bchain(noit_http_session_ctx *ctx,
868                                  struct bchain *b) {
869   struct bchain *o;
870   if(ctx->res.closed == noit_true) return noit_false;
871   if(ctx->res.output_started == noit_true &&
872      !(ctx->res.output_options & (NOIT_HTTP_CHUNKED | NOIT_HTTP_CLOSE)))
873     return noit_false;
874   if(!ctx->res.output)
875     ctx->res.output = b;
876   else {
877     o = ctx->res.output;
878     while(o->next) o = o->next;
879     o->next = b;
880     b->prev = o;
881   }
882   return noit_true;
883 }
884 static int
885 _http_construct_leader(noit_http_session_ctx *ctx) {
886   int len = 0, tlen;
887   struct bchain *b;
888   const char *protocol_str;
889   const char *key, *value;
890   int klen;
891   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
892
893   assert(!ctx->res.leader);
894   ctx->res.leader = b = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
895
896   protocol_str = ctx->res.protocol == NOIT_HTTP11 ?
897                    "HTTP/1.1" :
898                    (ctx->res.protocol == NOIT_HTTP10 ?
899                      "HTTP/1.0" :
900                      "HTTP/0.9");
901   tlen = snprintf(b->buff, b->allocd, "%s %03d %s\r\n",
902                   protocol_str, ctx->res.status_code, ctx->res.status_reason);
903   if(tlen < 0) return -1;
904   len = b->size = tlen;
905
906 #define CTX_LEADER_APPEND(s, slen) do { \
907   if(b->size + slen > DEFAULT_BCHAINSIZE) { \
908     b->next = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE); \
909     assert(b->next); \
910     b->next->prev = b; \
911     b = b->next; \
912   } \
913   assert(DEFAULT_BCHAINSIZE >= b->size + slen); \
914   memcpy(b->buff + b->start + b->size, s, slen); \
915   b->size += slen; \
916 } while(0)
917   while(noit_hash_next_str(&ctx->res.headers, &iter,
918                            &key, &klen, &value)) {
919     int vlen = strlen(value);
920     CTX_LEADER_APPEND(key, klen);
921     CTX_LEADER_APPEND(": ", 2);
922     CTX_LEADER_APPEND(value, vlen);
923     CTX_LEADER_APPEND("\r\n", 2);
924   }
925   CTX_LEADER_APPEND("\r\n", 2);
926   return len;
927 }
928 static int memgzip2(Bytef *dest, uLongf *destLen,
929                     const Bytef *source, uLong sourceLen, int level) {
930   z_stream stream;
931   int err;
932
933   memset(&stream, 0, sizeof(stream));
934   stream.next_in = (Bytef*)source;
935   stream.avail_in = (uInt)sourceLen;
936   stream.next_out = dest;
937   stream.avail_out = (uInt)*destLen;
938   if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
939
940   err = deflateInit2(&stream, level, Z_DEFLATED, 15+16, 8,
941                      Z_DEFAULT_STRATEGY);
942   if (err != Z_OK) return err;
943
944   err = deflate(&stream, Z_FINISH);
945   if (err != Z_STREAM_END) {
946     deflateEnd(&stream);
947     return err == Z_OK ? Z_BUF_ERROR : err;
948   }
949   *destLen = stream.total_out;
950
951   err = deflateEnd(&stream);
952   return err;
953 }
954 static noit_boolean
955 _http_encode_chain(struct bchain *out, struct bchain *in, int opts) {
956   /* implement gzip and deflate! */
957   if(opts & NOIT_HTTP_GZIP) {
958     uLongf olen;
959     olen = out->allocd - out->start;
960     if(Z_OK != memgzip2((Bytef *)(out->buff + out->start), &olen,
961                         (Bytef *)(in->buff + in->start), (uLong)in->size,
962                         9)) {
963       noitL(noit_error, "zlib compress2 error\n");
964       return noit_false;
965     }
966     out->size += olen;
967   }
968   else if(opts & NOIT_HTTP_DEFLATE) {
969     uLongf olen;
970     olen = out->allocd - out->start;
971     if(Z_OK != compress2((Bytef *)(out->buff + out->start), &olen,
972                          (Bytef *)(in->buff + in->start), (uLong)in->size,
973                          9)) {
974       noitL(noit_error, "zlib compress2 error\n");
975       return noit_false;
976     }
977     out->size += olen;
978   }
979   else {
980     if(in->size > out->allocd - out->start) return noit_false;
981     memcpy(out->buff + out->start, in->buff + in->start, in->size);
982     out->size += in->size;
983   }
984   return noit_true;
985 }
986 struct bchain *
987 noit_http_process_output_bchain(noit_http_session_ctx *ctx,
988                                 struct bchain *in) {
989   struct bchain *out;
990   int ilen, maxlen = in->size, hexlen;
991   int opts = ctx->res.output_options;
992
993   /* a chunked header looks like: hex*\r\ndata\r\n */
994   /* let's assume that content never gets "larger" */
995   if(opts & NOIT_HTTP_GZIP) maxlen = deflateBound(NULL, in->size);
996   else if(opts & NOIT_HTTP_DEFLATE) maxlen = compressBound(in->size);
997
998   /* So, the link size is the len(data) + 4 + ceil(log(len(data))/log(16)) */
999   ilen = maxlen;
1000   hexlen = 0;
1001   while(ilen) { ilen >>= 4; hexlen++; }
1002   if(hexlen == 0) hexlen = 1;
1003
1004   out = ALLOC_BCHAIN(hexlen + 4 + maxlen);
1005   /* if we're chunked, let's give outselved hexlen + 2 prefix space */
1006   if(opts & NOIT_HTTP_CHUNKED) out->start = hexlen + 2;
1007   if(_http_encode_chain(out, in, opts) == noit_false) {
1008     free(out);
1009     return NULL;
1010   }
1011   /* Too long! Out "larger" assumption is bad */
1012   if(opts & NOIT_HTTP_CHUNKED) {
1013     ilen = out->size;
1014     assert(out->start+out->size+2 <= out->allocd);
1015     out->buff[out->start + out->size++] = '\r';
1016     out->buff[out->start + out->size++] = '\n';
1017     out->start = 0;
1018     /* terminate */
1019     out->size += 2;
1020     out->buff[hexlen] = '\r';
1021     out->buff[hexlen+1] = '\n';
1022     /* backfill */
1023     out->size += hexlen;
1024     while(hexlen > 0) {
1025       out->buff[hexlen - 1] = _hexchars[ilen & 0xf];
1026       ilen >>= 4;
1027       hexlen--;
1028     }
1029     while(out->buff[out->start] == '0') {
1030       out->start++;
1031       out->size--;
1032     }
1033   }
1034   return out;
1035 }
1036 noit_boolean
1037 noit_http_response_flush(noit_http_session_ctx *ctx, noit_boolean final) {
1038   struct bchain *o, *r;
1039   int mask, rv;
1040
1041   if(ctx->res.closed == noit_true) return noit_false;
1042   if(ctx->res.output_started == noit_false) {
1043     _http_construct_leader(ctx);
1044     ctx->res.output_started = noit_true;
1045   }
1046   /* encode output to output_raw */
1047   r = ctx->res.output_raw;
1048   while(r && r->next) r = r->next;
1049   /* r is the last raw output link */
1050   o = ctx->res.output;
1051   /* o is the first output link to process */
1052   while(o) {
1053     struct bchain *tofree, *n;
1054     n = noit_http_process_output_bchain(ctx, o);
1055     if(!n) {
1056       /* Bad, response stops here! */
1057       noitL(noit_error, "noit_http_process_output_bchain: NULL\n");
1058       while(o) { tofree = o; o = o->next; free(tofree); }
1059       final = noit_true;
1060       break;
1061     }
1062     if(r) {
1063       r->next = n;
1064       n->prev = r;
1065       r = n;
1066     }
1067     else {
1068       r = ctx->res.output_raw = n;
1069     }
1070     tofree = o; o = o->next; FREE_BCHAIN(tofree); /* advance and free */
1071   }
1072   ctx->res.output = NULL;
1073   if(final) {
1074     struct bchain *n;
1075     ctx->res.closed = noit_true;
1076     if(ctx->res.output_options & NOIT_HTTP_CHUNKED)
1077       n = bchain_from_data("0\r\n\r\n", 5);
1078     else
1079       n = bchain_from_data("\r\n", 2);
1080     if(r) {
1081       r->next = n;
1082       n->prev = r;
1083     }
1084     else {
1085       ctx->res.output_raw = n;
1086     }
1087   }
1088
1089   rv = _http_perform_write(ctx, &mask);
1090   if(ctx->conn.e) {
1091     eventer_update(ctx->conn.e, mask);
1092   }
1093   if(rv < 0) return noit_false;
1094   /* If the write fails completely, the event will not be closed,
1095    * the following should not trigger the false case.
1096    */
1097   return ctx->conn.e ? noit_true : noit_false;
1098 }
1099
1100 noit_boolean
1101 noit_http_response_end(noit_http_session_ctx *ctx) {
1102   if(!noit_http_response_flush(ctx, noit_true)) return noit_false;
1103   return noit_true;
1104 }
1105
1106
1107 /* Helper functions */
1108
1109 static int
1110 noit_http_write_xml(void *vctx, const char *buffer, int len) {
1111   if(noit_http_response_append((noit_http_session_ctx *)vctx, buffer, len))
1112     return len;
1113   return -1;
1114 }
1115 static int
1116 noit_http_close_xml(void *vctx) {
1117   noit_http_response_end((noit_http_session_ctx *)vctx);
1118   return 0;
1119 }
1120 void
1121 noit_http_response_xml(noit_http_session_ctx *ctx, xmlDocPtr doc) {
1122   xmlOutputBufferPtr out;
1123   xmlCharEncodingHandlerPtr enc;
1124   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
1125   out = xmlOutputBufferCreateIO(noit_http_write_xml,
1126                                 noit_http_close_xml,
1127                                 ctx, enc);
1128   xmlSaveFormatFileTo(out, doc, "utf8", 1);
1129 }
1130
1131 void
1132 noit_http_init() {
1133   http_debug = noit_log_stream_find("debug/http");
1134   http_access = noit_log_stream_find("http/access");
1135   http_io = noit_log_stream_find("http/io");
1136 }
Note: See TracBrowser for help on using the browser.