root/src/noit_http.c

Revision 30339cdacb492886f0b53769c6346689274bde4d, 36.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

closes #280

The response is "complete" when it has been sent over the network.
However, it is "closed" when the application has written the output
that will be sent. We want closed, not complete to see if the
dispatcher has completed the response (not the driver)

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