2 Copyright (c) 2003-2006 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 static int getNextWord(const char *buf
, int i
, int *x_return
, int *y_return
);
26 static int getNextToken(const char *buf
, int i
, int *x_return
, int *y_return
);
27 static int getNextTokenInList(const char *buf
, int i
,
28 int *x_return
, int *y_return
,
29 int *z_return
, int *t_return
,
32 static 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
, atomContentType
, atomVary
, atomExpect
, atomAuthorization
,
40 atomSetCookie
, atomCookie
, atomCookie2
,
41 atomXPolipoDate
, atomXPolipoAccess
, atomXPolipoLocation
,
42 atomXPolipoBodyOffset
;
44 int censorReferer
= 0;
45 int laxHttpParser
= 1;
47 static AtomListPtr censoredHeaders
;
52 CONFIG_VARIABLE_SETTABLE(censorReferer
, CONFIG_TRISTATE
, configIntSetter
,
53 "Censor referer headers.");
54 censoredHeaders
= makeAtomList(NULL
, 0);
55 if(censoredHeaders
== NULL
) {
56 do_log(L_ERROR
, "Couldn't allocate censored atoms.\n");
59 CONFIG_VARIABLE(censoredHeaders
, CONFIG_ATOM_LIST_LOWER
,
60 "Headers to censor.");
61 CONFIG_VARIABLE_SETTABLE(laxHttpParser
, CONFIG_BOOLEAN
, configIntSetter
,
62 "Ignore unknown HTTP headers.");
68 #define A(name, value) name = internAtom(value); if(!name) goto fail;
69 /* These must be in lower-case */
70 A(atomConnection
, "connection");
71 A(atomProxyConnection
, "proxy-connection");
72 A(atomContentLength
, "content-length");
74 A(atomAcceptRange
, "accept-range");
76 A(atomReferer
, "referer");
77 A(atomProxyAuthenticate
, "proxy-authenticate");
78 A(atomProxyAuthorization
, "proxy-authorization");
79 A(atomKeepAlive
, "keep-alive");
80 A(atomTrailers
, "trailers");
81 A(atomUpgrade
, "upgrade");
83 A(atomExpires
, "expires");
84 A(atomIfModifiedSince
, "if-modified-since");
85 A(atomIfUnmodifiedSince
, "if-unmodified-since");
86 A(atomIfRange
, "if-range");
87 A(atomLastModified
, "last-modified");
88 A(atomIfMatch
, "if-match");
89 A(atomIfNoneMatch
, "if-none-match");
91 A(atomTransferEncoding
, "transfer-encoding");
93 A(atomCacheControl
, "cache-control");
94 A(atomPragma
, "pragma");
95 A(atomContentRange
, "content-range");
96 A(atomRange
, "range");
98 A(atomContentType
, "content-type");
100 A(atomExpect
, "expect");
101 A(atomAuthorization
, "authorization");
102 A(atomSetCookie
, "set-cookie");
103 A(atomCookie
, "cookie");
104 A(atomCookie2
, "cookie2");
105 A(atomXPolipoDate
, "x-polipo-date");
106 A(atomXPolipoAccess
, "x-polipo-access");
107 A(atomXPolipoLocation
, "x-polipo-location");
108 A(atomXPolipoBodyOffset
, "x-polipo-body-offset");
113 do_log(L_ERROR
, "Couldn't allocate atom.\n");
118 getNextWord(const char *restrict buf
, int i
, int *x_return
, int *y_return
)
121 while(buf
[i
] == ' ') i
++;
122 if(buf
[i
] == '\n' || buf
[i
] == '\r') return -1;
124 while(buf
[i
] > 32 && buf
[i
] < 127) i
++;
134 skipComment(const char *restrict buf
, int i
)
136 assert(buf
[i
] == '(');
140 if(buf
[i
] == '\\' && buf
[i
+ 1] == ')') i
+=2;
141 else if(buf
[i
] == ')') return i
+ 1;
142 else if(buf
[i
] == '\n') {
143 if(buf
[i
+ 1] == ' ' || buf
[i
+ 1] == '\t')
147 } else if(buf
[i
] == '\r') {
148 if(buf
[i
+ 1] != '\n') return -1;
149 if(buf
[i
+ 2] == ' ' || buf
[i
+ 2] == '\t')
162 skipWhitespace(const char *restrict buf
, int i
)
165 if(buf
[i
] == ' ' || buf
[i
] == '\t')
167 else if(buf
[i
] == '(') {
168 i
= skipComment(buf
, i
);
170 } else if(buf
[i
] == '\n') {
171 if(buf
[i
+ 1] == ' ' || buf
[i
+ 1] == '\t')
175 } else if(buf
[i
] == '\r' && buf
[i
+ 1] == '\n') {
176 if(buf
[i
+ 2] == ' ' || buf
[i
+ 2] == '\t')
186 getNextToken(const char *restrict buf
, int i
, int *x_return
, int *y_return
)
190 while(buf
[i
] == ' ' || buf
[i
] == '\t')
194 while(buf
[i
] != ')') {
195 if(buf
[i
] == '\n' || buf
[i
] == '\r')
197 if(buf
[i
] == '\\' && buf
[i
+ 1] != '\n' && buf
[i
+ 1] != '\r')
205 if(buf
[i
+ 1] == ' ' || buf
[i
+ 1] == '\t') {
213 if(buf
[i
+ 1] == '\n' && (buf
[i
+ 2] == ' ' || buf
[i
+ 2] == '\t')) {
221 while(buf
[i
] > 32 && buf
[i
] < 127) {
223 case '(': case ')': case '<': case '>': case '@':
224 case ',': case ';': case ':': case '\\': case '/':
225 case '[': case ']': case '?': case '=':
226 case '{': case '}': case ' ': case '\t':
242 getNextETag(const char * restrict buf
, int i
,
243 int *x_return
, int *y_return
, int *weak_return
)
247 while(buf
[i
] == ' ' || buf
[i
] == '\t')
249 if(buf
[i
] == 'W' && buf
[i
+ 1] == '/') {
259 while(buf
[i
] != '"') {
260 if(buf
[i
] == '\r' && buf
[i
] == '\n')
274 getNextTokenInList(const char *restrict buf
, int i
,
275 int *x_return
, int *y_return
,
276 int *z_return
, int *t_return
,
279 int j
, x
, y
, z
= -1, t
= -1, end
;
280 j
= getNextToken(buf
, i
, &x
, &y
);
283 while(buf
[j
] == ' ' || buf
[j
] == '\t')
288 while(buf
[j
] == ' ' || buf
[j
] == '\t')
291 while(buf
[j
] != ',' && buf
[j
] != '\n' && buf
[j
] != '\r')
295 if(buf
[j
] == '\n' || buf
[j
] == '\r') {
297 if(buf
[j
+ 1] != '\n')
303 if(buf
[j
] == ' ' || buf
[j
] == '\t') {
304 while(buf
[j
] == ' ' || buf
[j
] == '\t')
308 } else if(buf
[j
] == ',') {
310 while(buf
[j
] == ' ' || buf
[j
] == '\t')
328 token_compare(const char *buf
, int start
, int end
, const char *s
)
330 return (strcasecmp_n(s
, buf
+ start
, end
- start
) == 0);
334 skipEol(const char *restrict buf
, int i
)
340 else if(buf
[i
] == '\r') {
341 if(buf
[i
+ 1] == '\n')
351 skipToEol(const char *restrict buf
, int i
, int *start_return
)
353 while(buf
[i
] != '\n' && buf
[i
] != '\r')
358 } else if(buf
[i
] == '\r') {
359 if(buf
[i
+ 1] == '\n') {
370 getHeaderValue(const char *restrict buf
, int start
,
371 int *value_start_return
, int *value_end_return
)
375 while(buf
[start
] == ' ' || buf
[start
] == '\t')
379 j
= skipToEol(buf
, i
, &k
);
382 if(buf
[j
] == ' ' || buf
[j
] == '\t') {
386 *value_start_return
= start
;
387 *value_end_return
= k
;
392 httpParseClientFirstLine(const char *restrict buf
, int offset
,
401 int version
= HTTP_UNKNOWN
;
405 i
= getNextWord(buf
, i
, &x
, &y
);
407 if(y
== x
+ 3 && memcmp(buf
+ x
, "GET", 3) == 0)
409 else if(y
== x
+ 4 && memcmp(buf
+ x
, "HEAD", 3) == 0)
410 method
= METHOD_HEAD
;
411 else if(y
== x
+ 4 && memcmp(buf
+ x
, "POST", 3) == 0)
412 method
= METHOD_POST
;
413 else if(y
== x
+ 3 && memcmp(buf
+ x
, "PUT", 3) == 0)
415 else if(y
== x
+ 7 && memcmp(buf
+ x
, "CONNECT", 7) == 0)
416 method
= METHOD_CONNECT
;
418 method
= METHOD_UNKNOWN
;
420 i
= getNextWord(buf
, y
+ 1, &x
, &y
);
423 url
= internAtomN(buf
+ x
, y
- x
);
425 i
= getNextWord(buf
, y
+ 1, &x
, &y
);
432 if(memcmp(buf
+ x
, "HTTP/1.", 7) != 0)
433 version
= HTTP_UNKNOWN
;
434 else if(buf
[x
+ 7] == '0')
436 else if(buf
[x
+ 7] >= '1' && buf
[x
+ 7] <= '9')
439 version
= HTTP_UNKNOWN
;
442 eol
= skipEol(buf
, y
);
443 if(eol
< 0) return -1;
445 *method_return
= method
;
450 *version_return
= version
;
455 httpParseServerFirstLine(const char *restrict buf
,
458 AtomPtr
*message_return
)
463 int version
= HTTP_UNKNOWN
;
465 i
= getNextWord(buf
, 0, &x
, &y
);
468 if(y
== x
+ 8 && memcmp(buf
+ x
, "HTTP/1.0", 8) == 0)
470 else if(y
>= x
+ 8 && memcmp(buf
+ x
, "HTTP/1.", 7) == 0)
473 version
= HTTP_UNKNOWN
;
475 i
= getNextWord(buf
, y
+ 1, &x
, &y
);
478 status
= atol(buf
+ x
);
481 i
= skipToEol(buf
, y
, &eol
);
484 *status_return
= status
;
485 *version_return
= version
;
487 /* Netscape enterprise bug */
489 *message_return
= internAtomN(buf
+ y
+ 1, eol
- y
- 1);
491 *message_return
= internAtom("No message");
497 parseInt(const char *restrict buf
, int start
, int *val_return
)
499 int i
= start
, val
= 0;
502 while(digit(buf
[i
])) {
503 val
= val
* 10 + (buf
[i
] - '0');
510 /* Returned *name_start_return is -1 at end of headers, -2 if the line
511 couldn't be parsed. */
513 parseHeaderLine(const char *restrict buf
, int start
,
514 int *name_start_return
, int *name_end_return
,
515 int *value_start_return
, int *value_end_return
)
518 int name_start
, name_end
, value_start
, value_end
;
520 if(buf
[start
] == '\n') {
521 *name_start_return
= -1;
524 if(buf
[start
] == '\r' && buf
[start
+ 1] == '\n') {
525 *name_start_return
= -1;
529 i
= getNextToken(buf
, start
, &name_start
, &name_end
);
530 if(i
< 0 || buf
[i
] != ':')
533 while(buf
[i
] == ' ' || buf
[i
] == '\t')
536 i
= getHeaderValue(buf
, i
, &value_start
, &value_end
);
540 *name_start_return
= name_start
;
541 *name_end_return
= name_end
;
542 *value_start_return
= value_start
;
543 *value_end_return
= value_end
;
553 if(buf
[i
] == '\r' && buf
[i
+ 1] == '\n') {
559 *name_start_return
= -2;
564 findEndOfHeaders(const char *restrict buf
, int from
, int to
, int *body_return
)
571 *body_return
= i
+ 1;
576 } else if(buf
[i
] == '\r') {
577 if(i
< to
- 1 && buf
[i
+ 1] == '\n') {
597 parseContentRange(const char *restrict buf
, int i
,
598 int *from_return
, int *to_return
, int *full_len_return
)
601 int from
, to
, full_len
;
603 i
= skipWhitespace(buf
, i
);
605 if(!token_compare(buf
, i
, i
+ 5, "bytes"))
608 i
= skipWhitespace(buf
, i
);
614 i
= parseInt(buf
, i
, &from
);
616 if(buf
[i
] != '-') return -1;
618 i
= parseInt(buf
, i
, &to
);
628 i
= parseInt(buf
, i
, &full_len
);
637 *full_len_return
= full_len
;
642 parseRange(const char *restrict buf
, int i
,
643 int *from_return
, int *to_return
)
648 i
= skipWhitespace(buf
, i
);
651 if(!token_compare(buf
, i
, i
+ 6, "bytes="))
654 i
= skipWhitespace(buf
, i
);
658 i
= parseInt(buf
, i
, &from
);
664 j
= parseInt(buf
, i
, &to
);
679 urlSameHost(const char *url1
, int len1
, const char *url2
, int len2
)
682 if(len1
< 7 || len2
< 7)
684 if(memcmp(url1
+ 4, "://", 3) != 0 || memcmp(url2
+ 4, "://", 3) != 0)
688 while(i
< len1
&& i
< len2
&& url1
[i
] != '/' && url2
[i
] != '/') {
689 if((url1
[i
] | 0x20) != (url2
[i
] | 0x20))
694 if((i
== len1
|| url1
[i
] == '/') && ((i
== len2
|| url2
[i
] == '/')))
700 resize_hbuf(char *hbuf
, int *size
, char *hbuf_small
)
702 int new_size
= 2 * *size
;
705 if(new_size
<= *size
)
708 if(hbuf
== hbuf_small
) {
709 new_hbuf
= malloc(new_size
);
710 if(new_hbuf
== NULL
) goto fail
;
711 memcpy(new_hbuf
, hbuf
, *size
);
713 new_hbuf
= realloc(hbuf
, new_size
);
714 if(new_hbuf
== NULL
) goto fail
;
720 if(hbuf
!= hbuf_small
)
727 httpParseHeaders(int client
, AtomPtr url
,
728 const char *buf
, int start
, HTTPRequestPtr request
,
729 AtomPtr
*headers_return
,
730 int *len_return
, CacheControlPtr cache_control_return
,
731 HTTPConditionPtr
*condition_return
, int *te_return
,
732 time_t *date_return
, time_t *last_modified_return
,
733 time_t *expires_return
, time_t *polipo_age_return
,
734 time_t *polipo_access_return
, int *polipo_body_offset_return
,
735 int *age_return
, char **etag_return
, AtomPtr
*expect_return
,
736 HTTPRangePtr range_return
, HTTPRangePtr content_range_return
,
737 char **location_return
, AtomPtr
*via_return
,
738 AtomPtr
*auth_return
)
740 int local
= url
? urlIsLocal(url
->string
, url
->length
) : 0;
741 char hbuf_small
[512];
742 char *hbuf
= hbuf_small
;
743 int hbuf_size
= 512, hbuf_length
= 0;
745 name_start
, name_end
, value_start
, value_end
,
746 token_start
, token_end
, end
;
748 time_t date
= -1, last_modified
= -1, expires
= -1, polipo_age
= -1,
749 polipo_access
= -1, polipo_body_offset
= -1;
751 CacheControlRec cache_control
;
753 int te
= TE_IDENTITY
;
755 char *etag
= NULL
, *ifrange
= NULL
;
756 int persistent
= (!request
|| (request
->connection
->version
!= HTTP_10
));
757 char *location
= NULL
;
760 AtomPtr expect
= NULL
;
761 HTTPConditionPtr condition
;
762 time_t ims
= -1, inms
= -1;
763 char *im
= NULL
, *inm
= NULL
;
764 AtomListPtr hopToHop
= NULL
;
765 HTTPRangeRec range
= {-1, -1, -1}, content_range
= {-1, -1, -1};
766 int haveCacheControl
= 0;
768 #define RESIZE_HBUF() \
770 hbuf = resize_hbuf(hbuf, &hbuf_size, hbuf_small); \
775 cache_control
.flags
= 0;
776 cache_control
.max_age
= -1;
777 cache_control
.s_maxage
= -1;
778 cache_control
.min_fresh
= 0;
779 cache_control
.max_stale
= 0;
784 i
= parseHeaderLine(buf
, i
,
785 &name_start
, &name_end
, &value_start
, &value_end
);
787 do_log(L_ERROR
, "Couldn't find end of header line.\n");
797 name
= internAtomLowerN(buf
+ name_start
, name_end
- name_start
);
799 if(name
== atomConnection
) {
800 j
= getNextTokenInList(buf
, value_start
,
801 &token_start
, &token_end
, NULL
, NULL
,
805 do_log(L_ERROR
, "Couldn't parse Connection: ");
806 do_log_n(L_ERROR
, buf
+ value_start
,
807 value_end
- value_start
);
808 do_log(L_ERROR
, ".\n");
811 if(token_compare(buf
, token_start
, token_end
, "close")) {
813 } else if(token_compare(buf
, token_start
, token_end
,
818 hopToHop
= makeAtomList(NULL
, 0);
819 if(hopToHop
== NULL
) {
820 do_log(L_ERROR
, "Couldn't allocate atom list.\n");
823 atomListCons(internAtomLowerN(buf
+ token_start
,
824 token_end
- token_start
),
829 j
= getNextTokenInList(buf
, j
,
830 &token_start
, &token_end
, NULL
, NULL
,
833 } else if(name
== atomCacheControl
)
834 haveCacheControl
= 1;
843 i
= parseHeaderLine(buf
, i
,
844 &name_start
, &name_end
, &value_start
, &value_end
);
846 do_log(L_ERROR
, "Couldn't find end of header line.\n");
854 do_log(L_WARN
, "Couldn't parse header line.\n");
861 name
= internAtomLowerN(buf
+ name_start
, name_end
- name_start
);
863 if(name
== atomProxyConnection
) {
864 j
= getNextTokenInList(buf
, value_start
,
865 &token_start
, &token_end
, NULL
, NULL
,
869 do_log(L_WARN
, "Couldn't parse Proxy-Connection:");
870 do_log_n(L_WARN
, buf
+ value_start
,
871 value_end
- value_start
);
872 do_log(L_WARN
, ".\n");
876 if(token_compare(buf
, token_start
, token_end
, "close")) {
878 } else if(token_compare(buf
, token_start
, token_end
,
884 j
= getNextTokenInList(buf
, j
,
885 &token_start
, &token_end
, NULL
, NULL
,
888 } else if(name
== atomContentLength
) {
889 j
= skipWhitespace(buf
, value_start
);
891 do_log(L_WARN
, "Couldn't parse Content-Length: \n");
892 do_log_n(L_WARN
, buf
+ value_start
, value_end
- value_start
);
893 do_log(L_WARN
, ".\n");
896 len
= strtol(buf
+ value_start
, &endptr
, 10);
897 if(endptr
<= buf
+ value_start
) {
898 do_log(L_WARN
, "Couldn't parse Content-Length: \n");
899 do_log_n(L_WARN
, buf
+ value_start
,
900 value_end
- value_start
);
901 do_log(L_WARN
, ".\n");
905 } else if((!local
&& name
== atomProxyAuthorization
) ||
906 (local
&& name
== atomAuthorization
)) {
908 auth
= internAtomN(buf
+ value_start
, value_end
- value_start
);
910 do_log(L_ERROR
, "Couldn't allocate authorization.\n");
914 } else if(name
== atomReferer
) {
916 if(censorReferer
== 0 ||
917 (censorReferer
== 1 && url
!= NULL
&&
918 urlSameHost(url
->string
, url
->length
,
919 buf
+ value_start
, value_end
- value_start
))) {
920 while(hbuf_length
> hbuf_size
- 2)
922 hbuf
[hbuf_length
++] = '\r';
923 hbuf
[hbuf_length
++] = '\n';
925 h
= snnprint_n(hbuf
, hbuf_length
, hbuf_size
,
926 buf
+ name_start
, value_end
- name_start
);
927 if(h
< 0) RESIZE_HBUF();
931 } else if(name
== atomTrailers
|| name
== atomUpgrade
) {
932 do_log(L_ERROR
, "Trailers or upgrade present.\n");
934 } else if(name
== atomDate
|| name
== atomExpires
||
935 name
== atomIfModifiedSince
||
936 name
== atomIfUnmodifiedSince
||
937 name
== atomLastModified
||
938 name
== atomXPolipoDate
|| name
== atomXPolipoAccess
) {
940 j
= parse_time(buf
, value_start
, value_end
, &t
);
942 if(name
!= atomExpires
) {
943 do_log(L_WARN
, "Couldn't parse %s: ", name
->string
);
944 do_log_n(L_WARN
, buf
+ value_start
,
945 value_end
- value_start
);
946 do_log(L_WARN
, "\n");
950 if(name
== atomDate
) {
953 } else if(name
== atomExpires
) {
958 } else if(name
== atomLastModified
)
960 else if(name
== atomIfModifiedSince
)
962 else if(name
== atomIfUnmodifiedSince
)
964 else if(name
== atomXPolipoDate
)
966 else if(name
== atomXPolipoAccess
)
968 } else if(name
== atomAge
) {
969 j
= skipWhitespace(buf
, value_start
);
973 age
= strtol(buf
+ value_start
, &endptr
, 10);
974 if(endptr
<= buf
+ value_start
)
978 do_log(L_WARN
, "Couldn't parse age: \n");
979 do_log_n(L_WARN
, buf
+ value_start
, value_end
- value_start
);
980 do_log(L_WARN
, " -- ignored.\n");
982 } else if(name
== atomXPolipoBodyOffset
) {
983 j
= skipWhitespace(buf
, value_start
);
985 do_log(L_ERROR
, "Couldn't parse body offset.\n");
988 polipo_body_offset
= strtol(buf
+ value_start
, &endptr
, 10);
989 if(endptr
<= buf
+ value_start
) {
990 do_log(L_ERROR
, "Couldn't parse body offset.\n");
994 } else if(name
== atomTransferEncoding
) {
995 if(token_compare(buf
, value_start
, value_end
, "identity"))
997 else if(token_compare(buf
, value_start
, value_end
, "chunked"))
1001 } else if(name
== atomETag
||
1002 name
== atomIfNoneMatch
|| name
== atomIfMatch
||
1003 name
== atomIfRange
) {
1007 j
= getNextETag(buf
, value_start
, &x
, &y
, &weak
);
1009 if(buf
[value_start
] != '\r' && buf
[value_start
] != '\n')
1010 do_log(L_ERROR
, "Couldn't parse ETag.\n");
1012 do_log(L_WARN
, "Server returned weak ETag -- ignored.\n");
1014 e
= strdup_n(buf
+ x
, y
- x
);
1015 if(e
== NULL
) goto fail
;
1016 if(name
== atomETag
) {
1021 } else if(name
== atomIfNoneMatch
) {
1026 } else if(name
== atomIfMatch
) {
1031 } else if(name
== atomIfRange
) {
1040 } else if(name
== atomCacheControl
) {
1042 j
= getNextTokenInList(buf
, value_start
,
1043 &token_start
, &token_end
,
1048 do_log(L_WARN
, "Couldn't parse Cache-Control.\n");
1049 cache_control
.flags
|= CACHE_NO
;
1052 if(token_compare(buf
, token_start
, token_end
, "no-cache")) {
1053 cache_control
.flags
|= CACHE_NO
;
1054 } else if(token_compare(buf
, token_start
, token_end
,
1056 cache_control
.flags
|= CACHE_PUBLIC
;
1057 } else if(token_compare(buf
, token_start
, token_end
,
1059 cache_control
.flags
|= CACHE_PRIVATE
;
1060 } else if(token_compare(buf
, token_start
, token_end
,
1062 cache_control
.flags
|= CACHE_NO_STORE
;
1063 } else if(token_compare(buf
, token_start
, token_end
,
1065 cache_control
.flags
|= CACHE_NO_TRANSFORM
;
1066 } else if(token_compare(buf
, token_start
, token_end
,
1067 "must-revalidate") ||
1068 token_compare(buf
, token_start
, token_end
,
1069 "must-validate")) { /* losers */
1070 cache_control
.flags
|= CACHE_MUST_REVALIDATE
;
1071 } else if(token_compare(buf
, token_start
, token_end
,
1072 "proxy-revalidate")) {
1073 cache_control
.flags
|= CACHE_PROXY_REVALIDATE
;
1074 } else if(token_compare(buf
, token_start
, token_end
,
1075 "only-if-cached")) {
1076 cache_control
.flags
|= CACHE_ONLY_IF_CACHED
;
1077 } else if(token_compare(buf
, token_start
, token_end
,
1079 token_compare(buf
, token_start
, token_end
,
1080 "maxage")) { /* losers */
1082 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1083 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1084 do_log_n(L_WARN
, buf
+ token_start
,
1085 (v_end
>= 0 ? v_end
: token_end
) -
1087 do_log(L_WARN
, "\n");
1089 a
= atoi(buf
+ v_start
);
1090 cache_control
.max_age
= a
;
1091 } else if(token_compare(buf
, token_start
, token_end
,
1094 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1095 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1096 do_log_n(L_WARN
, buf
+ token_start
,
1097 (v_end
>= 0 ? v_end
: token_end
) -
1099 do_log(L_WARN
, "\n");
1101 a
= atoi(buf
+ v_start
);
1102 cache_control
.max_age
= a
;
1103 } else if(token_compare(buf
, token_start
, token_end
,
1106 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1107 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1108 do_log_n(L_WARN
, buf
+ token_start
,
1109 (v_end
>= 0 ? v_end
: token_end
) -
1111 do_log(L_WARN
, "\n");
1113 a
= atoi(buf
+ v_start
);
1114 cache_control
.max_age
= a
;
1115 } else if(token_compare(buf
, token_start
, token_end
,
1118 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1119 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1120 do_log_n(L_WARN
, buf
+ token_start
,
1121 (v_end
>= 0 ? v_end
: token_end
) -
1123 do_log(L_WARN
, "\n");
1125 a
= atoi(buf
+ v_start
);
1126 cache_control
.max_stale
= a
;
1128 do_log(L_WARN
, "Unsupported Cache-Control directive ");
1129 do_log_n(L_WARN
, buf
+ token_start
,
1130 (v_end
>= 0 ? v_end
: token_end
) - token_start
);
1131 do_log(L_WARN
, " -- ignored.\n");
1135 j
= getNextTokenInList(buf
, j
,
1136 &token_start
, &token_end
,
1140 } else if(name
== atomContentRange
) {
1142 j
= parseContentRange(buf
, value_start
,
1143 &content_range
.from
, &content_range
.to
,
1144 &content_range
.full_length
);
1146 do_log(L_ERROR
, "Couldn't parse Content-Range: ");
1147 do_log_n(L_ERROR
, buf
+ value_start
,
1148 value_end
- value_start
);
1149 do_log(L_ERROR
, "\n");
1153 do_log(L_ERROR
, "Content-Range from client.\n");
1156 } else if(name
== atomRange
) {
1158 j
= parseRange(buf
, value_start
, &range
.from
, &range
.to
);
1160 do_log(L_WARN
, "Couldn't parse Range -- ignored.\n");
1165 do_log(L_WARN
, "Range from server -- ignored\n");
1167 } else if(name
== atomXPolipoLocation
) {
1168 if(location_return
) {
1170 strdup_n(buf
+ value_start
, value_end
- value_start
);
1171 if(location
== NULL
) {
1172 do_log(L_ERROR
, "Couldn't allocate location.\n");
1176 } else if(name
== atomVia
) {
1178 via
= internAtomN(buf
+ value_start
, value_end
- value_start
);
1180 do_log(L_ERROR
, "Couldn't allocate via.\n");
1184 } else if(name
== atomExpect
) {
1186 expect
= internAtomLowerN(buf
+ value_start
,
1187 value_end
- value_start
);
1188 if(expect
== NULL
) {
1189 do_log(L_ERROR
, "Couldn't allocate expect.\n");
1194 if(!client
&& name
== atomContentType
) {
1195 if(token_compare(buf
, value_start
, value_end
,
1196 "multipart/byteranges")) {
1198 "Server returned multipart/byteranges -- yuck!\n");
1202 if(name
== atomVary
) {
1203 if(!token_compare(buf
, value_start
, value_end
, "host") &&
1204 !token_compare(buf
, value_start
, value_end
, "*")) {
1205 /* What other vary headers should be ignored? */
1206 do_log(L_VARY
, "Vary header present (");
1208 buf
+ value_start
, value_end
- value_start
);
1209 do_log(L_VARY
, ").\n");
1210 cache_control
.flags
|= CACHE_VARY
;
1212 } else if(name
== atomAuthorization
) {
1213 cache_control
.flags
|= CACHE_AUTHORIZATION
;
1216 if(name
== atomPragma
) {
1217 /* Pragma is only defined for the client, and the only
1218 standard value is no-cache (RFC 1945, 10.12).
1219 However, we honour a Pragma: no-cache for both the client
1220 and the server when there's no Cache-Control header. In
1221 all cases, we pass the Pragma header to the next hop. */
1222 if(!haveCacheControl
) {
1223 j
= getNextTokenInList(buf
, value_start
,
1224 &token_start
, &token_end
, NULL
, NULL
,
1228 do_log(L_WARN
, "Couldn't parse Pragma.\n");
1229 cache_control
.flags
|= CACHE_NO
;
1232 if(token_compare(buf
, token_start
, token_end
,
1234 cache_control
.flags
= CACHE_NO
;
1237 j
= getNextTokenInList(buf
, j
, &token_start
, &token_end
,
1243 (name
== atomSetCookie
||
1244 name
== atomCookie
|| name
== atomCookie2
))
1245 cache_control
.flags
|= CACHE_COOKIE
;
1248 if(name
!= atomConnection
&& name
!= atomHost
&&
1249 name
!= atomAcceptRange
&& name
!= atomTE
&&
1250 name
!= atomProxyAuthenticate
&&
1251 name
!= atomKeepAlive
&&
1252 (!hopToHop
|| !atomListMember(name
, hopToHop
)) &&
1253 !atomListMember(name
, censoredHeaders
)) {
1255 while(hbuf_length
> hbuf_size
- 2)
1257 hbuf
[hbuf_length
++] = '\r';
1258 hbuf
[hbuf_length
++] = '\n';
1260 h
= snnprint_n(hbuf
, hbuf_length
, hbuf_size
,
1262 value_end
- name_start
);
1263 if(h
< 0) RESIZE_HBUF();
1273 if(headers_return
) {
1274 AtomPtr pheaders
= NULL
;
1275 pheaders
= internAtomN(hbuf
, hbuf_length
);
1278 *headers_return
= pheaders
;
1280 if(hbuf
!= hbuf_small
)
1287 request
->flags
&= ~REQUEST_PERSISTENT
;
1289 if(te
!= TE_IDENTITY
) len
= -1;
1290 if(len_return
) *len_return
= len
;
1291 if(cache_control_return
) *cache_control_return
= cache_control
;
1292 if(condition_return
) {
1293 if(ims
>= 0 || inms
>= 0 || im
|| inm
|| ifrange
) {
1294 condition
= httpMakeCondition();
1296 condition
->ims
= ims
;
1297 condition
->inms
= inms
;
1299 condition
->inm
= inm
;
1300 condition
->ifrange
= ifrange
;
1302 do_log(L_ERROR
, "Couldn't allocate condition.\n");
1309 *condition_return
= condition
;
1311 assert(!im
&& !inm
);
1314 if(te_return
) *te_return
= te
;
1315 if(date_return
) *date_return
= date
;
1316 if(last_modified_return
) *last_modified_return
= last_modified
;
1317 if(expires_return
) *expires_return
= expires
;
1318 if(polipo_age_return
) *polipo_age_return
= polipo_age
;
1319 if(polipo_access_return
) *polipo_access_return
= polipo_access
;
1320 if(polipo_body_offset_return
)
1321 *polipo_body_offset_return
= polipo_body_offset
;
1322 if(age_return
) *age_return
= age
;
1324 *etag_return
= etag
;
1326 if(etag
) free(etag
);
1328 if(range_return
) *range_return
= range
;
1329 if(content_range_return
) *content_range_return
= content_range
;
1330 if(location_return
) {
1331 *location_return
= location
;
1343 *expect_return
= expect
;
1346 releaseAtom(expect
);
1349 *auth_return
= auth
;
1354 if(hopToHop
) destroyAtomList(hopToHop
);
1358 if(hbuf
&& hbuf
!= hbuf_small
) free(hbuf
);
1359 if(name
) releaseAtom(name
);
1360 if(etag
) free(etag
);
1361 if(location
) free(location
);
1362 if(via
) releaseAtom(via
);
1363 if(expect
) releaseAtom(expect
);
1364 if(auth
) releaseAtom(auth
);
1365 if(hopToHop
) destroyAtomList(hopToHop
);
1372 parseUrl(const char *url
, int len
,
1373 int *x_return
, int *y_return
, int *port_return
, int *z_return
)
1375 int x
, y
, z
, port
= -1, i
= 0;
1377 if(len
>= 7 && lwrcmp(url
, "http://", 7) == 0) {
1379 if(x
< len
&& url
[x
] == '[') {
1381 for(i
= x
+ 1; i
< len
; i
++) {
1386 if((url
[i
] != ':') && !letter(url
[i
]) && !digit(url
[i
]))
1390 for(i
= x
; i
< len
; i
++)
1391 if(url
[i
] == ':' || url
[i
] == '/')
1396 if(i
< len
&& url
[i
] == ':') {
1398 j
= atoi_n(url
, i
+ 1, len
, &port
);
1416 *port_return
= port
;
1422 urlIsLocal(const char *url
, int len
)
1424 return (len
> 0 && url
[0] == '/');
1428 urlIsSpecial(const char *url
, int len
)
1430 return (len
>= 8 && memcmp(url
, "/polipo/", 8) == 0);
1434 parseChunkSize(const char *restrict buf
, int i
, int end
,
1435 int *chunk_size_return
)
1453 if(buf
[i
] == ' ' || buf
[i
] == '\t')
1462 if(buf
[i
] != '\r' || buf
[i
+ 1] != '\n')
1470 if(buf
[i
] != '\r') {
1471 do_log(L_ERROR
, "Trailers present!\n");
1480 *chunk_size_return
= v
;
1486 checkVia(AtomPtr name
, AtomPtr via
)
1490 if(via
== NULL
|| via
->length
== 0)
1496 while(i
< via
->length
) {
1497 while(v
[i
] == ' ' || v
[i
] == '\t' || v
[i
] == ',' ||
1498 v
[i
] == '\r' || v
[i
] == '\n' ||
1499 digit(v
[i
]) || v
[i
] == '.')
1501 if(i
+ name
->length
> via
->length
)
1503 if(memcmp(v
+ i
, name
->string
, name
->length
) == 0) {
1504 char c
= v
[i
+ name
->length
];
1505 if(c
== '\0' || c
== ' ' || c
== '\t' || c
== ',' ||
1506 c
== '\r' || c
== '\n')
1510 while(letter(v
[i
]) || digit(v
[i
]) || v
[i
] == '.')