root/src/noit_http.c

Revision 5a1bd35e7f20d4ac7a7da8b615709dc403842685, 46.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 years ago)

Fix http_access duration logs to be microsecond granularity.

  • 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 <sys/mman.h>
42 #include <libxml/tree.h>
43
44 #define REQ_PAT "\r\n\r\n"
45 #define REQ_PATSIZE 4
46 #define HEADER_CONTENT_LENGTH "content-length"
47 #define HEADER_EXPECT "expect"
48
49 struct noit_http_connection {
50   eventer_t e;
51   int needs_close;
52 };
53
54 struct noit_http_request {
55   struct bchain *first_input; /* The start of the input chain */
56   struct bchain *last_input;  /* The end of the input chain */
57   struct bchain *current_input;  /* The point of the input where we */
58   size_t         current_offset; /* analyzing. */
59
60   enum { NOIT_HTTP_REQ_HEADERS = 0,
61          NOIT_HTTP_REQ_EXPECT,
62          NOIT_HTTP_REQ_PAYLOAD } state;
63   struct bchain *current_request_chain;
64   noit_boolean has_payload;
65   int64_t content_length;
66   int64_t content_length_read;
67   char *method_str;
68   char *uri_str;
69   char *protocol_str;
70   noit_hash_table querystring;
71   u_int32_t opts;
72   noit_http_method method;
73   noit_http_protocol protocol;
74   noit_hash_table headers;
75   noit_boolean complete;
76   struct timeval start_time;
77   char *orig_qs;
78 };
79
80 struct noit_http_response {
81   noit_http_protocol protocol;
82   int status_code;
83   char *status_reason;
84
85   noit_hash_table headers;
86   struct bchain *leader; /* serialization of status line and headers */
87
88   u_int32_t output_options;
89   struct bchain *output;       /* data is pushed in here */
90   struct bchain *output_raw;   /* internally transcoded here for output */
91   size_t output_raw_offset;    /* tracks our offset */
92   noit_boolean output_started; /* locks the options and leader */
93                                /*   and possibly output. */
94   noit_boolean closed;         /* set by _end() */
95   noit_boolean complete;       /* complete, drained and disposable */
96   size_t bytes_written;        /* tracks total bytes written */
97   z_stream *gzip;
98 };
99
100 struct noit_http_session_ctx {
101   noit_atomic32_t ref_cnt;
102   int64_t drainage;
103   int max_write;
104   noit_http_connection conn;
105   noit_http_request req;
106   noit_http_response res;
107   noit_http_dispatch_func dispatcher;
108   void *dispatcher_closure;
109   acceptor_closure_t *ac;
110 };
111
112 static noit_log_stream_t http_debug = NULL;
113 static noit_log_stream_t http_io = NULL;
114 static noit_log_stream_t http_access = NULL;
115
116 static const char gzip_header[10] =
117   { '\037', '\213', Z_DEFLATED, 0, 0, 0, 0, 0, 0, 0x03 };
118
119 #define CTX_ADD_HEADER(a,b) \
120     noit_hash_replace(&ctx->res.headers, \
121                       strdup(a), strlen(a), strdup(b), free, free)
122 static const char _hexchars[16] =
123   {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
124 static void inplace_urldecode(char *c) {
125   char *o = c;
126   while(*c) {
127     if(*c == '%') {
128       int i, ord = 0;
129       for(i = 1; i < 3; i++) {
130         if(c[i] >= '0' && c[i] <= '9') ord = (ord << 4) | (c[i] - '0');
131         else if (c[i] >= 'a' && c[i] <= 'f') ord = (ord << 4) | (c[i] - 'a' + 0xa);
132         else if (c[i] >= 'A' && c[i] <= 'F') ord = (ord << 4) | (c[i] - 'A' + 0xa);
133         else break;
134       }
135       if(i==3) {
136         *((unsigned char *)o++) = ord;
137         c+=3;
138         continue;
139       }
140     }
141     *o++ = *c++;
142   }
143   *o = '\0';
144 }
145
146 struct bchain *bchain_alloc(size_t size, int line) {
147   struct bchain *n;
148   n = malloc(size + offsetof(struct bchain, _buff));
149   /*noitL(noit_error, "bchain_alloc(%p) : %d\n", n, line);*/
150   if(!n) return NULL;
151   n->type = BCHAIN_INLINE;
152   n->prev = n->next = NULL;
153   n->start = n->size = 0;
154   n->allocd = size;
155   n->buff = n->_buff;
156   return n;
157 }
158 struct bchain *bchain_mmap(int fd, size_t len, int flags, off_t offset) {
159   struct bchain *n;
160   void *buff;
161   buff = mmap(NULL, len, PROT_READ, flags, fd, offset);
162   if(buff == MAP_FAILED) return NULL;
163   n = bchain_alloc(0, 0);
164   n->type = BCHAIN_MMAP;
165   n->buff = buff;
166   n->size = len;
167   n->allocd = len;
168   return n;
169 }
170 void bchain_free(struct bchain *b, int line) {
171   /*noitL(noit_error, "bchain_free(%p) : %d\n", b, line);*/
172   if(b->type == BCHAIN_MMAP) {
173     munmap(b->buff, b->allocd);
174   }
175   free(b);
176 }
177 #define ALLOC_BCHAIN(s) bchain_alloc(s, __LINE__)
178 #define FREE_BCHAIN(a) bchain_free(a, __LINE__)
179 #define RELEASE_BCHAIN(a) do { \
180   while(a) { \
181     struct bchain *__b; \
182     __b = a; \
183     a = __b->next; \
184     bchain_free(__b, __LINE__); \
185   } \
186 } while(0)
187 struct bchain *bchain_from_data(const void *d, size_t size) {
188   struct bchain *n;
189   n = ALLOC_BCHAIN(size);
190   if(!n) return NULL;
191   memcpy(n->buff, d, size);
192   n->size = size;
193   return n;
194 }
195
196 noit_http_request *
197 noit_http_session_request(noit_http_session_ctx *ctx) {
198   return &ctx->req;
199 }
200 noit_http_response *
201 noit_http_session_response(noit_http_session_ctx *ctx) {
202   return &ctx->res;
203 }
204 noit_http_connection *
205 noit_http_session_connection(noit_http_session_ctx *ctx) {
206   return &ctx->conn;
207 }
208 void
209 noit_http_session_set_dispatcher(noit_http_session_ctx *ctx,
210                                  int (*d)(noit_http_session_ctx *), void *dc) {
211   ctx->dispatcher = d;
212   ctx->dispatcher_closure = dc;
213 }
214 void *noit_http_session_dispatcher_closure(noit_http_session_ctx *ctx) {
215   return ctx->dispatcher_closure;
216 }
217 void noit_http_session_trigger(noit_http_session_ctx *ctx, int state) {
218   if(ctx->conn.e) eventer_trigger(ctx->conn.e, state);
219 }
220 uint32_t noit_http_session_ref_cnt(noit_http_session_ctx *ctx) {
221   return ctx->ref_cnt;
222 }
223 uint32_t noit_http_session_ref_dec(noit_http_session_ctx *ctx) {
224   return noit_atomic_dec32(&ctx->ref_cnt);
225 }
226 uint32_t noit_http_session_ref_inc(noit_http_session_ctx *ctx) {
227   return noit_atomic_inc32(&ctx->ref_cnt);
228 }
229 eventer_t noit_http_connection_event(noit_http_connection *conn) {
230   return conn->e;
231 }
232 const char *noit_http_request_uri_str(noit_http_request *req) {
233   return req->uri_str;
234 }
235 const char *noit_http_request_method_str(noit_http_request *req) {
236   return req->method_str;
237 }
238 const char *noit_http_request_protocol_str(noit_http_request *req) {
239   return req->protocol_str;
240 }
241 size_t noit_http_request_content_length(noit_http_request *req) {
242   return req->content_length;
243 }
244 const char *noit_http_request_querystring(noit_http_request *req, const char *k) {
245   void *vv;
246   const char *v = NULL;
247   if(noit_hash_retrieve(&req->querystring, k, strlen(k), &vv))
248     v = vv;
249   return v;
250 }
251 noit_hash_table *noit_http_request_querystring_table(noit_http_request *req) {
252   return &req->querystring;
253 }
254 noit_hash_table *noit_http_request_headers_table(noit_http_request *req) {
255   return &req->headers;
256 }
257 noit_boolean noit_http_response_closed(noit_http_response *res) {
258   return res->closed;
259 }
260 noit_boolean noit_http_response_complete(noit_http_response *res) {
261   return res->complete;
262 }
263
264 static noit_http_method
265 _method_enum(const char *s) {
266   switch(*s) {
267    case 'G':
268     if(!strcasecmp(s, "GET")) return NOIT_HTTP_GET;
269     break;
270    case 'H':
271     if(!strcasecmp(s, "HEAD")) return NOIT_HTTP_HEAD;
272     break;
273    case 'P':
274     if(!strcasecmp(s, "POST")) return NOIT_HTTP_POST;
275     break;
276    default:
277     break;
278   }
279   return NOIT_HTTP_OTHER;
280 }
281 static noit_http_protocol
282 _protocol_enum(const char *s) {
283   if(!strcasecmp(s, "HTTP/1.1")) return NOIT_HTTP11;
284   if(!strcasecmp(s, "HTTP/1.0")) return NOIT_HTTP10;
285   return NOIT_HTTP09;
286 }
287 static noit_boolean
288 _fixup_bchain(struct bchain *b) {
289   /* make sure lines (CRLF terminated) don't cross chain boundaries */
290   while(b) {
291     struct bchain *f;
292     int start_in_b, end_in_f;
293     size_t new_size;
294     const char *str_in_f;
295
296     start_in_b = b->start;
297     if(b->size > 2) {
298       if(memcmp(b->buff + b->start + b->size - 2, "\r\n", 2) == 0) {
299         b = b->next;
300         continue;
301       }
302       start_in_b = b->start + b->size - 3; /* we already checked -2 */
303       while(start_in_b >= b->start) {
304         if(b->buff[start_in_b] == '\r' && b->buff[start_in_b+1] == '\n') {
305           start_in_b += 2;
306           break;
307         }
308         start_in_b--;
309       }
310     }
311
312     /* start_in_b points to the beginning of the string we need to build
313      * into a new buffer.
314      */
315     f = b->next;
316     if(!f) return noit_false; /* Nothing left, can't complete the line */
317     str_in_f = strnstrn("\r\n", 2, f->buff + f->start, f->size);
318     if(!str_in_f) return noit_false; /* nothing in next chain -- too long */
319     str_in_f += 2;
320     end_in_f = (str_in_f - f->buff - f->start);
321     new_size = end_in_f + (b->start + b->size - start_in_b);
322     if(new_size > DEFAULT_BCHAINSIZE) return noit_false; /* string too long */
323     f = ALLOC_BCHAIN(new_size);
324     f->prev = b;
325     f->next = b->next;
326     f->start = 0;
327     f->size = new_size;
328     memcpy(f->buff, b->buff + start_in_b, b->start + b->size - start_in_b);
329     memcpy(f->buff + b->start + b->size - start_in_b,
330            f->buff + f->start, end_in_f);
331     f->next->prev = f;
332     f->prev->next = f;
333     f->prev->size -= start_in_b - b->start;
334     f->next->size -= end_in_f;
335     f->next->start += end_in_f;
336     b = f->next; /* skip f, we know it is right */
337   }
338   return noit_true;
339 }
340 static noit_boolean
341 _extract_header(char *l, const char **n, const char **v) {
342   *n = NULL;
343   if(*l == ' ' || *l == '\t') {
344     while(*l == ' ' || *l == '\t') l++;
345     *v = l;
346     return noit_true;
347   }
348   *n = l;
349   while(*l != ':' && *l) { *l = tolower(*l); l++; }
350   if(!*l) return noit_false;
351   *v = l+1;
352   /* Right trim the name */
353   *l-- = '\0';
354   while(*l == ' ' || *l == '\t') *l-- = '\0';
355   while(**v == ' ' || **v == '\t') (*v)++;
356   return noit_true;
357 }
358 static void
359 noit_http_log_request(noit_http_session_ctx *ctx) {
360   char ip[64], timestr[64];
361   double time_ms;
362   struct tm *tm, tbuf;
363   time_t now;
364   struct timeval end_time, diff;
365
366   if(ctx->req.start_time.tv_sec == 0) return;
367   gettimeofday(&end_time, NULL);
368   now = end_time.tv_sec;
369   tm = gmtime_r(&now, &tbuf);
370   strftime(timestr, sizeof(timestr), "%d/%b/%Y:%H:%M:%S -0000", tm);
371   sub_timeval(end_time, ctx->req.start_time, &diff);
372   time_ms = diff.tv_sec * 1000 + (double)diff.tv_usec / 1000.0;
373   noit_convert_sockaddr_to_buff(ip, sizeof(ip), &ctx->ac->remote.remote_addr);
374   noitL(http_access, "%s - - [%s] \"%s %s%s%s %s\" %d %llu %.3f\n",
375         ip, timestr,
376         ctx->req.method_str, ctx->req.uri_str,
377         ctx->req.orig_qs ? "?" : "", ctx->req.orig_qs ? ctx->req.orig_qs : "",
378         ctx->req.protocol_str,
379         ctx->res.status_code,
380         (long long unsigned)ctx->res.bytes_written,
381         time_ms);
382 }
383
384 static int
385 _http_perform_write(noit_http_session_ctx *ctx, int *mask) {
386   int len, tlen = 0;
387   size_t attempt_write_len;
388   struct bchain **head, *b;
389  choose_bucket:
390   head = ctx->res.leader ? &ctx->res.leader : &ctx->res.output_raw;
391   b = *head;
392
393   if(!ctx->conn.e) return 0;
394 #if 0
395   if(ctx->res.output_started == noit_false) return EVENTER_EXCEPTION;
396 #endif
397   if(!b) {
398     if(ctx->res.closed) ctx->res.complete = noit_true;
399     *mask = EVENTER_EXCEPTION;
400     return tlen;
401   }
402
403   if(ctx->res.output_raw_offset >= b->size) {
404     *head = b->next;
405     FREE_BCHAIN(b);
406     b = *head;
407     if(b) b->prev = NULL;
408     ctx->res.output_raw_offset = 0;
409     goto choose_bucket;
410   }
411
412   attempt_write_len = b->size - ctx->res.output_raw_offset;
413   attempt_write_len = MIN(attempt_write_len, ctx->max_write);
414
415   len = ctx->conn.e->opset->
416           write(ctx->conn.e->fd,
417                 b->buff + b->start + ctx->res.output_raw_offset,
418                 attempt_write_len, mask, ctx->conn.e);
419   if(len == -1 && errno == EAGAIN) {
420     *mask |= EVENTER_EXCEPTION;
421     return tlen;
422   }
423   if(len == -1) {
424     /* socket error */
425     ctx->res.complete = noit_true;
426     ctx->conn.needs_close = noit_true;
427     noit_http_log_request(ctx);
428     *mask |= EVENTER_EXCEPTION;
429     return -1;
430   }
431   noitL(http_io, " http_write(%d) => %d [\n%.*s\n]\n", ctx->conn.e->fd,
432         len, len, b->buff + b->start + ctx->res.output_raw_offset);
433   ctx->res.output_raw_offset += len;
434   ctx->res.bytes_written += len;
435   tlen += len;
436   goto choose_bucket;
437 }
438 static noit_boolean
439 noit_http_request_finalize_headers(noit_http_request *req, noit_boolean *err) {
440   int start;
441   void *vval;
442   const char *mstr, *last_name = NULL;
443   struct bchain *b;
444
445   if(req->state != NOIT_HTTP_REQ_HEADERS) return noit_false;
446   if(!req->current_input) req->current_input = req->first_input;
447   if(!req->current_input) return noit_false;
448   if(req->start_time.tv_sec == 0) gettimeofday(&req->start_time, NULL);
449  restart:
450   while(req->current_input->prev &&
451         (req->current_offset < (req->current_input->start + REQ_PATSIZE - 1))) {
452     int inset;
453     /* cross bucket */
454     if(req->current_input == req->last_input &&
455        req->current_offset >= (req->last_input->start + req->last_input->size))
456       return noit_false;
457     req->current_offset++;
458     inset = req->current_offset - req->current_input->start;
459     if(memcmp(req->current_input->buff + req->current_input->start,
460               REQ_PAT + (REQ_PATSIZE - inset), inset) == 0 &&
461        memcmp(req->current_input->prev->buff +
462                 req->current_input->prev->start +
463                 req->current_input->prev->size - REQ_PATSIZE + inset,
464               REQ_PAT + inset,
465               REQ_PATSIZE - inset) == 0) goto match;
466   }
467   start = MAX(req->current_offset - REQ_PATSIZE, req->current_input->start);
468   mstr = strnstrn(REQ_PAT, REQ_PATSIZE,
469                   req->current_input->buff + start,
470                   req->current_input->size -
471                     (start - req->current_input->start));
472   if(!mstr && req->current_input->next) {
473     req->current_input = req->current_input->next;
474     req->current_offset = req->current_input->start;
475     goto restart;
476   }
477   if(!mstr) return noit_false;
478   req->current_offset = mstr - req->current_input->buff + REQ_PATSIZE;
479  match:
480   req->current_request_chain = req->first_input;
481   noitL(http_debug, " noit_http_request_finalize : match(%d in %d)\n",
482         (int)(req->current_offset - req->current_input->start),
483         (int)req->current_input->size);
484   if(req->current_offset <
485      req->current_input->start + req->current_input->size) {
486     /* There are left-overs */
487     int lsize = req->current_input->size - req->current_offset;
488     noitL(http_debug, " noit_http_request_finalize -- leftovers: %d\n", lsize);
489     req->first_input = ALLOC_BCHAIN(lsize);
490     req->first_input->prev = NULL;
491     req->first_input->next = req->current_input->next;
492     req->first_input->start = 0;
493     req->first_input->size = lsize;
494     memcpy(req->first_input->buff,
495            req->current_input->buff + req->current_offset,
496            req->first_input->size);
497     req->current_input->size -= lsize;
498     if(req->last_input == req->current_input)
499       req->last_input = req->first_input;
500     else
501       FREE_BCHAIN(req->current_input);
502   }
503   else {
504     req->first_input = req->last_input = NULL;
505   }
506   req->current_input = NULL;
507   req->current_offset = 0;
508
509   /* Now we need to dissect the current_request_chain into an HTTP request */
510   /* First step: make sure that no line crosses a chain boundary by
511    * inserting new chains as necessary.
512    */
513   if(!_fixup_bchain(req->current_request_chain)) {
514     *err = noit_true;
515     return noit_false;
516   }
517   /* Second step is to parse out the request itself */
518   for(b = req->current_request_chain; b; b = b->next) {
519     char *curr_str, *next_str;
520     b->buff[b->start + b->size - 2] = '\0';
521     curr_str = b->buff + b->start;
522     do {
523       next_str = strstr(curr_str, "\r\n");
524       if(next_str) {
525         *((char *)next_str) = '\0';
526         next_str += 2;
527       }
528       if(req->method_str && *curr_str == '\0')
529         break; /* our CRLFCRLF... end of req */
530 #define FAIL do { *err = noit_true; return noit_false; } while(0)
531       if(!req->method_str) { /* request line */
532         req->method_str = (char *)curr_str;
533         req->uri_str = strchr(curr_str, ' ');
534         if(!req->uri_str) FAIL;
535         *(req->uri_str) = '\0';
536         req->uri_str++;
537         req->protocol_str = strchr(req->uri_str, ' ');
538         if(!req->protocol_str) FAIL;
539         *(req->protocol_str) = '\0';
540         req->protocol_str++;
541         req->method = _method_enum(req->method_str);
542         req->protocol = _protocol_enum(req->protocol_str);
543         req->opts |= NOIT_HTTP_CLOSE;
544         if(req->protocol == NOIT_HTTP11) req->opts |= NOIT_HTTP_CHUNKED;
545       }
546       else { /* request headers */
547         const char *name, *value;
548         if(_extract_header(curr_str, &name, &value) == noit_false) FAIL;
549         if(!name && !last_name) FAIL;
550         if(!strcmp(name ? name : last_name, "accept-encoding")) {
551           if(strstr(value, "gzip")) req->opts |= NOIT_HTTP_GZIP;
552           if(strstr(value, "deflate")) req->opts |= NOIT_HTTP_DEFLATE;
553         }
554         if(name)
555           noit_hash_replace(&req->headers, name, strlen(name), (void *)value,
556                             NULL, NULL);
557         else {
558           struct bchain *b;
559           const char *prefix = NULL;
560           int l1, l2;
561           noit_hash_retr_str(&req->headers, last_name, strlen(last_name),
562                              &prefix);
563           if(!prefix) FAIL;
564           l1 = strlen(prefix);
565           l2 = strlen(value);
566           b = ALLOC_BCHAIN(l1 + l2 + 2);
567           b->next = req->current_request_chain;
568           b->next->prev = b;
569           req->current_request_chain = b;
570           b->size = l1 + l2 + 2;
571           memcpy(b->buff, prefix, l1);
572           b->buff[l1] = ' ';
573           memcpy(b->buff + l1 + 1, value, l2);
574           b->buff[l1 + 1 + l2] = '\0';
575           noit_hash_replace(&req->headers, last_name, strlen(last_name),
576                             b->buff, NULL, NULL);
577         }
578         if(name) last_name = name;
579       }
580       curr_str = next_str;
581     } while(next_str);
582   }
583
584   /* headers are done... we could need to read a payload */
585   if(noit_hash_retrieve(&req->headers,
586                         HEADER_CONTENT_LENGTH,
587                         sizeof(HEADER_CONTENT_LENGTH)-1, &vval)) {
588     const char *val = vval;
589     req->has_payload = noit_true;
590     req->content_length = strtoll(val, NULL, 10);
591   }
592   if(noit_hash_retrieve(&req->headers, HEADER_EXPECT,
593                         sizeof(HEADER_EXPECT)-1, &vval)) {
594     const char *val = vval;
595     if(strncmp(val, "100-", 4) || /* Bad expect header */
596        req->has_payload == noit_false) /* expect, but no content length */
597       FAIL;
598     /* We need to tell the client to "go-ahead" -- HTTP sucks */
599     req->state = NOIT_HTTP_REQ_EXPECT;
600     return noit_false;
601   }
602   if(req->content_length > 0) {
603     /* switch modes... let's go read the payload */
604     req->state = NOIT_HTTP_REQ_PAYLOAD;
605     return noit_false;
606   }
607
608   req->complete = noit_true;
609   return noit_true;
610 }
611 void
612 noit_http_process_querystring(noit_http_request *req) {
613   char *cp, *interest, *brk = NULL;
614   cp = strchr(req->uri_str, '?');
615   if(!cp) return;
616   *cp++ = '\0';
617   req->orig_qs = strdup(cp);
618   for (interest = strtok_r(cp, "&", &brk);
619        interest;
620        interest = strtok_r(NULL, "&", &brk)) {
621     char *eq;
622     eq = strchr(interest, '=');
623     if(!eq) {
624       inplace_urldecode(interest);
625       noit_hash_store(&req->querystring, interest, strlen(interest), NULL);
626     }
627     else {
628       *eq++ = '\0';
629       inplace_urldecode(interest);
630       inplace_urldecode(eq);
631       noit_hash_store(&req->querystring, interest, strlen(interest), eq);
632     }
633   }
634 }
635 static noit_boolean
636 noit_http_request_finalize_payload(noit_http_request *req, noit_boolean *err) {
637   req->complete = noit_true;
638   return noit_true;
639 }
640 static noit_boolean
641 noit_http_request_finalize(noit_http_request *req, noit_boolean *err) {
642   if(req->state == NOIT_HTTP_REQ_HEADERS)
643     if(noit_http_request_finalize_headers(req, err)) return noit_true;
644   if(req->state == NOIT_HTTP_REQ_EXPECT) return noit_false;
645   if(req->state == NOIT_HTTP_REQ_PAYLOAD)
646     if(noit_http_request_finalize_payload(req, err)) return noit_true;
647   return noit_false;
648 }
649 static int
650 noit_http_complete_request(noit_http_session_ctx *ctx, int mask) {
651   struct bchain *in;
652   noit_boolean rv, err = noit_false;
653
654   if(mask & EVENTER_EXCEPTION) {
655    full_error:
656     ctx->conn.e->opset->close(ctx->conn.e->fd, &mask, ctx->conn.e);
657     ctx->conn.e = NULL;
658     return 0;
659   }
660   if(ctx->req.complete == noit_true) return EVENTER_EXCEPTION;
661
662   /* We could have a complete request in the tail of a previous request */
663   rv = noit_http_request_finalize(&ctx->req, &err);
664   if(rv == noit_true) return EVENTER_WRITE | EVENTER_EXCEPTION;
665   if(err == noit_true) goto full_error;
666
667   while(1) {
668     int len;
669
670     in = ctx->req.last_input;
671     if(!in) {
672       in = ctx->req.first_input = ctx->req.last_input =
673         ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
674       if(!in) goto full_error;
675     }
676     if(in->size > 0 && /* we've read something */
677        DEFAULT_BCHAINMINREAD > BCHAIN_SPACE(in) && /* we'd like read more */
678        DEFAULT_BCHAINMINREAD < DEFAULT_BCHAINSIZE) { /* and we can */
679       in->next = ctx->req.last_input =
680         ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
681       in->next->prev = in;
682       in = in->next;
683       if(!in) goto full_error;
684     }
685
686     len = ctx->conn.e->opset->read(ctx->conn.e->fd,
687                                    in->buff + in->start + in->size,
688                                    in->allocd - in->size - in->start,
689                                    &mask, ctx->conn.e);
690     noitL(http_debug, " noit_http -> read(%d) = %d\n", ctx->conn.e->fd, len);
691     noitL(http_io, " noit_http:read(%d) => %d [\n%.*s\n]\n", ctx->conn.e->fd, len, len, in->buff + in->start + in->size);
692     if(len == -1 && errno == EAGAIN) return mask;
693     if(len <= 0) goto full_error;
694     if(len > 0) in->size += len;
695     rv = noit_http_request_finalize(&ctx->req, &err);
696     if(len == -1 || err == noit_true) goto full_error;
697     if(ctx->req.state == NOIT_HTTP_REQ_EXPECT) {
698       const char *expect;
699       ctx->req.state = NOIT_HTTP_REQ_PAYLOAD;
700       assert(ctx->res.leader == NULL);
701       expect = "HTTP/1.1 100 Continue\r\n\r\n";
702       ctx->res.leader = bchain_from_data(expect, strlen(expect));
703       _http_perform_write(ctx, &mask);
704       ctx->req.complete = noit_true;
705       if(ctx->res.leader != NULL) return mask;
706     }
707     if(rv == noit_true) return mask | EVENTER_WRITE | EVENTER_EXCEPTION;
708   }
709   /* Not reached:
710    * return EVENTER_READ | EVENTER_EXCEPTION;
711    */
712 }
713 noit_boolean
714 noit_http_session_prime_input(noit_http_session_ctx *ctx,
715                               const void *data, size_t len) {
716   if(ctx->req.first_input != NULL) return noit_false;
717   if(len > DEFAULT_BCHAINSIZE) return noit_false;
718   ctx->req.first_input = ctx->req.last_input =
719       ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
720   memcpy(ctx->req.first_input->buff, data, len);
721   ctx->req.first_input->size = len;
722   return noit_true;
723 }
724
725 void
726 noit_http_request_release(noit_http_session_ctx *ctx) {
727   noit_hash_destroy(&ctx->req.querystring, NULL, NULL);
728   noit_hash_destroy(&ctx->req.headers, NULL, NULL);
729   /* If we expected a payload, we expect a trailing \r\n */
730   if(ctx->req.has_payload) {
731     int drained, mask;
732     ctx->drainage = ctx->req.content_length - ctx->req.content_length_read;
733     /* best effort, we'll drain it before the next request anyway */
734     drained = noit_http_session_req_consume(ctx, NULL, ctx->drainage, &mask);
735     ctx->drainage -= drained;
736   }
737   RELEASE_BCHAIN(ctx->req.current_request_chain);
738   if(ctx->req.orig_qs) free(ctx->req.orig_qs);
739   memset(&ctx->req.state, 0,
740          sizeof(ctx->req) - (unsigned long)&(((noit_http_request *)0)->state));
741 }
742 void
743 noit_http_response_release(noit_http_session_ctx *ctx) {
744   noit_hash_destroy(&ctx->res.headers, free, free);
745   if(ctx->res.status_reason) free(ctx->res.status_reason);
746   RELEASE_BCHAIN(ctx->res.leader);
747   RELEASE_BCHAIN(ctx->res.output);
748   RELEASE_BCHAIN(ctx->res.output_raw);
749   if(ctx->res.gzip) {
750     deflateEnd(ctx->res.gzip);
751     free(ctx->res.gzip);
752   }
753   memset(&ctx->res, 0, sizeof(ctx->res));
754 }
755 void
756 noit_http_ctx_session_release(noit_http_session_ctx *ctx) {
757   if(noit_atomic_dec32(&ctx->ref_cnt) == 0) {
758     noit_http_request_release(ctx);
759     if(ctx->req.first_input) RELEASE_BCHAIN(ctx->req.first_input);
760     noit_http_response_release(ctx);
761     free(ctx);
762   }
763 }
764 void
765 noit_http_ctx_acceptor_free(void *v) {
766   noit_http_ctx_session_release((noit_http_session_ctx *)v);
767 }
768 int
769 noit_http_session_req_consume(noit_http_session_ctx *ctx,
770                               void *buf, size_t len, int *mask) {
771   size_t bytes_read = 0;
772   /* We attempt to consume from the first_input */
773   struct bchain *in, *tofree;
774   noitL(http_debug, " ... noit_http_session_req_consume(%d) %d of %d\n",
775         ctx->conn.e->fd, (int)len,
776         (int)(ctx->req.content_length - ctx->req.content_length_read));
777   len = MIN(len, ctx->req.content_length - ctx->req.content_length_read);
778   while(bytes_read < len) {
779     int crlen = 0;
780     in = ctx->req.first_input;
781     while(in && bytes_read < len) {
782       int partial_len = MIN(in->size, len - bytes_read);
783       if(buf) memcpy((char *)buf+bytes_read, in->buff+in->start, partial_len);
784       bytes_read += partial_len;
785       ctx->req.content_length_read += partial_len;
786       noitL(http_debug, " ... filling %d bytes (read through %d/%d)\n",
787             (int)bytes_read, (int)ctx->req.content_length_read,
788             (int)ctx->req.content_length);
789       in->start += partial_len;
790       in->size -= partial_len;
791       if(in->size == 0) {
792         tofree = in;
793         ctx->req.first_input = in = in->next;
794         tofree->next = NULL;
795         RELEASE_BCHAIN(tofree);
796         if(in == NULL) {
797           ctx->req.last_input = NULL;
798           noitL(http_debug, " ... noit_http_session_req_consume = %d\n",
799                 (int)bytes_read);
800           return bytes_read;
801         }
802       }
803     }
804     while(bytes_read + crlen < len) {
805       int rlen;
806       in = ctx->req.last_input;
807       if(!in)
808         in = ctx->req.first_input = ctx->req.last_input =
809             ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
810       else if(in->start + in->size >= in->allocd) {
811         in->next = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
812         in = ctx->req.last_input = in->next;
813       }
814       /* pull next chunk */
815       if(ctx->conn.e == NULL) return -1;
816       rlen = ctx->conn.e->opset->read(ctx->conn.e->fd,
817                                       in->buff + in->start + in->size,
818                                       in->allocd - in->size - in->start,
819                                       mask, ctx->conn.e);
820       noitL(http_debug, " noit_http -> read(%d) = %d\n", ctx->conn.e->fd, rlen);
821     noitL(http_io, " noit_http:read(%d) => %d [\n%.*s\n]\n", ctx->conn.e->fd, rlen, rlen, in->buff + in->start + in->size);
822       if(rlen == -1 && errno == EAGAIN) {
823         /* We'd block to read more, but we have data,
824          * so do a short read */
825         if(ctx->req.first_input && ctx->req.first_input->size) break;
826         /* We've got nothing... */
827         noitL(http_debug, " ... noit_http_session_req_consume = -1 (EAGAIN)\n");
828         return -1;
829       }
830       if(rlen <= 0) {
831         noitL(http_debug, " ... noit_http_session_req_consume = -1 (error)\n");
832         return -1;
833       }
834       in->size += rlen;
835       crlen += rlen;
836     }
837   }
838   /* NOT REACHED */
839   return bytes_read;
840 }
841
842 int
843 noit_http_session_drive(eventer_t e, int origmask, void *closure,
844                         struct timeval *now, int *done) {
845   noit_http_session_ctx *ctx = closure;
846   int rv = 0;
847   int mask = origmask;
848
849   if(origmask & EVENTER_EXCEPTION)
850     goto abort_drive;
851
852   /* Drainage -- this is as nasty as it sounds
853    * The last request could have unread upload content, we would have
854    * noted that in noit_http_request_release.
855    */
856   noitL(http_debug, " -> noit_http_session_drive(%d) [%x]\n", e->fd, origmask);
857   while(ctx->drainage > 0) {
858     int len;
859     noitL(http_debug, "   ... draining last request(%d)\n", e->fd);
860     len = noit_http_session_req_consume(ctx, NULL, ctx->drainage, &mask);
861     if(len == -1 && errno == EAGAIN) {
862       noitL(http_debug, " <- noit_http_session_drive(%d) [%x]\n", e->fd, mask);
863       return mask;
864     }
865     if(len <= 0) goto abort_drive;
866     ctx->drainage -= len;
867   }
868
869  next_req:
870   if(ctx->req.complete != noit_true) {
871     int maybe_write_mask;
872     noitL(http_debug, "   -> noit_http_complete_request(%d)\n", e->fd);
873     mask = noit_http_complete_request(ctx, origmask);
874     noitL(http_debug, "   <- noit_http_complete_request(%d) = %d\n",
875           e->fd, mask);
876     _http_perform_write(ctx, &maybe_write_mask);
877     if(ctx->conn.e == NULL) goto release;
878     if(ctx->req.complete != noit_true) {
879       noitL(http_debug, " <- noit_http_session_drive(%d) [%x]\n", e->fd,
880             mask|maybe_write_mask);
881       return mask | maybe_write_mask;
882     }
883     noitL(http_debug, "HTTP start request (%s)\n", ctx->req.uri_str);
884     noit_http_process_querystring(&ctx->req);
885     inplace_urldecode(ctx->req.uri_str);
886   }
887
888   /* only dispatch if the response is not closed */
889   if(ctx->res.closed == noit_false) {
890     noitL(http_debug, "   -> dispatch(%d)\n", e->fd);
891     rv = ctx->dispatcher(ctx);
892     noitL(http_debug, "   <- dispatch(%d) = %d\n", e->fd, rv);
893   }
894
895   _http_perform_write(ctx, &mask);
896   if(ctx->res.complete == noit_true &&
897      ctx->conn.e &&
898      ctx->conn.needs_close == noit_true) {
899    abort_drive:
900     noit_http_log_request(ctx);
901     if(ctx->conn.e) {
902       ctx->conn.e->opset->close(ctx->conn.e->fd, &mask, ctx->conn.e);
903       ctx->conn.e = NULL;
904     }
905     goto release;
906   }
907   if(ctx->res.complete == noit_true) {
908     noit_http_log_request(ctx);
909     noit_http_request_release(ctx);
910     noit_http_response_release(ctx);
911   }
912   if(ctx->req.complete == noit_false) goto next_req;
913   if(ctx->conn.e) {
914     noitL(http_debug, " <- noit_http_session_drive(%d) [%x]\n", e->fd, mask|rv);
915     return mask | rv;
916   }
917   noitL(http_debug, " <- noit_http_session_drive(%d) [%x]\n", e->fd, 0);
918   goto abort_drive;
919
920  release:
921   *done = 1;
922   /* We're about to release, unhook us from the acceptor_closure so we
923    * don't get double freed */
924   if(ctx->ac->service_ctx == ctx) ctx->ac->service_ctx = NULL;
925   noit_http_ctx_session_release(ctx);
926   noitL(http_debug, " <- noit_http_session_drive(%d) [%x]\n", e->fd, 0);
927   return 0;
928 }
929
930 noit_http_session_ctx *
931 noit_http_session_ctx_new(noit_http_dispatch_func f, void *c, eventer_t e,
932                           acceptor_closure_t *ac) {
933   noit_http_session_ctx *ctx;
934   ctx = calloc(1, sizeof(*ctx));
935   ctx->ref_cnt = 1;
936   ctx->req.complete = noit_false;
937   ctx->conn.e = e;
938   ctx->max_write = DEFAULT_MAXWRITE;
939   ctx->dispatcher = f;
940   ctx->dispatcher_closure = c;
941   ctx->ac = ac;
942   return ctx;
943 }
944
945 noit_boolean
946 noit_http_response_status_set(noit_http_session_ctx *ctx,
947                               int code, const char *reason) {
948   if(ctx->res.output_started == noit_true) return noit_false;
949   ctx->res.protocol = ctx->req.protocol;
950   if(code < 100 || code > 999) return noit_false;
951   ctx->res.status_code = code;
952   if(ctx->res.status_reason) free(ctx->res.status_reason);
953   ctx->res.status_reason = strdup(reason);
954   return noit_true;
955 }
956 noit_boolean
957 noit_http_response_header_set(noit_http_session_ctx *ctx,
958                               const char *name, const char *value) {
959   if(ctx->res.output_started == noit_true) return noit_false;
960   noit_hash_replace(&ctx->res.headers, strdup(name), strlen(name),
961                     strdup(value), free, free);
962   return noit_true;
963 }
964 noit_boolean
965 noit_http_response_option_set(noit_http_session_ctx *ctx, u_int32_t opt) {
966   if(ctx->res.output_started == noit_true) return noit_false;
967   /* transfer and content encodings only allowed in HTTP/1.1 */
968   if(ctx->res.protocol != NOIT_HTTP11 &&
969      (opt & NOIT_HTTP_CHUNKED))
970     return noit_false;
971   if(ctx->res.protocol != NOIT_HTTP11 &&
972      (opt & (NOIT_HTTP_GZIP | NOIT_HTTP_DEFLATE)))
973     return noit_false;
974   if(((ctx->res.output_options | opt) &
975       (NOIT_HTTP_GZIP | NOIT_HTTP_DEFLATE)) ==
976         (NOIT_HTTP_GZIP | NOIT_HTTP_DEFLATE))
977     return noit_false;
978
979   /* Check out "accept" set */
980   if(!(opt & ctx->req.opts)) return noit_false;
981
982   ctx->res.output_options |= opt;
983   if(ctx->res.output_options & NOIT_HTTP_CHUNKED)
984     CTX_ADD_HEADER("Transfer-Encoding", "chunked");
985   if(ctx->res.output_options & (NOIT_HTTP_GZIP | NOIT_HTTP_DEFLATE)) {
986     CTX_ADD_HEADER("Vary", "Accept-Encoding");
987     if(ctx->res.output_options & NOIT_HTTP_GZIP)
988       CTX_ADD_HEADER("Content-Encoding", "gzip");
989     else if(ctx->res.output_options & NOIT_HTTP_DEFLATE)
990       CTX_ADD_HEADER("Content-Encoding", "deflate");
991   }
992   if(ctx->res.output_options & NOIT_HTTP_CLOSE) {
993     CTX_ADD_HEADER("Connection", "close");
994     ctx->conn.needs_close = noit_true;
995   }
996   return noit_true;
997 }
998 noit_boolean
999 noit_http_response_append(noit_http_session_ctx *ctx,
1000                           const void *b, size_t l) {
1001   struct bchain *o;
1002   int boff = 0;
1003   if(ctx->res.closed == noit_true) return noit_false;
1004   if(ctx->res.output_started == noit_true &&
1005      !(ctx->res.output_options & (NOIT_HTTP_CLOSE | NOIT_HTTP_CHUNKED)))
1006     return noit_false;
1007   if(!ctx->res.output)
1008     assert(ctx->res.output = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE));
1009   o = ctx->res.output;
1010   while(o->next) o = o->next;
1011   while(l > 0) {
1012     if(o->allocd == o->start + o->size) {
1013       /* Filled up, need another */
1014       o->next = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
1015       o->next->prev = o->next;
1016       o = o->next;
1017     }
1018     if(o->allocd > o->start + o->size) {
1019       int tocopy = MIN(l, o->allocd - o->start - o->size);
1020       memcpy(o->buff + o->start + o->size, (const char *)b + boff, tocopy);
1021       o->size += tocopy;
1022       boff += tocopy;
1023       l -= tocopy;
1024     }
1025   }
1026   return noit_true;
1027 }
1028 noit_boolean
1029 noit_http_response_append_bchain(noit_http_session_ctx *ctx,
1030                                  struct bchain *b) {
1031   struct bchain *o;
1032   if(ctx->res.closed == noit_true) return noit_false;
1033   if(ctx->res.output_started == noit_true &&
1034      !(ctx->res.output_options & (NOIT_HTTP_CHUNKED | NOIT_HTTP_CLOSE)))
1035     return noit_false;
1036   if(!ctx->res.output)
1037     ctx->res.output = b;
1038   else {
1039     o = ctx->res.output;
1040     while(o->next) o = o->next;
1041     o->allocd = o->size; /* so we know it is full */
1042     o->next = b;
1043     b->prev = o;
1044   }
1045   return noit_true;
1046 }
1047 noit_boolean
1048 noit_http_response_append_mmap(noit_http_session_ctx *ctx,
1049                                int fd, size_t len, int flags, off_t offset) {
1050   struct bchain *n;
1051   n = bchain_mmap(fd, len, flags, offset);
1052   if(n == NULL) return noit_false;
1053   return noit_http_response_append_bchain(ctx, n);
1054 }
1055 static int casesort(const void *a, const void *b) {
1056   return strcasecmp(*((const char **)a), *((const char **)b));
1057 }
1058 static int
1059 _http_construct_leader(noit_http_session_ctx *ctx) {
1060   int len = 0, tlen;
1061   struct bchain *b;
1062   const char *protocol_str;
1063   const char *key, *value;
1064   int klen, i;
1065   const char **keys;
1066   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1067
1068   assert(!ctx->res.leader);
1069   ctx->res.leader = b = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
1070
1071   protocol_str = ctx->res.protocol == NOIT_HTTP11 ?
1072                    "HTTP/1.1" :
1073                    (ctx->res.protocol == NOIT_HTTP10 ?
1074                      "HTTP/1.0" :
1075                      "HTTP/0.9");
1076   tlen = snprintf(b->buff, b->allocd, "%s %03d %s\r\n",
1077                   protocol_str, ctx->res.status_code,
1078                   ctx->res.status_reason ? ctx->res.status_reason : "unknown");
1079   if(tlen < 0) return -1;
1080   len = b->size = tlen;
1081
1082 #define CTX_LEADER_APPEND(s, slen) do { \
1083   if(b->size + slen > DEFAULT_BCHAINSIZE) { \
1084     b->next = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE); \
1085     assert(b->next); \
1086     b->next->prev = b; \
1087     b = b->next; \
1088   } \
1089   assert(DEFAULT_BCHAINSIZE >= b->size + slen); \
1090   memcpy(b->buff + b->start + b->size, s, slen); \
1091   b->size += slen; \
1092 } while(0)
1093   keys = alloca(sizeof(*keys)*ctx->res.headers.size);
1094   i = 0;
1095   while(noit_hash_next_str(&ctx->res.headers, &iter,
1096                            &key, &klen, &value)) {
1097     keys[i++] = key;
1098   }
1099   qsort(keys, i, sizeof(*keys), casesort);
1100   for(i=0;i<ctx->res.headers.size;i++) {
1101     int vlen;
1102     key = keys[i];
1103     klen = strlen(key);
1104     noit_hash_retr_str(&ctx->res.headers, key, klen, &value);
1105     vlen = strlen(value);
1106     CTX_LEADER_APPEND(key, klen);
1107     CTX_LEADER_APPEND(": ", 2);
1108     CTX_LEADER_APPEND(value, vlen);
1109     CTX_LEADER_APPEND("\r\n", 2);
1110   }
1111   CTX_LEADER_APPEND("\r\n", 2);
1112   return len;
1113 }
1114 static int memgzip2(noit_http_response *res, Bytef *dest, uLongf *destLen,
1115                     const Bytef *source, uLong sourceLen, int level,
1116                     int deflate_option, noit_boolean *done) {
1117   int err, skip=0, expect = Z_OK;
1118   if(!res->gzip) {
1119     res->gzip = calloc(1, sizeof(*res->gzip));
1120     err = deflateInit2(res->gzip, level, Z_DEFLATED, -15, 9,
1121                        Z_DEFAULT_STRATEGY);
1122     if (err != Z_OK) {
1123       noitL(noit_error, "memgzip2() -> deflateInit2: %d\n", err);
1124       return err;
1125     }
1126
1127     memcpy(dest, gzip_header, sizeof(gzip_header));
1128     skip = sizeof(gzip_header);
1129     *destLen -= skip;
1130   }
1131   res->gzip->next_in = (Bytef*)source;
1132   res->gzip->avail_in = (uInt)sourceLen;
1133   res->gzip->next_out = dest + skip;
1134   res->gzip->avail_out = (uInt)*destLen;
1135   if ((uLong)res->gzip->avail_out != *destLen) return Z_BUF_ERROR;
1136
1137   err = deflate(res->gzip, deflate_option);
1138
1139   if(deflate_option == Z_FINISH) expect = Z_STREAM_END;
1140   if (err != expect) {
1141     noitL(noit_error, "memgzip2() -> deflate: got %d, need %d\n", err, expect);
1142     deflateEnd(res->gzip);
1143     free(res->gzip);
1144     res->gzip = NULL;
1145     return err == Z_OK ? Z_BUF_ERROR : err;
1146   }
1147   if(done) *done = (err == Z_STREAM_END) ? noit_true : noit_false;
1148   *destLen = (*destLen - res->gzip->avail_out) + skip;
1149
1150   return Z_OK;
1151 }
1152 static noit_boolean
1153 _http_encode_chain(noit_http_response *res,
1154                    struct bchain *out, void *inbuff, int inlen,
1155                    noit_boolean final, noit_boolean *done) {
1156   int opts = res->output_options;
1157   /* implement gzip and deflate! */
1158   if(done && final) *done = noit_true;
1159   if(opts & NOIT_HTTP_GZIP) {
1160     uLongf olen;
1161     int err;
1162     olen = out->allocd - out->start - 2; /* leave 2 for the \r\n */
1163     err = memgzip2(res, (Bytef *)(out->buff + out->start), &olen,
1164                    (Bytef *)(inbuff), (uLong)inlen,
1165                    9, final ? Z_FINISH : Z_NO_FLUSH, done);
1166     if(Z_OK != err) {
1167       noitL(noit_error, "zlib compress2 error %d\n", err);
1168       return noit_false;
1169     }
1170     out->size += olen;
1171   }
1172   else if(opts & NOIT_HTTP_DEFLATE) {
1173     uLongf olen;
1174     olen = out->allocd - out->start - 2; /* leave 2 for the \r\n */
1175     if(Z_OK != compress2((Bytef *)(out->buff + out->start), &olen,
1176                          (Bytef *)(inbuff), (uLong)inlen,
1177                          9)) {
1178       noitL(noit_error, "zlib compress2 error\n");
1179       return noit_false;
1180     }
1181     out->size += olen;
1182   }
1183   else {
1184     /* leave 2 for the \r\n */
1185     if(inlen > out->allocd - out->start - 2) return noit_false;
1186     memcpy(out->buff + out->start, inbuff, inlen);
1187     out->size += inlen;
1188   }
1189   return noit_true;
1190 }
1191 struct bchain *
1192 noit_http_process_output_bchain(noit_http_session_ctx *ctx,
1193                                 struct bchain *in) {
1194   struct bchain *out;
1195   int ilen, maxlen = in->size, hexlen;
1196   int opts = ctx->res.output_options;
1197
1198   if(in->type == BCHAIN_MMAP &&
1199      0 == (opts & (NOIT_HTTP_GZIP | NOIT_HTTP_DEFLATE | NOIT_HTTP_CHUNKED))) {
1200     out = ALLOC_BCHAIN(0);
1201     out->buff = in->buff;
1202     out->type = in->type;
1203     out->size = in->size;
1204     out->allocd = in->allocd;
1205     in->type = BCHAIN_INLINE;
1206     return out;
1207   }
1208   /* a chunked header looks like: hex*\r\ndata\r\n */
1209   /* let's assume that content never gets "larger" */
1210   if(opts & NOIT_HTTP_GZIP) maxlen = deflateBound(NULL, in->size);
1211   else if(opts & NOIT_HTTP_DEFLATE) maxlen = compressBound(in->size);
1212
1213   /* So, the link size is the len(data) + 4 + ceil(log(len(data))/log(16)) */
1214   ilen = maxlen;
1215   hexlen = 0;
1216   while(ilen) { ilen >>= 4; hexlen++; }
1217   if(hexlen == 0) hexlen = 1;
1218
1219   out = ALLOC_BCHAIN(hexlen + 4 + maxlen);
1220   /* if we're chunked, let's give outselved hexlen + 2 prefix space */
1221   if(opts & NOIT_HTTP_CHUNKED) out->start = hexlen + 2;
1222   if(_http_encode_chain(&ctx->res, out,in->buff + in->start, in->size,
1223                         noit_false, NULL) == noit_false) {
1224     free(out);
1225     return NULL;
1226   }
1227   if(out->size == 0) {
1228     FREE_BCHAIN(out);
1229     out = ALLOC_BCHAIN(0);
1230   }
1231   if((out->size > 0) && (opts & NOIT_HTTP_CHUNKED)) {
1232     ilen = out->size;
1233     assert(out->start+out->size+2 <= out->allocd);
1234     out->buff[out->start + out->size++] = '\r';
1235     out->buff[out->start + out->size++] = '\n';
1236     out->start = 0;
1237     /* terminate */
1238     out->size += 2;
1239     out->buff[hexlen] = '\r';
1240     out->buff[hexlen+1] = '\n';
1241     /* backfill */
1242     out->size += hexlen;
1243     while(hexlen > 0) {
1244       out->buff[hexlen - 1] = _hexchars[ilen & 0xf];
1245       ilen >>= 4;
1246       hexlen--;
1247     }
1248     while(out->buff[out->start] == '0') {
1249       out->start++;
1250       out->size--;
1251     }
1252   }
1253   return out;
1254 }
1255 void
1256 raw_finalize_encoding(noit_http_response *res) {
1257   if(res->output_options & NOIT_HTTP_GZIP) {
1258     noit_boolean finished = noit_false;
1259     struct bchain *r = res->output_raw;
1260     while(r && r->next) r = r->next;
1261     while(finished == noit_false) {
1262       int hexlen, ilen;
1263       struct bchain *out = ALLOC_BCHAIN(DEFAULT_BCHAINSIZE);
1264
1265       /* The link size is the len(data) + 4 + ceil(log(len(data))/log(16)) */
1266       ilen = out->allocd;
1267       hexlen = 0;
1268       while(ilen) { ilen >>= 4; hexlen++; }
1269       if(hexlen == 0) hexlen = 1;
1270
1271       out->start += hexlen + 2;
1272       if(_http_encode_chain(res, out, "", 0, noit_true,
1273                             &finished) == noit_false) {
1274         FREE_BCHAIN(out);
1275         break;
1276       }
1277
1278       ilen = out->size;
1279       assert(out->start+out->size+2 <= out->allocd);
1280       out->buff[out->start + out->size++] = '\r';
1281       out->buff[out->start + out->size++] = '\n';
1282       out->start = 0;
1283       /* terminate */
1284       out->size += 2;
1285       out->buff[hexlen] = '\r';
1286       out->buff[hexlen+1] = '\n';
1287       /* backfill */
1288       out->size += hexlen;
1289       while(hexlen > 0) {
1290         out->buff[hexlen - 1] = _hexchars[ilen & 0xf];
1291         ilen >>= 4;
1292         hexlen--;
1293       }
1294       while(out->buff[out->start] == '0') {
1295         out->start++;
1296         out->size--;
1297       }
1298       if(r == NULL)
1299         res->output_raw = out;
1300       else {
1301         r->next = out;
1302         out->prev = r;
1303       }
1304       r = out;
1305     }
1306
1307     deflateEnd(res->gzip);
1308     free(res->gzip);
1309     res->gzip = NULL;
1310   }
1311 }
1312 static noit_boolean
1313 _noit_http_response_flush(noit_http_session_ctx *ctx,
1314                           noit_boolean final,
1315                           noit_boolean update_eventer) {
1316   struct bchain *o, *r;
1317   int mask, rv;
1318
1319   if(ctx->res.closed == noit_true) return noit_false;
1320   if(ctx->res.output_started == noit_false) {
1321     _http_construct_leader(ctx);
1322     ctx->res.output_started = noit_true;
1323   }
1324   /* encode output to output_raw */
1325   r = ctx->res.output_raw;
1326   while(r && r->next) r = r->next;
1327   /* r is the last raw output link */
1328   o = ctx->res.output;
1329   /* o is the first output link to process */
1330   while(o) {
1331     struct bchain *tofree, *n;
1332     n = noit_http_process_output_bchain(ctx, o);
1333     if(!n) {
1334       /* Bad, response stops here! */
1335       noitL(noit_error, "noit_http_process_output_bchain: NULL\n");
1336       while(o) { tofree = o; o = o->next; free(tofree); }
1337       final = noit_true;
1338       break;
1339     }
1340     if(r) {
1341       r->next = n;
1342       n->prev = r;
1343       r = n;
1344     }
1345     else {
1346       r = ctx->res.output_raw = n;
1347     }
1348     tofree = o; o = o->next; FREE_BCHAIN(tofree); /* advance and free */
1349   }
1350   ctx->res.output = NULL;
1351   if(final) {
1352     struct bchain *n;
1353     ctx->res.closed = noit_true;
1354     raw_finalize_encoding(&ctx->res);
1355     /* We could have just pushed in the only block */
1356     if(!r) r = ctx->res.output_raw;
1357     /* Advance to the end to append out ending */
1358     if(r) while(r->next) r = r->next;
1359     /* Create an ending */
1360     if(ctx->res.output_options & NOIT_HTTP_CHUNKED)
1361       n = bchain_from_data("0\r\n\r\n", 5);
1362     else
1363       n = NULL;
1364     /* Append an ending (chunked) */
1365     if(r) {
1366       r->next = n;
1367       if(n) n->prev = r;
1368     }
1369     else {
1370       ctx->res.output_raw = n;
1371     }
1372   }
1373
1374   rv = _http_perform_write(ctx, &mask);
1375   if(update_eventer && ctx->conn.e) {
1376     eventer_update(ctx->conn.e, mask);
1377   }
1378   if(rv < 0) return noit_false;
1379   /* If the write fails completely, the event will not be closed,
1380    * the following should not trigger the false case.
1381    */
1382   return ctx->conn.e ? noit_true : noit_false;
1383 }
1384
1385 noit_boolean
1386 noit_http_response_flush(noit_http_session_ctx *ctx,
1387                          noit_boolean final) {
1388   return _noit_http_response_flush(ctx, final, noit_true);
1389 }
1390 noit_boolean
1391 noit_http_response_flush_asynch(noit_http_session_ctx *ctx,
1392                                 noit_boolean final) {
1393   return _noit_http_response_flush(ctx, final, noit_false);
1394 }
1395
1396 noit_boolean
1397 noit_http_response_end(noit_http_session_ctx *ctx) {
1398   if(!noit_http_response_flush(ctx, noit_true)) return noit_false;
1399   return noit_true;
1400 }
1401
1402
1403 /* Helper functions */
1404
1405 static int
1406 noit_http_write_xml(void *vctx, const char *buffer, int len) {
1407   if(noit_http_response_append((noit_http_session_ctx *)vctx, buffer, len))
1408     return len;
1409   return -1;
1410 }
1411 static int
1412 noit_http_close_xml(void *vctx) {
1413   noit_http_response_end((noit_http_session_ctx *)vctx);
1414   return 0;
1415 }
1416 void
1417 noit_http_response_xml(noit_http_session_ctx *ctx, xmlDocPtr doc) {
1418   xmlOutputBufferPtr out;
1419   xmlCharEncodingHandlerPtr enc;
1420   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
1421   out = xmlOutputBufferCreateIO(noit_http_write_xml,
1422                                 noit_http_close_xml,
1423                                 ctx, enc);
1424   xmlSaveFormatFileTo(out, doc, "utf8", 1);
1425 }
1426
1427 void
1428 noit_http_init() {
1429   http_debug = noit_log_stream_find("debug/http");
1430   http_access = noit_log_stream_find("http/access");
1431   http_io = noit_log_stream_find("http/io");
1432 }
Note: See TracBrowser for help on using the browser.