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
, atomTrailer
, 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(atomTrailer
, "trailer");
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
== atomTrailer
|| 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 AtomPtr new_via
, full_via
;
1183 internAtomN(buf
+ value_start
, value_end
- value_start
);
1184 if(new_via
== NULL
) {
1185 do_log(L_ERROR
, "Couldn't allocate via.\n");
1190 internAtomF("%s, %s", via
->string
, new_via
->string
);
1191 releaseAtom(new_via
);
1192 if(full_via
== NULL
) {
1193 do_log(L_ERROR
, "Couldn't allocate via");
1202 } else if(name
== atomExpect
) {
1204 expect
= internAtomLowerN(buf
+ value_start
,
1205 value_end
- value_start
);
1206 if(expect
== NULL
) {
1207 do_log(L_ERROR
, "Couldn't allocate expect.\n");
1212 if(!client
&& name
== atomContentType
) {
1213 if(token_compare(buf
, value_start
, value_end
,
1214 "multipart/byteranges")) {
1216 "Server returned multipart/byteranges -- yuck!\n");
1220 if(name
== atomVary
) {
1221 if(!token_compare(buf
, value_start
, value_end
, "host") &&
1222 !token_compare(buf
, value_start
, value_end
, "*")) {
1223 /* What other vary headers should be ignored? */
1224 do_log(L_VARY
, "Vary header present (");
1226 buf
+ value_start
, value_end
- value_start
);
1227 do_log(L_VARY
, ").\n");
1229 cache_control
.flags
|= CACHE_VARY
;
1230 } else if(name
== atomAuthorization
) {
1231 cache_control
.flags
|= CACHE_AUTHORIZATION
;
1234 if(name
== atomPragma
) {
1235 /* Pragma is only defined for the client, and the only
1236 standard value is no-cache (RFC 1945, 10.12).
1237 However, we honour a Pragma: no-cache for both the client
1238 and the server when there's no Cache-Control header. In
1239 all cases, we pass the Pragma header to the next hop. */
1240 if(!haveCacheControl
) {
1241 j
= getNextTokenInList(buf
, value_start
,
1242 &token_start
, &token_end
, NULL
, NULL
,
1246 do_log(L_WARN
, "Couldn't parse Pragma.\n");
1247 cache_control
.flags
|= CACHE_NO
;
1250 if(token_compare(buf
, token_start
, token_end
,
1252 cache_control
.flags
= CACHE_NO
;
1255 j
= getNextTokenInList(buf
, j
, &token_start
, &token_end
,
1261 (name
== atomSetCookie
||
1262 name
== atomCookie
|| name
== atomCookie2
))
1263 cache_control
.flags
|= CACHE_COOKIE
;
1266 if(name
!= atomConnection
&& name
!= atomHost
&&
1267 name
!= atomAcceptRange
&& name
!= atomTE
&&
1268 name
!= atomProxyAuthenticate
&&
1269 name
!= atomKeepAlive
&&
1270 (!hopToHop
|| !atomListMember(name
, hopToHop
)) &&
1271 !atomListMember(name
, censoredHeaders
)) {
1273 while(hbuf_length
> hbuf_size
- 2)
1275 hbuf
[hbuf_length
++] = '\r';
1276 hbuf
[hbuf_length
++] = '\n';
1278 h
= snnprint_n(hbuf
, hbuf_length
, hbuf_size
,
1280 value_end
- name_start
);
1281 if(h
< 0) RESIZE_HBUF();
1291 if(headers_return
) {
1292 AtomPtr pheaders
= NULL
;
1293 pheaders
= internAtomN(hbuf
, hbuf_length
);
1296 *headers_return
= pheaders
;
1298 if(hbuf
!= hbuf_small
)
1305 request
->flags
&= ~REQUEST_PERSISTENT
;
1307 if(te
!= TE_IDENTITY
) len
= -1;
1308 if(len_return
) *len_return
= len
;
1309 if(cache_control_return
) *cache_control_return
= cache_control
;
1310 if(condition_return
) {
1311 if(ims
>= 0 || inms
>= 0 || im
|| inm
|| ifrange
) {
1312 condition
= httpMakeCondition();
1314 condition
->ims
= ims
;
1315 condition
->inms
= inms
;
1317 condition
->inm
= inm
;
1318 condition
->ifrange
= ifrange
;
1320 do_log(L_ERROR
, "Couldn't allocate condition.\n");
1327 *condition_return
= condition
;
1329 assert(!im
&& !inm
);
1332 if(te_return
) *te_return
= te
;
1333 if(date_return
) *date_return
= date
;
1334 if(last_modified_return
) *last_modified_return
= last_modified
;
1335 if(expires_return
) *expires_return
= expires
;
1336 if(polipo_age_return
) *polipo_age_return
= polipo_age
;
1337 if(polipo_access_return
) *polipo_access_return
= polipo_access
;
1338 if(polipo_body_offset_return
)
1339 *polipo_body_offset_return
= polipo_body_offset
;
1340 if(age_return
) *age_return
= age
;
1342 *etag_return
= etag
;
1344 if(etag
) free(etag
);
1346 if(range_return
) *range_return
= range
;
1347 if(content_range_return
) *content_range_return
= content_range
;
1348 if(location_return
) {
1349 *location_return
= location
;
1361 *expect_return
= expect
;
1364 releaseAtom(expect
);
1367 *auth_return
= auth
;
1372 if(hopToHop
) destroyAtomList(hopToHop
);
1376 if(hbuf
&& hbuf
!= hbuf_small
) free(hbuf
);
1377 if(name
) releaseAtom(name
);
1378 if(etag
) free(etag
);
1379 if(location
) free(location
);
1380 if(via
) releaseAtom(via
);
1381 if(expect
) releaseAtom(expect
);
1382 if(auth
) releaseAtom(auth
);
1383 if(hopToHop
) destroyAtomList(hopToHop
);
1390 httpFindHeader(AtomPtr header
, const char *headers
, int hlen
,
1391 int *value_begin_return
, int *value_end_return
)
1393 int len
= header
->length
;
1396 while(i
+ len
+ 1 < hlen
) {
1397 if(headers
[i
+ len
] == ':' &&
1398 lwrcmp(headers
+ i
, header
->string
, len
) == 0) {
1399 int j
= i
+ len
+ 1, k
;
1400 while(j
< hlen
&& headers
[j
] == ' ')
1403 while(k
< hlen
&& headers
[k
] != '\n' && headers
[k
] != '\r')
1405 *value_begin_return
= j
;
1406 *value_end_return
= k
;
1409 while(i
< hlen
&& headers
[i
] != '\n' && headers
[i
] != '\r')
1412 if(i
< hlen
&& headers
[i
] == '\n')
1420 parseUrl(const char *url
, int len
,
1421 int *x_return
, int *y_return
, int *port_return
, int *z_return
)
1423 int x
, y
, z
, port
= -1, i
= 0;
1425 if(len
>= 7 && lwrcmp(url
, "http://", 7) == 0) {
1427 if(x
< len
&& url
[x
] == '[') {
1429 for(i
= x
+ 1; i
< len
; i
++) {
1434 if((url
[i
] != ':') && !letter(url
[i
]) && !digit(url
[i
]))
1438 for(i
= x
; i
< len
; i
++)
1439 if(url
[i
] == ':' || url
[i
] == '/')
1444 if(i
< len
&& url
[i
] == ':') {
1446 j
= atoi_n(url
, i
+ 1, len
, &port
);
1464 *port_return
= port
;
1470 urlIsLocal(const char *url
, int len
)
1472 return (len
> 0 && url
[0] == '/');
1476 urlIsSpecial(const char *url
, int len
)
1478 return (len
>= 8 && memcmp(url
, "/polipo/", 8) == 0);
1482 parseChunkSize(const char *restrict buf
, int i
, int end
,
1483 int *chunk_size_return
)
1501 if(buf
[i
] == ' ' || buf
[i
] == '\t')
1510 if(buf
[i
] != '\r' || buf
[i
+ 1] != '\n')
1518 if(buf
[i
] != '\r') {
1519 do_log(L_ERROR
, "Trailers present!\n");
1528 *chunk_size_return
= v
;
1534 checkVia(AtomPtr name
, AtomPtr via
)
1538 if(via
== NULL
|| via
->length
== 0)
1544 while(i
< via
->length
) {
1545 while(v
[i
] == ' ' || v
[i
] == '\t' || v
[i
] == ',' ||
1546 v
[i
] == '\r' || v
[i
] == '\n' ||
1547 digit(v
[i
]) || v
[i
] == '.')
1549 if(i
+ name
->length
> via
->length
)
1551 if(memcmp(v
+ i
, name
->string
, name
->length
) == 0) {
1552 char c
= v
[i
+ name
->length
];
1553 if(c
== '\0' || c
== ' ' || c
== '\t' || c
== ',' ||
1554 c
== '\r' || c
== '\n')
1558 while(letter(v
[i
]) || digit(v
[i
]) || v
[i
] == '.')