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 int dontIdentifyToClients
= 0;
65 static int timeoutSetter(ConfigVariablePtr var
, void *value
);
70 proxyAddress
= internAtom("127.0.0.1");
71 CONFIG_VARIABLE_SETTABLE(disableProxy
, CONFIG_BOOLEAN
, configIntSetter
,
72 "Whether to be a web server only.");
73 CONFIG_VARIABLE_SETTABLE(proxyOffline
, CONFIG_BOOLEAN
, configIntSetter
,
74 "Avoid contacting remote servers.");
75 CONFIG_VARIABLE_SETTABLE(relaxTransparency
, CONFIG_TRISTATE
,
77 "Avoid contacting remote servers.");
78 CONFIG_VARIABLE(proxyPort
, CONFIG_INT
,
79 "The TCP port on which the proxy listens.");
80 CONFIG_VARIABLE(proxyAddress
, CONFIG_ATOM_LOWER
,
81 "The IP address on which the proxy listens.");
82 CONFIG_VARIABLE_SETTABLE(proxyName
, CONFIG_ATOM_LOWER
, configAtomSetter
,
83 "The name by which the proxy is known.");
84 CONFIG_VARIABLE_SETTABLE(clientTimeout
, CONFIG_TIME
,
85 timeoutSetter
, "Client-side timeout.");
86 CONFIG_VARIABLE_SETTABLE(serverTimeout
, CONFIG_TIME
,
87 timeoutSetter
, "Server-side timeout.");
88 CONFIG_VARIABLE_SETTABLE(serverIdleTimeout
, CONFIG_TIME
,
89 timeoutSetter
, "Server-side idle timeout.");
90 CONFIG_VARIABLE(authRealm
, CONFIG_ATOM
,
91 "Authentication realm.");
92 CONFIG_VARIABLE(authCredentials
, CONFIG_PASSWORD
,
93 "username:password.");
94 CONFIG_VARIABLE(parentAuthCredentials
, CONFIG_PASSWORD
,
95 "username:password.");
96 CONFIG_VARIABLE(allowedClients
, CONFIG_ATOM_LIST_LOWER
,
97 "Networks from which clients are allowed to connect.");
98 CONFIG_VARIABLE(tunnelAllowedPorts
, CONFIG_INT_LIST
,
99 "Ports to which tunnelled connections are allowed.");
100 CONFIG_VARIABLE(allowedPorts
, CONFIG_INT_LIST
,
101 "Ports to which connections are allowed.");
102 CONFIG_VARIABLE(expectContinue
, CONFIG_TRISTATE
,
103 "Send Expect-Continue to servers.");
104 CONFIG_VARIABLE(bigBufferSize
, CONFIG_INT
,
105 "Size of big buffers (max size of headers).");
106 CONFIG_VARIABLE_SETTABLE(disableVia
, CONFIG_BOOLEAN
, configIntSetter
,
107 "Don't use Via headers.");
108 CONFIG_VARIABLE(dontTrustVaryETag
, CONFIG_TRISTATE
,
109 "Whether to trust the ETag when there's Vary.");
110 CONFIG_VARIABLE(dontIdentifyToClients
, CONFIG_BOOLEAN
,
111 "Avoid sending machine-identifiable information "
117 timeoutSetter(ConfigVariablePtr var
, void *value
)
119 configIntSetter(var
, value
);
120 if(clientTimeout
<= serverTimeout
)
121 clientTimeout
= serverTimeout
+ 1;
131 struct hostent
*host
;
135 atom100Continue
= internAtom("100-continue");
137 if(clientTimeout
<= serverTimeout
) {
138 clientTimeout
= serverTimeout
+ 1;
139 do_log(L_WARN
, "Value of clientTimeout too small -- setting to %d.\n",
143 if(authCredentials
!= NULL
&& authRealm
== NULL
)
144 authRealm
= internAtom("Polipo");
147 allowedNets
= parseNetAddress(allowedClients
);
148 if(allowedNets
== NULL
)
152 if(allowedPorts
== NULL
) {
153 allowedPorts
= makeIntList(0);
154 if(allowedPorts
== NULL
) {
155 do_log(L_ERROR
, "Couldn't allocate allowedPorts.\n");
158 intListCons(80, 100, allowedPorts
);
159 intListCons(1024, 0xFFFF, allowedPorts
);
162 if(tunnelAllowedPorts
== NULL
) {
163 tunnelAllowedPorts
= makeIntList(0);
164 if(tunnelAllowedPorts
== NULL
) {
165 do_log(L_ERROR
, "Couldn't allocate tunnelAllowedPorts.\n");
168 intListCons(22, 22, tunnelAllowedPorts
); /* ssh */
169 intListCons(80, 80, tunnelAllowedPorts
); /* HTTP */
170 intListCons(109, 110, tunnelAllowedPorts
); /* POP 2 and 3*/
171 intListCons(143, 143, tunnelAllowedPorts
); /* IMAP 2/4 */
172 intListCons(443, 443, tunnelAllowedPorts
); /* HTTP/SSL */
173 intListCons(873, 873, tunnelAllowedPorts
); /* rsync */
174 intListCons(993, 993, tunnelAllowedPorts
); /* IMAP/SSL */
175 intListCons(995, 995, tunnelAllowedPorts
); /* POP/SSL */
176 intListCons(2401, 2401, tunnelAllowedPorts
); /* CVS */
177 intListCons(5222, 5223, tunnelAllowedPorts
); /* Jabber */
178 intListCons(9418, 9418, tunnelAllowedPorts
); /* Git */
186 do_log(L_ERROR
, "Couldn't allocate chunk for host name.\n");
190 n
= gethostname(buf
, CHUNK_SIZE
);
192 do_log_error(L_WARN
, errno
, "Gethostname");
193 strcpy(buf
, "polipo");
196 /* gethostname doesn't necessarily NUL-terminate on overflow */
197 buf
[CHUNK_SIZE
- 1] = '\0';
199 if(strcmp(buf
, "(none)") == 0 ||
200 strcmp(buf
, "localhost") == 0 ||
201 strcmp(buf
, "localhost.localdomain") == 0) {
202 do_log(L_WARN
, "Couldn't determine host name -- using ``polipo''.\n");
203 strcpy(buf
, "polipo");
207 if(strchr(buf
, '.') != NULL
)
210 host
= gethostbyname(buf
);
215 if(host
->h_addrtype
!= AF_INET
)
218 host
= gethostbyaddr(host
->h_addr_list
[0], host
->h_length
, AF_INET
);
220 if(!host
|| !host
->h_name
|| strcmp(host
->h_name
, "localhost") == 0 ||
221 strcmp(host
->h_name
, "localhost.localdomain") == 0)
224 namelen
= strlen(host
->h_name
);
225 if(namelen
>= CHUNK_SIZE
) {
226 do_log(L_ERROR
, "Host name too long.\n");
230 memcpy(buf
, host
->h_name
, namelen
+ 1);
233 proxyName
= internAtom(buf
);
234 if(proxyName
== NULL
) {
235 do_log(L_ERROR
, "Couldn't allocate proxy name.\n");
249 httpSetTimeout(HTTPConnectionPtr connection
, int secs
)
251 TimeEventHandlerPtr
new;
253 if(connection
->timeout
)
254 cancelTimeEvent(connection
->timeout
);
255 connection
->timeout
= NULL
;
258 new = scheduleTimeEvent(secs
, httpTimeoutHandler
,
259 sizeof(connection
), &connection
);
261 do_log(L_ERROR
, "Couldn't schedule timeout for connection 0x%lx\n",
262 (unsigned long)connection
);
269 connection
->timeout
= new;
274 httpTimeoutHandler(TimeEventHandlerPtr event
)
276 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
278 if(connection
->fd
>= 0) {
280 rc
= shutdown(connection
->fd
, 2);
281 if(rc
< 0 && errno
!= ENOTCONN
)
282 do_log_error(L_ERROR
, errno
, "Timeout: shutdown failed");
283 pokeFdEvent(connection
->fd
, -EDOTIMEOUT
, POLLIN
| POLLOUT
);
285 connection
->timeout
= NULL
;
290 httpWriteObjectHeaders(char *buf
, int offset
, int len
,
291 ObjectPtr object
, int from
, int to
)
295 if(from
<= 0 && to
< 0) {
296 if(object
->length
>= 0) {
297 n
= snnprintf(buf
, n
, len
,
298 "\r\nContent-Length: %d", object
->length
);
302 n
= snnprintf(buf
, n
, len
,
303 "\r\nContent-Length: %d", to
- from
);
307 if(from
> 0 || to
> 0) {
308 if(object
->length
>= 0) {
310 n
= snnprintf(buf
, n
, len
,
311 "\r\nContent-Range: bytes */%d",
314 n
= snnprintf(buf
, n
, len
,
315 "\r\nContent-Range: bytes %d-%d/%d",
321 n
= snnprintf(buf
, n
, len
,
322 "\r\nContent-Range: bytes %d-/*",
325 n
= snnprintf(buf
, n
, len
,
326 "\r\nContent-Range: bytes %d-%d/*",
333 n
= snnprintf(buf
, n
, len
, "\r\nETag: \"%s\"", object
->etag
);
335 if((object
->flags
& OBJECT_LOCAL
) || object
->date
>= 0) {
336 n
= snnprintf(buf
, n
, len
, "\r\nDate: ");
337 n
= format_time(buf
, n
, len
,
338 (object
->flags
& OBJECT_LOCAL
) ?
339 current_time
.tv_sec
: object
->date
);
344 if(object
->last_modified
>= 0) {
345 n
= snnprintf(buf
, n
, len
, "\r\nLast-Modified: ");
346 n
= format_time(buf
, n
, len
, object
->last_modified
);
351 if(object
->expires
>= 0) {
352 n
= snnprintf(buf
, n
, len
, "\r\nExpires: ");
353 n
= format_time(buf
, n
, len
, object
->expires
);
358 n
= httpPrintCacheControl(buf
, n
, len
,
359 object
->cache_control
, NULL
);
363 if(!disableVia
&& object
->via
)
364 n
= snnprintf(buf
, n
, len
, "\r\nVia: %s", object
->via
->string
);
367 n
= snnprint_n(buf
, n
, len
, object
->headers
->string
,
368 object
->headers
->length
);
380 cachePrintSeparator(char *buf
, int offset
, int len
,
385 n
= snnprintf(buf
, offset
, len
, ", ");
387 n
= snnprintf(buf
, offset
, len
, "\r\nCache-Control: ");
392 httpPrintCacheControl(char *buf
, int offset
, int len
,
393 int flags
, CacheControlPtr cache_control
)
398 #define PRINT_SEP() \
400 n = cachePrintSeparator(buf, n, len, sub); \
405 flags
|= cache_control
->flags
;
407 if(flags
& CACHE_NO
) {
409 n
= snnprintf(buf
, n
, len
, "no-cache");
411 if(flags
& CACHE_PUBLIC
) {
413 n
= snnprintf(buf
, n
, len
, "public");
415 if(flags
& CACHE_PRIVATE
) {
417 n
= snnprintf(buf
, n
, len
, "private");
419 if(flags
& CACHE_NO_STORE
) {
421 n
= snnprintf(buf
, n
, len
, "no-store");
423 if(flags
& CACHE_NO_TRANSFORM
) {
425 n
= snnprintf(buf
, n
, len
, "no-transform");
427 if(flags
& CACHE_MUST_REVALIDATE
) {
429 n
= snnprintf(buf
, n
, len
, "must-revalidate");
431 if(flags
& CACHE_PROXY_REVALIDATE
) {
433 n
= snnprintf(buf
, n
, len
, "proxy-revalidate");
435 if(flags
& CACHE_ONLY_IF_CACHED
) {
437 n
= snnprintf(buf
, n
, len
, "only-if-cached");
440 if(cache_control
->max_age
>= 0) {
442 n
= snnprintf(buf
, n
, len
, "max-age=%d",
443 cache_control
->max_age
);
445 if(cache_control
->s_maxage
>= 0) {
447 n
= snnprintf(buf
, n
, len
, "s-maxage=%d",
448 cache_control
->s_maxage
);
450 if(cache_control
->min_fresh
> 0) {
452 n
= snnprintf(buf
, n
, len
, "min-fresh=%d",
453 cache_control
->min_fresh
);
455 if(cache_control
->max_stale
> 0) {
457 n
= snnprintf(buf
, n
, len
, "max-stale=%d",
458 cache_control
->min_fresh
);
466 httpMessage(int code
)
472 return "Partial content";
474 return "Multiple choices";
476 return "Moved permanently";
482 return "Not changed";
484 return "Temporary redirect";
486 return "Authentication Required";
492 return "Method not allowed";
494 return "Proxy authentication required";
496 return "Unknown error code";
501 htmlString(char *buf
, int n
, int len
, char *s
, int slen
)
504 while(i
< slen
&& n
+ 5 < len
) {
507 buf
[n
++] = '&'; buf
[n
++] = 'a'; buf
[n
++] = 'm'; buf
[n
++] = 'p';
511 buf
[n
++] = '&'; buf
[n
++] = 'l'; buf
[n
++] = 't'; buf
[n
++] = ';';
514 buf
[n
++] = '&'; buf
[n
++] = 'g'; buf
[n
++] = 't'; buf
[n
++] = ';';
517 buf
[n
++] = '&'; buf
[n
++] = 'q'; buf
[n
++] = 'u'; buf
[n
++] = 'o';
518 buf
[n
++] = 't'; buf
[n
++] = ';';
531 htmlPrint(FILE *out
, char *s
, int slen
)
534 for(i
= 0; i
< slen
; i
++) {
554 HTTPConnectionPtr connection
;
555 connection
= malloc(sizeof(HTTPConnectionRec
));
556 if(connection
== NULL
)
558 connection
->flags
= 0;
560 connection
->buf
= NULL
;
562 connection
->offset
= 0;
563 connection
->request
= NULL
;
564 connection
->request_last
= NULL
;
565 connection
->serviced
= 0;
566 connection
->version
= HTTP_UNKNOWN
;
567 connection
->time
= current_time
.tv_sec
;
568 connection
->timeout
= NULL
;
569 connection
->te
= TE_IDENTITY
;
570 connection
->reqbuf
= NULL
;
571 connection
->reqlen
= 0;
572 connection
->reqbegin
= 0;
573 connection
->reqoffset
= 0;
574 connection
->bodylen
= -1;
575 connection
->reqte
= TE_IDENTITY
;
576 connection
->chunk_remaining
= 0;
577 connection
->server
= NULL
;
578 connection
->pipelined
= 0;
579 connection
->connecting
= 0;
580 connection
->server
= NULL
;
585 httpDestroyConnection(HTTPConnectionPtr connection
)
587 assert(connection
->flags
== 0);
588 httpConnectionDestroyBuf(connection
);
589 assert(!connection
->request
);
590 assert(!connection
->request_last
);
591 httpConnectionDestroyReqbuf(connection
);
592 assert(!connection
->timeout
);
593 assert(!connection
->server
);
598 httpConnectionDestroyBuf(HTTPConnectionPtr connection
)
600 if(connection
->buf
) {
601 if(connection
->flags
& CONN_BIGBUF
)
602 free(connection
->buf
);
604 dispose_chunk(connection
->buf
);
606 connection
->flags
&= ~CONN_BIGBUF
;
607 connection
->buf
= NULL
;
611 httpConnectionDestroyReqbuf(HTTPConnectionPtr connection
)
613 if(connection
->reqbuf
) {
614 if(connection
->flags
& CONN_BIGREQBUF
)
615 free(connection
->reqbuf
);
617 dispose_chunk(connection
->reqbuf
);
619 connection
->flags
&= ~CONN_BIGREQBUF
;
620 connection
->reqbuf
= NULL
;
626 HTTPRequestPtr request
;
627 request
= malloc(sizeof(HTTPRequestRec
));
631 request
->connection
= NULL
;
632 request
->object
= NULL
;
633 request
->method
= METHOD_UNKNOWN
;
636 request
->cache_control
= no_cache_control
;
637 request
->condition
= NULL
;
639 request
->chandler
= NULL
;
640 request
->can_mutate
= NULL
;
641 request
->error_code
= 0;
642 request
->error_message
= NULL
;
643 request
->error_headers
= NULL
;
644 request
->headers
= NULL
;
645 request
->time0
= null_time
;
646 request
->time1
= null_time
;
647 request
->request
= NULL
;
648 request
->next
= NULL
;
653 httpDestroyRequest(HTTPRequestPtr request
)
656 releaseObject(request
->object
);
657 if(request
->condition
)
658 httpDestroyCondition(request
->condition
);
659 releaseAtom(request
->via
);
660 assert(request
->chandler
== NULL
);
661 releaseAtom(request
->error_message
);
662 releaseAtom(request
->headers
);
663 releaseAtom(request
->error_headers
);
664 assert(request
->request
== NULL
);
665 assert(request
->next
== NULL
);
670 httpQueueRequest(HTTPConnectionPtr connection
, HTTPRequestPtr request
)
672 assert(request
->next
== NULL
&& request
->connection
== NULL
);
673 request
->connection
= connection
;
674 if(connection
->request_last
) {
675 assert(connection
->request
);
676 connection
->request_last
->next
= request
;
677 connection
->request_last
= request
;
679 assert(!connection
->request_last
);
680 connection
->request
= request
;
681 connection
->request_last
= request
;
686 httpDequeueRequest(HTTPConnectionPtr connection
)
688 HTTPRequestPtr request
= connection
->request
;
690 assert(connection
->request_last
);
691 connection
->request
= request
->next
;
692 if(!connection
->request
) connection
->request_last
= NULL
;
693 request
->next
= NULL
;
699 httpConnectionBigify(HTTPConnectionPtr connection
)
702 assert(!(connection
->flags
& CONN_BIGBUF
));
704 if(bigBufferSize
<= CHUNK_SIZE
)
707 bigbuf
= malloc(bigBufferSize
);
710 if(connection
->len
> 0)
711 memcpy(bigbuf
, connection
->buf
, connection
->len
);
713 dispose_chunk(connection
->buf
);
714 connection
->buf
= bigbuf
;
715 connection
->flags
|= CONN_BIGBUF
;
720 httpConnectionBigifyReqbuf(HTTPConnectionPtr connection
)
723 assert(!(connection
->flags
& CONN_BIGREQBUF
));
725 if(bigBufferSize
<= CHUNK_SIZE
)
728 bigbuf
= malloc(bigBufferSize
);
731 if(connection
->reqlen
> 0)
732 memcpy(bigbuf
, connection
->reqbuf
, connection
->reqlen
);
733 if(connection
->reqbuf
)
734 dispose_chunk(connection
->reqbuf
);
735 connection
->reqbuf
= bigbuf
;
736 connection
->flags
|= CONN_BIGREQBUF
;
741 httpConnectionUnbigify(HTTPConnectionPtr connection
)
744 assert(connection
->flags
& CONN_BIGBUF
);
745 assert(connection
->len
< CHUNK_SIZE
);
750 if(connection
->len
> 0)
751 memcpy(buf
, connection
->buf
, connection
->len
);
752 free(connection
->buf
);
753 connection
->buf
= buf
;
754 connection
->flags
&= ~CONN_BIGBUF
;
759 httpConnectionUnbigifyReqbuf(HTTPConnectionPtr connection
)
762 assert(connection
->flags
& CONN_BIGREQBUF
);
763 assert(connection
->reqlen
< CHUNK_SIZE
);
768 if(connection
->reqlen
> 0)
769 memcpy(buf
, connection
->reqbuf
, connection
->reqlen
);
770 free(connection
->reqbuf
);
771 connection
->reqbuf
= buf
;
772 connection
->flags
&= ~CONN_BIGREQBUF
;
779 HTTPConditionPtr condition
;
780 condition
= malloc(sizeof(HTTPConditionRec
));
781 if(condition
== NULL
)
784 condition
->inms
= -1;
785 condition
->im
= NULL
;
786 condition
->inm
= NULL
;
787 condition
->ifrange
= NULL
;
792 httpDestroyCondition(HTTPConditionPtr condition
)
795 free(condition
->inm
);
798 if(condition
->ifrange
)
799 free(condition
->ifrange
);
804 httpCondition(ObjectPtr object
, HTTPConditionPtr condition
)
806 int rc
= CONDITION_MATCH
;
808 assert(!(object
->flags
& OBJECT_INITIAL
));
810 if(!condition
) return CONDITION_MATCH
;
812 if(condition
->ims
>= 0) {
813 if(object
->last_modified
< 0 ||
814 condition
->ims
< object
->last_modified
)
817 rc
= CONDITION_NOT_MODIFIED
;
820 if(condition
->inms
>= 0) {
821 if(object
->last_modified
< 0 ||
822 condition
->inms
>= object
->last_modified
)
825 rc
= CONDITION_FAILED
;
829 if(!object
->etag
|| strcmp(object
->etag
, condition
->inm
) != 0)
832 rc
= CONDITION_NOT_MODIFIED
;
836 if(!object
->etag
|| strcmp(object
->etag
, condition
->im
) != 0)
837 rc
= CONDITION_FAILED
;
846 httpWriteErrorHeaders(char *buf
, int size
, int offset
, int do_body
,
847 int code
, AtomPtr message
, int close
, AtomPtr headers
,
848 char *url
, int url_len
, char *etag
)
852 char htmlMessage
[100];
856 i
= htmlString(htmlMessage
, 0, 100, message
->string
, message
->length
);
858 strcpy(htmlMessage
, "(Couldn't format message)");
860 htmlMessage
[MIN(i
, 99)] = '\0';
865 do_log(L_ERROR
, "Couldn't allocate body buffer.\n");
868 m
= snnprintf(body
, 0, CHUNK_SIZE
,
869 "<!DOCTYPE HTML PUBLIC "
870 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
871 "\"http://www.w3.org/TR/html4/loose.dtd\">"
873 "\n<title>Proxy %s: %3d %s.</title>"
876 "\n<p>The following %s",
877 code
>= 400 ? "error" : "result",
882 "status was returned");
884 m
= snnprintf(body
, m
, CHUNK_SIZE
,
885 " while trying to access <strong>");
886 m
= htmlString(body
, m
, CHUNK_SIZE
, url
, url_len
);
887 m
= snnprintf(body
, m
, CHUNK_SIZE
, "</strong>");
890 m
= snnprintf(body
, m
, CHUNK_SIZE
,
892 "\n<strong>%3d %s</strong></p>",
894 if (!dontIdentifyToClients
) {
896 /* On BSD systems, tv_sec is a long. */
897 const time_t ct
= current_time
.tv_sec
;
898 /*Mon, 24 Sep 2004 17:46:35 GMT*/
899 strftime(timeStr
, sizeof(timeStr
), "%a, %d %b %Y %H:%M:%S %Z",
902 m
= snnprintf(body
, m
, CHUNK_SIZE
,
903 "\n<hr>Generated %s by Polipo on <em>%s:%d</em>.",
904 timeStr
, proxyName
->string
, proxyPort
);
907 m
= snnprintf(body
, m
, CHUNK_SIZE
, "\n</body></html>\r\n");
909 if(m
<= 0 || m
>= CHUNK_SIZE
) {
910 do_log(L_ERROR
, "Couldn't write error body.\n");
919 n
= snnprintf(buf
, 0, size
,
923 code
, atomString(message
),
924 close
? "close" : "keep-alive");
925 n
= format_time(buf
, n
, size
, current_time
.tv_sec
);
927 n
= snnprintf(buf
, n
, size
,
928 "\r\nContent-Type: text/html"
929 "\r\nContent-Length: %d", m
);
932 n
= snnprintf(buf
, n
, size
, "\r\nETag: \"%s\"", etag
);
935 if(code
!= 304 && code
!= 412) {
936 n
= snnprintf(buf
, n
, size
,
938 "\r\nCache-Control: no-cache"
939 "\r\nPragma: no-cache");
943 n
= snnprint_n(buf
, n
, size
,
944 headers
->string
, headers
->length
);
946 n
= snnprintf(buf
, n
, size
, "\r\n\r\n");
948 if(n
< 0 || n
>= size
) {
949 do_log(L_ERROR
, "Couldn't write error.\n");
954 if(code
!= 304 && do_body
) {
955 if(m
> 0) memcpy(buf
+ n
, body
, m
);
966 urlDecode(char *buf
, int n
)
973 list
= makeAtomList(NULL
, 0);
987 mybuf
[j
++] = (char)((a
<< 4) | b
);
989 if(j
>= 500) goto fail
;
990 } else if(buf
[i
] == '&') {
991 atom
= internAtomN(mybuf
, j
);
994 atomListCons(atom
, list
);
998 mybuf
[j
++] = buf
[i
++];
999 if(j
>= 500) goto fail
;
1003 atom
= internAtomN(mybuf
, j
);
1006 atomListCons(atom
, list
);
1010 destroyAtomList(list
);
1015 httpTweakCachability(ObjectPtr object
)
1017 int code
= object
->code
;
1019 if((object
->cache_control
& CACHE_AUTHORIZATION
) &&
1020 !(object
->cache_control
& CACHE_PUBLIC
))
1021 object
->cache_control
|= (CACHE_NO_HIDDEN
| OBJECT_LINEAR
);
1023 /* This is not required by RFC 2616 -- but see RFC 3143 2.1.1. We
1024 manically avoid caching replies that we don't know how to
1025 handle, even if Expires or Cache-Control says otherwise. As to
1026 known uncacheable replies, we obey Cache-Control and default to
1027 allowing sharing but not caching. */
1028 if(code
!= 200 && code
!= 206 &&
1029 code
!= 300 && code
!= 301 && code
!= 302 && code
!= 303 &&
1030 code
!= 304 && code
!= 307 &&
1031 code
!= 403 && code
!= 404 && code
!= 405 && code
!= 416) {
1032 object
->cache_control
|=
1033 (CACHE_NO_HIDDEN
| CACHE_MISMATCH
| OBJECT_LINEAR
);
1034 } else if(code
!= 200 && code
!= 206 &&
1035 code
!= 300 && code
!= 301 && code
!= 304 &&
1037 if(object
->expires
< 0 && !(object
->cache_control
& CACHE_PUBLIC
)) {
1038 object
->cache_control
|= CACHE_NO_HIDDEN
;
1040 } else if(dontCacheRedirects
&& (code
== 301 || code
== 302)) {
1041 object
->cache_control
|= CACHE_NO_HIDDEN
;
1044 if(urlIsUncachable(object
->key
, object
->key_size
)) {
1045 object
->cache_control
|= CACHE_NO_HIDDEN
;
1048 if((object
->cache_control
& CACHE_NO_STORE
) != 0) {
1049 object
->cache_control
|= CACHE_NO_HIDDEN
;
1052 if(object
->cache_control
& CACHE_VARY
) {
1053 if(!object
->etag
|| dontTrustVaryETag
>= 2) {
1054 object
->cache_control
|= CACHE_MISMATCH
;
1060 httpHeaderMatch(AtomPtr header
, AtomPtr headers1
, AtomPtr headers2
)
1062 int rc1
, b1
, e1
, rc2
, b2
, e2
;
1064 /* Short cut if both sets of headers are identical */
1065 if(headers1
== headers2
)
1068 rc1
= httpFindHeader(header
, headers1
->string
, headers1
->length
,
1070 rc2
= httpFindHeader(header
, headers2
->string
, headers2
->length
,
1073 if(rc1
== 0 && rc2
== 0)
1076 if(rc1
== 0 || rc2
== 0)
1079 if(e1
- b1
!= e2
- b2
)
1082 if(memcmp(headers1
->string
+ b1
, headers2
->string
+ b2
, e1
- b1
) != 0)
1089 getScrubbedProxyName(void)
1091 if(dontIdentifyToClients
)
1094 return proxyName
->string
;