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
26 AtomPtr proxyName
= NULL
;
29 int clientTimeout
= 120;
30 int serverTimeout
= 90;
31 int serverIdleTimeout
= 45;
33 int bigBufferSize
= (32 * 1024);
35 AtomPtr authRealm
= NULL
;
36 AtomPtr authCredentials
= NULL
;
38 AtomPtr parentAuthCredentials
= NULL
;
40 AtomListPtr allowedClients
= NULL
;
41 NetAddressPtr allowedNets
= NULL
;
43 IntListPtr allowedPorts
= NULL
;
44 IntListPtr tunnelAllowedPorts
= NULL
;
45 int expectContinue
= 1;
46 int dontTrustVaryETag
= 1;
48 AtomPtr atom100Continue
;
52 /* 0 means that all failures lead to errors. 1 means that failures to
53 connect are reported in a Warning header when stale objects are
54 served. 2 means that only missing data is fetched from the net,
55 stale data is served without revalidation (browser-side
56 Cache-Control directives are still honoured). 3 means that no
57 connections are ever attempted. */
60 int relaxTransparency
= 0;
61 AtomPtr proxyAddress
= NULL
;
63 static int timeoutSetter(ConfigVariablePtr var
, void *value
);
68 proxyAddress
= internAtom("127.0.0.1");
69 CONFIG_VARIABLE_SETTABLE(disableProxy
, CONFIG_BOOLEAN
, configIntSetter
,
70 "Whether to be a web server only.");
71 CONFIG_VARIABLE_SETTABLE(proxyOffline
, CONFIG_BOOLEAN
, configIntSetter
,
72 "Avoid contacting remote servers.");
73 CONFIG_VARIABLE_SETTABLE(relaxTransparency
, CONFIG_TRISTATE
,
75 "Avoid contacting remote servers.");
76 CONFIG_VARIABLE(proxyPort
, CONFIG_INT
,
77 "The TCP port on which the proxy listens.");
78 CONFIG_VARIABLE(proxyAddress
, CONFIG_ATOM_LOWER
,
79 "The IP address on which the proxy listens.");
80 CONFIG_VARIABLE_SETTABLE(proxyName
, CONFIG_ATOM_LOWER
, configAtomSetter
,
81 "The name by which the proxy is known.");
82 CONFIG_VARIABLE_SETTABLE(clientTimeout
, CONFIG_TIME
,
83 timeoutSetter
, "Client-side timeout.");
84 CONFIG_VARIABLE_SETTABLE(serverTimeout
, CONFIG_TIME
,
85 timeoutSetter
, "Server-side timeout.");
86 CONFIG_VARIABLE_SETTABLE(serverIdleTimeout
, CONFIG_TIME
,
87 timeoutSetter
, "Server-side idle timeout.");
88 CONFIG_VARIABLE(authRealm
, CONFIG_ATOM
,
89 "Authentication realm.");
90 CONFIG_VARIABLE(authCredentials
, CONFIG_PASSWORD
,
91 "username:password.");
92 CONFIG_VARIABLE(parentAuthCredentials
, CONFIG_PASSWORD
,
93 "username:password.");
94 CONFIG_VARIABLE(allowedClients
, CONFIG_ATOM_LIST_LOWER
,
95 "Networks from which clients are allowed to connect.");
96 CONFIG_VARIABLE(tunnelAllowedPorts
, CONFIG_INT_LIST
,
97 "Ports to which tunnelled connections are allowed.");
98 CONFIG_VARIABLE(allowedPorts
, CONFIG_INT_LIST
,
99 "Ports to which connections are allowed.");
100 CONFIG_VARIABLE(expectContinue
, CONFIG_TRISTATE
,
101 "Send Expect-Continue to servers.");
102 CONFIG_VARIABLE(bigBufferSize
, CONFIG_INT
,
103 "Size of big buffers (max size of headers).");
104 CONFIG_VARIABLE_SETTABLE(disableVia
, CONFIG_BOOLEAN
, configIntSetter
,
105 "Don't use Via headers.");
106 CONFIG_VARIABLE(dontTrustVaryETag
, CONFIG_TRISTATE
,
107 "Whether to trust the ETag when there's Vary.");
112 timeoutSetter(ConfigVariablePtr var
, void *value
)
114 configIntSetter(var
, value
);
115 if(clientTimeout
<= serverTimeout
)
116 clientTimeout
= serverTimeout
+ 1;
126 struct hostent
*host
;
130 atom100Continue
= internAtom("100-continue");
132 if(clientTimeout
<= serverTimeout
) {
133 clientTimeout
= serverTimeout
+ 1;
134 do_log(L_WARN
, "Value of clientTimeout too small -- setting to %d.\n",
138 if(authCredentials
!= NULL
&& authRealm
== NULL
)
139 authRealm
= internAtom("Polipo");
142 allowedNets
= parseNetAddress(allowedClients
);
143 if(allowedNets
== NULL
)
147 if(allowedPorts
== NULL
) {
148 allowedPorts
= makeIntList(0);
149 if(allowedPorts
== NULL
) {
150 do_log(L_ERROR
, "Couldn't allocate allowedPorts.\n");
153 intListCons(80, 100, allowedPorts
);
154 intListCons(1024, 0xFFFF, allowedPorts
);
157 if(tunnelAllowedPorts
== NULL
) {
158 tunnelAllowedPorts
= makeIntList(0);
159 if(tunnelAllowedPorts
== NULL
) {
160 do_log(L_ERROR
, "Couldn't allocate tunnelAllowedPorts.\n");
163 intListCons(22, 22, tunnelAllowedPorts
); /* ssh */
164 intListCons(80, 80, tunnelAllowedPorts
); /* HTTP */
165 intListCons(109, 110, tunnelAllowedPorts
); /* POP 2 and 3*/
166 intListCons(143, 143, tunnelAllowedPorts
); /* IMAP 2/4 */
167 intListCons(443, 443, tunnelAllowedPorts
); /* HTTP/SSL */
168 intListCons(873, 873, tunnelAllowedPorts
); /* rsync */
169 intListCons(993, 993, tunnelAllowedPorts
); /* IMAP/SSL */
170 intListCons(995, 995, tunnelAllowedPorts
); /* POP/SSL */
178 do_log(L_ERROR
, "Couldn't allocate chunk for host name.\n");
182 n
= gethostname(buf
, CHUNK_SIZE
);
184 do_log_error(L_WARN
, errno
, "Gethostname");
185 strcpy(buf
, "polipo");
188 /* gethostname doesn't necessarily NUL-terminate on overflow */
189 buf
[CHUNK_SIZE
- 1] = '\0';
191 if(strcmp(buf
, "(none)") == 0 ||
192 strcmp(buf
, "localhost") == 0 ||
193 strcmp(buf
, "localhost.localdomain") == 0) {
194 do_log(L_WARN
, "Couldn't determine host name -- using ``polipo''.\n");
195 strcpy(buf
, "polipo");
199 if(strchr(buf
, '.') != NULL
)
202 host
= gethostbyname(buf
);
207 if(host
->h_addrtype
!= AF_INET
)
210 host
= gethostbyaddr(host
->h_addr_list
[0], host
->h_length
, AF_INET
);
212 if(!host
|| !host
->h_name
|| strcmp(host
->h_name
, "localhost") == 0 ||
213 strcmp(host
->h_name
, "localhost.localdomain") == 0)
216 namelen
= strlen(host
->h_name
);
217 if(namelen
>= CHUNK_SIZE
) {
218 do_log(L_ERROR
, "Host name too long.\n");
222 memcpy(buf
, host
->h_name
, namelen
+ 1);
225 proxyName
= internAtom(buf
);
226 if(proxyName
== NULL
) {
227 do_log(L_ERROR
, "Couldn't allocate proxy name.\n");
241 httpSetTimeout(HTTPConnectionPtr connection
, int secs
)
243 TimeEventHandlerPtr
new;
245 if(connection
->timeout
)
246 cancelTimeEvent(connection
->timeout
);
247 connection
->timeout
= NULL
;
250 new = scheduleTimeEvent(secs
, httpTimeoutHandler
,
251 sizeof(connection
), &connection
);
253 do_log(L_ERROR
, "Couldn't schedule timeout for connection 0x%lx\n",
254 (unsigned long)connection
);
261 connection
->timeout
= new;
266 httpTimeoutHandler(TimeEventHandlerPtr event
)
268 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
270 if(connection
->fd
>= 0) {
272 rc
= shutdown(connection
->fd
, 2);
273 if(rc
< 0 && errno
!= ENOTCONN
)
274 do_log_error(L_ERROR
, errno
, "Timeout: shutdown failed");
275 pokeFdEvent(connection
->fd
, -EDOTIMEOUT
, POLLIN
| POLLOUT
);
277 connection
->timeout
= NULL
;
282 httpWriteObjectHeaders(char *buf
, int offset
, int len
,
283 ObjectPtr object
, int from
, int to
)
287 if(from
<= 0 && to
< 0) {
288 if(object
->length
>= 0) {
289 n
= snnprintf(buf
, n
, len
,
290 "\r\nContent-Length: %d", object
->length
);
294 n
= snnprintf(buf
, n
, len
,
295 "\r\nContent-Length: %d", to
- from
);
299 if(from
> 0 || to
> 0) {
300 if(object
->length
>= 0) {
302 n
= snnprintf(buf
, n
, len
,
303 "\r\nContent-Range: bytes */%d",
306 n
= snnprintf(buf
, n
, len
,
307 "\r\nContent-Range: bytes %d-%d/%d",
313 n
= snnprintf(buf
, n
, len
,
314 "\r\nContent-Range: bytes %d-/*",
317 n
= snnprintf(buf
, n
, len
,
318 "\r\nContent-Range: bytes %d-%d/*",
325 n
= snnprintf(buf
, n
, len
, "\r\nETag: \"%s\"", object
->etag
);
327 if((object
->flags
& OBJECT_LOCAL
) || object
->date
>= 0) {
328 n
= snnprintf(buf
, n
, len
, "\r\nDate: ");
329 n
= format_time(buf
, n
, len
,
330 (object
->flags
& OBJECT_LOCAL
) ?
331 current_time
.tv_sec
: object
->date
);
336 if(object
->last_modified
>= 0) {
337 n
= snnprintf(buf
, n
, len
, "\r\nLast-Modified: ");
338 n
= format_time(buf
, n
, len
, object
->last_modified
);
343 if(object
->expires
>= 0) {
344 n
= snnprintf(buf
, n
, len
, "\r\nExpires: ");
345 n
= format_time(buf
, n
, len
, object
->expires
);
350 n
= httpPrintCacheControl(buf
, n
, len
,
351 object
->cache_control
, NULL
);
355 if(!disableVia
&& object
->via
)
356 n
= snnprintf(buf
, n
, len
, "\r\nVia: %s", object
->via
->string
);
359 n
= snnprint_n(buf
, n
, len
, object
->headers
->string
,
360 object
->headers
->length
);
372 cachePrintSeparator(char *buf
, int offset
, int len
,
377 n
= snnprintf(buf
, offset
, len
, ", ");
379 n
= snnprintf(buf
, offset
, len
, "\r\nCache-Control: ");
384 httpPrintCacheControl(char *buf
, int offset
, int len
,
385 int flags
, CacheControlPtr cache_control
)
390 #define PRINT_SEP() \
392 n = cachePrintSeparator(buf, n, len, sub); \
397 flags
|= cache_control
->flags
;
399 if(flags
& CACHE_NO
) {
401 n
= snnprintf(buf
, n
, len
, "no-cache");
403 if(flags
& CACHE_PUBLIC
) {
405 n
= snnprintf(buf
, n
, len
, "public");
407 if(flags
& CACHE_PRIVATE
) {
409 n
= snnprintf(buf
, n
, len
, "private");
411 if(flags
& CACHE_NO_STORE
) {
413 n
= snnprintf(buf
, n
, len
, "no-store");
415 if(flags
& CACHE_NO_TRANSFORM
) {
417 n
= snnprintf(buf
, n
, len
, "no-transform");
419 if(flags
& CACHE_MUST_REVALIDATE
) {
421 n
= snnprintf(buf
, n
, len
, "must-revalidate");
423 if(flags
& CACHE_PROXY_REVALIDATE
) {
425 n
= snnprintf(buf
, n
, len
, "proxy-revalidate");
427 if(flags
& CACHE_ONLY_IF_CACHED
) {
429 n
= snnprintf(buf
, n
, len
, "only-if-cached");
432 if(cache_control
->max_age
>= 0) {
434 n
= snnprintf(buf
, n
, len
, "max-age=%d",
435 cache_control
->max_age
);
437 if(cache_control
->s_maxage
>= 0) {
439 n
= snnprintf(buf
, n
, len
, "s-maxage=%d",
440 cache_control
->s_maxage
);
442 if(cache_control
->min_fresh
> 0) {
444 n
= snnprintf(buf
, n
, len
, "min-fresh=%d",
445 cache_control
->min_fresh
);
447 if(cache_control
->max_stale
> 0) {
449 n
= snnprintf(buf
, n
, len
, "max-stale=%d",
450 cache_control
->min_fresh
);
458 httpMessage(int code
)
464 return "Partial content";
466 return "Multiple choices";
468 return "Moved permanently";
474 return "Not changed";
476 return "Temporary redirect";
478 return "Authentication Required";
484 return "Method not allowed";
486 return "Proxy authentication required";
488 return "Unknown error code";
493 htmlString(char *buf
, int n
, int len
, char *s
, int slen
)
496 while(i
< slen
&& n
+ 5 < len
) {
499 buf
[n
++] = '&'; buf
[n
++] = 'a'; buf
[n
++] = 'm'; buf
[n
++] = 'p';
503 buf
[n
++] = '&'; buf
[n
++] = 'l'; buf
[n
++] = 't'; buf
[n
++] = ';';
506 buf
[n
++] = '&'; buf
[n
++] = 'g'; buf
[n
++] = 't'; buf
[n
++] = ';';
509 buf
[n
++] = '&'; buf
[n
++] = 'q'; buf
[n
++] = 'u'; buf
[n
++] = 'o';
510 buf
[n
++] = 't'; buf
[n
++] = ';';
523 htmlPrint(FILE *out
, char *s
, int slen
)
526 for(i
= 0; i
< slen
; i
++) {
546 HTTPConnectionPtr connection
;
547 connection
= malloc(sizeof(HTTPConnectionRec
));
548 if(connection
== NULL
)
550 connection
->flags
= 0;
552 connection
->buf
= NULL
;
554 connection
->offset
= 0;
555 connection
->request
= NULL
;
556 connection
->request_last
= NULL
;
557 connection
->serviced
= 0;
558 connection
->version
= HTTP_UNKNOWN
;
559 connection
->time
= current_time
.tv_sec
;
560 connection
->timeout
= NULL
;
561 connection
->te
= TE_IDENTITY
;
562 connection
->reqbuf
= NULL
;
563 connection
->reqlen
= 0;
564 connection
->reqbegin
= 0;
565 connection
->reqoffset
= 0;
566 connection
->bodylen
= -1;
567 connection
->reqte
= TE_IDENTITY
;
568 connection
->chunk_remaining
= 0;
569 connection
->server
= NULL
;
570 connection
->pipelined
= 0;
571 connection
->connecting
= 0;
572 connection
->server
= NULL
;
577 httpDestroyConnection(HTTPConnectionPtr connection
)
579 assert(connection
->flags
== 0);
580 httpConnectionDestroyBuf(connection
);
581 assert(!connection
->request
);
582 assert(!connection
->request_last
);
583 httpConnectionDestroyReqbuf(connection
);
584 assert(!connection
->timeout
);
585 assert(!connection
->server
);
590 httpConnectionDestroyBuf(HTTPConnectionPtr connection
)
592 if(connection
->buf
) {
593 if(connection
->flags
& CONN_BIGBUF
)
594 free(connection
->buf
);
596 dispose_chunk(connection
->buf
);
598 connection
->flags
&= ~CONN_BIGBUF
;
599 connection
->buf
= NULL
;
603 httpConnectionDestroyReqbuf(HTTPConnectionPtr connection
)
605 if(connection
->reqbuf
) {
606 if(connection
->flags
& CONN_BIGREQBUF
)
607 free(connection
->reqbuf
);
609 dispose_chunk(connection
->reqbuf
);
611 connection
->flags
&= ~CONN_BIGREQBUF
;
612 connection
->reqbuf
= NULL
;
618 HTTPRequestPtr request
;
619 request
= malloc(sizeof(HTTPRequestRec
));
623 request
->connection
= NULL
;
624 request
->object
= NULL
;
625 request
->method
= METHOD_UNKNOWN
;
628 request
->cache_control
= no_cache_control
;
629 request
->condition
= NULL
;
631 request
->chandler
= NULL
;
632 request
->can_mutate
= NULL
;
633 request
->error_code
= 0;
634 request
->error_message
= NULL
;
635 request
->error_headers
= NULL
;
636 request
->headers
= NULL
;
637 request
->time0
= null_time
;
638 request
->time1
= null_time
;
639 request
->request
= NULL
;
640 request
->next
= NULL
;
645 httpDestroyRequest(HTTPRequestPtr request
)
648 releaseObject(request
->object
);
649 if(request
->condition
)
650 httpDestroyCondition(request
->condition
);
651 releaseAtom(request
->via
);
652 assert(request
->chandler
== NULL
);
653 releaseAtom(request
->error_message
);
654 releaseAtom(request
->headers
);
655 releaseAtom(request
->error_headers
);
656 assert(request
->request
== NULL
);
657 assert(request
->next
== NULL
);
662 httpQueueRequest(HTTPConnectionPtr connection
, HTTPRequestPtr request
)
664 assert(request
->next
== NULL
&& request
->connection
== NULL
);
665 request
->connection
= connection
;
666 if(connection
->request_last
) {
667 assert(connection
->request
);
668 connection
->request_last
->next
= request
;
669 connection
->request_last
= request
;
671 assert(!connection
->request_last
);
672 connection
->request
= request
;
673 connection
->request_last
= request
;
678 httpDequeueRequest(HTTPConnectionPtr connection
)
680 HTTPRequestPtr request
= connection
->request
;
682 assert(connection
->request_last
);
683 connection
->request
= request
->next
;
684 if(!connection
->request
) connection
->request_last
= NULL
;
685 request
->next
= NULL
;
691 httpConnectionBigify(HTTPConnectionPtr connection
)
694 assert(!(connection
->flags
& CONN_BIGBUF
));
696 if(bigBufferSize
<= CHUNK_SIZE
)
699 bigbuf
= malloc(bigBufferSize
);
702 if(connection
->len
> 0)
703 memcpy(bigbuf
, connection
->buf
, connection
->len
);
705 dispose_chunk(connection
->buf
);
706 connection
->buf
= bigbuf
;
707 connection
->flags
|= CONN_BIGBUF
;
712 httpConnectionBigifyReqbuf(HTTPConnectionPtr connection
)
715 assert(!(connection
->flags
& CONN_BIGREQBUF
));
717 if(bigBufferSize
<= CHUNK_SIZE
)
720 bigbuf
= malloc(bigBufferSize
);
723 if(connection
->reqlen
> 0)
724 memcpy(bigbuf
, connection
->reqbuf
, connection
->reqlen
);
725 if(connection
->reqbuf
)
726 dispose_chunk(connection
->reqbuf
);
727 connection
->reqbuf
= bigbuf
;
728 connection
->flags
|= CONN_BIGREQBUF
;
733 httpConnectionUnbigify(HTTPConnectionPtr connection
)
736 assert(connection
->flags
& CONN_BIGBUF
);
737 assert(connection
->len
< CHUNK_SIZE
);
742 if(connection
->len
> 0)
743 memcpy(buf
, connection
->buf
, connection
->len
);
744 free(connection
->buf
);
745 connection
->buf
= buf
;
746 connection
->flags
&= ~CONN_BIGBUF
;
751 httpConnectionUnbigifyReqbuf(HTTPConnectionPtr connection
)
754 assert(connection
->flags
& CONN_BIGREQBUF
);
755 assert(connection
->reqlen
< CHUNK_SIZE
);
760 if(connection
->reqlen
> 0)
761 memcpy(buf
, connection
->reqbuf
, connection
->reqlen
);
762 free(connection
->reqbuf
);
763 connection
->reqbuf
= buf
;
764 connection
->flags
&= ~CONN_BIGREQBUF
;
771 HTTPConditionPtr condition
;
772 condition
= malloc(sizeof(HTTPConditionRec
));
773 if(condition
== NULL
)
776 condition
->inms
= -1;
777 condition
->im
= NULL
;
778 condition
->inm
= NULL
;
779 condition
->ifrange
= NULL
;
784 httpDestroyCondition(HTTPConditionPtr condition
)
787 free(condition
->inm
);
790 if(condition
->ifrange
)
791 free(condition
->ifrange
);
796 httpCondition(ObjectPtr object
, HTTPConditionPtr condition
)
798 int rc
= CONDITION_MATCH
;
800 assert(!(object
->flags
& OBJECT_INITIAL
));
802 if(!condition
) return CONDITION_MATCH
;
804 if(condition
->ims
>= 0) {
805 if(object
->last_modified
< 0 ||
806 condition
->ims
< object
->last_modified
)
809 rc
= CONDITION_NOT_MODIFIED
;
812 if(condition
->inms
>= 0) {
813 if(object
->last_modified
< 0 ||
814 condition
->inms
>= object
->last_modified
)
817 rc
= CONDITION_FAILED
;
821 if(!object
->etag
|| strcmp(object
->etag
, condition
->inm
) != 0)
824 rc
= CONDITION_NOT_MODIFIED
;
828 if(!object
->etag
|| strcmp(object
->etag
, condition
->im
) != 0)
829 rc
= CONDITION_FAILED
;
838 httpWriteErrorHeaders(char *buf
, int size
, int offset
, int do_body
,
839 int code
, AtomPtr message
, int close
, AtomPtr headers
,
840 char *url
, int url_len
, char *etag
)
844 char htmlMessage
[100];
849 i
= htmlString(htmlMessage
, 0, 100, message
->string
, message
->length
);
851 strcpy(htmlMessage
, "(Couldn't format message)");
853 htmlMessage
[MIN(i
, 99)] = '\0';
858 do_log(L_ERROR
, "Couldn't allocate body buffer.\n");
861 m
= snnprintf(body
, 0, CHUNK_SIZE
,
862 "<!DOCTYPE HTML PUBLIC "
863 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
864 "\"http://www.w3.org/TR/html4/loose.dtd\">"
866 "\n<title>Proxy %s: %3d %s.</title>"
869 "\n<p>The following %s",
870 code
>= 400 ? "error" : "result",
875 "status was returned");
877 m
= snnprintf(body
, m
, CHUNK_SIZE
,
878 " while trying to access <strong>");
879 m
= htmlString(body
, m
, CHUNK_SIZE
, url
, url_len
);
880 m
= snnprintf(body
, m
, CHUNK_SIZE
, "</strong>");
884 /* On BSD systems, tv_sec is a long. */
885 const time_t ct
= current_time
.tv_sec
;
886 /*Mon, 24 Sep 2004 17:46:35 GMT*/
887 strftime(timeStr
, sizeof(timeStr
), "%a, %d %b %Y %H:%M:%S %Z",
891 m
= snnprintf(body
, m
, CHUNK_SIZE
,
893 "\n<strong>%3d %s</strong></p>"
894 "\n<hr>Generated %s by Polipo on <em>%s:%d</em>."
895 "\n</body></html>\r\n",
897 timeStr
, proxyName
->string
, proxyPort
);
898 if(m
<= 0 || m
>= CHUNK_SIZE
) {
899 do_log(L_ERROR
, "Couldn't write error body.\n");
908 n
= snnprintf(buf
, 0, size
,
912 code
, atomString(message
),
913 close
? "close" : "keep-alive");
914 n
= format_time(buf
, n
, size
, current_time
.tv_sec
);
916 n
= snnprintf(buf
, n
, size
,
917 "\r\nContent-Type: text/html"
918 "\r\nContent-Length: %d", m
);
921 n
= snnprintf(buf
, n
, size
, "\r\nETag: \"%s\"", etag
);
924 if(code
!= 304 && code
!= 412) {
925 n
= snnprintf(buf
, n
, size
,
927 "\r\nCache-Control: no-cache"
928 "\r\nPragma: no-cache");
932 n
= snnprint_n(buf
, n
, size
,
933 headers
->string
, headers
->length
);
935 n
= snnprintf(buf
, n
, size
, "\r\n\r\n");
937 if(n
< 0 || n
>= size
) {
938 do_log(L_ERROR
, "Couldn't write error.\n");
943 if(code
!= 304 && do_body
) {
944 if(m
> 0) memcpy(buf
+ n
, body
, m
);
955 urlDecode(char *buf
, int n
)
962 list
= makeAtomList(NULL
, 0);
976 mybuf
[j
++] = (char)((a
<< 4) | b
);
978 if(j
> 500) goto fail
;
979 } else if(buf
[i
] == '&') {
980 atom
= internAtomN(mybuf
, j
);
983 atomListCons(atom
, list
);
987 mybuf
[j
++] = buf
[i
++];
988 if(j
> 500) goto fail
;
992 atom
= internAtomN(mybuf
, j
);
995 atomListCons(atom
, list
);
999 destroyAtomList(list
);
1004 httpTweakCachability(ObjectPtr object
)
1006 int code
= object
->code
;
1008 if((object
->cache_control
& CACHE_AUTHORIZATION
) &&
1009 !(object
->cache_control
& CACHE_PUBLIC
))
1010 object
->cache_control
|= (CACHE_NO_HIDDEN
| OBJECT_LINEAR
);
1012 /* This is not required by RFC 2616 -- but see RFC 3143 2.1.1. We
1013 manically avoid caching replies that we don't know how to
1014 handle, even if Expires or Cache-Control says otherwise. As to
1015 known uncacheable replies, we obey Cache-Control and default to
1016 allowing sharing but not caching. */
1017 if(code
!= 200 && code
!= 206 &&
1018 code
!= 300 && code
!= 301 && code
!= 302 && code
!= 303 &&
1019 code
!= 304 && code
!= 307 &&
1020 code
!= 403 && code
!= 404 && code
!= 405 && code
!= 416) {
1021 object
->cache_control
|=
1022 (CACHE_NO_HIDDEN
| CACHE_MISMATCH
| OBJECT_LINEAR
);
1023 } else if(code
!= 200 && code
!= 206 &&
1024 code
!= 300 && code
!= 301 && code
!= 304 &&
1026 if(object
->expires
< 0 && !(object
->cache_control
& CACHE_PUBLIC
)) {
1027 object
->cache_control
|= CACHE_NO_HIDDEN
;
1029 } else if(dontCacheRedirects
&& (code
== 301 || code
== 302)) {
1030 object
->cache_control
|= CACHE_NO_HIDDEN
;
1033 if(urlIsUncachable(object
->key
, object
->key_size
)) {
1034 object
->cache_control
|= CACHE_NO_HIDDEN
;
1037 if((object
->cache_control
& CACHE_NO_STORE
) != 0) {
1038 object
->cache_control
|= CACHE_NO_HIDDEN
;
1041 if(object
->cache_control
& CACHE_VARY
) {
1042 if(!object
->etag
|| dontTrustVaryETag
>= 2) {
1043 object
->cache_control
|= CACHE_MISMATCH
;
1049 httpHeaderMatch(AtomPtr header
, AtomPtr headers1
, AtomPtr headers2
)
1051 int rc1
, b1
, e1
, rc2
, b2
, e2
;
1053 /* Short cut if both sets of headers are identical */
1054 if(headers1
== headers2
)
1057 rc1
= httpFindHeader(header
, headers1
->string
, headers1
->length
,
1059 rc2
= httpFindHeader(header
, headers2
->string
, headers2
->length
,
1062 if(rc1
== 0 && rc2
== 0)
1065 if(rc1
== 0 || rc2
== 0)
1068 if(e1
- b1
!= e2
- b2
)
1071 if(memcmp(headers1
->string
+ b1
, headers2
->string
+ b2
, e1
- b1
) != 0)