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");
902 len
= strtol(buf
+ value_start
, &endptr
, 10);
903 if(errno
== ERANGE
|| endptr
<= buf
+ value_start
) {
904 do_log(L_WARN
, "Couldn't parse Content-Length: \n");
905 do_log_n(L_WARN
, buf
+ value_start
,
906 value_end
- value_start
);
907 do_log(L_WARN
, ".\n");
911 } else if((!local
&& name
== atomProxyAuthorization
) ||
912 (local
&& name
== atomAuthorization
)) {
914 auth
= internAtomN(buf
+ value_start
, value_end
- value_start
);
916 do_log(L_ERROR
, "Couldn't allocate authorization.\n");
920 } else if(name
== atomReferer
) {
922 if(censorReferer
== 0 ||
923 (censorReferer
== 1 && url
!= NULL
&&
924 urlSameHost(url
->string
, url
->length
,
925 buf
+ value_start
, value_end
- value_start
))) {
926 while(hbuf_length
> hbuf_size
- 2)
928 hbuf
[hbuf_length
++] = '\r';
929 hbuf
[hbuf_length
++] = '\n';
931 h
= snnprint_n(hbuf
, hbuf_length
, hbuf_size
,
932 buf
+ name_start
, value_end
- name_start
);
933 if(h
< 0) RESIZE_HBUF();
937 } else if(name
== atomTrailer
|| name
== atomUpgrade
) {
938 do_log(L_ERROR
, "Trailers or upgrade present.\n");
940 } else if(name
== atomDate
|| name
== atomExpires
||
941 name
== atomIfModifiedSince
||
942 name
== atomIfUnmodifiedSince
||
943 name
== atomLastModified
||
944 name
== atomXPolipoDate
|| name
== atomXPolipoAccess
) {
946 j
= parse_time(buf
, value_start
, value_end
, &t
);
948 if(name
!= atomExpires
) {
949 do_log(L_WARN
, "Couldn't parse %s: ", name
->string
);
950 do_log_n(L_WARN
, buf
+ value_start
,
951 value_end
- value_start
);
952 do_log(L_WARN
, "\n");
956 if(name
== atomDate
) {
959 } else if(name
== atomExpires
) {
964 } else if(name
== atomLastModified
)
966 else if(name
== atomIfModifiedSince
)
968 else if(name
== atomIfUnmodifiedSince
)
970 else if(name
== atomXPolipoDate
)
972 else if(name
== atomXPolipoAccess
)
974 } else if(name
== atomAge
) {
975 j
= skipWhitespace(buf
, value_start
);
980 age
= strtol(buf
+ value_start
, &endptr
, 10);
981 if(errno
== ERANGE
|| endptr
<= buf
+ value_start
)
985 do_log(L_WARN
, "Couldn't parse age: \n");
986 do_log_n(L_WARN
, buf
+ value_start
, value_end
- value_start
);
987 do_log(L_WARN
, " -- ignored.\n");
989 } else if(name
== atomXPolipoBodyOffset
) {
990 j
= skipWhitespace(buf
, value_start
);
992 do_log(L_ERROR
, "Couldn't parse body offset.\n");
996 polipo_body_offset
= strtol(buf
+ value_start
, &endptr
, 10);
997 if(errno
== ERANGE
|| endptr
<= buf
+ value_start
) {
998 do_log(L_ERROR
, "Couldn't parse body offset.\n");
1002 } else if(name
== atomTransferEncoding
) {
1003 if(token_compare(buf
, value_start
, value_end
, "identity"))
1005 else if(token_compare(buf
, value_start
, value_end
, "chunked"))
1009 } else if(name
== atomETag
||
1010 name
== atomIfNoneMatch
|| name
== atomIfMatch
||
1011 name
== atomIfRange
) {
1015 j
= getNextETag(buf
, value_start
, &x
, &y
, &weak
);
1017 if(buf
[value_start
] != '\r' && buf
[value_start
] != '\n')
1018 do_log(L_ERROR
, "Couldn't parse ETag.\n");
1020 do_log(L_WARN
, "Server returned weak ETag -- ignored.\n");
1022 e
= strdup_n(buf
+ x
, y
- x
);
1023 if(e
== NULL
) goto fail
;
1024 if(name
== atomETag
) {
1029 } else if(name
== atomIfNoneMatch
) {
1034 } else if(name
== atomIfMatch
) {
1039 } else if(name
== atomIfRange
) {
1048 } else if(name
== atomCacheControl
) {
1050 j
= getNextTokenInList(buf
, value_start
,
1051 &token_start
, &token_end
,
1056 do_log(L_WARN
, "Couldn't parse Cache-Control.\n");
1057 cache_control
.flags
|= CACHE_NO
;
1060 if(token_compare(buf
, token_start
, token_end
, "no-cache")) {
1061 cache_control
.flags
|= CACHE_NO
;
1062 } else if(token_compare(buf
, token_start
, token_end
,
1064 cache_control
.flags
|= CACHE_PUBLIC
;
1065 } else if(token_compare(buf
, token_start
, token_end
,
1067 cache_control
.flags
|= CACHE_PRIVATE
;
1068 } else if(token_compare(buf
, token_start
, token_end
,
1070 cache_control
.flags
|= CACHE_NO_STORE
;
1071 } else if(token_compare(buf
, token_start
, token_end
,
1073 cache_control
.flags
|= CACHE_NO_TRANSFORM
;
1074 } else if(token_compare(buf
, token_start
, token_end
,
1075 "must-revalidate") ||
1076 token_compare(buf
, token_start
, token_end
,
1077 "must-validate")) { /* losers */
1078 cache_control
.flags
|= CACHE_MUST_REVALIDATE
;
1079 } else if(token_compare(buf
, token_start
, token_end
,
1080 "proxy-revalidate")) {
1081 cache_control
.flags
|= CACHE_PROXY_REVALIDATE
;
1082 } else if(token_compare(buf
, token_start
, token_end
,
1083 "only-if-cached")) {
1084 cache_control
.flags
|= CACHE_ONLY_IF_CACHED
;
1085 } else if(token_compare(buf
, token_start
, token_end
,
1087 token_compare(buf
, token_start
, token_end
,
1088 "maxage")) { /* losers */
1090 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1091 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1092 do_log_n(L_WARN
, buf
+ token_start
,
1093 (v_end
>= 0 ? v_end
: token_end
) -
1095 do_log(L_WARN
, "\n");
1097 a
= atoi(buf
+ v_start
);
1098 cache_control
.max_age
= a
;
1099 } else if(token_compare(buf
, token_start
, token_end
,
1102 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1103 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1104 do_log_n(L_WARN
, buf
+ token_start
,
1105 (v_end
>= 0 ? v_end
: token_end
) -
1107 do_log(L_WARN
, "\n");
1109 a
= atoi(buf
+ v_start
);
1110 cache_control
.max_age
= a
;
1111 } else if(token_compare(buf
, token_start
, token_end
,
1114 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1115 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1116 do_log_n(L_WARN
, buf
+ token_start
,
1117 (v_end
>= 0 ? v_end
: token_end
) -
1119 do_log(L_WARN
, "\n");
1121 a
= atoi(buf
+ v_start
);
1122 cache_control
.max_age
= a
;
1123 } else if(token_compare(buf
, token_start
, token_end
,
1126 if(v_start
<= 0 || !digit(buf
[v_start
])) {
1127 do_log(L_WARN
, "Couldn't parse Cache-Control: ");
1128 do_log_n(L_WARN
, buf
+ token_start
,
1129 (v_end
>= 0 ? v_end
: token_end
) -
1131 do_log(L_WARN
, "\n");
1133 a
= atoi(buf
+ v_start
);
1134 cache_control
.max_stale
= a
;
1136 do_log(L_WARN
, "Unsupported Cache-Control directive ");
1137 do_log_n(L_WARN
, buf
+ token_start
,
1138 (v_end
>= 0 ? v_end
: token_end
) - token_start
);
1139 do_log(L_WARN
, " -- ignored.\n");
1143 j
= getNextTokenInList(buf
, j
,
1144 &token_start
, &token_end
,
1148 } else if(name
== atomContentRange
) {
1150 j
= parseContentRange(buf
, value_start
,
1151 &content_range
.from
, &content_range
.to
,
1152 &content_range
.full_length
);
1154 do_log(L_ERROR
, "Couldn't parse Content-Range: ");
1155 do_log_n(L_ERROR
, buf
+ value_start
,
1156 value_end
- value_start
);
1157 do_log(L_ERROR
, "\n");
1161 do_log(L_ERROR
, "Content-Range from client.\n");
1164 } else if(name
== atomRange
) {
1166 j
= parseRange(buf
, value_start
, &range
.from
, &range
.to
);
1168 do_log(L_WARN
, "Couldn't parse Range -- ignored.\n");
1173 do_log(L_WARN
, "Range from server -- ignored\n");
1175 } else if(name
== atomXPolipoLocation
) {
1176 if(location_return
) {
1178 strdup_n(buf
+ value_start
, value_end
- value_start
);
1179 if(location
== NULL
) {
1180 do_log(L_ERROR
, "Couldn't allocate location.\n");
1184 } else if(name
== atomVia
) {
1186 AtomPtr new_via
, full_via
;
1188 internAtomN(buf
+ value_start
, value_end
- value_start
);
1189 if(new_via
== NULL
) {
1190 do_log(L_ERROR
, "Couldn't allocate via.\n");
1195 internAtomF("%s, %s", via
->string
, new_via
->string
);
1196 releaseAtom(new_via
);
1197 if(full_via
== NULL
) {
1198 do_log(L_ERROR
, "Couldn't allocate via");
1207 } else if(name
== atomExpect
) {
1209 expect
= internAtomLowerN(buf
+ value_start
,
1210 value_end
- value_start
);
1211 if(expect
== NULL
) {
1212 do_log(L_ERROR
, "Couldn't allocate expect.\n");
1217 if(!client
&& name
== atomContentType
) {
1218 if(token_compare(buf
, value_start
, value_end
,
1219 "multipart/byteranges")) {
1221 "Server returned multipart/byteranges -- yuck!\n");
1225 if(name
== atomVary
) {
1226 if(!token_compare(buf
, value_start
, value_end
, "host") &&
1227 !token_compare(buf
, value_start
, value_end
, "*")) {
1228 /* What other vary headers should be ignored? */
1229 do_log(L_VARY
, "Vary header present (");
1231 buf
+ value_start
, value_end
- value_start
);
1232 do_log(L_VARY
, ").\n");
1234 cache_control
.flags
|= CACHE_VARY
;
1235 } else if(name
== atomAuthorization
) {
1236 cache_control
.flags
|= CACHE_AUTHORIZATION
;
1239 if(name
== atomPragma
) {
1240 /* Pragma is only defined for the client, and the only
1241 standard value is no-cache (RFC 1945, 10.12).
1242 However, we honour a Pragma: no-cache for both the client
1243 and the server when there's no Cache-Control header. In
1244 all cases, we pass the Pragma header to the next hop. */
1245 if(!haveCacheControl
) {
1246 j
= getNextTokenInList(buf
, value_start
,
1247 &token_start
, &token_end
, NULL
, NULL
,
1251 do_log(L_WARN
, "Couldn't parse Pragma.\n");
1252 cache_control
.flags
|= CACHE_NO
;
1255 if(token_compare(buf
, token_start
, token_end
,
1257 cache_control
.flags
= CACHE_NO
;
1260 j
= getNextTokenInList(buf
, j
, &token_start
, &token_end
,
1266 (name
== atomSetCookie
||
1267 name
== atomCookie
|| name
== atomCookie2
))
1268 cache_control
.flags
|= CACHE_COOKIE
;
1271 if(name
!= atomConnection
&& name
!= atomHost
&&
1272 name
!= atomAcceptRange
&& name
!= atomTE
&&
1273 name
!= atomProxyAuthenticate
&&
1274 name
!= atomKeepAlive
&&
1275 (!hopToHop
|| !atomListMember(name
, hopToHop
)) &&
1276 !atomListMember(name
, censoredHeaders
)) {
1278 while(hbuf_length
> hbuf_size
- 2)
1280 hbuf
[hbuf_length
++] = '\r';
1281 hbuf
[hbuf_length
++] = '\n';
1283 h
= snnprint_n(hbuf
, hbuf_length
, hbuf_size
,
1285 value_end
- name_start
);
1286 if(h
< 0) RESIZE_HBUF();
1296 if(headers_return
) {
1297 AtomPtr pheaders
= NULL
;
1298 pheaders
= internAtomN(hbuf
, hbuf_length
);
1301 *headers_return
= pheaders
;
1303 if(hbuf
!= hbuf_small
)
1310 request
->flags
&= ~REQUEST_PERSISTENT
;
1312 if(te
!= TE_IDENTITY
) len
= -1;
1313 if(len_return
) *len_return
= len
;
1314 if(cache_control_return
) *cache_control_return
= cache_control
;
1315 if(condition_return
) {
1316 if(ims
>= 0 || inms
>= 0 || im
|| inm
|| ifrange
) {
1317 condition
= httpMakeCondition();
1319 condition
->ims
= ims
;
1320 condition
->inms
= inms
;
1322 condition
->inm
= inm
;
1323 condition
->ifrange
= ifrange
;
1325 do_log(L_ERROR
, "Couldn't allocate condition.\n");
1332 *condition_return
= condition
;
1334 assert(!im
&& !inm
);
1337 if(te_return
) *te_return
= te
;
1338 if(date_return
) *date_return
= date
;
1339 if(last_modified_return
) *last_modified_return
= last_modified
;
1340 if(expires_return
) *expires_return
= expires
;
1341 if(polipo_age_return
) *polipo_age_return
= polipo_age
;
1342 if(polipo_access_return
) *polipo_access_return
= polipo_access
;
1343 if(polipo_body_offset_return
)
1344 *polipo_body_offset_return
= polipo_body_offset
;
1345 if(age_return
) *age_return
= age
;
1347 *etag_return
= etag
;
1349 if(etag
) free(etag
);
1351 if(range_return
) *range_return
= range
;
1352 if(content_range_return
) *content_range_return
= content_range
;
1353 if(location_return
) {
1354 *location_return
= location
;
1366 *expect_return
= expect
;
1369 releaseAtom(expect
);
1372 *auth_return
= auth
;
1377 if(hopToHop
) destroyAtomList(hopToHop
);
1381 if(hbuf
&& hbuf
!= hbuf_small
) free(hbuf
);
1382 if(name
) releaseAtom(name
);
1383 if(etag
) free(etag
);
1384 if(location
) free(location
);
1385 if(via
) releaseAtom(via
);
1386 if(expect
) releaseAtom(expect
);
1387 if(auth
) releaseAtom(auth
);
1388 if(hopToHop
) destroyAtomList(hopToHop
);
1395 httpFindHeader(AtomPtr header
, const char *headers
, int hlen
,
1396 int *value_begin_return
, int *value_end_return
)
1398 int len
= header
->length
;
1401 while(i
+ len
+ 1 < hlen
) {
1402 if(headers
[i
+ len
] == ':' &&
1403 lwrcmp(headers
+ i
, header
->string
, len
) == 0) {
1404 int j
= i
+ len
+ 1, k
;
1405 while(j
< hlen
&& headers
[j
] == ' ')
1408 while(k
< hlen
&& headers
[k
] != '\n' && headers
[k
] != '\r')
1410 *value_begin_return
= j
;
1411 *value_end_return
= k
;
1414 while(i
< hlen
&& headers
[i
] != '\n' && headers
[i
] != '\r')
1417 if(i
< hlen
&& headers
[i
] == '\n')
1425 parseUrl(const char *url
, int len
,
1426 int *x_return
, int *y_return
, int *port_return
, int *z_return
)
1428 int x
, y
, z
, port
= -1, i
= 0;
1430 if(len
>= 7 && lwrcmp(url
, "http://", 7) == 0) {
1432 if(x
< len
&& url
[x
] == '[') {
1434 for(i
= x
+ 1; i
< len
; i
++) {
1439 if((url
[i
] != ':') && !letter(url
[i
]) && !digit(url
[i
]))
1443 for(i
= x
; i
< len
; i
++)
1444 if(url
[i
] == ':' || url
[i
] == '/')
1449 if(i
< len
&& url
[i
] == ':') {
1451 j
= atoi_n(url
, i
+ 1, len
, &port
);
1469 *port_return
= port
;
1475 urlIsLocal(const char *url
, int len
)
1477 return (len
> 0 && url
[0] == '/');
1481 urlIsSpecial(const char *url
, int len
)
1483 return (len
>= 8 && memcmp(url
, "/polipo/", 8) == 0);
1487 parseChunkSize(const char *restrict buf
, int i
, int end
,
1488 int *chunk_size_return
)
1506 if(buf
[i
] == ' ' || buf
[i
] == '\t')
1515 if(buf
[i
] != '\r' || buf
[i
+ 1] != '\n')
1523 if(buf
[i
] != '\r') {
1524 do_log(L_ERROR
, "Trailers present!\n");
1533 *chunk_size_return
= v
;
1539 checkVia(AtomPtr name
, AtomPtr via
)
1543 if(via
== NULL
|| via
->length
== 0)
1549 while(i
< via
->length
) {
1550 while(v
[i
] == ' ' || v
[i
] == '\t' || v
[i
] == ',' ||
1551 v
[i
] == '\r' || v
[i
] == '\n' ||
1552 digit(v
[i
]) || v
[i
] == '.')
1554 if(i
+ name
->length
> via
->length
)
1556 if(memcmp(v
+ i
, name
->string
, name
->length
) == 0) {
1557 char c
= v
[i
+ name
->length
];
1558 if(c
== '\0' || c
== ' ' || c
== '\t' || c
== ',' ||
1559 c
== '\r' || c
== '\n')
1563 while(letter(v
[i
]) || digit(v
[i
]) || v
[i
] == '.')