Updated CHANGES for 1.0.2.
[polipo.git] / http.c
blobcd30b7967d3198bc6d1b807c5b304debd585c7c4
1 /*
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
20 THE SOFTWARE.
23 #include "polipo.h"
25 int disableProxy = 0;
26 AtomPtr proxyName = NULL;
27 int proxyPort = 8123;
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;
50 int disableVia = 1;
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. */
59 int proxyOffline = 0;
60 int relaxTransparency = 0;
61 AtomPtr proxyAddress = NULL;
63 static int timeoutSetter(ConfigVariablePtr var, void *value);
65 void
66 preinitHttp()
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,
74 configIntSetter,
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.");
108 preinitHttpParser();
111 static int
112 timeoutSetter(ConfigVariablePtr var, void *value)
114 configIntSetter(var, value);
115 if(clientTimeout <= serverTimeout)
116 clientTimeout = serverTimeout + 1;
117 return 1;
120 void
121 initHttp()
123 char *buf = NULL;
124 int namelen;
125 int n;
126 struct hostent *host;
128 initHttpParser();
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",
135 clientTimeout);
138 if(authCredentials != NULL && authRealm == NULL)
139 authRealm = internAtom("Polipo");
141 if(allowedClients) {
142 allowedNets = parseNetAddress(allowedClients);
143 if(allowedNets == NULL)
144 exit(1);
147 if(allowedPorts == NULL) {
148 allowedPorts = makeIntList(0);
149 if(allowedPorts == NULL) {
150 do_log(L_ERROR, "Couldn't allocate allowedPorts.\n");
151 exit(1);
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");
161 exit(1);
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 */
173 if(proxyName)
174 return;
176 buf = get_chunk();
177 if(buf == NULL) {
178 do_log(L_ERROR, "Couldn't allocate chunk for host name.\n");
179 goto fail;
182 n = gethostname(buf, CHUNK_SIZE);
183 if(n != 0) {
184 do_log_error(L_WARN, errno, "Gethostname");
185 strcpy(buf, "polipo");
186 goto success;
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");
196 goto success;
199 if(strchr(buf, '.') != NULL)
200 goto success;
202 host = gethostbyname(buf);
203 if(host == NULL) {
204 goto success;
207 if(host->h_addrtype != AF_INET)
208 goto success;
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)
214 goto success;
216 namelen = strlen(host->h_name);
217 if(namelen >= CHUNK_SIZE) {
218 do_log(L_ERROR, "Host name too long.\n");
219 goto success;
222 memcpy(buf, host->h_name, namelen + 1);
224 success:
225 proxyName = internAtom(buf);
226 if(proxyName == NULL) {
227 do_log(L_ERROR, "Couldn't allocate proxy name.\n");
228 goto fail;
230 dispose_chunk(buf);
231 return;
233 fail:
234 if(buf)
235 dispose_chunk(buf);
236 exit(1);
237 return;
241 httpSetTimeout(HTTPConnectionPtr connection, int secs)
243 TimeEventHandlerPtr new;
245 if(connection->timeout)
246 cancelTimeEvent(connection->timeout);
247 connection->timeout = NULL;
249 if(secs > 0) {
250 new = scheduleTimeEvent(secs, httpTimeoutHandler,
251 sizeof(connection), &connection);
252 if(!new) {
253 do_log(L_ERROR, "Couldn't schedule timeout for connection 0x%lx\n",
254 (unsigned long)connection);
255 return -1;
257 } else {
258 new = NULL;
261 connection->timeout = new;
262 return 1;
265 int
266 httpTimeoutHandler(TimeEventHandlerPtr event)
268 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
270 if(connection->fd >= 0) {
271 int rc;
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;
278 return 1;
282 httpWriteObjectHeaders(char *buf, int offset, int len,
283 ObjectPtr object, int from, int to)
285 int n = offset;
287 if(from <= 0 && to < 0) {
288 if(object->length >= 0) {
289 n = snnprintf(buf, n, len,
290 "\r\nContent-Length: %d", object->length);
292 } else {
293 if(to >= 0) {
294 n = snnprintf(buf, n, len,
295 "\r\nContent-Length: %d", to - from);
299 if(from > 0 || to > 0) {
300 if(object->length >= 0) {
301 if(from >= to) {
302 n = snnprintf(buf, n, len,
303 "\r\nContent-Range: bytes */%d",
304 object->length);
305 } else {
306 n = snnprintf(buf, n, len,
307 "\r\nContent-Range: bytes %d-%d/%d",
308 from, to - 1,
309 object->length);
311 } else {
312 if(to >= 0) {
313 n = snnprintf(buf, n, len,
314 "\r\nContent-Range: bytes %d-/*",
315 from);
316 } else {
317 n = snnprintf(buf, n, len,
318 "\r\nContent-Range: bytes %d-%d/*",
319 from, to);
324 if(object->etag) {
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);
332 if(n < 0)
333 goto fail;
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);
339 if(n < 0)
340 goto fail;
343 if(object->expires >= 0) {
344 n = snnprintf(buf, n, len, "\r\nExpires: ");
345 n = format_time(buf, n, len, object->expires);
346 if(n < 0)
347 goto fail;
350 n = httpPrintCacheControl(buf, n, len,
351 object->cache_control, NULL);
352 if(n < 0)
353 goto fail;
355 if(!disableVia && object->via)
356 n = snnprintf(buf, n, len, "\r\nVia: %s", object->via->string);
358 if(object->headers)
359 n = snnprint_n(buf, n, len, object->headers->string,
360 object->headers->length);
362 if(n < len)
363 return n;
364 else
365 return -1;
367 fail:
368 return -1;
371 static int
372 cachePrintSeparator(char *buf, int offset, int len,
373 int subsequent)
375 int n = offset;
376 if(subsequent)
377 n = snnprintf(buf, offset, len, ", ");
378 else
379 n = snnprintf(buf, offset, len, "\r\nCache-Control: ");
380 return n;
384 httpPrintCacheControl(char *buf, int offset, int len,
385 int flags, CacheControlPtr cache_control)
387 int n = offset;
388 int sub = 0;
390 #define PRINT_SEP() \
391 do {\
392 n = cachePrintSeparator(buf, n, len, sub); \
393 sub = 1; \
394 } while(0)
396 if(cache_control)
397 flags |= cache_control->flags;
399 if(flags & CACHE_NO) {
400 PRINT_SEP();
401 n = snnprintf(buf, n, len, "no-cache");
403 if(flags & CACHE_PUBLIC) {
404 PRINT_SEP();
405 n = snnprintf(buf, n, len, "public");
407 if(flags & CACHE_PRIVATE) {
408 PRINT_SEP();
409 n = snnprintf(buf, n, len, "private");
411 if(flags & CACHE_NO_STORE) {
412 PRINT_SEP();
413 n = snnprintf(buf, n, len, "no-store");
415 if(flags & CACHE_NO_TRANSFORM) {
416 PRINT_SEP();
417 n = snnprintf(buf, n, len, "no-transform");
419 if(flags & CACHE_MUST_REVALIDATE) {
420 PRINT_SEP();
421 n = snnprintf(buf, n, len, "must-revalidate");
423 if(flags & CACHE_PROXY_REVALIDATE) {
424 PRINT_SEP();
425 n = snnprintf(buf, n, len, "proxy-revalidate");
427 if(flags & CACHE_ONLY_IF_CACHED) {
428 PRINT_SEP();
429 n = snnprintf(buf, n, len, "only-if-cached");
431 if(cache_control) {
432 if(cache_control->max_age >= 0) {
433 PRINT_SEP();
434 n = snnprintf(buf, n, len, "max-age=%d",
435 cache_control->max_age);
437 if(cache_control->s_maxage >= 0) {
438 PRINT_SEP();
439 n = snnprintf(buf, n, len, "s-maxage=%d",
440 cache_control->s_maxage);
442 if(cache_control->min_fresh > 0) {
443 PRINT_SEP();
444 n = snnprintf(buf, n, len, "min-fresh=%d",
445 cache_control->min_fresh);
447 if(cache_control->max_stale > 0) {
448 PRINT_SEP();
449 n = snnprintf(buf, n, len, "max-stale=%d",
450 cache_control->min_fresh);
453 return n;
454 #undef PRINT_SEP
457 char *
458 httpMessage(int code)
460 switch(code) {
461 case 200:
462 return "Okay";
463 case 206:
464 return "Partial content";
465 case 300:
466 return "Multiple choices";
467 case 301:
468 return "Moved permanently";
469 case 302:
470 return "Found";
471 case 303:
472 return "See other";
473 case 304:
474 return "Not changed";
475 case 307:
476 return "Temporary redirect";
477 case 401:
478 return "Authentication Required";
479 case 403:
480 return "Forbidden";
481 case 404:
482 return "Not found";
483 case 405:
484 return "Method not allowed";
485 case 407:
486 return "Proxy authentication required";
487 default:
488 return "Unknown error code";
493 htmlString(char *buf, int n, int len, char *s, int slen)
495 int i = 0;
496 while(i < slen && n + 5 < len) {
497 switch(s[i]) {
498 case '&':
499 buf[n++] = '&'; buf[n++] = 'a'; buf[n++] = 'm'; buf[n++] = 'p';
500 buf[n++] = ';';
501 break;
502 case '<':
503 buf[n++] = '&'; buf[n++] = 'l'; buf[n++] = 't'; buf[n++] = ';';
504 break;
505 case '>':
506 buf[n++] = '&'; buf[n++] = 'g'; buf[n++] = 't'; buf[n++] = ';';
507 break;
508 case '"':
509 buf[n++] = '&'; buf[n++] = 'q'; buf[n++] = 'u'; buf[n++] = 'o';
510 buf[n++] = 't'; buf[n++] = ';';
511 break;
512 case '\0':
513 break;
514 default:
515 buf[n++] = s[i];
517 i++;
519 return n;
522 void
523 htmlPrint(FILE *out, char *s, int slen)
525 int i;
526 for(i = 0; i < slen; i++) {
527 switch(s[i]) {
528 case '&':
529 fputs("&amp;", out);
530 break;
531 case '<':
532 fputs("&lt;", out);
533 break;
534 case '>':
535 fputs("&gt;", out);
536 break;
537 default:
538 fputc(s[i], out);
543 HTTPConnectionPtr
544 httpMakeConnection()
546 HTTPConnectionPtr connection;
547 connection = malloc(sizeof(HTTPConnectionRec));
548 if(connection == NULL)
549 return NULL;
550 connection->flags = 0;
551 connection->fd = -1;
552 connection->buf = NULL;
553 connection->len = 0;
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;
573 return connection;
576 void
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);
586 free(connection);
589 void
590 httpConnectionDestroyBuf(HTTPConnectionPtr connection)
592 if(connection->buf) {
593 if(connection->flags & CONN_BIGBUF)
594 free(connection->buf);
595 else
596 dispose_chunk(connection->buf);
598 connection->flags &= ~CONN_BIGBUF;
599 connection->buf = NULL;
602 void
603 httpConnectionDestroyReqbuf(HTTPConnectionPtr connection)
605 if(connection->reqbuf) {
606 if(connection->flags & CONN_BIGREQBUF)
607 free(connection->reqbuf);
608 else
609 dispose_chunk(connection->reqbuf);
611 connection->flags &= ~CONN_BIGREQBUF;
612 connection->reqbuf = NULL;
615 HTTPRequestPtr
616 httpMakeRequest()
618 HTTPRequestPtr request;
619 request = malloc(sizeof(HTTPRequestRec));
620 if(request == NULL)
621 return NULL;
622 request->flags = 0;
623 request->connection = NULL;
624 request->object = NULL;
625 request->method = METHOD_UNKNOWN;
626 request->from = 0;
627 request->to = -1;
628 request->cache_control = no_cache_control;
629 request->condition = NULL;
630 request->via = 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;
641 return request;
644 void
645 httpDestroyRequest(HTTPRequestPtr request)
647 if(request->object)
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);
658 free(request);
661 void
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;
670 } else {
671 assert(!connection->request_last);
672 connection->request = request;
673 connection->request_last = request;
677 HTTPRequestPtr
678 httpDequeueRequest(HTTPConnectionPtr connection)
680 HTTPRequestPtr request = connection->request;
681 if(request) {
682 assert(connection->request_last);
683 connection->request = request->next;
684 if(!connection->request) connection->request_last = NULL;
685 request->next = NULL;
687 return request;
691 httpConnectionBigify(HTTPConnectionPtr connection)
693 char *bigbuf;
694 assert(!(connection->flags & CONN_BIGBUF));
696 if(bigBufferSize <= CHUNK_SIZE)
697 return 0;
699 bigbuf = malloc(bigBufferSize);
700 if(bigbuf == NULL)
701 return -1;
702 if(connection->len > 0)
703 memcpy(bigbuf, connection->buf, connection->len);
704 if(connection->buf)
705 dispose_chunk(connection->buf);
706 connection->buf = bigbuf;
707 connection->flags |= CONN_BIGBUF;
708 return 1;
712 httpConnectionBigifyReqbuf(HTTPConnectionPtr connection)
714 char *bigbuf;
715 assert(!(connection->flags & CONN_BIGREQBUF));
717 if(bigBufferSize <= CHUNK_SIZE)
718 return 0;
720 bigbuf = malloc(bigBufferSize);
721 if(bigbuf == NULL)
722 return -1;
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;
729 return 1;
733 httpConnectionUnbigify(HTTPConnectionPtr connection)
735 char *buf;
736 assert(connection->flags & CONN_BIGBUF);
737 assert(connection->len < CHUNK_SIZE);
739 buf = get_chunk();
740 if(buf == NULL)
741 return -1;
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;
747 return 1;
751 httpConnectionUnbigifyReqbuf(HTTPConnectionPtr connection)
753 char *buf;
754 assert(connection->flags & CONN_BIGREQBUF);
755 assert(connection->reqlen < CHUNK_SIZE);
757 buf = get_chunk();
758 if(buf == NULL)
759 return -1;
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;
765 return 1;
768 HTTPConditionPtr
769 httpMakeCondition()
771 HTTPConditionPtr condition;
772 condition = malloc(sizeof(HTTPConditionRec));
773 if(condition == NULL)
774 return NULL;
775 condition->ims = -1;
776 condition->inms = -1;
777 condition->im = NULL;
778 condition->inm = NULL;
779 condition->ifrange = NULL;
780 return condition;
783 void
784 httpDestroyCondition(HTTPConditionPtr condition)
786 if(condition->inm)
787 free(condition->inm);
788 if(condition->im)
789 free(condition->im);
790 if(condition->ifrange)
791 free(condition->ifrange);
792 free(condition);
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)
807 return rc;
808 else
809 rc = CONDITION_NOT_MODIFIED;
812 if(condition->inms >= 0) {
813 if(object->last_modified < 0 ||
814 condition->inms >= object->last_modified)
815 return rc;
816 else
817 rc = CONDITION_FAILED;
820 if(condition->inm) {
821 if(!object->etag || strcmp(object->etag, condition->inm) != 0)
822 return rc;
823 else
824 rc = CONDITION_NOT_MODIFIED;
827 if(condition->im) {
828 if(!object->etag || strcmp(object->etag, condition->im) != 0)
829 rc = CONDITION_FAILED;
830 else
831 return rc;
834 return rc;
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)
842 int n, m, i;
843 char *body;
844 char htmlMessage[100];
845 char timeStr[100];
847 assert(code != 0);
849 i = htmlString(htmlMessage, 0, 100, message->string, message->length);
850 if(i < 0)
851 strcpy(htmlMessage, "(Couldn't format message)");
852 else
853 htmlMessage[MIN(i, 99)] = '\0';
855 if(code != 304) {
856 body = get_chunk();
857 if(!body) {
858 do_log(L_ERROR, "Couldn't allocate body buffer.\n");
859 return -1;
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\">"
865 "\n<html><head>"
866 "\n<title>Proxy %s: %3d %s.</title>"
867 "\n</head><body>"
868 "\n<h1>%3d %s</h1>"
869 "\n<p>The following %s",
870 code >= 400 ? "error" : "result",
871 code, htmlMessage,
872 code, htmlMessage,
873 code >= 400 ?
874 "error occurred" :
875 "status was returned");
876 if(url_len > 0) {
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",
888 localtime(&ct));
891 m = snnprintf(body, m, CHUNK_SIZE,
892 ":<br><br>"
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",
896 code, htmlMessage,
897 timeStr, proxyName->string, proxyPort);
898 if(m <= 0 || m >= CHUNK_SIZE) {
899 do_log(L_ERROR, "Couldn't write error body.\n");
900 dispose_chunk(body);
901 return -1;
903 } else {
904 body = NULL;
905 m = 0;
908 n = snnprintf(buf, 0, size,
909 "HTTP/1.1 %3d %s"
910 "\r\nConnection: %s"
911 "\r\nDate: ",
912 code, atomString(message),
913 close ? "close" : "keep-alive");
914 n = format_time(buf, n, size, current_time.tv_sec);
915 if(code != 304) {
916 n = snnprintf(buf, n, size,
917 "\r\nContent-Type: text/html"
918 "\r\nContent-Length: %d", m);
919 } else {
920 if(etag)
921 n = snnprintf(buf, n, size, "\r\nETag: \"%s\"", etag);
924 if(code != 304 && code != 412) {
925 n = snnprintf(buf, n, size,
926 "\r\nExpires: 0"
927 "\r\nCache-Control: no-cache"
928 "\r\nPragma: no-cache");
931 if(headers)
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");
939 dispose_chunk(body);
940 return -1;
943 if(code != 304 && do_body) {
944 if(m > 0) memcpy(buf + n, body, m);
945 n += m;
948 if(body)
949 dispose_chunk(body);
951 return n;
954 AtomListPtr
955 urlDecode(char *buf, int n)
957 char mybuf[500];
958 int i, j = 0;
959 AtomListPtr list;
960 AtomPtr atom;
962 list = makeAtomList(NULL, 0);
963 if(list == NULL)
964 return NULL;
966 i = 0;
967 while(i < n) {
968 if(buf[i] == '%') {
969 int a, b;
970 if(i + 3 > n)
971 goto fail;
972 a = h2i(buf[i + 1]);
973 b = h2i(buf[i + 2]);
974 if(a < 0 || b < 0)
975 goto fail;
976 mybuf[j++] = (char)((a << 4) | b);
977 i += 3;
978 if(j > 500) goto fail;
979 } else if(buf[i] == '&') {
980 atom = internAtomN(mybuf, j);
981 if(atom == NULL)
982 goto fail;
983 atomListCons(atom, list);
984 j = 0;
985 i++;
986 } else {
987 mybuf[j++] = buf[i++];
988 if(j > 500) goto fail;
992 atom = internAtomN(mybuf, j);
993 if(atom == NULL)
994 goto fail;
995 atomListCons(atom, list);
996 return list;
998 fail:
999 destroyAtomList(list);
1000 return NULL;
1003 void
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 &&
1025 code != 410) {
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)
1055 return 1;
1057 rc1 = httpFindHeader(header, headers1->string, headers1->length,
1058 &b1, &e1);
1059 rc2 = httpFindHeader(header, headers2->string, headers2->length,
1060 &b2, &e2);
1062 if(rc1 == 0 && rc2 == 0)
1063 return 1;
1065 if(rc1 == 0 || rc2 == 0)
1066 return 0;
1068 if(e1 - b1 != e2 - b2)
1069 return 0;
1071 if(memcmp(headers1->string + b1, headers2->string + b2, e1 - b1) != 0)
1072 return 0;
1074 return 1;