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
, atomVary
, atomExpect
, atomAuthorization
,
40 atomSetCookie
, atomCookie
, atomCookie2
,
41 atomXPolipoDate
, atomXPolipoAccess
, atomXPolipoLocation
,
42 atomXPolipoBodyOffset
;
44 AtomPtr atomContentType
, atomContentEncoding
;
46 int censorReferer
= 0;
47 int laxHttpParser
= 1;
49 static AtomListPtr censoredHeaders
;
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");
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.");
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");
76 A(atomAcceptRange
, "accept-range");
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");
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");
93 A(atomTransferEncoding
, "transfer-encoding");
95 A(atomCacheControl
, "cache-control");
96 A(atomPragma
, "pragma");
97 A(atomContentRange
, "content-range");
98 A(atomRange
, "range");
100 A(atomContentType
, "content-type");
101 A(atomContentEncoding
, "content-encoding");
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");
116 do_log(L_ERROR
, "Couldn't allocate atom.\n");
121 getNextWord(const char *restrict buf
, int i
, int *x_return
, int *y_return
)
124 while(buf
[i
] == ' ') i
++;
125 if(buf
[i
] == '\n' || buf
[i
] == '\r') return -1;
127 while(buf
[i
] > 32 && buf
[i
] < 127) i
++;
137 skipComment(const char *restrict buf
, int i
)
139 assert(buf
[i
] == '(');
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')
150 } else if(buf
[i
] == '\r') {
151 if(buf
[i
+ 1] != '\n') return -1;
152 if(buf
[i
+ 2] == ' ' || buf
[i
+ 2] == '\t')
165 skipWhitespace(const char *restrict buf
, int i
)
168 if(buf
[i
] == ' ' || buf
[i
] == '\t')
170 else if(buf
[i
] == '(') {
171 i
= skipComment(buf
, i
);
173 } else if(buf
[i
] == '\n') {
174 if(buf
[i
+ 1] == ' ' || buf
[i
+ 1] == '\t')
178 } else if(buf
[i
] == '\r' && buf
[i
+ 1] == '\n') {
179 if(buf
[i
+ 2] == ' ' || buf
[i
+ 2] == '\t')
189 getNextToken(const char *restrict buf
, int i
, int *x_return
, int *y_return
)
193 while(buf
[i
] == ' ' || buf
[i
] == '\t')
197 while(buf
[i
] != ')') {
198 if(buf
[i
] == '\n' || buf
[i
] == '\r')
200 if(buf
[i
] == '\\' && buf
[i
+ 1] != '\n' && buf
[i
+ 1] != '\r')
208 if(buf
[i
+ 1] == ' ' || buf
[i
+ 1] == '\t') {
216 if(buf
[i
+ 1] == '\n' && (buf
[i
+ 2] == ' ' || buf
[i
+ 2] == '\t')) {
224 while(buf
[i
] > 32 && buf
[i
] < 127) {
226 case '(': case ')': case '<': case '>': case '@':
227 case ',': case ';': case ':': case '\\': case '/':
228 case '[': case ']': case '?': case '=':
229 case '{': case '}': case ' ': case '\t':
245 getNextETag(const char * restrict buf
, int i
,
246 int *x_return
, int *y_return
, int *weak_return
)
250 while(buf
[i
] == ' ' || buf
[i
] == '\t')
252 if(buf
[i
] == 'W' && buf
[i
+ 1] == '/') {
262 while(buf
[i
] != '"') {
263 if(buf
[i
] == '\r' && buf
[i
] == '\n')
277 getNextTokenInList(const char *restrict buf
, int i
,
278 int *x_return
, int *y_return
,
279 int *z_return
, int *t_return
,
282 int j
, x
, y
, z
= -1, t
= -1, end
;
283 j
= getNextToken(buf
, i
, &x
, &y
);
286 while(buf
[j
] == ' ' || buf
[j
] == '\t')
291 while(buf
[j
] == ' ' || buf
[j
] == '\t')
294 while(buf
[j
] != ',' && buf
[j
] != '\n' && buf
[j
] != '\r')
298 if(buf
[j
] == '\n' || buf
[j
] == '\r') {
300 if(buf
[j
+ 1] != '\n')
306 if(buf
[j
] == ' ' || buf
[j
] == '\t') {
307 while(buf
[j
] == ' ' || buf
[j
] == '\t')
311 } else if(buf
[j
] == ',') {
313 while(buf
[j
] == ' ' || buf
[j
] == '\t')
331 token_compare(const char *buf
, int start
, int end
, const char *s
)
333 return (strcasecmp_n(s
, buf
+ start
, end
- start
) == 0);
337 skipEol(const char *restrict buf
, int i
)
343 else if(buf
[i
] == '\r') {
344 if(buf
[i
+ 1] == '\n')
354 skipToEol(const char *restrict buf
, int i
, int *start_return
)
356 while(buf
[i
] != '\n' && buf
[i
] != '\r')
361 } else if(buf
[i
] == '\r') {
362 if(buf
[i
+ 1] == '\n') {
373 getHeaderValue(const char *restrict buf
, int start
,
374 int *value_start_return
, int *value_end_return
)
378 while(buf
[start
] == ' ' || buf
[start
] == '\t')
382 j
= skipToEol(buf
, i
, &k
);
385 if(buf
[j
] == ' ' || buf
[j
] == '\t') {
389 *value_start_return
= start
;
390 *value_end_return
= k
;
395 httpParseClientFirstLine(const char *restrict buf
, int offset
,
404 int version
= HTTP_UNKNOWN
;
408 i
= getNextWord(buf
, i
, &x
, &y
);
410 if(y
== x
+ 3 && memcmp(buf
+ x
, "GET", 3) == 0)
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)
418 else if(y
== x
+ 7 && memcmp(buf
+ x
, "CONNECT", 7) == 0)
419 method
= METHOD_CONNECT
;
421 method
= METHOD_UNKNOWN
;
423 i
= getNextWord(buf
, y
+ 1, &x
, &y
);
426 url
= internAtomN(buf
+ x
, y
- x
);
428 i
= getNextWord(buf
, y
+ 1, &x
, &y
);
435 if(memcmp(buf
+ x
, "HTTP/1.", 7) != 0)
436 version
= HTTP_UNKNOWN
;
437 else if(buf
[x
+ 7] == '0')
439 else if(buf
[x
+ 7] >= '1' && buf
[x
+ 7] <= '9')
442 version
= HTTP_UNKNOWN
;
445 eol
= skipEol(buf
, y
);
446 if(eol
< 0) return -1;
448 *method_return
= method
;
453 *version_return
= version
;
458 httpParseServerFirstLine(const char *restrict buf
,
461 AtomPtr
*message_return
)
466 int version
= HTTP_UNKNOWN
;
468 i
= getNextWord(buf
, 0, &x
, &y
);
471 if(y
== x
+ 8 && memcmp(buf
+ x
, "HTTP/1.0", 8) == 0)
473 else if(y
>= x
+ 8 && memcmp(buf
+ x
, "HTTP/1.", 7) == 0)
476 version
= HTTP_UNKNOWN
;
478 i
= getNextWord(buf
, y
+ 1, &x
, &y
);
481 status
= atol(buf
+ x
);
484 i
= skipToEol(buf
, y
, &eol
);
487 *status_return
= status
;
488 *version_return
= version
;
490 /* Netscape enterprise bug */
492 *message_return
= internAtomN(buf
+ y
+ 1, eol
- y
- 1);
494 *message_return
= internAtom("No message");
500 parseInt(const char *restrict buf
, int start
, int *val_return
)
502 int i
= start
, val
= 0;
505 while(digit(buf
[i
])) {
506 val
= val
* 10 + (buf
[i
] - '0');
513 /* Returned *name_start_return is -1 at end of headers, -2 if the line
514 couldn't be parsed. */
516 parseHeaderLine(const char *restrict buf
, int start
,
517 int *name_start_return
, int *name_end_return
,
518 int *value_start_return
, int *value_end_return
)
521 int name_start
, name_end
, value_start
, value_end
;
523 if(buf
[start
] == '\n') {
524 *name_start_return
= -1;
527 if(buf
[start
] == '\r' && buf
[start
+ 1] == '\n') {
528 *name_start_return
= -1;
532 i
= getNextToken(buf
, start
, &name_start
, &name_end
);
533 if(i
< 0 || buf
[i
] != ':')
536 while(buf
[i
] == ' ' || buf
[i
] == '\t')
539 i
= getHeaderValue(buf
, i
, &value_start
, &value_end
);
543 *name_start_return
= name_start
;
544 *name_end_return
= name_end
;
545 *value_start_return
= value_start
;
546 *value_end_return
= value_end
;
556 if(buf
[i
] == '\r' && buf
[i
+ 1] == '\n') {
562 *name_start_return
= -2;
567 findEndOfHeaders(const char *restrict buf
, int from
, int to
, int *body_return
)
574 *body_return
= i
+ 1;
579 } else if(buf
[i
] == '\r') {
580 if(i
< to
- 1 && buf
[i
+ 1] == '\n') {
600 parseContentRange(const char *restrict buf
, int i
,
601 int *from_return
, int *to_return
, int *full_len_return
)
604 int from
, to
, full_len
;
606 i
= skipWhitespace(buf
, i
);
608 if(!token_compare(buf
, i
, i
+ 5, "bytes"))
611 i
= skipWhitespace(buf
, i
);
617 i
= parseInt(buf
, i
, &from
);
619 if(buf
[i
] != '-') return -1;
621 i
= parseInt(buf
, i
, &to
);
631 i
= parseInt(buf
, i
, &full_len
);
640 *full_len_return
= full_len
;
645 parseRange(const char *restrict buf
, int i
,
646 int *from_return
, int *to_return
)
651 i
= skipWhitespace(buf
, i
);
654 if(!token_compare(buf
, i
, i
+ 6, "bytes="))
657 i
= skipWhitespace(buf
, i
);
661 i
= parseInt(buf
, i
, &from
);
667 j
= parseInt(buf
, i
, &to
);
682 urlSameHost(const char *url1
, int len1
, const char *url2
, int len2
)
685 if(len1
< 7 || len2
< 7)
687 if(memcmp(url1
+ 4, "://", 3) != 0 || memcmp(url2
+ 4, "://", 3) != 0)
691 while(i
< len1
&& i
< len2
&& url1
[i
] != '/' && url2
[i
] != '/') {
692 if((url1
[i
] | 0x20) != (url2
[i
] | 0x20))
697 if((i
== len1
|| url1
[i
] == '/') && ((i
== len2
|| url2
[i
] == '/')))
703 resize_hbuf(char *hbuf
, int *size
, char *hbuf_small
)
705 int new_size
= 2 * *size
;
708 if(new_size
<= *size
)
711 if(hbuf
== hbuf_small
) {
712 new_hbuf
= malloc(new_size
);
713 if(new_hbuf
== NULL
) goto fail
;
714 memcpy(new_hbuf
, hbuf
, *size
);
716 new_hbuf
= realloc(hbuf
, new_size
);
717 if(new_hbuf
== NULL
) goto fail
;
723 if(hbuf
!= hbuf_small
)
730 httpParseHeaders(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
)
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;
748 name_start
, name_end
, value_start
, value_end
,
749 token_start
, token_end
, end
;
751 time_t date
= -1, last_modified
= -1, expires
= -1, polipo_age
= -1,
752 polipo_access
= -1, polipo_body_offset
= -1;
754 CacheControlRec cache_control
;
756 int te
= TE_IDENTITY
;
758 char *etag
= NULL
, *ifrange
= NULL
;
759 int persistent
= (!request
|| (request
->connection
->version
!= HTTP_10
));
760 char *location
= NULL
;
763 AtomPtr expect
= NULL
;
764 HTTPConditionPtr condition
;
765 time_t ims
= -1, inms
= -1;
766 char *im
= NULL
, *inm
= NULL
;
767 AtomListPtr hopToHop
= NULL
;
768 HTTPRangeRec range
= {-1, -1, -1}, content_range
= {-1, -1, -1};
769 int haveCacheControl
= 0;
771 #define RESIZE_HBUF() \
773 hbuf = resize_hbuf(hbuf, &hbuf_size, hbuf_small); \
778 cache_control
.flags
= 0;
779 cache_control
.max_age
= -1;
780 cache_control
.s_maxage
= -1;
781 cache_control
.min_fresh
= -1;
782 cache_control
.max_stale
= -1;
787 i
= parseHeaderLine(buf
, i
,
788 &name_start
, &name_end
, &value_start
, &value_end
);
790 do_log(L_ERROR
, "Couldn't find end of header line.\n");
800 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
,
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");
814 if(token_compare(buf
, token_start
, token_end
, "close")) {
816 } else if(token_compare(buf
, token_start
, token_end
,
821 hopToHop
= makeAtomList(NULL
, 0);
822 if(hopToHop
== NULL
) {
823 do_log(L_ERROR
, "Couldn't allocate atom list.\n");
826 atomListCons(internAtomLowerN(buf
+ token_start
,
827 token_end
- token_start
),
832 j
= getNextTokenInList(buf
, j
,
833 &token_start
, &token_end
, NULL
, NULL
,
836 } else if(name
== atomCacheControl
)
837 haveCacheControl
= 1;
846 i
= parseHeaderLine(buf
, i
,
847 &name_start
, &name_end
, &value_start
, &value_end
);
849 do_log(L_ERROR
, "Couldn't find end of header line.\n");
857 do_log(L_WARN
, "Couldn't parse header line.\n");
864 name
= internAtomLowerN(buf
+ name_start
, name_end
- name_start
);
866 if(name
== atomProxyConnection
) {
867 j
= getNextTokenInList(buf
, value_start
,
868 &token_start
, &token_end
, NULL
, NULL
,
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");
879 if(token_compare(buf
, token_start
, token_end
, "close")) {
881 } else if(token_compare(buf
, token_start
, token_end
,
887 j
= getNextTokenInList(buf
, j
,
888 &token_start
, &token_end
, NULL
, NULL
,
891 } else if(name
== atomContentLength
) {
892 j
= skipWhitespace(buf
, value_start
);
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");
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");
908 } else if((!local
&& name
== atomProxyAuthorization
) ||
909 (local
&& name
== atomAuthorization
)) {
911 auth
= internAtomN(buf
+ value_start
, value_end
- value_start
);
913 do_log(L_ERROR
, "Couldn't allocate authorization.\n");
917 } else if(name
== atomReferer
) {
919 if(censorReferer
== 0 ||
920 (censorReferer
== 1 && url
!= NULL
&&
921 urlSameHost(url
->string
, url
->length
,
922 buf
+ value_start
, value_end
- value_start
))) {
923 while(hbuf_length
> hbuf_size
- 2)
925 hbuf
[hbuf_length
++] = '\r';
926 hbuf
[hbuf_length
++] = '\n';
928 h
= snnprint_n(hbuf
, hbuf_length
, hbuf_size
,
929 buf
+ name_start
, value_end
- name_start
);
930 if(h
< 0) RESIZE_HBUF();
934 } else if(name
== atomTrailers
|| name
== atomUpgrade
) {
935 do_log(L_ERROR
, "Trailers or upgrade present.\n");
937 } else if(name
== atomDate
|| name
== atomExpires
||
938 name
== atomIfModifiedSince
||
939 name
== atomIfUnmodifiedSince
||
940 name
== atomLastModified
||
941 name
== atomXPolipoDate
|| name
== atomXPolipoAccess
) {
943 j
= parse_time(buf
, value_start
, value_end
, &t
);
945 if(name
!= atomExpires
) {
946 do_log(L_WARN
, "Couldn't parse %s: ", name
->string
);
947 do_log_n(L_WARN
, buf
+ value_start
,
948 value_end
- value_start
);
949 do_log(L_WARN
, "\n");
953 if(name
== atomDate
) {
956 } else if(name
== atomExpires
) {
961 } else if(name
== atomLastModified
)
963 else if(name
== atomIfModifiedSince
)
965 else if(name
== atomIfUnmodifiedSince
)
967 else if(name
== atomXPolipoDate
)
969 else if(name
== atomXPolipoAccess
)
971 } else if(name
== atomAge
) {
972 j
= skipWhitespace(buf
, value_start
);
976 age
= strtol(buf
+ value_start
, &endptr
, 10);
977 if(endptr
<= buf
+ value_start
)
981 do_log(L_WARN
, "Couldn't parse age: \n");
982 do_log_n(L_WARN
, buf
+ value_start
, value_end
- value_start
);
983 do_log(L_WARN
, " -- ignored.\n");
985 } else if(name
== atomXPolipoBodyOffset
) {
986 j
= skipWhitespace(buf
, value_start
);
988 do_log(L_ERROR
, "Couldn't parse body offset.\n");
991 polipo_body_offset
= strtol(buf
+ value_start
, &endptr
, 10);
992 if(endptr
<= buf
+ value_start
) {
993 do_log(L_ERROR
, "Couldn't parse body offset.\n");
997 } else if(name
== atomTransferEncoding
) {
998 if(token_compare(buf
, value_start
, value_end
, "identity"))
1000 else if(token_compare(buf
, value_start
, value_end
, "chunked"))
1004 } else if(name
== atomETag
||
1005 name
== atomIfNoneMatch
|| name
== atomIfMatch
||
1006 name
== atomIfRange
) {
1010 j
= getNextETag(buf
, value_start
, &x
, &y
, &weak
);
1012 if(buf
[value_start
] != '\r' && buf
[value_start
] != '\n')
1013 do_log(L_ERROR
, "Couldn't parse ETag.\n");
1015 do_log(L_WARN
, "Server returned weak ETag -- ignored.\n");
1017 e
= strdup_n(buf
+ x
, y
- x
);
1018 if(e
== NULL
) goto fail
;
1019 if(name
== atomETag
) {
1024 } else if(name
== atomIfNoneMatch
) {
1029 } else if(name
== atomIfMatch
) {
1034 } else if(name
== atomIfRange
) {
1043 } else if(name
== atomCacheControl
) {
1045 j
= getNextTokenInList(buf
, value_start
,
1046 &token_start
, &token_end
,
1051 do_log(L_WARN
, "Couldn't parse Cache-Control.\n");
1052 cache_control
.flags
|= CACHE_NO
;
1055 if(token_compare(buf
, token_start
, token_end
, "no-cache")) {
1056 cache_control
.flags
|= CACHE_NO
;
1057 } else if(token_compare(buf
, token_start
, token_end
,
1059 cache_control
.flags
|= CACHE_PUBLIC
;
1060 } else if(token_compare(buf
, token_start
, token_end
,
1062 cache_control
.flags
|= CACHE_PRIVATE
;
1063 } else if(token_compare(buf
, token_start
, token_end
,
1065 cache_control
.flags
|= CACHE_NO_STORE
;
1066 } else if(token_compare(buf
, token_start
, token_end
,
1068 cache_control
.flags
|= CACHE_NO_TRANSFORM
;
1069 } else if(token_compare(buf
, token_start
, token_end
,
1070 "must-revalidate") ||
1071 token_compare(buf
, token_start
, token_end
,
1072 "must-validate")) { /* losers */
1073 cache_control
.flags
|= CACHE_MUST_REVALIDATE
;
1074 } else if(token_compare(buf
, token_start
, token_end
,
1075 "proxy-revalidate")) {
1076 cache_control
.flags
|= CACHE_PROXY_REVALIDATE
;
1077 } else if(token_compare(buf
, token_start
, token_end
,
1078 "only-if-cached")) {
1079 cache_control
.flags
|= CACHE_ONLY_IF_CACHED
;
1080 } else if(token_compare(buf
, token_start
, token_end
,
1082 token_compare(buf
, token_start
, token_end
,
1083 "maxage")) { /* losers */
1085 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1086 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1087 do_log_n(L_WARN
, buf
+ token_start
,
1088 (v_end
>= 0 ? v_end
: token_end
) -
1090 do_log(L_WARN
, "\n");
1092 a
= atoi(buf
+ v_start
);
1093 cache_control
.max_age
= a
;
1094 } else if(token_compare(buf
, token_start
, token_end
,
1097 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1098 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1099 do_log_n(L_WARN
, buf
+ token_start
,
1100 (v_end
>= 0 ? v_end
: token_end
) -
1102 do_log(L_WARN
, "\n");
1104 a
= atoi(buf
+ v_start
);
1105 cache_control
.max_age
= a
;
1106 } else if(token_compare(buf
, token_start
, token_end
,
1109 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1110 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1111 do_log_n(L_WARN
, buf
+ token_start
,
1112 (v_end
>= 0 ? v_end
: token_end
) -
1114 do_log(L_WARN
, "\n");
1116 a
= atoi(buf
+ v_start
);
1117 cache_control
.max_age
= a
;
1118 } else if(token_compare(buf
, token_start
, token_end
,
1121 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1122 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1123 do_log_n(L_WARN
, buf
+ token_start
,
1124 (v_end
>= 0 ? v_end
: token_end
) -
1126 do_log(L_WARN
, "\n");
1128 a
= atoi(buf
+ v_start
);
1129 cache_control
.max_stale
= a
;
1131 do_log(L_WARN
, "Unsupported Cache-Control directive ");
1132 do_log_n(L_WARN
, buf
+ token_start
,
1133 (v_end
>= 0 ? v_end
: token_end
) - token_start
);
1134 do_log(L_WARN
, " -- ignored.\n");
1138 j
= getNextTokenInList(buf
, j
,
1139 &token_start
, &token_end
,
1143 } else if(name
== atomContentRange
) {
1145 j
= parseContentRange(buf
, value_start
,
1146 &content_range
.from
, &content_range
.to
,
1147 &content_range
.full_length
);
1149 do_log(L_ERROR
, "Couldn't parse Content-Range: ");
1150 do_log_n(L_ERROR
, buf
+ value_start
,
1151 value_end
- value_start
);
1152 do_log(L_ERROR
, "\n");
1156 do_log(L_ERROR
, "Content-Range from client.\n");
1159 } else if(name
== atomRange
) {
1161 j
= parseRange(buf
, value_start
, &range
.from
, &range
.to
);
1163 do_log(L_WARN
, "Couldn't parse Range -- ignored.\n");
1168 do_log(L_WARN
, "Range from server -- ignored\n");
1170 } else if(name
== atomXPolipoLocation
) {
1171 if(location_return
) {
1173 strdup_n(buf
+ value_start
, value_end
- value_start
);
1174 if(location
== NULL
) {
1175 do_log(L_ERROR
, "Couldn't allocate location.\n");
1179 } else if(name
== atomVia
) {
1181 via
= internAtomN(buf
+ value_start
, value_end
- value_start
);
1183 do_log(L_ERROR
, "Couldn't allocate via.\n");
1187 } else if(name
== atomExpect
) {
1189 expect
= internAtomLowerN(buf
+ value_start
,
1190 value_end
- value_start
);
1191 if(expect
== NULL
) {
1192 do_log(L_ERROR
, "Couldn't allocate expect.\n");
1197 if(!client
&& name
== atomContentType
) {
1198 if(token_compare(buf
, value_start
, value_end
,
1199 "multipart/byteranges")) {
1201 "Server returned multipart/byteranges -- yuck!\n");
1205 if(name
== atomVary
) {
1206 if(!token_compare(buf
, value_start
, value_end
, "host") &&
1207 !token_compare(buf
, value_start
, value_end
, "*")) {
1208 /* What other vary headers should be ignored? */
1209 do_log(L_VARY
, "Vary header present (");
1211 buf
+ value_start
, value_end
- value_start
);
1212 do_log(L_VARY
, ").\n");
1213 cache_control
.flags
|= CACHE_VARY
;
1215 } else if(name
== atomAuthorization
) {
1216 cache_control
.flags
|= CACHE_AUTHORIZATION
;
1219 if(name
== atomPragma
) {
1220 /* Pragma is only defined for the client, and the only
1221 standard value is no-cache (RFC 1945, 10.12).
1222 However, we honour a Pragma: no-cache for both the client
1223 and the server when there's no Cache-Control header. In
1224 all cases, we pass the Pragma header to the next hop. */
1225 if(!haveCacheControl
) {
1226 j
= getNextTokenInList(buf
, value_start
,
1227 &token_start
, &token_end
, NULL
, NULL
,
1231 do_log(L_WARN
, "Couldn't parse Pragma.\n");
1232 cache_control
.flags
|= CACHE_NO
;
1235 if(token_compare(buf
, token_start
, token_end
,
1237 cache_control
.flags
= CACHE_NO
;
1240 j
= getNextTokenInList(buf
, j
, &token_start
, &token_end
,
1246 (name
== atomSetCookie
||
1247 name
== atomCookie
|| name
== atomCookie2
))
1248 cache_control
.flags
|= CACHE_COOKIE
;
1251 if(name
!= atomConnection
&& name
!= atomHost
&&
1252 name
!= atomAcceptRange
&& name
!= atomTE
&&
1253 name
!= atomProxyAuthenticate
&&
1254 name
!= atomKeepAlive
&&
1255 (!hopToHop
|| !atomListMember(name
, hopToHop
)) &&
1256 !atomListMember(name
, censoredHeaders
)) {
1258 while(hbuf_length
> hbuf_size
- 2)
1260 hbuf
[hbuf_length
++] = '\r';
1261 hbuf
[hbuf_length
++] = '\n';
1263 h
= snnprint_n(hbuf
, hbuf_length
, hbuf_size
,
1265 value_end
- name_start
);
1266 if(h
< 0) RESIZE_HBUF();
1276 if(headers_return
) {
1277 AtomPtr pheaders
= NULL
;
1278 pheaders
= internAtomN(hbuf
, hbuf_length
);
1281 *headers_return
= pheaders
;
1283 if(hbuf
!= hbuf_small
)
1290 request
->flags
&= ~REQUEST_PERSISTENT
;
1292 if(te
!= TE_IDENTITY
) len
= -1;
1293 if(len_return
) *len_return
= len
;
1294 if(cache_control_return
) *cache_control_return
= cache_control
;
1295 if(condition_return
) {
1296 if(ims
>= 0 || inms
>= 0 || im
|| inm
|| ifrange
) {
1297 condition
= httpMakeCondition();
1299 condition
->ims
= ims
;
1300 condition
->inms
= inms
;
1302 condition
->inm
= inm
;
1303 condition
->ifrange
= ifrange
;
1305 do_log(L_ERROR
, "Couldn't allocate condition.\n");
1312 *condition_return
= condition
;
1314 assert(!im
&& !inm
);
1317 if(te_return
) *te_return
= te
;
1318 if(date_return
) *date_return
= date
;
1319 if(last_modified_return
) *last_modified_return
= last_modified
;
1320 if(expires_return
) *expires_return
= expires
;
1321 if(polipo_age_return
) *polipo_age_return
= polipo_age
;
1322 if(polipo_access_return
) *polipo_access_return
= polipo_access
;
1323 if(polipo_body_offset_return
)
1324 *polipo_body_offset_return
= polipo_body_offset
;
1325 if(age_return
) *age_return
= age
;
1327 *etag_return
= etag
;
1329 if(etag
) free(etag
);
1331 if(range_return
) *range_return
= range
;
1332 if(content_range_return
) *content_range_return
= content_range
;
1333 if(location_return
) {
1334 *location_return
= location
;
1346 *expect_return
= expect
;
1349 releaseAtom(expect
);
1352 *auth_return
= auth
;
1357 if(hopToHop
) destroyAtomList(hopToHop
);
1361 if(hbuf
&& hbuf
!= hbuf_small
) free(hbuf
);
1362 if(name
) releaseAtom(name
);
1363 if(etag
) free(etag
);
1364 if(location
) free(location
);
1365 if(via
) releaseAtom(via
);
1366 if(expect
) releaseAtom(expect
);
1367 if(auth
) releaseAtom(auth
);
1368 if(hopToHop
) destroyAtomList(hopToHop
);
1375 httpFindHeader(AtomPtr header
, const char *headers
, int hlen
,
1376 int *value_begin_return
, int *value_end_return
)
1378 int len
= header
->length
;
1381 while(i
+ len
+ 1 < hlen
) {
1382 if(headers
[i
+ len
] == ':' &&
1383 lwrcmp(headers
+ i
, header
->string
, len
) == 0) {
1384 int j
= i
+ len
+ 1, k
;
1385 while(j
< hlen
&& headers
[j
] == ' ')
1388 while(k
< hlen
&& headers
[k
] != '\n' && headers
[k
] != '\r')
1390 *value_begin_return
= j
;
1391 *value_end_return
= k
;
1394 while(i
< hlen
&& headers
[i
] != '\n' && headers
[i
] != '\r')
1397 if(i
< hlen
&& headers
[i
] == '\n')
1405 parseUrl(const char *url
, int len
,
1406 int *x_return
, int *y_return
, int *port_return
, int *z_return
)
1408 int x
, y
, z
, port
= -1, i
= 0;
1410 if(len
>= 7 && lwrcmp(url
, "http://", 7) == 0) {
1412 if(x
< len
&& url
[x
] == '[') {
1414 for(i
= x
+ 1; i
< len
; i
++) {
1419 if((url
[i
] != ':') && !letter(url
[i
]) && !digit(url
[i
]))
1423 for(i
= x
; i
< len
; i
++)
1424 if(url
[i
] == ':' || url
[i
] == '/')
1429 if(i
< len
&& url
[i
] == ':') {
1431 j
= atoi_n(url
, i
+ 1, len
, &port
);
1449 *port_return
= port
;
1455 urlIsLocal(const char *url
, int len
)
1457 return (len
> 0 && url
[0] == '/');
1461 urlIsSpecial(const char *url
, int len
)
1463 return (len
>= 8 && memcmp(url
, "/polipo/", 8) == 0);
1467 parseChunkSize(const char *restrict buf
, int i
, int end
,
1468 int *chunk_size_return
)
1486 if(buf
[i
] == ' ' || buf
[i
] == '\t')
1495 if(buf
[i
] != '\r' || buf
[i
+ 1] != '\n')
1503 if(buf
[i
] != '\r') {
1504 do_log(L_ERROR
, "Trailers present!\n");
1513 *chunk_size_return
= v
;
1519 checkVia(AtomPtr name
, AtomPtr via
)
1523 if(via
== NULL
|| via
->length
== 0)
1529 while(i
< via
->length
) {
1530 while(v
[i
] == ' ' || v
[i
] == '\t' || v
[i
] == ',' ||
1531 v
[i
] == '\r' || v
[i
] == '\n' ||
1532 digit(v
[i
]) || v
[i
] == '.')
1534 if(i
+ name
->length
> via
->length
)
1536 if(memcmp(v
+ i
, name
->string
, name
->length
) == 0) {
1537 char c
= v
[i
+ name
->length
];
1538 if(c
== '\0' || c
== ' ' || c
== '\t' || c
== ',' ||
1539 c
== '\r' || c
== '\n')
1543 while(letter(v
[i
]) || digit(v
[i
]) || v
[i
] == '.')