root/src/noit_http.c

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

valgrind points out that this was uninitialized

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