root/src/noit_http.c

Revision 84b15c2c96cf473ee3add485c0e1ac3ee2be9906, 45.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

implement and prefer gzip

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