root/src/noit_http.c

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

fix a memory leak

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