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")) {
609 do_log(L_WARN
, "Incorrect Content-Range header -- chugging along.\n");
613 i
= skipWhitespace(buf
, i
);
619 i
= parseInt(buf
, i
, &from
);
621 if(buf
[i
] != '-') return -1;
623 i
= parseInt(buf
, i
, &to
);
633 i
= parseInt(buf
, i
, &full_len
);
642 *full_len_return
= full_len
;
647 parseRange(const char *restrict buf
, int i
,
648 int *from_return
, int *to_return
)
653 i
= skipWhitespace(buf
, i
);
656 if(!token_compare(buf
, i
, i
+ 6, "bytes="))
659 i
= skipWhitespace(buf
, i
);
663 i
= parseInt(buf
, i
, &from
);
669 j
= parseInt(buf
, i
, &to
);
684 urlSameHost(const char *url1
, int len1
, const char *url2
, int len2
)
687 if(len1
< 7 || len2
< 7)
689 if(memcmp(url1
+ 4, "://", 3) != 0 || memcmp(url2
+ 4, "://", 3) != 0)
693 while(i
< len1
&& i
< len2
&& url1
[i
] != '/' && url2
[i
] != '/') {
694 if((url1
[i
] | 0x20) != (url2
[i
] | 0x20))
699 if((i
== len1
|| url1
[i
] == '/') && ((i
== len2
|| url2
[i
] == '/')))
705 resize_hbuf(char *hbuf
, int *size
, char *hbuf_small
)
707 int new_size
= 2 * *size
;
710 if(new_size
<= *size
)
713 if(hbuf
== hbuf_small
) {
714 new_hbuf
= malloc(new_size
);
715 if(new_hbuf
== NULL
) goto fail
;
716 memcpy(new_hbuf
, hbuf
, *size
);
718 new_hbuf
= realloc(hbuf
, new_size
);
719 if(new_hbuf
== NULL
) goto fail
;
725 if(hbuf
!= hbuf_small
)
732 httpParseHeaders(int client
, AtomPtr url
,
733 const char *buf
, int start
, HTTPRequestPtr request
,
734 AtomPtr
*headers_return
,
735 int *len_return
, CacheControlPtr cache_control_return
,
736 HTTPConditionPtr
*condition_return
, int *te_return
,
737 time_t *date_return
, time_t *last_modified_return
,
738 time_t *expires_return
, time_t *polipo_age_return
,
739 time_t *polipo_access_return
, int *polipo_body_offset_return
,
740 int *age_return
, char **etag_return
, AtomPtr
*expect_return
,
741 HTTPRangePtr range_return
, HTTPRangePtr content_range_return
,
742 char **location_return
, AtomPtr
*via_return
,
743 AtomPtr
*auth_return
)
745 int local
= url
? urlIsLocal(url
->string
, url
->length
) : 0;
746 char hbuf_small
[512];
747 char *hbuf
= hbuf_small
;
748 int hbuf_size
= 512, hbuf_length
= 0;
750 name_start
, name_end
, value_start
, value_end
,
751 token_start
, token_end
, end
;
753 time_t date
= -1, last_modified
= -1, expires
= -1, polipo_age
= -1,
754 polipo_access
= -1, polipo_body_offset
= -1;
756 CacheControlRec cache_control
;
758 int te
= TE_IDENTITY
;
760 char *etag
= NULL
, *ifrange
= NULL
;
761 int persistent
= (!request
|| (request
->connection
->version
!= HTTP_10
));
762 char *location
= NULL
;
765 AtomPtr expect
= NULL
;
766 HTTPConditionPtr condition
;
767 time_t ims
= -1, inms
= -1;
768 char *im
= NULL
, *inm
= NULL
;
769 AtomListPtr hopToHop
= NULL
;
770 HTTPRangeRec range
= {-1, -1, -1}, content_range
= {-1, -1, -1};
771 int haveCacheControl
= 0;
773 #define RESIZE_HBUF() \
775 hbuf = resize_hbuf(hbuf, &hbuf_size, hbuf_small); \
780 cache_control
.flags
= 0;
781 cache_control
.max_age
= -1;
782 cache_control
.s_maxage
= -1;
783 cache_control
.min_fresh
= -1;
784 cache_control
.max_stale
= -1;
789 i
= parseHeaderLine(buf
, i
,
790 &name_start
, &name_end
, &value_start
, &value_end
);
792 do_log(L_ERROR
, "Couldn't find end of header line.\n");
802 name
= internAtomLowerN(buf
+ name_start
, name_end
- name_start
);
804 if(name
== atomConnection
) {
805 j
= getNextTokenInList(buf
, value_start
,
806 &token_start
, &token_end
, NULL
, NULL
,
810 do_log(L_ERROR
, "Couldn't parse Connection: ");
811 do_log_n(L_ERROR
, buf
+ value_start
,
812 value_end
- value_start
);
813 do_log(L_ERROR
, ".\n");
816 if(token_compare(buf
, token_start
, token_end
, "close")) {
818 } else if(token_compare(buf
, token_start
, token_end
,
823 hopToHop
= makeAtomList(NULL
, 0);
824 if(hopToHop
== NULL
) {
825 do_log(L_ERROR
, "Couldn't allocate atom list.\n");
828 atomListCons(internAtomLowerN(buf
+ token_start
,
829 token_end
- token_start
),
834 j
= getNextTokenInList(buf
, j
,
835 &token_start
, &token_end
, NULL
, NULL
,
838 } else if(name
== atomCacheControl
)
839 haveCacheControl
= 1;
848 i
= parseHeaderLine(buf
, i
,
849 &name_start
, &name_end
, &value_start
, &value_end
);
851 do_log(L_ERROR
, "Couldn't find end of header line.\n");
859 do_log(L_WARN
, "Couldn't parse header line.\n");
866 name
= internAtomLowerN(buf
+ name_start
, name_end
- name_start
);
868 if(name
== atomProxyConnection
) {
869 j
= getNextTokenInList(buf
, value_start
,
870 &token_start
, &token_end
, NULL
, NULL
,
874 do_log(L_WARN
, "Couldn't parse Proxy-Connection:");
875 do_log_n(L_WARN
, buf
+ value_start
,
876 value_end
- value_start
);
877 do_log(L_WARN
, ".\n");
881 if(token_compare(buf
, token_start
, token_end
, "close")) {
883 } else if(token_compare(buf
, token_start
, token_end
,
889 j
= getNextTokenInList(buf
, j
,
890 &token_start
, &token_end
, NULL
, NULL
,
893 } else if(name
== atomContentLength
) {
894 j
= skipWhitespace(buf
, value_start
);
896 do_log(L_WARN
, "Couldn't parse Content-Length: \n");
897 do_log_n(L_WARN
, buf
+ value_start
, value_end
- value_start
);
898 do_log(L_WARN
, ".\n");
901 len
= strtol(buf
+ value_start
, &endptr
, 10);
902 if(endptr
<= buf
+ value_start
) {
903 do_log(L_WARN
, "Couldn't parse Content-Length: \n");
904 do_log_n(L_WARN
, buf
+ value_start
,
905 value_end
- value_start
);
906 do_log(L_WARN
, ".\n");
910 } else if((!local
&& name
== atomProxyAuthorization
) ||
911 (local
&& name
== atomAuthorization
)) {
913 auth
= internAtomN(buf
+ value_start
, value_end
- value_start
);
915 do_log(L_ERROR
, "Couldn't allocate authorization.\n");
919 } else if(name
== atomReferer
) {
921 if(censorReferer
== 0 ||
922 (censorReferer
== 1 && url
!= NULL
&&
923 urlSameHost(url
->string
, url
->length
,
924 buf
+ value_start
, value_end
- value_start
))) {
925 while(hbuf_length
> hbuf_size
- 2)
927 hbuf
[hbuf_length
++] = '\r';
928 hbuf
[hbuf_length
++] = '\n';
930 h
= snnprint_n(hbuf
, hbuf_length
, hbuf_size
,
931 buf
+ name_start
, value_end
- name_start
);
932 if(h
< 0) RESIZE_HBUF();
936 } else if(name
== atomTrailer
|| name
== atomUpgrade
) {
937 do_log(L_ERROR
, "Trailers or upgrade present.\n");
939 } else if(name
== atomDate
|| name
== atomExpires
||
940 name
== atomIfModifiedSince
||
941 name
== atomIfUnmodifiedSince
||
942 name
== atomLastModified
||
943 name
== atomXPolipoDate
|| name
== atomXPolipoAccess
) {
945 j
= parse_time(buf
, value_start
, value_end
, &t
);
947 if(name
!= atomExpires
) {
948 do_log(L_WARN
, "Couldn't parse %s: ", name
->string
);
949 do_log_n(L_WARN
, buf
+ value_start
,
950 value_end
- value_start
);
951 do_log(L_WARN
, "\n");
955 if(name
== atomDate
) {
958 } else if(name
== atomExpires
) {
963 } else if(name
== atomLastModified
)
965 else if(name
== atomIfModifiedSince
)
967 else if(name
== atomIfUnmodifiedSince
)
969 else if(name
== atomXPolipoDate
)
971 else if(name
== atomXPolipoAccess
)
973 } else if(name
== atomAge
) {
974 j
= skipWhitespace(buf
, value_start
);
978 age
= strtol(buf
+ value_start
, &endptr
, 10);
979 if(endptr
<= buf
+ value_start
)
983 do_log(L_WARN
, "Couldn't parse age: \n");
984 do_log_n(L_WARN
, buf
+ value_start
, value_end
- value_start
);
985 do_log(L_WARN
, " -- ignored.\n");
987 } else if(name
== atomXPolipoBodyOffset
) {
988 j
= skipWhitespace(buf
, value_start
);
990 do_log(L_ERROR
, "Couldn't parse body offset.\n");
993 polipo_body_offset
= strtol(buf
+ value_start
, &endptr
, 10);
994 if(endptr
<= buf
+ value_start
) {
995 do_log(L_ERROR
, "Couldn't parse body offset.\n");
999 } else if(name
== atomTransferEncoding
) {
1000 if(token_compare(buf
, value_start
, value_end
, "identity"))
1002 else if(token_compare(buf
, value_start
, value_end
, "chunked"))
1006 } else if(name
== atomETag
||
1007 name
== atomIfNoneMatch
|| name
== atomIfMatch
||
1008 name
== atomIfRange
) {
1012 j
= getNextETag(buf
, value_start
, &x
, &y
, &weak
);
1014 if(buf
[value_start
] != '\r' && buf
[value_start
] != '\n')
1015 do_log(L_ERROR
, "Couldn't parse ETag.\n");
1017 do_log(L_WARN
, "Server returned weak ETag -- ignored.\n");
1019 e
= strdup_n(buf
+ x
, y
- x
);
1020 if(e
== NULL
) goto fail
;
1021 if(name
== atomETag
) {
1026 } else if(name
== atomIfNoneMatch
) {
1031 } else if(name
== atomIfMatch
) {
1036 } else if(name
== atomIfRange
) {
1045 } else if(name
== atomCacheControl
) {
1047 j
= getNextTokenInList(buf
, value_start
,
1048 &token_start
, &token_end
,
1053 do_log(L_WARN
, "Couldn't parse Cache-Control.\n");
1054 cache_control
.flags
|= CACHE_NO
;
1057 if(token_compare(buf
, token_start
, token_end
, "no-cache")) {
1058 cache_control
.flags
|= CACHE_NO
;
1059 } else if(token_compare(buf
, token_start
, token_end
,
1061 cache_control
.flags
|= CACHE_PUBLIC
;
1062 } else if(token_compare(buf
, token_start
, token_end
,
1064 cache_control
.flags
|= CACHE_PRIVATE
;
1065 } else if(token_compare(buf
, token_start
, token_end
,
1067 cache_control
.flags
|= CACHE_NO_STORE
;
1068 } else if(token_compare(buf
, token_start
, token_end
,
1070 cache_control
.flags
|= CACHE_NO_TRANSFORM
;
1071 } else if(token_compare(buf
, token_start
, token_end
,
1072 "must-revalidate") ||
1073 token_compare(buf
, token_start
, token_end
,
1074 "must-validate")) { /* losers */
1075 cache_control
.flags
|= CACHE_MUST_REVALIDATE
;
1076 } else if(token_compare(buf
, token_start
, token_end
,
1077 "proxy-revalidate")) {
1078 cache_control
.flags
|= CACHE_PROXY_REVALIDATE
;
1079 } else if(token_compare(buf
, token_start
, token_end
,
1080 "only-if-cached")) {
1081 cache_control
.flags
|= CACHE_ONLY_IF_CACHED
;
1082 } else if(token_compare(buf
, token_start
, token_end
,
1084 token_compare(buf
, token_start
, token_end
,
1085 "maxage")) { /* losers */
1087 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1088 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1089 do_log_n(L_WARN
, buf
+ token_start
,
1090 (v_end
>= 0 ? v_end
: token_end
) -
1092 do_log(L_WARN
, "\n");
1094 a
= atoi(buf
+ v_start
);
1095 cache_control
.max_age
= a
;
1096 } else if(token_compare(buf
, token_start
, token_end
,
1099 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1100 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1101 do_log_n(L_WARN
, buf
+ token_start
,
1102 (v_end
>= 0 ? v_end
: token_end
) -
1104 do_log(L_WARN
, "\n");
1106 a
= atoi(buf
+ v_start
);
1107 cache_control
.max_age
= a
;
1108 } else if(token_compare(buf
, token_start
, token_end
,
1111 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1112 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1113 do_log_n(L_WARN
, buf
+ token_start
,
1114 (v_end
>= 0 ? v_end
: token_end
) -
1116 do_log(L_WARN
, "\n");
1118 a
= atoi(buf
+ v_start
);
1119 cache_control
.max_age
= a
;
1120 } else if(token_compare(buf
, token_start
, token_end
,
1123 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1124 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1125 do_log_n(L_WARN
, buf
+ token_start
,
1126 (v_end
>= 0 ? v_end
: token_end
) -
1128 do_log(L_WARN
, "\n");
1130 a
= atoi(buf
+ v_start
);
1131 cache_control
.max_stale
= a
;
1133 do_log(L_WARN
, "Unsupported Cache-Control directive ");
1134 do_log_n(L_WARN
, buf
+ token_start
,
1135 (v_end
>= 0 ? v_end
: token_end
) - token_start
);
1136 do_log(L_WARN
, " -- ignored.\n");
1140 j
= getNextTokenInList(buf
, j
,
1141 &token_start
, &token_end
,
1145 } else if(name
== atomContentRange
) {
1147 j
= parseContentRange(buf
, value_start
,
1148 &content_range
.from
, &content_range
.to
,
1149 &content_range
.full_length
);
1151 do_log(L_ERROR
, "Couldn't parse Content-Range: ");
1152 do_log_n(L_ERROR
, buf
+ value_start
,
1153 value_end
- value_start
);
1154 do_log(L_ERROR
, "\n");
1158 do_log(L_ERROR
, "Content-Range from client.\n");
1161 } else if(name
== atomRange
) {
1163 j
= parseRange(buf
, value_start
, &range
.from
, &range
.to
);
1165 do_log(L_WARN
, "Couldn't parse Range -- ignored.\n");
1170 do_log(L_WARN
, "Range from server -- ignored\n");
1172 } else if(name
== atomXPolipoLocation
) {
1173 if(location_return
) {
1175 strdup_n(buf
+ value_start
, value_end
- value_start
);
1176 if(location
== NULL
) {
1177 do_log(L_ERROR
, "Couldn't allocate location.\n");
1181 } else if(name
== atomVia
) {
1183 AtomPtr new_via
, full_via
;
1185 internAtomN(buf
+ value_start
, value_end
- value_start
);
1186 if(new_via
== NULL
) {
1187 do_log(L_ERROR
, "Couldn't allocate via.\n");
1192 internAtomF("%s, %s", via
->string
, new_via
->string
);
1193 releaseAtom(new_via
);
1194 if(full_via
== NULL
) {
1195 do_log(L_ERROR
, "Couldn't allocate via");
1204 } else if(name
== atomExpect
) {
1206 expect
= internAtomLowerN(buf
+ value_start
,
1207 value_end
- value_start
);
1208 if(expect
== NULL
) {
1209 do_log(L_ERROR
, "Couldn't allocate expect.\n");
1214 if(!client
&& name
== atomContentType
) {
1215 if(token_compare(buf
, value_start
, value_end
,
1216 "multipart/byteranges")) {
1218 "Server returned multipart/byteranges -- yuck!\n");
1222 if(name
== atomVary
) {
1223 if(!token_compare(buf
, value_start
, value_end
, "host") &&
1224 !token_compare(buf
, value_start
, value_end
, "*")) {
1225 /* What other vary headers should be ignored? */
1226 do_log(L_VARY
, "Vary header present (");
1228 buf
+ value_start
, value_end
- value_start
);
1229 do_log(L_VARY
, ").\n");
1231 cache_control
.flags
|= CACHE_VARY
;
1232 } else if(name
== atomAuthorization
) {
1233 cache_control
.flags
|= CACHE_AUTHORIZATION
;
1236 if(name
== atomPragma
) {
1237 /* Pragma is only defined for the client, and the only
1238 standard value is no-cache (RFC 1945, 10.12).
1239 However, we honour a Pragma: no-cache for both the client
1240 and the server when there's no Cache-Control header. In
1241 all cases, we pass the Pragma header to the next hop. */
1242 if(!haveCacheControl
) {
1243 j
= getNextTokenInList(buf
, value_start
,
1244 &token_start
, &token_end
, NULL
, NULL
,
1248 do_log(L_WARN
, "Couldn't parse Pragma.\n");
1249 cache_control
.flags
|= CACHE_NO
;
1252 if(token_compare(buf
, token_start
, token_end
,
1254 cache_control
.flags
= CACHE_NO
;
1257 j
= getNextTokenInList(buf
, j
, &token_start
, &token_end
,
1263 (name
== atomSetCookie
||
1264 name
== atomCookie
|| name
== atomCookie2
))
1265 cache_control
.flags
|= CACHE_COOKIE
;
1268 if(name
!= atomConnection
&& name
!= atomHost
&&
1269 name
!= atomAcceptRange
&& name
!= atomTE
&&
1270 name
!= atomProxyAuthenticate
&&
1271 name
!= atomKeepAlive
&&
1272 (!hopToHop
|| !atomListMember(name
, hopToHop
)) &&
1273 !atomListMember(name
, censoredHeaders
)) {
1275 while(hbuf_length
> hbuf_size
- 2)
1277 hbuf
[hbuf_length
++] = '\r';
1278 hbuf
[hbuf_length
++] = '\n';
1280 h
= snnprint_n(hbuf
, hbuf_length
, hbuf_size
,
1282 value_end
- name_start
);
1283 if(h
< 0) RESIZE_HBUF();
1293 if(headers_return
) {
1294 AtomPtr pheaders
= NULL
;
1295 pheaders
= internAtomN(hbuf
, hbuf_length
);
1298 *headers_return
= pheaders
;
1300 if(hbuf
!= hbuf_small
)
1307 request
->flags
&= ~REQUEST_PERSISTENT
;
1309 if(te
!= TE_IDENTITY
) len
= -1;
1310 if(len_return
) *len_return
= len
;
1311 if(cache_control_return
) *cache_control_return
= cache_control
;
1312 if(condition_return
) {
1313 if(ims
>= 0 || inms
>= 0 || im
|| inm
|| ifrange
) {
1314 condition
= httpMakeCondition();
1316 condition
->ims
= ims
;
1317 condition
->inms
= inms
;
1319 condition
->inm
= inm
;
1320 condition
->ifrange
= ifrange
;
1322 do_log(L_ERROR
, "Couldn't allocate condition.\n");
1329 *condition_return
= condition
;
1331 assert(!im
&& !inm
);
1334 if(te_return
) *te_return
= te
;
1335 if(date_return
) *date_return
= date
;
1336 if(last_modified_return
) *last_modified_return
= last_modified
;
1337 if(expires_return
) *expires_return
= expires
;
1338 if(polipo_age_return
) *polipo_age_return
= polipo_age
;
1339 if(polipo_access_return
) *polipo_access_return
= polipo_access
;
1340 if(polipo_body_offset_return
)
1341 *polipo_body_offset_return
= polipo_body_offset
;
1342 if(age_return
) *age_return
= age
;
1344 *etag_return
= etag
;
1346 if(etag
) free(etag
);
1348 if(range_return
) *range_return
= range
;
1349 if(content_range_return
) *content_range_return
= content_range
;
1350 if(location_return
) {
1351 *location_return
= location
;
1363 *expect_return
= expect
;
1366 releaseAtom(expect
);
1369 *auth_return
= auth
;
1374 if(hopToHop
) destroyAtomList(hopToHop
);
1378 if(hbuf
&& hbuf
!= hbuf_small
) free(hbuf
);
1379 if(name
) releaseAtom(name
);
1380 if(etag
) free(etag
);
1381 if(location
) free(location
);
1382 if(via
) releaseAtom(via
);
1383 if(expect
) releaseAtom(expect
);
1384 if(auth
) releaseAtom(auth
);
1385 if(hopToHop
) destroyAtomList(hopToHop
);
1392 httpFindHeader(AtomPtr header
, const char *headers
, int hlen
,
1393 int *value_begin_return
, int *value_end_return
)
1395 int len
= header
->length
;
1398 while(i
+ len
+ 1 < hlen
) {
1399 if(headers
[i
+ len
] == ':' &&
1400 lwrcmp(headers
+ i
, header
->string
, len
) == 0) {
1401 int j
= i
+ len
+ 1, k
;
1402 while(j
< hlen
&& headers
[j
] == ' ')
1405 while(k
< hlen
&& headers
[k
] != '\n' && headers
[k
] != '\r')
1407 *value_begin_return
= j
;
1408 *value_end_return
= k
;
1411 while(i
< hlen
&& headers
[i
] != '\n' && headers
[i
] != '\r')
1414 if(i
< hlen
&& headers
[i
] == '\n')
1422 parseUrl(const char *url
, int len
,
1423 int *x_return
, int *y_return
, int *port_return
, int *z_return
)
1425 int x
, y
, z
, port
= -1, i
= 0;
1427 if(len
>= 7 && lwrcmp(url
, "http://", 7) == 0) {
1429 if(x
< len
&& url
[x
] == '[') {
1431 for(i
= x
+ 1; i
< len
; i
++) {
1436 if((url
[i
] != ':') && !letter(url
[i
]) && !digit(url
[i
]))
1440 for(i
= x
; i
< len
; i
++)
1441 if(url
[i
] == ':' || url
[i
] == '/')
1446 if(i
< len
&& url
[i
] == ':') {
1448 j
= atoi_n(url
, i
+ 1, len
, &port
);
1466 *port_return
= port
;
1472 urlIsLocal(const char *url
, int len
)
1474 return (len
> 0 && url
[0] == '/');
1478 urlIsSpecial(const char *url
, int len
)
1480 return (len
>= 8 && memcmp(url
, "/polipo/", 8) == 0);
1484 parseChunkSize(const char *restrict buf
, int i
, int end
,
1485 int *chunk_size_return
)
1503 if(buf
[i
] == ' ' || buf
[i
] == '\t')
1512 if(buf
[i
] != '\r' || buf
[i
+ 1] != '\n')
1520 if(buf
[i
] != '\r') {
1521 do_log(L_ERROR
, "Trailers present!\n");
1530 *chunk_size_return
= v
;
1536 checkVia(AtomPtr name
, AtomPtr via
)
1540 if(via
== NULL
|| via
->length
== 0)
1546 while(i
< via
->length
) {
1547 while(v
[i
] == ' ' || v
[i
] == '\t' || v
[i
] == ',' ||
1548 v
[i
] == '\r' || v
[i
] == '\n' ||
1549 digit(v
[i
]) || v
[i
] == '.')
1551 if(i
+ name
->length
> via
->length
)
1553 if(memcmp(v
+ i
, name
->string
, name
->length
) == 0) {
1554 char c
= v
[i
+ name
->length
];
1555 if(c
== '\0' || c
== ' ' || c
== '\t' || c
== ',' ||
1556 c
== '\r' || c
== '\n')
1560 while(letter(v
[i
]) || digit(v
[i
]) || v
[i
] == '.')