]> mj.ucw.cz Git - netgrind.git/blob - netgrind/http.c
d65672cd643354943e304bbdbb4f4007217c85f7
[netgrind.git] / netgrind / http.c
1 /*
2  *      Netgrind -- HTTP Analyser
3  *
4  *      (c) 2003--2013 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU General Public License.
8  */
9
10 #undef LOCAL_DEBUG
11
12 #include "lib/lib.h"
13 #include "lib/pools.h"
14 #include "netgrind/pkt.h"
15 #include "netgrind/netgrind.h"
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <alloca.h>
21 #include <time.h>
22 #include <netinet/in.h>
23
24 #define MAXLINE 4096
25
26 struct http_header {
27   node n;
28   byte *name, *value;
29   byte buf[1];
30 };
31
32 struct http_state {
33   struct flow *flow;
34   enum {
35     HTTP_IDLE,                  /* initialized, waiting for request */
36     HTTP_ERROR,                 /* protocol error, ignoring everything else */
37     HTTP_CUT,                   /* unexpected EOF in one direction, ignoring everything else */
38     HTTP_REQUEST,               /* parsing request */
39     HTTP_BODY_CHUNKED,          /* receiving body: chunked encoding */
40     HTTP_BODY_LENGTH,           /* receiving body: length given */
41     HTTP_BODY_INF,              /* receiving body: till EOF */
42     HTTP_RESPONSE,              /* parsing response */
43     HTTP_DONE,                  /* transaction finished, logging it */
44     HTTP_CONNECT,               /* inside CONNECT transaction */
45   } state;
46   byte *error;
47   u64 req_start_time, resp_start_time;
48   uns conn_id;
49   struct mempool *pool;
50   list tx_queue, rx_queue;
51   uns xact_id;
52   byte *req_line, *resp_line;
53   list req_headers, resp_headers;
54   byte line[MAXLINE];
55   uns line_len;
56   uns body_len;
57   uns body_trailer;
58   list *body_queue;
59   uns body_end_state;
60   uns body_total_size;
61   uns req_counter;
62   FILE *log_file;
63 };
64
65 char *http_log_dir;
66
67 static uns http_conn_counter;
68 static uns http_xact_counter;
69
70 static void http_open(struct flow *f, u64 when)
71 {
72   struct http_state *s = xmalloc_zero(sizeof(*s));
73   s->flow = f;
74   f->appl_data = s;
75   s->conn_id = http_conn_counter++;
76   DBG("HTTP: %d NEW %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d\n", s->conn_id,
77       IPQUAD(f->saddr), ntohs(f->sport), IPQUAD(f->daddr), ntohs(f->dport));
78   list_init(&s->tx_queue);
79   list_init(&s->rx_queue);
80   s->req_start_time = when;
81 }
82
83 static byte *http_lookup_hdr(list *l, byte *name)
84 {
85   struct http_header *h;
86   WALK_LIST(h, *l)
87     if (!strcasecmp(h->name, name))
88       return h->value;
89   return NULL;
90 }
91
92 static uns find_token(byte *hay, byte *needle)
93 {
94   if (!hay)
95     return 0;
96   while (*hay)
97     {
98       if (*hay == ' ' || *hay == '\t' || *hay == ',')
99         hay++;
100       else
101         {
102           byte *h = hay;
103           while (*hay && *hay != ',' && *hay != ' ' && *hay != '\t')
104             hay++;
105           uns old = *hay;
106           *hay = 0;
107           uns found = !strcasecmp(h, needle);
108           *hay = old;
109           if (found)
110             return 1;
111         }
112     }
113   return 0;
114 }
115
116 static byte *find_token_val(byte *hay, byte *needle)
117 {
118   if (!hay)
119     return NULL;
120   while (*hay)
121     {
122       if (*hay == ' ' || *hay == '\t' || *hay == ',')
123         hay++;
124       else
125         {
126           byte *h = hay;
127           while (*hay && *hay != ',' && *hay != ' ' && *hay != '\t' && *hay != '=')
128             hay++;
129           if (*hay == '=')
130             {
131               uns old = *hay;
132               *hay = 0;
133               uns found = !strcasecmp(h, needle);
134               *hay = old;
135               if (found)
136                 return hay + 1;
137             }
138           while (*hay && *hay != ',' && *hay != ' ' && *hay != '\t')
139             hay++;
140         }
141     }
142   return NULL;
143 }
144
145 /* Logging */
146
147 static void http_log_start(struct http_state *s)
148 {
149   if (!http_log_dir)
150     return;
151
152   char name[256], stamp[TIMESTAMP_LEN];
153   struct flow *f = s->flow;
154
155   sprintf(name, "%s/%06u-%d.%d.%d.%d:%d-%d.%d.%d.%d:%d", http_log_dir, s->xact_id,
156     IPQUAD(f->saddr), ntohs(f->sport), IPQUAD(f->daddr), ntohs(f->dport));
157   if (!(s->log_file = fopen(name, "w")))
158     die("Unable to create %s: %m", name);
159
160   format_timestamp(stamp, s->req_start_time);
161   fprintf(s->log_file, "; [%s] From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d (req %u)\n",
162     stamp, IPQUAD(f->saddr), ntohs(f->sport), IPQUAD(f->daddr), ntohs(f->dport),
163     s->req_counter + 1);
164 }
165
166 static void http_log_end(struct http_state *s)
167 {
168   if (!s->log_file)
169     return;
170   fclose(s->log_file);
171   s->log_file = NULL;
172 }
173
174 static void http_log_req_line(struct http_state *s, byte *line)
175 {
176   if (s->log_file)
177     fprintf(s->log_file, "> %s\n", line);
178 }
179
180 static void http_log_resp_line(struct http_state *s, byte *line)
181 {
182   if (s->log_file)
183     fprintf(s->log_file, "< %s\n", line);
184 }
185
186 static void http_log_body(struct http_state *s, byte *data, uns len)
187 {
188   if (s->log_file)
189     fwrite(data, len, 1, s->log_file);
190 }
191
192 /* Date parsing */
193
194 static int
195 validate_time(byte *a, byte *p)
196 {
197   while (*p && *a)
198     {
199       switch (*p)
200         {
201         case '.':
202           break;
203         case '$':
204           if (*a == ' ')
205             break;
206           /* Fall-thru */
207         case '#':
208           if (*a < '0' || *a > '9')
209             return 0;
210           break;
211         case 'a':
212           if (*a < 'a' || *a > 'z')
213             return 0;
214           break;
215         case 'A':
216           if (*a < 'A' || *a > 'Z')
217             return 0;
218           break;
219         case '@':
220           if ((*a < 'a' && *a > 'z') && (*a < 'A' && *a > 'Z'))
221             return 0;
222           break;
223         case '*':
224           return 1;
225         case '_':
226           if (*a != ' ' && *a != '-')
227             return 0;
228           break;
229         default:
230           if (*p != *a)
231             return 0;
232         }
233       p++;
234       a++;
235     }
236   return (*p == *a || *p == '*');
237 }
238
239 static int
240 twodig(byte *p)
241 {
242   if (p[0] == ' ')
243     return p[1] - '0';
244   else
245     return (p[0] - '0')*10 + p[1] - '0';
246 }
247
248 static int
249 fourdig(byte *p)
250 {
251   return twodig(p)*100 + twodig(p+2);
252 }
253
254 static byte short_months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
255
256 static int
257 find_month(byte *p)
258 {
259   byte *z = short_months;
260   int m = 0;
261
262   while (*z)
263     {
264       m++;
265       if (p[0] == z[0] && p[1] == z[1] && p[2] == z[2])
266         return m;
267       z += 3;
268     }
269   return 0;
270 }
271
272 static s64 http_parse_date(char *date)
273 {
274   char *p = date;
275   char *q;
276   struct tm tm;
277   int m, y;
278
279   if (!date)
280     return -1;
281
282   bzero(&tm, sizeof(tm));
283   if (validate_time(p, "Aaa, ##_Aaa_#### ##:##:##*"))
284     {                                   /* RFC 822/1123 */
285       tm.tm_mday = twodig(p+5);
286       m = find_month(p+8);
287       if (!m)
288         goto unk;
289       tm.tm_mon = m - 1;
290       y = fourdig(p+12);
291       if (y < 1970)
292         goto unk;
293       tm.tm_year = y - 1900;
294       tm.tm_hour = twodig(p+17);
295       tm.tm_min = twodig(p+20);
296       tm.tm_sec = twodig(p+23);
297       goto ook;
298     }
299   if (q = strchr(p, ','))
300     {
301       int flag = 0;
302       if (validate_time(q, ", ##-Aaa-## ##:##:## GMT")) /* RFC 850 */
303         flag = 1;
304       else if (validate_time(q, ", #-Aaa-## ##:##:## GMT"))     /* Incorrectly implemented RFC 850 */
305         flag = 2;
306       if (flag)
307         {
308           tm.tm_mday = twodig(q+2);
309           if (flag == 2)
310             q--;
311           m = find_month(q+5);
312           if (!m)
313             goto unk;
314           tm.tm_mon = m - 1;
315           tm.tm_year = twodig(q+9);
316           if (tm.tm_year < 76)
317             tm.tm_year += 100;
318           tm.tm_hour = twodig(q+12);
319           tm.tm_min = twodig(q+15);
320           tm.tm_sec = twodig(q+18);
321           goto ook;
322         }
323     }
324   if (validate_time(p, "Aaa Aaa $# ##:##:## ####"))
325     {                                   /* ANSI C asctime() */
326       m = find_month(p+4);
327       if (!m)
328         goto unk;
329       tm.tm_mon = m - 1;
330       tm.tm_mday = twodig(p+8);
331       tm.tm_hour = twodig(p+11);
332       tm.tm_min = twodig(p+14);
333       tm.tm_sec = twodig(p+17);
334       y = fourdig(p+20);
335       if (y < 1980)
336         goto unk;
337       tm.tm_year = y - 1900;
338       goto ook;
339     }
340   if (!strcmp(p, "0") || !strcmp(p, "-1"))
341     return 0;                           /* Porcine hacks */
342
343 unk:
344   fprintf(stderr, "Unable to parse date `%s'\n", date);
345   return 0;
346
347 ook:
348   m = timegm(&tm);
349   if (m == (time_t) -1)
350     goto unk;
351   return (u64) m * 1000000;
352 }
353
354 static void http_cache_report(struct http_state *s, char *buf)
355 {
356   byte *rq_pragma = http_lookup_hdr(&s->req_headers, "Pragma:");
357   byte *rp_pragma = http_lookup_hdr(&s->resp_headers, "Pragma:");
358   byte *rq_cc = http_lookup_hdr(&s->req_headers, "Cache-control:");
359   byte *rp_cc = http_lookup_hdr(&s->resp_headers, "Cache-control:");
360   byte *rp_vary = http_lookup_hdr(&s->resp_headers, "Vary:");
361   byte *rp_cache = http_lookup_hdr(&s->resp_headers, "X-Cache:");
362   s64 rp_expires = http_parse_date(http_lookup_hdr(&s->resp_headers, "Expires:"));
363   s64 rp_date = http_parse_date(http_lookup_hdr(&s->resp_headers, "Date:"));
364   s64 rp_time = rp_date > 0 ? rp_date : s->resp_line ? (s64) s->resp_start_time : (s64) s->req_start_time;
365
366   // Cache control in request
367   if (find_token(rq_pragma, "no-cache") || find_token(rq_cc, "no-cache"))
368     buf[0] = 'N';
369   else if (find_token(rq_cc, "max-age=0") || find_token(rq_cc, "must-revalidate"))
370     buf[0] = 'R';
371   else
372     buf[0] = '.';
373
374   // HTTP/1.0 cache control in reply
375   uns expired10 = (rp_expires > 0 && rp_expires <= rp_time);
376   uns nocache10 = find_token(rp_pragma, "no-cache");
377
378   // Expiration of reply
379   byte *rp_maxage_arg = find_token_val(rp_cc, "max-age");
380   s64 rp_maxage;
381   if (rp_maxage_arg)
382     rp_maxage = (s64) atoi(rp_maxage_arg) * 1000000;
383   else if (expired10)
384     rp_maxage = 0;
385   else if (rp_expires >= rp_time)
386     rp_maxage = rp_expires - rp_time;
387   else
388     rp_maxage = -1;
389 #if 0
390   if (s->log_file)
391     fprintf(s->log_file, "; rp_expires=%Ld rp_time=%Ld rp_maxage=%Ld expired10=%d nocache10=%d\n", rp_expires, rp_time, rp_maxage, expired10, nocache10);
392 #endif
393
394   // Cache control in reply
395   if (nocache10 || find_token(rp_cc, "no-cache"))
396     buf[1] = 'N';
397   else if (find_token(rp_cc, "private"))
398     buf[1] = 'P';
399   else if (find_token(rp_cc, "no-store"))
400     buf[1] = 'S';
401   else if (expired10 || !rp_maxage)
402     buf[1] = 'X';
403   else if (rp_vary)
404     buf[1] = 'V';
405   else if (find_token(rp_cc, "must-revalidate"))
406     buf[1] = 'R';
407   else if (rp_maxage > 0 && rp_maxage < (s64) 300 * 1000000)
408     buf[1] = 'E';
409   else if (rp_maxage > 0)
410     buf[1] = 'L';
411   else
412     buf[1] = '.';
413
414   // Do HTTP/1.1 and HTTP/1.0 behaviour match?
415   if (buf[1] != '.' && buf[1] != 'E' && buf[1] != 'L' && !expired10 && !nocache10)
416     buf[1] |= 0x20;             // Lowercase
417
418   // Validators in reply
419   byte *rp_etag = http_lookup_hdr(&s->resp_headers, "ETag:");
420   s64 rp_lastmod = http_parse_date(http_lookup_hdr(&s->resp_headers, "Last-Modified:"));
421   if (rp_etag)
422     {
423       if (rp_etag[0] == 'W' && rp_etag[1] == '/')
424         buf[2] = 'W';
425       else
426         buf[2] = 'E';
427     }
428   else if (rp_lastmod > 0)
429     buf[2] = 'L';
430   else
431     buf[2] = '.';
432
433   // Is there cache status in reply?
434   if (!rp_cache)
435     buf[3] = '.';
436   else if (!strncmp(rp_cache, "HIT ", 4))
437     buf[3] = '+';
438   else if (!strncmp(rp_cache, "MISS ", 5))
439     buf[3] = '-';
440   else
441     buf[3] = '?';
442
443   buf[4] = 0;
444 }
445
446 static void http_report(struct flow *f, struct http_state *s, u64 when, byte *reason)
447 {
448   byte *method, *url, *x, *y, *stat;
449
450   if (!(method = s->req_line))
451     {
452       http_log_end(s);
453       return;
454     }
455
456   /* Analyse request line */
457   url = method;
458   while (*url && *url != ' ')
459     url++;
460   while (*url == ' ')
461     *url++ = 0;
462   x = url;
463   while (*x != ' ')
464     x++;
465   *x = 0;
466
467   /* Analyse response line */
468   if (stat = s->resp_line)
469     {
470       while (*stat && *stat != ' ')
471         stat++;
472       while (*stat == ' ')
473         stat++;
474       x = stat;
475       while (*x && *x != ' ')
476         x++;
477       *x = 0;
478     }
479   else
480     stat = "";
481   if (!reason)
482     reason = stat[0] ? stat : (byte*)"???";
483
484   /* Reconstruct full URL */
485   if (!strstr(url, "://") && strcasecmp(method, "CONNECT"))
486     {
487       if (!(x = http_lookup_hdr(&s->req_headers, "Host:")))
488         x = "???";
489       y = url;
490       url = alloca(7 + strlen(x) + strlen(y) + 1);
491       sprintf(url, "http://%s%s", x, y);
492     }
493   char *ffor = http_lookup_hdr(&s->req_headers, "X-Forwarded-For:");
494
495   /* Find out cacheability */
496   char cache_flags[16];
497   http_cache_report(s, cache_flags);
498
499   /* Format log message */
500   byte stamp[TIMESTAMP_LEN], src[22], dst[22];
501   sprintf(src, "%d.%d.%d.%d:%d", IPQUAD(f->saddr), ntohs(f->sport));
502   sprintf(dst, "%d.%d.%d.%d:%d", IPQUAD(f->daddr), ntohs(f->dport));
503   format_timestamp(stamp, s->req_start_time);
504   u64 ttotal = when - s->req_start_time;
505   u64 tresp = (s->resp_line ? (s->resp_start_time - s->req_start_time) : 0);
506   byte *ctype = (http_lookup_hdr(&s->resp_headers, "Content-type:") ? : http_lookup_hdr(&s->req_headers, "Content-type:")) ? : (byte*)"-";
507   byte *sep;
508   if (sep = strchr(ctype, ';'))
509     *sep = 0;
510   if (!s->xact_id)
511     printf("# id   timestamp               source                destination           forwarded-for   res cach que   length total time  wait time ctype      method URL\n");
512          /* 000000 2003-06-06 22:53:38.642 81.27.194.19:1175     205.217.153.53:80     123.123.123.123 200 ....   0    14030      0.957      0.444 text/plain GET http://... */
513   printf("%06u %s %-21s %-21s %-15s %-3s %s %3d %8d %6d.%03d %6d.%03d %-12s %s %s\n",
514          s->xact_id, stamp, src, dst, (ffor ? : "-"), reason,
515          cache_flags,
516          s->req_counter,
517          s->body_total_size,
518          (uns)(ttotal/1000000), (uns)(ttotal%1000000)/1000,
519          (uns)(tresp/1000000), (uns)(tresp%1000000)/1000,
520          ctype, method, url);
521
522   http_log_end(s);
523   s->req_counter++;
524 }
525
526 static void http_close(struct flow *f, int cause, u64 when)
527 {
528   struct http_state *s = f->appl_data;
529   DBG("HTTP: %d CLOSE in state %d (cause %d)\n", s->conn_id, s->state, cause);
530   if (cause != CAUSE_CLOSE)
531     {
532       if (s->state != HTTP_IDLE)
533         {
534           byte buf[16];
535           sprintf(buf, "T%s", flow_cause_names_short[cause]);
536           http_report(f, s, when, buf);
537         }
538     }
539   else
540     switch (s->state)
541       {
542       case HTTP_ERROR:
543         http_report(f, s, when, "ERR");
544         break;
545       case HTTP_CUT:
546         http_report(f, s, when, "CUT");
547         break;
548       case HTTP_CONNECT:
549         http_report(f, s, when, "FIN");
550         break;
551       default: ;
552       }
553   http_log_end(s);
554   pkt_flush_queue(&s->rx_queue);
555   pkt_flush_queue(&s->tx_queue);
556   if (s->pool)
557     mp_delete(s->pool);
558   xfree(s);
559 }
560
561 static struct http_header *http_get_line(struct http_state *s, list *l)
562 {
563   for(;;)
564     {
565       struct pkt *p = list_head(l);
566       if (!p)
567         return NULL;
568       while (p->data < p->stop)
569         {
570           uns c = *p->data++;
571           if (c == '\r')
572             ;
573           else if (c == '\n')
574             {
575               struct http_header *h = mp_alloc(s->pool, sizeof(*h) + s->line_len);
576               memcpy(h->buf, s->line, s->line_len);
577               h->buf[s->line_len] = 0;
578               h->name = h->value = NULL;
579               s->line_len = 0;
580               return h;
581             }
582           else if (s->line_len >= MAXLINE-1)
583             {
584               DBG("HTTP: Line too long!\n");
585               s->state = HTTP_ERROR;
586               return NULL;
587             }
588           else
589             s->line[s->line_len++] = c;
590         }
591       list_remove(&p->n);
592       pkt_free(p);
593     }
594 }
595
596 static int http_skip_body_bytes(struct http_state *s)
597 {
598   for(;;)
599     {
600       struct pkt *p = list_head(s->body_queue);
601       if (!p)
602         return 0;
603       uns avail = pkt_len(p);
604       uns want = s->body_len;
605       uns go = MIN(avail, want);
606       http_log_body(s, p->data, go);
607       p->data += go;
608       s->body_len -= go;
609       s->body_total_size += go;
610       if (!s->body_len)
611         return 1;
612       if (!pkt_len(p))
613         {
614           list_remove(&p->n);
615           pkt_free(p);
616         }
617     }
618 }
619
620 static int http_have_input(list *l)
621 {
622   for(;;)
623     {
624       struct pkt *p = list_head(l);
625       if (!p)
626         return 0;
627       if (pkt_len(p))
628         return 1;
629       list_remove(&p->n);
630       pkt_free(p);
631     }
632 }
633
634 static void http_init_xact(struct http_state *s)
635 {
636   list_init(&s->req_headers);
637   list_init(&s->resp_headers);
638   if (s->pool)
639     mp_flush(s->pool);
640   else
641     s->pool = mp_new(4096);
642   s->req_line = s->resp_line = NULL;
643   s->line_len = 0;
644   s->body_total_size = 0;
645   s->xact_id = http_xact_counter++;
646
647   http_log_start(s);
648 }
649
650 static void http_parse_hdr(list *l, struct http_header *h)
651 {
652   byte *x = h->buf;
653   h->name = x;
654   while (*x && *x != ' ' && *x != '\t')
655     x++;
656   while (*x == ' ' || *x == '\t')
657     *x++ = 0;
658   h->value = x;
659   list_add_tail(l, &h->n);
660 }
661
662 static int http_ask_body(struct http_state *s, list *hdr)
663 {
664   byte *x;
665   if (x = http_lookup_hdr(hdr, "Transfer-Encoding:"))
666     {
667       DBG("\tBody encoding: %s\n", x);
668       if (!strcasecmp(x, "chunked"))
669         {
670           s->state = HTTP_BODY_CHUNKED;
671           s->body_len = 0;
672           s->body_trailer = 0;
673         }
674       else
675         s->state = HTTP_ERROR;
676     }
677   else if (x = http_lookup_hdr(hdr, "Content-Length:"))
678     {
679       s->body_len = atol(x);
680       DBG("\tBody length: %d\n", s->body_len);
681       s->state = HTTP_BODY_LENGTH;
682     }
683   else
684     return 0;
685   return 1;
686 }
687
688 static void http_parse_req(struct http_state *s)
689 {
690   if (!strstr(s->req_line, " HTTP/1"))
691     {
692       DBG("\tNot a HTTP/1.x request!\n");
693       s->state = HTTP_ERROR;
694     }
695   else if (http_ask_body(s, &s->req_headers))
696     ;
697   else if (!strncasecmp(s->req_line, "POST ", 4))
698     {
699       DBG("\tPOST with no request body, that smells!\n");
700       s->state = HTTP_BODY_INF;
701     }
702   else
703     {
704       DBG("\tNo request body, awaiting reply\n");
705       s->state = HTTP_RESPONSE;
706     }
707   s->body_queue = &s->tx_queue;
708   s->body_end_state = HTTP_RESPONSE;
709 }
710
711 static void http_parse_resp(struct http_state *s)
712 {
713   if (!strncasecmp(s->req_line, "HEAD ", 5))
714     {
715       DBG("\tHEAD has no body :)\n");
716       s->state = HTTP_DONE;
717     }
718   else if (http_ask_body(s, &s->resp_headers))
719     ;
720   else if (!strncasecmp(s->req_line, "GET ", 4) && strstr(s->resp_line, " 200 "))
721     {
722       DBG("\tGET with no response body, that smells!\n");
723       s->state = HTTP_BODY_INF;
724     }
725   else
726     {
727       DBG("\tNo response body\n");
728       s->state = HTTP_DONE;
729     }
730   s->body_queue = &s->rx_queue;
731   s->body_end_state = HTTP_DONE;
732 }
733
734 static void http_input(struct flow *f, int dir, struct pkt *p)
735 {
736   struct http_state *s = f->appl_data;
737   struct http_header *h;
738   int fin_tx = (f->pipe[0].state == FLOW_FINISHED);
739   int fin_rx = (f->pipe[1].state == FLOW_FINISHED);
740
741   // DBG("dir=%d txf=%d rxf=%d len=%d\n", dir, fin_tx, fin_rx, pkt_len(p));
742   if (s->state == HTTP_ERROR || s->state == HTTP_CUT)
743     {
744       DBG("HTTP: %d DROPPING INPUT\n", s->conn_id);
745       pkt_free(p);
746       return;
747     }
748   if (pkt_len(p))
749     list_add_tail((dir ? &s->tx_queue : &s->rx_queue), &p->n);
750   for(;;)
751     {
752       DBG("HTTP: %d STATE %d\n", s->conn_id, s->state);
753       switch (s->state)
754         {
755         case HTTP_IDLE:
756           if (fin_tx || !http_have_input(&s->tx_queue))
757             return;
758           s->state = HTTP_REQUEST;
759           if (!s->req_start_time)
760             s->req_start_time = p->timestamp;
761           http_init_xact(s);
762           break;
763         case HTTP_REQUEST:
764           if (fin_tx || fin_rx)
765             goto cut;
766           if (!(h = http_get_line(s, &s->tx_queue)))
767             return;
768           DBG("\t>> %s\n", h->buf);
769           http_log_req_line(s, h->buf);
770           if (!s->req_line)
771             {
772               if (!h->buf[0])
773                 goto err;
774               s->req_line = h->buf;
775             }
776           else if (h->buf[0])
777             http_parse_hdr(&s->req_headers, h);
778           else
779             http_parse_req(s);
780           break;
781         case HTTP_BODY_LENGTH:
782           if (fin_rx)
783             goto cut;
784           if (!http_skip_body_bytes(s))
785             return;
786           DBG("\tEnd of body\n");
787           s->state = s->body_end_state;
788           break;
789         case HTTP_BODY_CHUNKED:
790           if (fin_rx)
791             goto cut;
792           if (s->body_len)
793             {
794               if (!http_skip_body_bytes(s))
795                 return;
796             }
797           else if (s->body_trailer)
798             {
799               if (!(h = http_get_line(s, s->body_queue)))
800                 return;
801               if (!h->buf[0])
802                 {
803                   DBG("\tEnd of chunk-encoded body\n");
804                   s->state = s->body_end_state;
805                 }
806             }
807           else
808             {
809               if (!(h = http_get_line(s, s->body_queue)))
810                 return;
811               if (sscanf(h->buf, "%x", &s->body_len) != 1)
812                 goto err;
813               if (s->body_len)
814                 s->body_len += 2; /* extra CRLF */
815               else                /* last chunk */
816                 s->body_trailer = 1;
817             }
818           break;
819         case HTTP_BODY_INF:
820           s->body_len = ~0U;
821           http_skip_body_bytes(s);
822           if (fin_rx)
823             {
824               DBG("\tEnd of FIN-delimited body\n");
825               s->state = s->body_end_state;
826             }
827           else
828             return;
829           break;
830         case HTTP_RESPONSE:
831           if (fin_rx)
832             goto cut;
833           if (!(h = http_get_line(s, &s->rx_queue)))
834             return;
835           DBG("\t<< %s\n", h->buf);
836           http_log_resp_line(s, h->buf);
837           if (!s->resp_line)
838             {
839               if (!h->buf[0])
840                 goto err;
841               s->resp_line = h->buf;
842               s->resp_start_time = p->timestamp;
843             }
844           else if (h->buf[0])
845             http_parse_hdr(&s->resp_headers, h);
846           else
847             http_parse_resp(s);
848           break;
849         case HTTP_DONE:
850           DBG("\tTransaction finished.\n");
851           if (!strncasecmp(s->req_line, "CONNECT ", 8))
852             {
853               s->state = HTTP_CONNECT;
854               return;
855             }
856           http_report(f, s, p->timestamp, NULL);
857           s->state = HTTP_IDLE;
858           s->req_start_time = 0;
859           break;
860         case HTTP_CONNECT:
861           s->body_len = ~0U;
862           s->body_queue = &s->rx_queue;
863           http_skip_body_bytes(s);
864           s->body_len = ~0U;
865           s->body_queue = &s->tx_queue;
866           http_skip_body_bytes(s);
867           return;
868         case HTTP_ERROR:
869         case HTTP_CUT:
870           return;
871         default:
872           ASSERT(0);
873         }
874     }
875
876  err:
877   DBG("HTTP: %d ERROR: PROTOCOL VIOLATION\n", s->conn_id);
878   s->state = HTTP_ERROR;
879   return;
880
881  cut:
882   DBG("HTTP: %d ERROR: UNEXPECTED EOF\n", s->conn_id);
883   s->state = HTTP_CUT;
884 }
885
886 struct appl_hooks appl_http = {
887   .open = http_open,
888   .input = http_input,
889   .close = http_close
890 };