root / trunk / src / dot / proxy / http_parse.c

Revision 7795, 47.5 kB (checked in by BradNeuberg, 22 months ago)

Created way to have JavaScript? layer 'query' to see if the PAC file has been updated yet -- if not, we tell the user to restart their browser

Line 
1/*
2Copyright (c) 2003-2006 by Juliusz Chroboczek
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE SOFTWARE.
21*/
22
23#include "polipo.h"
24
25static int getNextWord(const char *buf, int i, int *x_return, int *y_return);
26static int getNextToken(const char *buf, int i, int *x_return, int *y_return);
27static int getNextTokenInList(const char *buf, int i,
28                              int *x_return, int *y_return,
29                              int *z_return, int *t_return,
30                              int *end_return);
31
32static AtomPtr atomConnection, atomProxyConnection, atomContentLength,
33    atomHost, atomAcceptRange, atomTE,
34    atomReferer, atomProxyAuthenticate, atomProxyAuthorization,
35    atomKeepAlive, atomTrailers, atomUpgrade, atomDate, atomExpires,
36    atomIfModifiedSince, atomIfUnmodifiedSince, atomIfRange, atomLastModified,
37    atomIfMatch, atomIfNoneMatch, atomAge, atomTransferEncoding,
38    atomETag, atomCacheControl, atomPragma, atomContentRange, atomRange,
39    atomVia, atomVary, atomExpect, atomAuthorization,
40    atomSetCookie, atomCookie, atomCookie2,
41    atomXPolipoDate, atomXPolipoAccess, atomXPolipoLocation,
42    atomXPolipoBodyOffset;
43
44AtomPtr atomContentType, atomContentEncoding;
45
46int censorReferer = 0;
47int laxHttpParser = 1;
48
49static AtomListPtr censoredHeaders;
50
51void
52preinitHttpParser()
53{
54    CONFIG_VARIABLE_SETTABLE(censorReferer, CONFIG_TRISTATE, configIntSetter,
55                             "Censor referer headers.");
56    censoredHeaders = makeAtomList(NULL, 0);
57    if(censoredHeaders == NULL) {
58        do_log(L_ERROR, "Couldn't allocate censored atoms.\n");
59        exit(1);
60    }
61    CONFIG_VARIABLE(censoredHeaders, CONFIG_ATOM_LIST_LOWER,
62                    "Headers to censor.");
63    CONFIG_VARIABLE_SETTABLE(laxHttpParser, CONFIG_BOOLEAN, configIntSetter,
64                             "Ignore unknown HTTP headers.");
65}
66
67void
68initHttpParser()
69{
70#define A(name, value) name = internAtom(value); if(!name) goto fail;
71    /* These must be in lower-case */
72    A(atomConnection, "connection");
73    A(atomProxyConnection, "proxy-connection");
74    A(atomContentLength, "content-length");
75    A(atomHost, "host");
76    A(atomAcceptRange, "accept-range");
77    A(atomTE, "te");
78    A(atomReferer, "referer");
79    A(atomProxyAuthenticate, "proxy-authenticate");
80    A(atomProxyAuthorization, "proxy-authorization");
81    A(atomKeepAlive, "keep-alive");
82    A(atomTrailers, "trailers");
83    A(atomUpgrade, "upgrade");
84    A(atomDate, "date");
85    A(atomExpires, "expires");
86    A(atomIfModifiedSince, "if-modified-since");
87    A(atomIfUnmodifiedSince, "if-unmodified-since");
88    A(atomIfRange, "if-range");
89    A(atomLastModified, "last-modified");
90    A(atomIfMatch, "if-match");
91    A(atomIfNoneMatch, "if-none-match");
92    A(atomAge, "age");
93    A(atomTransferEncoding, "transfer-encoding");
94    A(atomETag, "etag");
95    A(atomCacheControl, "cache-control");
96    A(atomPragma, "pragma");
97    A(atomContentRange, "content-range");
98    A(atomRange, "range");
99    A(atomVia, "via");
100    A(atomContentType, "content-type");
101    A(atomContentEncoding, "content-encoding");
102    A(atomVary, "vary");
103    A(atomExpect, "expect");
104    A(atomAuthorization, "authorization");
105    A(atomSetCookie, "set-cookie");
106    A(atomCookie, "cookie");
107    A(atomCookie2, "cookie2");
108    A(atomXPolipoDate, "x-polipo-date");
109    A(atomXPolipoAccess, "x-polipo-access");
110    A(atomXPolipoLocation, "x-polipo-location");
111    A(atomXPolipoBodyOffset, "x-polipo-body-offset");
112#undef A
113    return;
114
115 fail:
116    do_log(L_ERROR, "Couldn't allocate atom.\n");
117    exit(1);
118}
119
120static int
121getNextWord(const char *restrict buf, int i, int *x_return, int *y_return)
122{
123    int x, y;
124    while(buf[i] == ' ') i++;
125    if(buf[i] == '\n' || buf[i] == '\r') return -1;
126    x = i;
127    while(buf[i] > 32 && buf[i] < 127) i++;
128    y = i;
129
130    *x_return = x;
131    *y_return = y;
132
133    return 0;
134}
135
136static int
137skipComment(const char *restrict buf, int i)
138{
139    assert(buf[i] == '(');
140
141    i++;
142    while(1) {
143        if(buf[i] == '\\' && buf[i + 1] == ')') i+=2;
144        else if(buf[i] == ')') return i + 1;
145        else if(buf[i] == '\n') {
146            if(buf[i + 1] == ' ' || buf[i + 1] == '\t')
147                i += 2;
148            else
149                return -1;
150        } else if(buf[i] == '\r') {
151            if(buf[i + 1] != '\n') return -1;
152            if(buf[i + 2] == ' ' || buf[i + 2] == '\t')
153                i += 3;
154            else
155                return -1;
156        } else {
157            i++;
158        }
159    }
160    return i;
161}
162           
163
164static int
165skipWhitespace(const char *restrict buf, int i)
166{
167    while(1) {
168        if(buf[i] == ' ' || buf[i] == '\t')
169            i++;
170        else  if(buf[i] == '(') {
171            i = skipComment(buf, i);
172            if(i < 0) return -1;
173        } else if(buf[i] == '\n') {
174            if(buf[i + 1] == ' ' || buf[i + 1] == '\t')
175                i += 2;
176            else
177                return i;
178        } else if(buf[i] == '\r' && buf[i + 1] == '\n') {
179            if(buf[i + 2] == ' ' || buf[i + 2] == '\t')
180                i += 3;
181            else
182                return i;
183        } else
184            return i;
185    }
186}
187
188static int
189getNextToken(const char *restrict buf, int i, int *x_return, int *y_return)
190{
191    int x, y;
192 again:
193    while(buf[i] == ' ' || buf[i] == '\t')
194        i++;
195    if(buf[i] == '(') {
196        i++;
197        while(buf[i] != ')') {
198            if(buf[i] == '\n' || buf[i] == '\r')
199                return -1;
200            if(buf[i] == '\\' && buf[i + 1] != '\n' && buf[i + 1] != '\r')
201                buf += 2;
202            else
203                buf++;
204        }
205        goto again;
206    }
207    if(buf[i] == '\n') {
208        if(buf[i + 1] == ' ' || buf[i + 1] == '\t') {
209            i += 2;
210            goto again;
211        } else {
212            return -1;
213        }
214    }
215    if(buf[i] == '\r') {
216        if(buf[i + 1] == '\n' && (buf[i + 2] == ' ' || buf[i + 2] == '\t')) {
217            i += 3;
218            goto again;
219        } else {
220            return -1;
221        }
222    }
223    x = i;
224    while(buf[i] > 32 && buf[i] < 127) {
225        switch(buf[i]) {
226        case '(': case ')': case '<': case '>': case '@':
227        case ',': case ';': case ':': case '\\': case '/':
228        case '[': case ']': case '?': case '=':
229        case '{': case '}': case ' ': case '\t':
230            goto out;
231        default:
232            i++;
233        }
234    }
235 out:
236    y = i;
237
238    *x_return = x;
239    *y_return = y;
240
241    return y;
242}
243
244static int
245getNextETag(const char * restrict buf, int i,
246            int *x_return, int *y_return, int *weak_return)
247{
248    int weak = 0;
249    int x, y;
250    while(buf[i] == ' ' || buf[i] == '\t')
251        i++;
252    if(buf[i] == 'W' && buf[i + 1] == '/') {
253        weak = 1;
254        i += 2;
255    }
256    if(buf[i] == '"')
257        i++;
258    else
259        return -1;
260
261    x = i;
262    while(buf[i] != '"') {
263        if(buf[i] == '\r' && buf[i] == '\n')
264            return -1;
265        i++;
266    }
267    y = i;
268    i++;
269
270    *x_return = x;
271    *y_return = y;
272    *weak_return = weak;
273    return i;
274}
275
276static int
277getNextTokenInList(const char *restrict buf, int i,
278                   int *x_return, int *y_return,
279                   int *z_return, int *t_return,
280                   int *end_return)
281{
282    int j, x, y, z = -1, t = -1, end;
283    j = getNextToken(buf, i, &x, &y);
284    if(j < 0)
285        return -1;
286    while(buf[j] == ' ' || buf[j] == '\t')
287        j++;
288
289    if(buf[j] == '=') {
290        j++;
291        while(buf[j] == ' ' || buf[j] == '\t')
292            j++;
293        z = j;
294        while(buf[j] != ',' && buf[j] != '\n' && buf[j] != '\r')
295            j++;
296    }
297
298    if(buf[j] == '\n' || buf[j] == '\r') {
299        if(buf[j] == '\r') {
300            if(buf[j + 1] != '\n')
301                return -1;
302            j += 2;
303        } else
304            j++;
305        end = 1;
306        if(buf[j] == ' ' || buf[j] == '\t') {
307            while(buf[j] == ' ' || buf[j] == '\t')
308                j++;
309            end = 0;
310        }
311    } else if(buf[j] == ',') {
312        j++;
313        while(buf[j] == ' ' || buf[j] == '\t')
314            j++;
315        end = 0;
316    } else {
317        return -1;
318    }
319
320    *x_return = x;
321    *y_return = y;
322    if(z_return)
323        *z_return = z;
324    if(t_return)
325        *t_return = t;
326    *end_return = end;
327    return j;
328}
329
330static inline int
331token_compare(const char *buf, int start, int end, const char *s)
332{
333    return (strcasecmp_n(s, buf + start, end - start) == 0);
334}
335
336static int
337skipEol(const char *restrict buf, int i)
338{
339    while(buf[i] == ' ')
340        i++;
341    if(buf[i] == '\n')
342        return i + 1;
343    else if(buf[i] == '\r') {
344        if(buf[i + 1] == '\n')
345            return i + 2;
346        else
347            return -1;
348    } else {
349        return -1;
350    }
351}
352   
353static int
354skipToEol(const char *restrict buf, int i, int *start_return)
355{
356    while(buf[i] != '\n' && buf[i] != '\r')
357        i++;
358    if(buf[i] == '\n') {
359        *start_return = i;
360        return i + 1;
361    } else if(buf[i] == '\r') {
362        if(buf[i + 1] == '\n') {
363            *start_return = i;
364            return i + 2;
365        } else {
366            return -1;
367        }
368    }
369    return -1;
370}
371
372static int
373getHeaderValue(const char *restrict buf, int start,
374               int *value_start_return, int *value_end_return)
375{
376    int i, j, k;
377
378    while(buf[start] == ' ' || buf[start] == '\t')
379        start++;
380    i = start;
381 again:
382    j = skipToEol(buf, i, &k);
383    if(j < 0)
384        return -1;
385    if(buf[j] == ' ' || buf[j] == '\t') {
386        i = j + 1;
387        goto again;
388    }
389    *value_start_return = start;
390    *value_end_return = k;
391    return j;
392}
393   
394int
395httpParseClientFirstLine(const char *restrict buf, int offset,
396                         int *method_return,
397                         AtomPtr *url_return,
398                         int *version_return)
399{
400    int i = 0;
401    int x, y;
402    int method;
403    AtomPtr url;
404    int version = HTTP_UNKNOWN;
405    int eol;
406
407    i = offset;
408    i = getNextWord(buf, i, &x, &y);
409    if(i < 0) return -1;
410    if(y == x + 3 && memcmp(buf + x, "GET", 3) == 0)
411        method = METHOD_GET;
412    else if(y == x + 4 && memcmp(buf + x, "HEAD", 4) == 0)
413        method = METHOD_HEAD;
414    else if(y == x + 4 && memcmp(buf + x, "POST", 4) == 0)
415        method = METHOD_POST;
416    else if(y == x + 3 && memcmp(buf + x, "PUT", 3) == 0)
417        method = METHOD_PUT;
418    else if(y == x + 7 && memcmp(buf + x, "CONNECT", 7) == 0)
419        method = METHOD_CONNECT;
420    else
421        method = METHOD_UNKNOWN;
422
423    i = getNextWord(buf, y + 1, &x, &y);
424    if(i < 0) return -1;
425
426    url = internAtomN(buf + x, y - x);
427
428    i = getNextWord(buf, y + 1, &x, &y);
429    if(i < 0) {
430        releaseAtom(url);
431        return -1;
432    }
433
434    if(y == x + 8) {
435        if(memcmp(buf + x, "HTTP/1.", 7) != 0)
436            version = HTTP_UNKNOWN;
437        else if(buf[x + 7] == '0')
438            version = HTTP_10;
439        else if(buf[x + 7] >= '1' && buf[x + 7] <= '9')
440            version = HTTP_11;
441        else
442            version = HTTP_UNKNOWN;
443    }
444
445    eol = skipEol(buf, y);
446    if(eol < 0) return -1;
447       
448    *method_return = method;
449    if(url_return)
450        *url_return = url;
451    else
452        releaseAtom(url);
453    *version_return = version;
454    return eol;
455}
456
457int
458httpParseServerFirstLine(const char *restrict buf,
459                         int *status_return,
460                         int *version_return,
461                         AtomPtr *message_return)
462{
463    int i = 0;
464    int x, y, eol;
465    int status;
466    int version = HTTP_UNKNOWN;
467   
468    i = getNextWord(buf, 0, &x, &y);
469    if(i < 0)
470        return -1;
471    if(y == x + 8 && memcmp(buf + x, "HTTP/1.0", 8) == 0)
472        version = HTTP_10;
473    else if(y >= x + 8 && memcmp(buf + x, "HTTP/1.", 7) == 0)
474        version = HTTP_11;
475    else
476        version = HTTP_UNKNOWN;
477
478    i = getNextWord(buf, y + 1, &x, &y);
479    if(i < 0) return -1;
480    if(y == x + 3)
481        status = atol(buf + x);
482    else return -1;
483
484    i = skipToEol(buf, y, &eol);
485    if(i < 0) return -1;
486       
487    *status_return = status;
488    *version_return = version;
489    if(message_return) {
490        /* Netscape enterprise bug */
491        if(eol > y)
492            *message_return = internAtomN(buf + y + 1, eol - y - 1);
493        else
494            *message_return = internAtom("No message");
495    }
496    return i;
497}
498
499static int
500parseInt(const char *restrict buf, int start, int *val_return)
501{
502    int i = start, val = 0;
503    if(!digit(buf[i]))
504        return -1;
505    while(digit(buf[i])) {
506        val = val * 10 + (buf[i] - '0');
507        i++;
508    }
509    *val_return = val;
510    return i;
511}
512
513/* Returned *name_start_return is -1 at end of headers, -2 if the line
514   couldn't be parsed. */
515static int
516parseHeaderLine(const char *restrict buf, int start,
517                int *name_start_return, int *name_end_return,
518                int *value_start_return, int *value_end_return)
519{
520    int i;
521    int name_start, name_end, value_start, value_end;
522
523    if(buf[start] == '\n') {
524        *name_start_return = -1;
525        return start + 1;
526    }
527    if(buf[start] == '\r' && buf[start + 1] == '\n') {
528        *name_start_return = -1;
529        return start + 2;
530    }
531
532    i = getNextToken(buf, start, &name_start, &name_end);
533    if(i < 0 || buf[i] != ':')
534        goto syntax;
535    i++;
536    while(buf[i] == ' ' || buf[i] == '\t')
537        i++;
538
539    i = getHeaderValue(buf, i, &value_start, &value_end);
540    if(i < 0)
541        goto syntax;
542
543    *name_start_return = name_start;
544    *name_end_return = name_end;
545    *value_start_return = value_start;
546    *value_end_return = value_end;
547    return i;
548
549 syntax:
550    i = start;
551    while(1) {
552        if(buf[i] == '\n') {
553            i++;
554            break;
555        }
556        if(buf[i] == '\r' && buf[i + 1] == '\n') {
557            i += 2;
558            break;
559        }
560        i++;
561    }
562    *name_start_return = -2;
563    return i;
564}
565
566int
567findEndOfHeaders(const char *restrict buf, int from, int to, int *body_return)
568{
569    int i = from;
570    int eol = 0;
571    while(i < to) {
572        if(buf[i] == '\n') {
573            if(eol) {
574                *body_return = i + 1;
575                return eol;
576            }
577            eol = i;
578            i++;
579        } else if(buf[i] == '\r') {
580            if(i < to - 1 && buf[i + 1] == '\n') {
581                if(eol) {
582                    *body_return = eol;
583                    return i + 2;
584                }
585                eol = i;
586                i += 2;
587            } else {
588                eol = 0;
589                i++;
590            }
591        } else {
592            eol = 0;
593            i++;
594        }
595    }
596    return -1;
597}
598
599static int
600parseContentRange(const char *restrict buf, int i,
601                  int *from_return, int *to_return, int *full_len_return)
602{
603    int j;
604    int from, to, full_len;
605
606    i = skipWhitespace(buf, i);
607    if(i < 0) return -1;
608    if(!token_compare(buf, i, i + 5, "bytes"))
609        return -1;
610    i += 5;
611    i = skipWhitespace(buf, i);
612    if(buf[i] == '*') {
613        from = 0;
614        to = -1;
615        i++;
616    } else {
617        i = parseInt(buf, i, &from);
618        if(i < 0) return -1;
619        if(buf[i] != '-') return -1;
620        i++;
621        i = parseInt(buf, i, &to);
622        if(i < 0) return -1;
623        to = to + 1;
624    }
625    if(buf[i] != '/')
626        return -1;
627    i++;
628    if(buf[i] == '*')
629        full_len = -1;
630    else {
631        i = parseInt(buf, i, &full_len);
632        if(i < 0) return -1;
633    }
634    j = skipEol(buf, i);
635    if(j < 0)
636        return -1;
637
638    *from_return = from;
639    *to_return = to;
640    *full_len_return = full_len;
641    return i;
642}
643
644static int
645parseRange(const char *restrict buf, int i,
646           int *from_return, int *to_return)
647{
648    int j;
649    int from, to;
650
651    i = skipWhitespace(buf, i);
652    if(i < 0)
653        return -1;
654    if(!token_compare(buf, i, i + 6, "bytes="))
655        return -1;
656    i += 6;
657    i = skipWhitespace(buf, i);
658    if(buf[i] == '-') {
659        from = 0;
660    } else {
661        i = parseInt(buf, i, &from);
662        if(i < 0) return -1;
663    }
664    if(buf[i] != '-')
665        return -1;
666    i++;
667    j = parseInt(buf, i, &to);
668    if(j < 0)
669        to = -1;
670    else {
671        to = to + 1;
672        i = j;
673    }
674    j = skipEol(buf, i);
675    if(j < 0) return -1;
676    *from_return = from;
677    *to_return = to;
678    return i;
679}
680
681static int
682urlSameHost(const char *url1, int len1, const char *url2, int len2)
683{
684    int i;
685    if(len1 < 7 || len2 < 7)
686        return 0;
687    if(memcmp(url1 + 4, "://", 3) != 0 || memcmp(url2 + 4, "://", 3) != 0)
688        return 0;
689
690    i = 7;
691    while(i < len1 && i < len2 && url1[i] != '/' && url2[i] != '/') {
692        if((url1[i] | 0x20) != (url2[i] | 0x20))
693            break;
694        i++;
695    }
696
697    if((i == len1 || url1[i] == '/') && ((i == len2 || url2[i] == '/')))
698        return 1;
699    return 0;
700}
701
702static char *
703resize_hbuf(char *hbuf, int *size, char *hbuf_small)
704{
705    int new_size = 2 * *size;
706    char *new_hbuf;
707
708    if(new_size <= *size)
709        goto fail;
710
711    if(hbuf == hbuf_small) {
712        new_hbuf = malloc(new_size);
713        if(new_hbuf == NULL) goto fail;
714        memcpy(new_hbuf, hbuf, *size);
715    } else {
716        new_hbuf = realloc(hbuf, new_size);
717        if(new_hbuf == NULL) goto fail;
718    }
719    *size = new_size;
720    return new_hbuf;
721
722 fail:
723    if(hbuf != hbuf_small)
724        free(hbuf);
725    *size = 0;
726    return NULL;
727}
728
729int
730httpParseHeaders(int client, AtomPtr url,
731                 const char *buf, int start, HTTPRequestPtr request,
732                 AtomPtr *headers_return,
733                 int *len_return, CacheControlPtr cache_control_return,
734                 HTTPConditionPtr *condition_return, int *te_return,
735                 time_t *date_return, time_t *last_modified_return,
736                 time_t *expires_return, time_t *polipo_age_return,
737                 time_t *polipo_access_return, int *polipo_body_offset_return,
738                 int *age_return, char **etag_return, AtomPtr *expect_return,
739                 HTTPRangePtr range_return, HTTPRangePtr content_range_return,
740                 char **location_return, AtomPtr *via_return,
741                 AtomPtr *auth_return, AtomPtr *referer_return)
742{
743    int local = url ? urlIsLocal(url->string, url->length) : 0;
744    char hbuf_small[512];
745    char *hbuf = hbuf_small;
746    int hbuf_size = 512, hbuf_length = 0;
747    int i, j,
748        name_start, name_end, value_start, value_end,
749        token_start, token_end, end;
750    AtomPtr name = NULL;
751    time_t date = -1, last_modified = -1, expires = -1, polipo_age = -1,
752        polipo_access = -1, polipo_body_offset = -1;
753    int len = -1;
754    CacheControlRec cache_control;
755    char *endptr;
756    int te = TE_IDENTITY;
757    int age = -1;
758    char *etag = NULL, *ifrange = NULL;
759    int persistent = (!request || (request->connection->version != HTTP_10));
760    char *location = NULL;
761    AtomPtr via = NULL;
762    AtomPtr auth = NULL;
763    AtomPtr expect = NULL;
764    AtomPtr referer = NULL;
765    HTTPConditionPtr condition;
766    time_t ims = -1, inms = -1;
767    char *im = NULL, *inm = NULL;
768    AtomListPtr hopToHop = NULL;
769    HTTPRangeRec range = {-1, -1, -1}, content_range = {-1, -1, -1};
770    int haveCacheControl = 0;
771 
772#define RESIZE_HBUF() \
773    do { \
774        hbuf = resize_hbuf(hbuf, &hbuf_size, hbuf_small); \
775        if(hbuf == NULL) \
776            goto fail; \
777    } while(0)
778
779    cache_control.flags = 0;
780    cache_control.max_age = -1;
781    cache_control.s_maxage = -1;
782    cache_control.min_fresh = -1;
783    cache_control.max_stale = -1;
784   
785    i = start;
786
787    while(1) {
788        i = parseHeaderLine(buf, i,
789                            &name_start, &name_end, &value_start, &value_end);
790        if(i < 0) {
791            do_log(L_ERROR, "Couldn't find end of header line.\n");
792            goto fail;
793        }
794
795        if(name_start == -1)
796            break;
797
798        if(name_start < 0)
799            continue;
800
801        name = internAtomLowerN(buf + name_start, name_end - name_start);
802        if(name == atomConnection) {
803            j = getNextTokenInList(buf, value_start,
804                                   &token_start, &token_end, NULL, NULL,
805                                   &end);
806            while(1) {
807                if(j < 0) {
808                    do_log(L_ERROR, "Couldn't parse Connection: ");
809                    do_log_n(L_ERROR, buf + value_start,
810                             value_end - value_start);
811                    do_log(L_ERROR, ".\n");
812                    goto fail;
813                }
814                if(token_compare(buf, token_start, token_end, "close")) {
815                    persistent = 0;
816                } else if(token_compare(buf, token_start, token_end,
817                                        "keep-alive")) {
818                    persistent = 1;
819                } else {
820                    if(hopToHop == NULL)
821                        hopToHop = makeAtomList(NULL, 0);
822                    if(hopToHop == NULL) {
823                        do_log(L_ERROR, "Couldn't allocate atom list.\n");
824                        goto fail;
825                    }
826                    atomListCons(internAtomLowerN(buf + token_start,
827                                                  token_end - token_start),
828                                 hopToHop);
829                }
830                if(end)
831                    break;
832                j = getNextTokenInList(buf, j,
833                                       &token_start, &token_end, NULL, NULL,
834                                       &end);
835            }
836        } else if(name == atomCacheControl)
837            haveCacheControl = 1;
838
839        releaseAtom(name);
840        name = NULL;
841    }
842   
843    i = start;
844
845    while(1) {
846        i = parseHeaderLine(buf, i,
847                            &name_start, &name_end, &value_start, &value_end);
848        if(i < 0) {
849            do_log(L_ERROR, "Couldn't find end of header line.\n");
850            goto fail;
851        }
852
853        if(name_start == -1)
854            break;
855
856        if(name_start < 0) {
857            do_log(L_WARN, "Couldn't parse header line.\n");
858            if(laxHttpParser)
859                continue;
860            else
861                goto fail;
862        }
863
864        name = internAtomLowerN(buf + name_start, name_end - name_start);
865       
866        if(name == atomProxyConnection) {
867            j = getNextTokenInList(buf, value_start,
868                                   &token_start, &token_end, NULL, NULL,
869                                   &end);
870            while(1) {
871                if(j < 0) {
872                    do_log(L_WARN, "Couldn't parse Proxy-Connection:");
873                    do_log_n(L_WARN, buf + value_start,
874                             value_end - value_start);
875                    do_log(L_WARN, ".\n");
876                    persistent = 0;
877                    break;
878                }
879                if(token_compare(buf, token_start, token_end, "close")) {
880                    persistent = 0;
881                } else if(token_compare(buf, token_start, token_end,
882                                        "keep-alive")) {
883                    persistent = 1;
884                }
885                if(end)
886                    break;
887                j = getNextTokenInList(buf, j,
888                                       &token_start, &token_end, NULL, NULL,
889                                       &end);
890            }
891        } else if(name == atomContentLength) {
892            j = skipWhitespace(buf, value_start);
893            if(j < 0) {
894                do_log(L_WARN, "Couldn't parse Content-Length: \n");
895                do_log_n(L_WARN, buf + value_start, value_end - value_start);
896                do_log(L_WARN, ".\n");
897                len = -1;
898            } else {
899                len = strtol(buf + value_start, &endptr, 10);
900                if(endptr <= buf + value_start) {
901                    do_log(L_WARN, "Couldn't parse Content-Length: \n");
902                    do_log_n(L_WARN, buf + value_start,
903                             value_end - value_start);
904                    do_log(L_WARN, ".\n");
905                    len = -1;
906                }
907            }
908        } else if((!local && name == atomProxyAuthorization) ||
909                  (local && name == atomAuthorization)) {
910            if(auth_return) {
911                auth = internAtomN(buf + value_start, value_end - value_start);
912                if(auth == NULL) {
913                    do_log(L_ERROR, "Couldn't allocate authorization.\n");
914                    goto fail;
915                }
916            }
917        } else if(name == atomReferer) {
918            int h;
919
920            if(referer_return) {
921                                referer = internAtomN(buf + value_start, value_end - value_start);
922                                if(referer == NULL) {
923                        do_log(L_ERROR, "Couldn't allocate referer.\n");
924                        goto fail;
925                    }
926            }
927
928            if(censorReferer == 0 ||
929               (censorReferer == 1 && url != NULL &&
930                urlSameHost(url->string, url->length,
931                            buf + value_start, value_end - value_start))) {
932                while(hbuf_length > hbuf_size - 2)
933                    RESIZE_HBUF();
934                hbuf[hbuf_length++] = '\r';
935                hbuf[hbuf_length++] = '\n';
936                do {
937                    h = snnprint_n(hbuf, hbuf_length, hbuf_size,
938                                   buf + name_start, value_end - name_start);
939                    if(h < 0) RESIZE_HBUF();
940                } while(h < 0);
941                hbuf_length = h;
942            }
943        } else if(name == atomTrailers || name == atomUpgrade) {
944            do_log(L_ERROR, "Trailers or upgrade present.\n");
945            goto fail;
946        } else if(name == atomDate || name == atomExpires ||
947                  name == atomIfModifiedSince ||
948                  name == atomIfUnmodifiedSince ||
949                  name == atomLastModified ||
950                  name == atomXPolipoDate || name == atomXPolipoAccess) {
951            time_t t;
952            j = parse_time(buf, value_start, value_end, &t);
953            if(j < 0) {
954                if(name != atomExpires) {
955                    do_log(L_WARN, "Couldn't parse %s: ", name->string);
956                    do_log_n(L_WARN, buf + value_start,
957                             value_end - value_start);
958                    do_log(L_WARN, "\n");
959                }
960                t = -1;
961            }