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 */
171 intListCons(2401, 2401, tunnelAllowedPorts
); /* CVS */
172 intListCons(5222, 5223, tunnelAllowedPorts
); /* Jabber */
173 intListCons(9418, 9418, tunnelAllowedPorts
); /* Git */
181 do_log(L_ERROR
, "Couldn't allocate chunk for host name.\n");
185 n
= gethostname(buf
, CHUNK_SIZE
);
187 do_log_error(L_WARN
, errno
, "Gethostname");
188 strcpy(buf
, "polipo");
191 /* gethostname doesn't necessarily NUL-terminate on overflow */
192 buf
[CHUNK_SIZE
- 1] = '\0';
194 if(strcmp(buf
, "(none)") == 0 ||
195 strcmp(buf
, "localhost") == 0 ||
196 strcmp(buf
, "localhost.localdomain") == 0) {
197 do_log(L_WARN
, "Couldn't determine host name -- using ``polipo''.\n");
198 strcpy(buf
, "polipo");
202 if(strchr(buf
, '.') != NULL
)
205 host
= gethostbyname(buf
);
210 if(host
->h_addrtype
!= AF_INET
)
213 host
= gethostbyaddr(host
->h_addr_list
[0], host
->h_length
, AF_INET
);
215 if(!host
|| !host
->h_name
|| strcmp(host
->h_name
, "localhost") == 0 ||
216 strcmp(host
->h_name
, "localhost.localdomain") == 0)
219 namelen
= strlen(host
->h_name
);
220 if(namelen
>= CHUNK_SIZE
) {
221 do_log(L_ERROR
, "Host name too long.\n");
225 memcpy(buf
, host
->h_name
, namelen
+ 1);
228 proxyName
= internAtom(buf
);
229 if(proxyName
== NULL
) {
230 do_log(L_ERROR
, "Couldn't allocate proxy name.\n");
244 httpSetTimeout(HTTPConnectionPtr connection
, int secs
)
246 TimeEventHandlerPtr
new;
248 if(connection
->timeout
)
249 cancelTimeEvent(connection
->timeout
);
250 connection
->timeout
= NULL
;
253 new = scheduleTimeEvent(secs
, httpTimeoutHandler
,
254 sizeof(connection
), &connection
);
256 do_log(L_ERROR
, "Couldn't schedule timeout for connection 0x%lx\n",
257 (unsigned long)connection
);
264 connection
->timeout
= new;
269 httpTimeoutHandler(TimeEventHandlerPtr event
)
271 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
273 if(connection
->fd
>= 0) {
275 rc
= shutdown(connection
->fd
, 2);
276 if(rc
< 0 && errno
!= ENOTCONN
)
277 do_log_error(L_ERROR
, errno
, "Timeout: shutdown failed");
278 pokeFdEvent(connection
->fd
, -EDOTIMEOUT
, POLLIN
| POLLOUT
);
280 connection
->timeout
= NULL
;
285 httpWriteObjectHeaders(char *buf
, int offset
, int len
,
286 ObjectPtr object
, int from
, int to
)
290 if(from
<= 0 && to
< 0) {
291 if(object
->length
>= 0) {
292 n
= snnprintf(buf
, n
, len
,
293 "\r\nContent-Length: %d", object
->length
);
297 n
= snnprintf(buf
, n
, len
,
298 "\r\nContent-Length: %d", to
- from
);
302 if(from
> 0 || to
> 0) {
303 if(object
->length
>= 0) {
305 n
= snnprintf(buf
, n
, len
,
306 "\r\nContent-Range: bytes */%d",
309 n
= snnprintf(buf
, n
, len
,
310 "\r\nContent-Range: bytes %d-%d/%d",
316 n
= snnprintf(buf
, n
, len
,
317 "\r\nContent-Range: bytes %d-/*",
320 n
= snnprintf(buf
, n
, len
,
321 "\r\nContent-Range: bytes %d-%d/*",
328 n
= snnprintf(buf
, n
, len
, "\r\nETag: \"%s\"", object
->etag
);
330 if((object
->flags
& OBJECT_LOCAL
) || object
->date
>= 0) {
331 n
= snnprintf(buf
, n
, len
, "\r\nDate: ");
332 n
= format_time(buf
, n
, len
,
333 (object
->flags
& OBJECT_LOCAL
) ?
334 current_time
.tv_sec
: object
->date
);
339 if(object
->last_modified
>= 0) {
340 n
= snnprintf(buf
, n
, len
, "\r\nLast-Modified: ");
341 n
= format_time(buf
, n
, len
, object
->last_modified
);
346 if(object
->expires
>= 0) {
347 n
= snnprintf(buf
, n
, len
, "\r\nExpires: ");
348 n
= format_time(buf
, n
, len
, object
->expires
);
353 n
= httpPrintCacheControl(buf
, n
, len
,
354 object
->cache_control
, NULL
);
358 if(!disableVia
&& object
->via
)
359 n
= snnprintf(buf
, n
, len
, "\r\nVia: %s", object
->via
->string
);
362 n
= snnprint_n(buf
, n
, len
, object
->headers
->string
,
363 object
->headers
->length
);
375 cachePrintSeparator(char *buf
, int offset
, int len
,
380 n
= snnprintf(buf
, offset
, len
, ", ");
382 n
= snnprintf(buf
, offset
, len
, "\r\nCache-Control: ");
387 httpPrintCacheControl(char *buf
, int offset
, int len
,
388 int flags
, CacheControlPtr cache_control
)
393 #define PRINT_SEP() \
395 n = cachePrintSeparator(buf, n, len, sub); \
400 flags
|= cache_control
->flags
;
402 if(flags
& CACHE_NO
) {
404 n
= snnprintf(buf
, n
, len
, "no-cache");
406 if(flags
& CACHE_PUBLIC
) {
408 n
= snnprintf(buf
, n
, len
, "public");
410 if(flags
& CACHE_PRIVATE
) {
412 n
= snnprintf(buf
, n
, len
, "private");
414 if(flags
& CACHE_NO_STORE
) {
416 n
= snnprintf(buf
, n
, len
, "no-store");
418 if(flags
& CACHE_NO_TRANSFORM
) {
420 n
= snnprintf(buf
, n
, len
, "no-transform");
422 if(flags
& CACHE_MUST_REVALIDATE
) {
424 n
= snnprintf(buf
, n
, len
, "must-revalidate");
426 if(flags
& CACHE_PROXY_REVALIDATE
) {
428 n
= snnprintf(buf
, n
, len
, "proxy-revalidate");
430 if(flags
& CACHE_ONLY_IF_CACHED
) {
432 n
= snnprintf(buf
, n
, len
, "only-if-cached");
435 if(cache_control
->max_age
>= 0) {
437 n
= snnprintf(buf
, n
, len
, "max-age=%d",
438 cache_control
->max_age
);
440 if(cache_control
->s_maxage
>= 0) {
442 n
= snnprintf(buf
, n
, len
, "s-maxage=%d",
443 cache_control
->s_maxage
);
445 if(cache_control
->min_fresh
> 0) {
447 n
= snnprintf(buf
, n
, len
, "min-fresh=%d",
448 cache_control
->min_fresh
);
450 if(cache_control
->max_stale
> 0) {
452 n
= snnprintf(buf
, n
, len
, "max-stale=%d",
453 cache_control
->min_fresh
);
461 httpMessage(int code
)
467 return "Partial content";
469 return "Multiple choices";
471 return "Moved permanently";
477 return "Not changed";
479 return "Temporary redirect";
481 return "Authentication Required";
487 return "Method not allowed";
489 return "Proxy authentication required";
491 return "Unknown error code";
496 htmlString(char *buf
, int n
, int len
, char *s
, int slen
)
499 while(i
< slen
&& n
+ 5 < len
) {
502 buf
[n
++] = '&'; buf
[n
++] = 'a'; buf
[n
++] = 'm'; buf
[n
++] = 'p';
506 buf
[n
++] = '&'; buf
[n
++] = 'l'; buf
[n
++] = 't'; buf
[n
++] = ';';
509 buf
[n
++] = '&'; buf
[n
++] = 'g'; buf
[n
++] = 't'; buf
[n
++] = ';';
512 buf
[n
++] = '&'; buf
[n
++] = 'q'; buf
[n
++] = 'u'; buf
[n
++] = 'o';
513 buf
[n
++] = 't'; buf
[n
++] = ';';
526 htmlPrint(FILE *out
, char *s
, int slen
)
529 for(i
= 0; i
< slen
; i
++) {
549 HTTPConnectionPtr connection
;
550 connection
= malloc(sizeof(HTTPConnectionRec
));
551 if(connection
== NULL
)
553 connection
->flags
= 0;
555 connection
->buf
= NULL
;
557 connection
->offset
= 0;
558 connection
->request
= NULL
;
559 connection
->request_last
= NULL
;
560 connection
->serviced
= 0;
561 connection
->version
= HTTP_UNKNOWN
;
562 connection
->time
= current_time
.tv_sec
;
563 connection
->timeout
= NULL
;
564 connection
->te
= TE_IDENTITY
;
565 connection
->reqbuf
= NULL
;
566 connection
->reqlen
= 0;
567 connection
->reqbegin
= 0;
568 connection
->reqoffset
= 0;
569 connection
->bodylen
= -1;
570 connection
->reqte
= TE_IDENTITY
;
571 connection
->chunk_remaining
= 0;
572 connection
->server
= NULL
;
573 connection
->pipelined
= 0;
574 connection
->connecting
= 0;
575 connection
->server
= NULL
;
580 httpDestroyConnection(HTTPConnectionPtr connection
)
582 assert(connection
->flags
== 0);
583 httpConnectionDestroyBuf(connection
);
584 assert(!connection
->request
);
585 assert(!connection
->request_last
);
586 httpConnectionDestroyReqbuf(connection
);
587 assert(!connection
->timeout
);
588 assert(!connection
->server
);
593 httpConnectionDestroyBuf(HTTPConnectionPtr connection
)
595 if(connection
->buf
) {
596 if(connection
->flags
& CONN_BIGBUF
)
597 free(connection
->buf
);
599 dispose_chunk(connection
->buf
);
601 connection
->flags
&= ~CONN_BIGBUF
;
602 connection
->buf
= NULL
;
606 httpConnectionDestroyReqbuf(HTTPConnectionPtr connection
)
608 if(connection
->reqbuf
) {
609 if(connection
->flags
& CONN_BIGREQBUF
)
610 free(connection
->reqbuf
);
612 dispose_chunk(connection
->reqbuf
);
614 connection
->flags
&= ~CONN_BIGREQBUF
;
615 connection
->reqbuf
= NULL
;
621 HTTPRequestPtr request
;
622 request
= malloc(sizeof(HTTPRequestRec
));
626 request
->connection
= NULL
;
627 request
->object
= NULL
;
628 request
->method
= METHOD_UNKNOWN
;
631 request
->cache_control
= no_cache_control
;
632 request
->condition
= NULL
;
634 request
->chandler
= NULL
;
635 request
->can_mutate
= NULL
;
636 request
->error_code
= 0;
637 request
->error_message
= NULL
;
638 request
->error_headers
= NULL
;
639 request
->headers
= NULL
;
640 request
->time0
= null_time
;
641 request
->time1
= null_time
;
642 request
->request
= NULL
;
643 request
->next
= NULL
;
648 httpDestroyRequest(HTTPRequestPtr request
)
651 releaseObject(request
->object
);
652 if(request
->condition
)
653 httpDestroyCondition(request
->condition
);
654 releaseAtom(request
->via
);
655 assert(request
->chandler
== NULL
);
656 releaseAtom(request
->error_message
);
657 releaseAtom(request
->headers
);
658 releaseAtom(request
->error_headers
);
659 assert(request
->request
== NULL
);
660 assert(request
->next
== NULL
);
665 httpQueueRequest(HTTPConnectionPtr connection
, HTTPRequestPtr request
)
667 assert(request
->next
== NULL
&& request
->connection
== NULL
);
668 request
->connection
= connection
;
669 if(connection
->request_last
) {
670 assert(connection
->request
);
671 connection
->request_last
->next
= request
;
672 connection
->request_last
= request
;
674 assert(!connection
->request_last
);
675 connection
->request
= request
;
676 connection
->request_last
= request
;
681 httpDequeueRequest(HTTPConnectionPtr connection
)
683 HTTPRequestPtr request
= connection
->request
;
685 assert(connection
->request_last
);
686 connection
->request
= request
->next
;
687 if(!connection
->request
) connection
->request_last
= NULL
;
688 request
->next
= NULL
;
694 httpConnectionBigify(HTTPConnectionPtr connection
)
697 assert(!(connection
->flags
& CONN_BIGBUF
));
699 if(bigBufferSize
<= CHUNK_SIZE
)
702 bigbuf
= malloc(bigBufferSize
);
705 if(connection
->len
> 0)
706 memcpy(bigbuf
, connection
->buf
, connection
->len
);
708 dispose_chunk(connection
->buf
);
709 connection
->buf
= bigbuf
;
710 connection
->flags
|= CONN_BIGBUF
;
715 httpConnectionBigifyReqbuf(HTTPConnectionPtr connection
)
718 assert(!(connection
->flags
& CONN_BIGREQBUF
));
720 if(bigBufferSize
<= CHUNK_SIZE
)
723 bigbuf
= malloc(bigBufferSize
);
726 if(connection
->reqlen
> 0)
727 memcpy(bigbuf
, connection
->reqbuf
, connection
->reqlen
);
728 if(connection
->reqbuf
)
729 dispose_chunk(connection
->reqbuf
);
730 connection
->reqbuf
= bigbuf
;
731 connection
->flags
|= CONN_BIGREQBUF
;
736 httpConnectionUnbigify(HTTPConnectionPtr connection
)
739 assert(connection
->flags
& CONN_BIGBUF
);
740 assert(connection
->len
< CHUNK_SIZE
);
745 if(connection
->len
> 0)
746 memcpy(buf
, connection
->buf
, connection
->len
);
747 free(connection
->buf
);
748 connection
->buf
= buf
;
749 connection
->flags
&= ~CONN_BIGBUF
;
754 httpConnectionUnbigifyReqbuf(HTTPConnectionPtr connection
)
757 assert(connection
->flags
& CONN_BIGREQBUF
);
758 assert(connection
->reqlen
< CHUNK_SIZE
);
763 if(connection
->reqlen
> 0)
764 memcpy(buf
, connection
->reqbuf
, connection
->reqlen
);
765 free(connection
->reqbuf
);
766 connection
->reqbuf
= buf
;
767 connection
->flags
&= ~CONN_BIGREQBUF
;
774 HTTPConditionPtr condition
;
775 condition
= malloc(sizeof(HTTPConditionRec
));
776 if(condition
== NULL
)
779 condition
->inms
= -1;
780 condition
->im
= NULL
;
781 condition
->inm
= NULL
;
782 condition
->ifrange
= NULL
;
787 httpDestroyCondition(HTTPConditionPtr condition
)
790 free(condition
->inm
);
793 if(condition
->ifrange
)
794 free(condition
->ifrange
);
799 httpCondition(ObjectPtr object
, HTTPConditionPtr condition
)
801 int rc
= CONDITION_MATCH
;
803 assert(!(object
->flags
& OBJECT_INITIAL
));
805 if(!condition
) return CONDITION_MATCH
;
807 if(condition
->ims
>= 0) {
808 if(object
->last_modified
< 0 ||
809 condition
->ims
< object
->last_modified
)
812 rc
= CONDITION_NOT_MODIFIED
;
815 if(condition
->inms
>= 0) {
816 if(object
->last_modified
< 0 ||
817 condition
->inms
>= object
->last_modified
)
820 rc
= CONDITION_FAILED
;
824 if(!object
->etag
|| strcmp(object
->etag
, condition
->inm
) != 0)
827 rc
= CONDITION_NOT_MODIFIED
;
831 if(!object
->etag
|| strcmp(object
->etag
, condition
->im
) != 0)
832 rc
= CONDITION_FAILED
;
841 httpWriteErrorHeaders(char *buf
, int size
, int offset
, int do_body
,
842 int code
, AtomPtr message
, int close
, AtomPtr headers
,
843 char *url
, int url_len
, char *etag
)
847 char htmlMessage
[100];
852 i
= htmlString(htmlMessage
, 0, 100, message
->string
, message
->length
);
854 strcpy(htmlMessage
, "(Couldn't format message)");
856 htmlMessage
[MIN(i
, 99)] = '\0';
861 do_log(L_ERROR
, "Couldn't allocate body buffer.\n");
864 m
= snnprintf(body
, 0, CHUNK_SIZE
,
865 "<!DOCTYPE HTML PUBLIC "
866 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
867 "\"http://www.w3.org/TR/html4/loose.dtd\">"
869 "\n<title>Proxy %s: %3d %s.</title>"
872 "\n<p>The following %s",
873 code
>= 400 ? "error" : "result",
878 "status was returned");
880 m
= snnprintf(body
, m
, CHUNK_SIZE
,
881 " while trying to access <strong>");
882 m
= htmlString(body
, m
, CHUNK_SIZE
, url
, url_len
);
883 m
= snnprintf(body
, m
, CHUNK_SIZE
, "</strong>");
887 /* On BSD systems, tv_sec is a long. */
888 const time_t ct
= current_time
.tv_sec
;
889 /*Mon, 24 Sep 2004 17:46:35 GMT*/
890 strftime(timeStr
, sizeof(timeStr
), "%a, %d %b %Y %H:%M:%S %Z",
894 m
= snnprintf(body
, m
, CHUNK_SIZE
,
896 "\n<strong>%3d %s</strong></p>"
897 "\n<hr>Generated %s by Polipo on <em>%s:%d</em>."
898 "\n</body></html>\r\n",
900 timeStr
, proxyName
->string
, proxyPort
);
901 if(m
<= 0 || m
>= CHUNK_SIZE
) {
902 do_log(L_ERROR
, "Couldn't write error body.\n");
911 n
= snnprintf(buf
, 0, size
,
915 code
, atomString(message
),
916 close
? "close" : "keep-alive");
917 n
= format_time(buf
, n
, size
, current_time
.tv_sec
);
919 n
= snnprintf(buf
, n
, size
,
920 "\r\nContent-Type: text/html"
921 "\r\nContent-Length: %d", m
);
924 n
= snnprintf(buf
, n
, size
, "\r\nETag: \"%s\"", etag
);
927 if(code
!= 304 && code
!= 412) {
928 n
= snnprintf(buf
, n
, size
,
930 "\r\nCache-Control: no-cache"
931 "\r\nPragma: no-cache");
935 n
= snnprint_n(buf
, n
, size
,
936 headers
->string
, headers
->length
);
938 n
= snnprintf(buf
, n
, size
, "\r\n\r\n");
940 if(n
< 0 || n
>= size
) {
941 do_log(L_ERROR
, "Couldn't write error.\n");
946 if(code
!= 304 && do_body
) {
947 if(m
> 0) memcpy(buf
+ n
, body
, m
);
958 urlDecode(char *buf
, int n
)
965 list
= makeAtomList(NULL
, 0);
979 mybuf
[j
++] = (char)((a
<< 4) | b
);
981 if(j
> 500) goto fail
;
982 } else if(buf
[i
] == '&') {
983 atom
= internAtomN(mybuf
, j
);
986 atomListCons(atom
, list
);
990 mybuf
[j
++] = buf
[i
++];
991 if(j
> 500) goto fail
;
995 atom
= internAtomN(mybuf
, j
);
998 atomListCons(atom
, list
);
1002 destroyAtomList(list
);
1007 httpTweakCachability(ObjectPtr object
)
1009 int code
= object
->code
;
1011 if((object
->cache_control
& CACHE_AUTHORIZATION
) &&
1012 !(object
->cache_control
& CACHE_PUBLIC
))
1013 object
->cache_control
|= (CACHE_NO_HIDDEN
| OBJECT_LINEAR
);
1015 /* This is not required by RFC 2616 -- but see RFC 3143 2.1.1. We
1016 manically avoid caching replies that we don't know how to
1017 handle, even if Expires or Cache-Control says otherwise. As to
1018 known uncacheable replies, we obey Cache-Control and default to
1019 allowing sharing but not caching. */
1020 if(code
!= 200 && code
!= 206 &&
1021 code
!= 300 && code
!= 301 && code
!= 302 && code
!= 303 &&
1022 code
!= 304 && code
!= 307 &&
1023 code
!= 403 && code
!= 404 && code
!= 405 && code
!= 416) {
1024 object
->cache_control
|=
1025 (CACHE_NO_HIDDEN
| CACHE_MISMATCH
| OBJECT_LINEAR
);
1026 } else if(code
!= 200 && code
!= 206 &&
1027 code
!= 300 && code
!= 301 && code
!= 304 &&
1029 if(object
->expires
< 0 && !(object
->cache_control
& CACHE_PUBLIC
)) {
1030 object
->cache_control
|= CACHE_NO_HIDDEN
;
1032 } else if(dontCacheRedirects
&& (code
== 301 || code
== 302)) {
1033 object
->cache_control
|= CACHE_NO_HIDDEN
;
1036 if(urlIsUncachable(object
->key
, object
->key_size
)) {
1037 object
->cache_control
|= CACHE_NO_HIDDEN
;
1040 if((object
->cache_control
& CACHE_NO_STORE
) != 0) {
1041 object
->cache_control
|= CACHE_NO_HIDDEN
;
1044 if(object
->cache_control
& CACHE_VARY
) {
1045 if(!object
->etag
|| dontTrustVaryETag
>= 2) {
1046 object
->cache_control
|= CACHE_MISMATCH
;
1052 httpHeaderMatch(AtomPtr header
, AtomPtr headers1
, AtomPtr headers2
)
1054 int rc1
, b1
, e1
, rc2
, b2
, e2
;
1056 /* Short cut if both sets of headers are identical */
1057 if(headers1
== headers2
)
1060 rc1
= httpFindHeader(header
, headers1
->string
, headers1
->length
,
1062 rc2
= httpFindHeader(header
, headers2
->string
, headers2
->length
,
1065 if(rc1
== 0 && rc2
== 0)
1068 if(rc1
== 0 || rc2
== 0)
1071 if(e1
- b1
!= e2
- b2
)
1074 if(memcmp(headers1
->string
+ b1
, headers2
->string
+ b2
, e1
- b1
) != 0)