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;
32 int bigBufferSize
= (32 * 1024);
34 AtomPtr authRealm
= NULL
;
35 AtomPtr authCredentials
= NULL
;
37 AtomPtr parentAuthCredentials
= NULL
;
39 AtomListPtr allowedClients
= NULL
;
40 NetAddressPtr allowedNets
= NULL
;
42 IntListPtr allowedPorts
= NULL
;
43 IntListPtr tunnelAllowedPorts
= NULL
;
44 int expectContinue
= 1;
45 int dontTrustVaryETag
= 1;
47 AtomPtr atom100Continue
;
51 /* 0 means that all failures lead to errors. 1 means that failures to
52 connect are reported in a Warning header when stale objects are
53 served. 2 means that only missing data is fetched from the net,
54 stale data is served without revalidation (browser-side
55 Cache-Control directives are still honoured). 3 means that no
56 connections are ever attempted. */
59 int relaxTransparency
= 0;
60 AtomPtr proxyAddress
= NULL
;
62 static int timeoutSetter(ConfigVariablePtr var
, void *value
);
67 proxyAddress
= internAtom("127.0.0.1");
68 CONFIG_VARIABLE_SETTABLE(disableProxy
, CONFIG_BOOLEAN
, configIntSetter
,
69 "Whether to be a web server only.");
70 CONFIG_VARIABLE_SETTABLE(proxyOffline
, CONFIG_BOOLEAN
, configIntSetter
,
71 "Avoid contacting remote servers.");
72 CONFIG_VARIABLE_SETTABLE(relaxTransparency
, CONFIG_TRISTATE
,
74 "Avoid contacting remote servers.");
75 CONFIG_VARIABLE(proxyPort
, CONFIG_INT
,
76 "The TCP port on which the proxy listens.");
77 CONFIG_VARIABLE(proxyAddress
, CONFIG_ATOM_LOWER
,
78 "The IP address on which the proxy listens.");
79 CONFIG_VARIABLE_SETTABLE(proxyName
, CONFIG_ATOM_LOWER
, configAtomSetter
,
80 "The name by which the proxy is known.");
81 CONFIG_VARIABLE_SETTABLE(clientTimeout
, CONFIG_TIME
,
82 timeoutSetter
, "Client-side timeout.");
83 CONFIG_VARIABLE_SETTABLE(serverTimeout
, CONFIG_TIME
,
84 timeoutSetter
, "Server-side timeout.");
85 CONFIG_VARIABLE(authRealm
, CONFIG_ATOM
,
86 "Authentication realm.");
87 CONFIG_VARIABLE(authCredentials
, CONFIG_PASSWORD
,
88 "username:password.");
89 CONFIG_VARIABLE(parentAuthCredentials
, CONFIG_PASSWORD
,
90 "username:password.");
91 CONFIG_VARIABLE(allowedClients
, CONFIG_ATOM_LIST_LOWER
,
92 "Networks from which clients are allowed to connect.");
93 CONFIG_VARIABLE(tunnelAllowedPorts
, CONFIG_INT_LIST
,
94 "Ports to which tunnelled connections are allowed.");
95 CONFIG_VARIABLE(allowedPorts
, CONFIG_INT_LIST
,
96 "Ports to which connections are allowed.");
97 CONFIG_VARIABLE(expectContinue
, CONFIG_TRISTATE
,
98 "Send Expect-Continue to servers.");
99 CONFIG_VARIABLE(bigBufferSize
, CONFIG_INT
,
100 "Size of big buffers (max size of headers).");
101 CONFIG_VARIABLE_SETTABLE(disableVia
, CONFIG_BOOLEAN
, configIntSetter
,
102 "Don't use Via headers.");
103 CONFIG_VARIABLE(dontTrustVaryETag
, CONFIG_TRISTATE
,
104 "Whether to trust the ETag when there's Vary.");
109 timeoutSetter(ConfigVariablePtr var
, void *value
)
111 configIntSetter(var
, value
);
112 if(clientTimeout
<= serverTimeout
)
113 clientTimeout
= serverTimeout
+ 1;
123 struct hostent
*host
;
127 atom100Continue
= internAtom("100-continue");
129 if(clientTimeout
<= serverTimeout
) {
130 clientTimeout
= serverTimeout
+ 1;
131 do_log(L_WARN
, "Value of clientTimeout too small -- setting to %d.\n",
135 if(authCredentials
!= NULL
&& authRealm
== NULL
)
136 authRealm
= internAtom("Polipo");
139 allowedNets
= parseNetAddress(allowedClients
);
140 if(allowedNets
== NULL
)
144 if(allowedPorts
== NULL
) {
145 allowedPorts
= makeIntList(0);
146 if(allowedPorts
== NULL
) {
147 do_log(L_ERROR
, "Couldn't allocate allowedPorts.\n");
150 intListCons(80, 100, allowedPorts
);
151 intListCons(1024, 0xFFFF, allowedPorts
);
154 if(tunnelAllowedPorts
== NULL
) {
155 tunnelAllowedPorts
= makeIntList(0);
156 if(tunnelAllowedPorts
== NULL
) {
157 do_log(L_ERROR
, "Couldn't allocate tunnelAllowedPorts.\n");
160 intListCons(22, 22, tunnelAllowedPorts
); /* ssh */
161 intListCons(80, 80, tunnelAllowedPorts
); /* HTTP */
162 intListCons(109, 110, tunnelAllowedPorts
); /* POP 2 and 3*/
163 intListCons(143, 143, tunnelAllowedPorts
); /* IMAP 2/4 */
164 intListCons(443, 443, tunnelAllowedPorts
); /* HTTP/SSL */
165 intListCons(873, 873, tunnelAllowedPorts
); /* rsync */
166 intListCons(993, 993, tunnelAllowedPorts
); /* IMAP/SSL */
167 intListCons(995, 995, tunnelAllowedPorts
); /* POP/SSL */
175 do_log(L_ERROR
, "Couldn't allocate chunk for host name.\n");
179 n
= gethostname(buf
, CHUNK_SIZE
);
181 do_log_error(L_WARN
, errno
, "Gethostname");
182 strcpy(buf
, "polipo");
185 /* gethostname doesn't necessarily NUL-terminate on overflow */
186 buf
[CHUNK_SIZE
- 1] = '\0';
188 if(strcmp(buf
, "(none)") == 0 ||
189 strcmp(buf
, "localhost") == 0 ||
190 strcmp(buf
, "localhost.localdomain") == 0) {
191 do_log(L_WARN
, "Couldn't determine host name -- using ``polipo''.\n");
192 strcpy(buf
, "polipo");
196 if(strchr(buf
, '.') != NULL
)
199 host
= gethostbyname(buf
);
204 if(host
->h_addrtype
!= AF_INET
)
207 host
= gethostbyaddr(host
->h_addr_list
[0], host
->h_length
, AF_INET
);
209 if(!host
|| !host
->h_name
|| strcmp(host
->h_name
, "localhost") == 0 ||
210 strcmp(host
->h_name
, "localhost.localdomain") == 0)
213 namelen
= strlen(host
->h_name
);
214 if(namelen
>= CHUNK_SIZE
) {
215 do_log(L_ERROR
, "Host name too long.\n");
219 memcpy(buf
, host
->h_name
, namelen
+ 1);
222 proxyName
= internAtom(buf
);
223 if(proxyName
== NULL
) {
224 do_log(L_ERROR
, "Couldn't allocate proxy name.\n");
238 httpSetTimeout(HTTPConnectionPtr connection
, int secs
)
240 TimeEventHandlerPtr
new;
242 if(connection
->timeout
)
243 cancelTimeEvent(connection
->timeout
);
244 connection
->timeout
= NULL
;
247 new = scheduleTimeEvent(secs
, httpTimeoutHandler
,
248 sizeof(connection
), &connection
);
250 do_log(L_ERROR
, "Couldn't schedule timeout for connection 0x%lx\n",
251 (unsigned long)connection
);
258 connection
->timeout
= new;
263 httpTimeoutHandler(TimeEventHandlerPtr event
)
265 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
267 if(connection
->fd
>= 0) {
269 rc
= shutdown(connection
->fd
, 2);
270 if(rc
< 0 && errno
!= ENOTCONN
)
271 do_log_error(L_ERROR
, errno
, "Timeout: shutdown failed");
272 pokeFdEvent(connection
->fd
, -EDOTIMEOUT
, POLLIN
| POLLOUT
);
274 connection
->timeout
= NULL
;
279 httpWriteObjectHeaders(char *buf
, int offset
, int len
,
280 ObjectPtr object
, int from
, int to
)
284 if(from
<= 0 && to
< 0) {
285 if(object
->length
>= 0) {
286 n
= snnprintf(buf
, n
, len
,
287 "\r\nContent-Length: %d", object
->length
);
291 n
= snnprintf(buf
, n
, len
,
292 "\r\nContent-Length: %d", to
- from
);
296 if(from
> 0 || to
> 0) {
297 if(object
->length
>= 0) {
299 n
= snnprintf(buf
, n
, len
,
300 "\r\nContent-Range: bytes */%d",
303 n
= snnprintf(buf
, n
, len
,
304 "\r\nContent-Range: bytes %d-%d/%d",
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/*",
322 n
= snnprintf(buf
, n
, len
, "\r\nETag: \"%s\"", object
->etag
);
324 if((object
->flags
& OBJECT_LOCAL
) || object
->date
>= 0) {
325 n
= snnprintf(buf
, n
, len
, "\r\nDate: ");
326 n
= format_time(buf
, n
, len
,
327 (object
->flags
& OBJECT_LOCAL
) ?
328 current_time
.tv_sec
: object
->date
);
333 if(object
->last_modified
>= 0) {
334 n
= snnprintf(buf
, n
, len
, "\r\nLast-Modified: ");
335 n
= format_time(buf
, n
, len
, object
->last_modified
);
340 if(object
->expires
>= 0) {
341 n
= snnprintf(buf
, n
, len
, "\r\nExpires: ");
342 n
= format_time(buf
, n
, len
, object
->expires
);
347 n
= httpPrintCacheControl(buf
, n
, len
,
348 object
->cache_control
, NULL
);
352 if(!disableVia
&& object
->via
)
353 n
= snnprintf(buf
, n
, len
, "\r\nVia: %s", object
->via
->string
);
356 n
= snnprint_n(buf
, n
, len
, object
->headers
->string
,
357 object
->headers
->length
);
369 cachePrintSeparator(char *buf
, int offset
, int len
,
374 n
= snnprintf(buf
, offset
, len
, ", ");
376 n
= snnprintf(buf
, offset
, len
, "\r\nCache-Control: ");
381 httpPrintCacheControl(char *buf
, int offset
, int len
,
382 int flags
, CacheControlPtr cache_control
)
387 #define PRINT_SEP() \
389 n = cachePrintSeparator(buf, n, len, sub); \
394 flags
|= cache_control
->flags
;
396 if(flags
& CACHE_NO
) {
398 n
= snnprintf(buf
, n
, len
, "no-cache");
400 if(flags
& CACHE_PUBLIC
) {
402 n
= snnprintf(buf
, n
, len
, "public");
404 if(flags
& CACHE_PRIVATE
) {
406 n
= snnprintf(buf
, n
, len
, "private");
408 if(flags
& CACHE_NO_STORE
) {
410 n
= snnprintf(buf
, n
, len
, "no-store");
412 if(flags
& CACHE_NO_TRANSFORM
) {
414 n
= snnprintf(buf
, n
, len
, "no-transform");
416 if(flags
& CACHE_MUST_REVALIDATE
) {
418 n
= snnprintf(buf
, n
, len
, "must-revalidate");
420 if(flags
& CACHE_PROXY_REVALIDATE
) {
422 n
= snnprintf(buf
, n
, len
, "proxy-revalidate");
424 if(flags
& CACHE_ONLY_IF_CACHED
) {
426 n
= snnprintf(buf
, n
, len
, "only-if-cached");
429 if(cache_control
->max_age
>= 0) {
431 n
= snnprintf(buf
, n
, len
, "max-age=%d",
432 cache_control
->max_age
);
434 if(cache_control
->s_maxage
>= 0) {
436 n
= snnprintf(buf
, n
, len
, "s-maxage=%d",
437 cache_control
->s_maxage
);
439 if(cache_control
->min_fresh
> 0) {
441 n
= snnprintf(buf
, n
, len
, "min-fresh=%d",
442 cache_control
->min_fresh
);
444 if(cache_control
->max_stale
> 0) {
446 n
= snnprintf(buf
, n
, len
, "max-stale=%d",
447 cache_control
->min_fresh
);
455 httpMessage(int code
)
461 return "Partial content";
463 return "Multiple choices";
465 return "Moved permanently";
471 return "Not changed";
473 return "Temporary redirect";
475 return "Authentication Required";
481 return "Method not allowed";
483 return "Proxy authentication required";
485 return "Unknown error code";
490 htmlString(char *buf
, int n
, int len
, char *s
, int slen
)
493 while(i
< slen
&& n
+ 5 < len
) {
496 buf
[n
++] = '&'; buf
[n
++] = 'a'; buf
[n
++] = 'm'; buf
[n
++] = 'p';
500 buf
[n
++] = '&'; buf
[n
++] = 'l'; buf
[n
++] = 't'; buf
[n
++] = ';';
503 buf
[n
++] = '&'; buf
[n
++] = 'g'; buf
[n
++] = 't'; buf
[n
++] = ';';
506 buf
[n
++] = '&'; buf
[n
++] = 'q'; buf
[n
++] = 'u'; buf
[n
++] = 'o';
507 buf
[n
++] = 't'; buf
[n
++] = ';';
520 htmlPrint(FILE *out
, char *s
, int slen
)
523 for(i
= 0; i
< slen
; i
++) {
543 HTTPConnectionPtr connection
;
544 connection
= malloc(sizeof(HTTPConnectionRec
));
545 if(connection
== NULL
)
547 connection
->flags
= 0;
549 connection
->buf
= NULL
;
551 connection
->offset
= 0;
552 connection
->request
= NULL
;
553 connection
->request_last
= NULL
;
554 connection
->serviced
= 0;
555 connection
->version
= HTTP_UNKNOWN
;
556 connection
->timeout
= NULL
;
557 connection
->te
= TE_IDENTITY
;
558 connection
->reqbuf
= NULL
;
559 connection
->reqlen
= 0;
560 connection
->reqbegin
= 0;
561 connection
->reqoffset
= 0;
562 connection
->bodylen
= -1;
563 connection
->reqte
= TE_IDENTITY
;
564 connection
->chunk_remaining
= 0;
565 connection
->server
= NULL
;
566 connection
->pipelined
= 0;
567 connection
->connecting
= 0;
568 connection
->server
= NULL
;
573 httpDestroyConnection(HTTPConnectionPtr connection
)
575 assert(connection
->flags
== 0);
576 httpConnectionDestroyBuf(connection
);
577 assert(!connection
->request
);
578 assert(!connection
->request_last
);
579 httpConnectionDestroyReqbuf(connection
);
580 assert(!connection
->timeout
);
581 assert(!connection
->server
);
586 httpConnectionDestroyBuf(HTTPConnectionPtr connection
)
588 if(connection
->buf
) {
589 if(connection
->flags
& CONN_BIGBUF
)
590 free(connection
->buf
);
592 dispose_chunk(connection
->buf
);
594 connection
->flags
&= ~CONN_BIGBUF
;
595 connection
->buf
= NULL
;
599 httpConnectionDestroyReqbuf(HTTPConnectionPtr connection
)
601 if(connection
->reqbuf
) {
602 if(connection
->flags
& CONN_BIGREQBUF
)
603 free(connection
->reqbuf
);
605 dispose_chunk(connection
->reqbuf
);
607 connection
->flags
&= ~CONN_BIGREQBUF
;
608 connection
->reqbuf
= NULL
;
614 HTTPRequestPtr request
;
615 request
= malloc(sizeof(HTTPRequestRec
));
619 request
->connection
= NULL
;
620 request
->object
= NULL
;
621 request
->method
= METHOD_UNKNOWN
;
624 request
->cache_control
= no_cache_control
;
625 request
->condition
= NULL
;
627 request
->chandler
= NULL
;
628 request
->can_mutate
= NULL
;
629 request
->error_code
= 0;
630 request
->error_message
= NULL
;
631 request
->error_headers
= NULL
;
632 request
->headers
= NULL
;
633 request
->time0
= null_time
;
634 request
->time1
= null_time
;
635 request
->request
= NULL
;
636 request
->next
= NULL
;
641 httpDestroyRequest(HTTPRequestPtr request
)
644 releaseObject(request
->object
);
645 if(request
->condition
)
646 httpDestroyCondition(request
->condition
);
647 releaseAtom(request
->via
);
648 assert(request
->chandler
== NULL
);
649 releaseAtom(request
->error_message
);
650 releaseAtom(request
->headers
);
651 releaseAtom(request
->error_headers
);
652 assert(request
->request
== NULL
);
653 assert(request
->next
== NULL
);
658 httpQueueRequest(HTTPConnectionPtr connection
, HTTPRequestPtr request
)
660 assert(request
->next
== NULL
&& request
->connection
== NULL
);
661 request
->connection
= connection
;
662 if(connection
->request_last
) {
663 assert(connection
->request
);
664 connection
->request_last
->next
= request
;
665 connection
->request_last
= request
;
667 assert(!connection
->request_last
);
668 connection
->request
= request
;
669 connection
->request_last
= request
;
674 httpDequeueRequest(HTTPConnectionPtr connection
)
676 HTTPRequestPtr request
= connection
->request
;
678 assert(connection
->request_last
);
679 connection
->request
= request
->next
;
680 if(!connection
->request
) connection
->request_last
= NULL
;
681 request
->next
= NULL
;
687 httpConnectionBigify(HTTPConnectionPtr connection
)
690 assert(!(connection
->flags
& CONN_BIGBUF
));
692 if(bigBufferSize
<= CHUNK_SIZE
)
695 bigbuf
= malloc(bigBufferSize
);
698 if(connection
->len
> 0)
699 memcpy(bigbuf
, connection
->buf
, connection
->len
);
701 dispose_chunk(connection
->buf
);
702 connection
->buf
= bigbuf
;
703 connection
->flags
|= CONN_BIGBUF
;
708 httpConnectionBigifyReqbuf(HTTPConnectionPtr connection
)
711 assert(!(connection
->flags
& CONN_BIGREQBUF
));
713 if(bigBufferSize
<= CHUNK_SIZE
)
716 bigbuf
= malloc(bigBufferSize
);
719 if(connection
->reqlen
> 0)
720 memcpy(bigbuf
, connection
->reqbuf
, connection
->reqlen
);
721 if(connection
->reqbuf
)
722 dispose_chunk(connection
->reqbuf
);
723 connection
->reqbuf
= bigbuf
;
724 connection
->flags
|= CONN_BIGREQBUF
;
729 httpConnectionUnbigify(HTTPConnectionPtr connection
)
732 assert(connection
->flags
& CONN_BIGBUF
);
733 assert(connection
->len
< CHUNK_SIZE
);
738 if(connection
->len
> 0)
739 memcpy(buf
, connection
->buf
, connection
->len
);
740 free(connection
->buf
);
741 connection
->buf
= buf
;
742 connection
->flags
&= ~CONN_BIGBUF
;
747 httpConnectionUnbigifyReqbuf(HTTPConnectionPtr connection
)
750 assert(connection
->flags
& CONN_BIGREQBUF
);
751 assert(connection
->reqlen
< CHUNK_SIZE
);
756 if(connection
->reqlen
> 0)
757 memcpy(buf
, connection
->reqbuf
, connection
->reqlen
);
758 free(connection
->reqbuf
);
759 connection
->reqbuf
= buf
;
760 connection
->flags
&= ~CONN_BIGREQBUF
;
767 HTTPConditionPtr condition
;
768 condition
= malloc(sizeof(HTTPConditionRec
));
769 if(condition
== NULL
)
772 condition
->inms
= -1;
773 condition
->im
= NULL
;
774 condition
->inm
= NULL
;
775 condition
->ifrange
= NULL
;
780 httpDestroyCondition(HTTPConditionPtr condition
)
783 free(condition
->inm
);
786 if(condition
->ifrange
)
787 free(condition
->ifrange
);
792 httpCondition(ObjectPtr object
, HTTPConditionPtr condition
)
794 int rc
= CONDITION_MATCH
;
796 assert(!(object
->flags
& OBJECT_INITIAL
));
798 if(!condition
) return CONDITION_MATCH
;
800 if(condition
->ims
>= 0) {
801 if(object
->last_modified
< 0 ||
802 condition
->ims
< object
->last_modified
)
805 rc
= CONDITION_NOT_MODIFIED
;
808 if(condition
->inms
>= 0) {
809 if(object
->last_modified
< 0 ||
810 condition
->inms
>= object
->last_modified
)
813 rc
= CONDITION_FAILED
;
817 if(!object
->etag
|| strcmp(object
->etag
, condition
->inm
) != 0)
820 rc
= CONDITION_NOT_MODIFIED
;
824 if(!object
->etag
|| strcmp(object
->etag
, condition
->im
) != 0)
825 rc
= CONDITION_FAILED
;
834 httpWriteErrorHeaders(char *buf
, int size
, int offset
, int do_body
,
835 int code
, AtomPtr message
, int close
, AtomPtr headers
,
836 char *url
, int url_len
, char *etag
)
840 char htmlMessage
[100];
845 i
= htmlString(htmlMessage
, 0, 100, message
->string
, message
->length
);
847 strcpy(htmlMessage
, "(Couldn't format message)");
849 htmlMessage
[MIN(i
, 99)] = '\0';
854 do_log(L_ERROR
, "Couldn't allocate body buffer.\n");
857 m
= snnprintf(body
, 0, CHUNK_SIZE
,
858 "<!DOCTYPE HTML PUBLIC "
859 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
860 "\"http://www.w3.org/TR/html4/loose.dtd\">"
862 "\n<title>Proxy %s: %3d %s.</title>"
865 "\n<p>The following %s",
866 code
>= 400 ? "error" : "result",
871 "status was returned");
873 m
= snnprintf(body
, m
, CHUNK_SIZE
,
874 " while trying to access <strong>");
875 m
= htmlString(body
, m
, CHUNK_SIZE
, url
, url_len
);
876 m
= snnprintf(body
, m
, CHUNK_SIZE
, "</strong>");
880 /* On BSD systems, tv_sec is a long. */
881 const time_t ct
= current_time
.tv_sec
;
882 /*Mon, 24 Sep 2004 17:46:35 GMT*/
883 strftime(timeStr
, sizeof(timeStr
), "%a, %d %b %Y %H:%M:%S %Z",
887 m
= snnprintf(body
, m
, CHUNK_SIZE
,
889 "\n<strong>%3d %s</strong></p>"
890 "\n<hr>Generated %s by Polipo on <em>%s:%d</em>."
891 "\n</body></html>\r\n",
893 timeStr
, proxyName
->string
, proxyPort
);
894 if(m
<= 0 || m
>= CHUNK_SIZE
) {
895 do_log(L_ERROR
, "Couldn't write error body.\n");
904 n
= snnprintf(buf
, 0, size
,
908 code
, atomString(message
),
909 close
? "close" : "keep-alive");
910 n
= format_time(buf
, n
, size
, current_time
.tv_sec
);
912 n
= snnprintf(buf
, n
, size
,
913 "\r\nContent-Type: text/html"
914 "\r\nContent-Length: %d", m
);
917 n
= snnprintf(buf
, n
, size
, "\r\nETag: \"%s\"", etag
);
920 if(code
!= 304 && code
!= 412) {
921 n
= snnprintf(buf
, n
, size
,
923 "\r\nCache-Control: no-cache"
924 "\r\nPragma: no-cache");
928 n
= snnprint_n(buf
, n
, size
,
929 headers
->string
, headers
->length
);
931 n
= snnprintf(buf
, n
, size
, "\r\n\r\n");
933 if(n
< 0 || n
>= size
) {
934 do_log(L_ERROR
, "Couldn't write error.\n");
939 if(code
!= 304 && do_body
) {
940 if(m
> 0) memcpy(buf
+ n
, body
, m
);
951 urlDecode(char *buf
, int n
)
958 list
= makeAtomList(NULL
, 0);
972 mybuf
[j
++] = (char)((a
<< 4) | b
);
974 if(j
> 500) goto fail
;
975 } else if(buf
[i
] == '&') {
976 atom
= internAtomN(mybuf
, j
);
979 atomListCons(atom
, list
);
983 mybuf
[j
++] = buf
[i
++];
984 if(j
> 500) goto fail
;
988 atom
= internAtomN(mybuf
, j
);
991 atomListCons(atom
, list
);
995 destroyAtomList(list
);
1000 httpTweakCachability(ObjectPtr object
)
1002 int code
= object
->code
;
1004 if((object
->cache_control
& CACHE_AUTHORIZATION
) &&
1005 !(object
->cache_control
& CACHE_PUBLIC
))
1006 object
->cache_control
|= (CACHE_NO_HIDDEN
| OBJECT_LINEAR
);
1008 /* This is not required by RFC 2616 -- but see RFC 3143 2.1.1. We
1009 manically avoid caching replies that we don't know how to
1010 handle, even if Expires or Cache-Control says otherwise. As to
1011 known uncacheable replies, we obey Cache-Control and default to
1012 allowing sharing but not caching. */
1013 if(code
!= 200 && code
!= 206 &&
1014 code
!= 300 && code
!= 301 && code
!= 302 && code
!= 303 &&
1015 code
!= 304 && code
!= 307 &&
1016 code
!= 403 && code
!= 404 && code
!= 405 && code
!= 416) {
1017 object
->cache_control
|=
1018 (CACHE_NO_HIDDEN
| CACHE_MISMATCH
| OBJECT_LINEAR
);
1019 } else if(code
!= 200 && code
!= 206 &&
1020 code
!= 300 && code
!= 301 && code
!= 304 &&
1022 if(object
->expires
< 0 && !(object
->cache_control
& CACHE_PUBLIC
)) {
1023 object
->cache_control
|= CACHE_NO_HIDDEN
;
1025 } else if(dontCacheRedirects
&& (code
== 301 || code
== 302)) {
1026 object
->cache_control
|= CACHE_NO_HIDDEN
;
1029 if(urlIsUncachable(object
->key
, object
->key_size
)) {
1030 object
->cache_control
|= CACHE_NO_HIDDEN
;
1033 if((object
->cache_control
& CACHE_NO_STORE
) != 0) {
1034 object
->cache_control
|= CACHE_NO_HIDDEN
;
1037 if(object
->cache_control
& CACHE_VARY
) {
1038 if(!object
->etag
|| dontTrustVaryETag
>= 2) {
1039 object
->cache_control
|= CACHE_MISMATCH
;
1045 httpHeaderMatch(AtomPtr header
, AtomPtr headers1
, AtomPtr headers2
)
1047 int rc1
, b1
, e1
, rc2
, b2
, e2
;
1049 /* Short cut if both sets of headers are identical */
1050 if(headers1
== headers2
)
1053 rc1
= httpFindHeader(header
, headers1
->string
, headers1
->length
,
1055 rc2
= httpFindHeader(header
, headers2
->string
, headers2
->length
,
1058 if(rc1
== 0 && rc2
== 0)
1061 if(rc1
== 0 || rc2
== 0)
1064 if(e1
- b1
!= e2
- b2
)
1067 if(memcmp(headers1
->string
+ b1
, headers2
->string
+ b2
, e1
- b1
) != 0)