]> git.ipfire.org Git - thirdparty/squid.git/blob - src/http.cc
Henrik thought DW's code was unreadable.
[thirdparty/squid.git] / src / http.cc
1
2 /*
3 * $Id: http.cc,v 1.266 1998/04/27 19:52:48 wessels Exp $
4 *
5 * DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
31
32 /*
33 * Copyright (c) 1994, 1995. All rights reserved.
34 *
35 * The Harvest software was developed by the Internet Research Task
36 * Force Research Group on Resource Discovery (IRTF-RD):
37 *
38 * Mic Bowman of Transarc Corporation.
39 * Peter Danzig of the University of Southern California.
40 * Darren R. Hardy of the University of Colorado at Boulder.
41 * Udi Manber of the University of Arizona.
42 * Michael F. Schwartz of the University of Colorado at Boulder.
43 * Duane Wessels of the University of Colorado at Boulder.
44 *
45 * This copyright notice applies to software in the Harvest
46 * ``src/'' directory only. Users should consult the individual
47 * copyright notices in the ``components/'' subdirectories for
48 * copyright information about other software bundled with the
49 * Harvest source code distribution.
50 *
51 * TERMS OF USE
52 *
53 * The Harvest software may be used and re-distributed without
54 * charge, provided that the software origin and research team are
55 * cited in any use of the system. Most commonly this is
56 * accomplished by including a link to the Harvest Home Page
57 * (http://harvest.cs.colorado.edu/) from the query page of any
58 * Broker you deploy, as well as in the query result pages. These
59 * links are generated automatically by the standard Broker
60 * software distribution.
61 *
62 * The Harvest software is provided ``as is'', without express or
63 * implied warranty, and with no support nor obligation to assist
64 * in its use, correction, modification or enhancement. We assume
65 * no liability with respect to the infringement of copyrights,
66 * trade secrets, or any patents, and are not responsible for
67 * consequential damages. Proper use of the Harvest software is
68 * entirely the responsibility of the user.
69 *
70 * DERIVATIVE WORKS
71 *
72 * Users may make derivative works from the Harvest software, subject
73 * to the following constraints:
74 *
75 * - You must include the above copyright notice and these
76 * accompanying paragraphs in all forms of derivative works,
77 * and any documentation and other materials related to such
78 * distribution and use acknowledge that the software was
79 * developed at the above institutions.
80 *
81 * - You must notify IRTF-RD regarding your distribution of
82 * the derivative work.
83 *
84 * - You must clearly notify users that your are distributing
85 * a modified version and not the original Harvest software.
86 *
87 * - Any derivative product is also subject to these copyright
88 * and use restrictions.
89 *
90 * Note that the Harvest software is NOT in the public domain. We
91 * retain copyright, as specified above.
92 *
93 * HISTORY OF FREE SOFTWARE STATUS
94 *
95 * Originally we required sites to license the software in cases
96 * where they were going to build commercial products/services
97 * around Harvest. In June 1995 we changed this policy. We now
98 * allow people to use the core Harvest software (the code found in
99 * the Harvest ``src/'' directory) for free. We made this change
100 * in the interest of encouraging the widest possible deployment of
101 * the technology. The Harvest software is really a reference
102 * implementation of a set of protocols and formats, some of which
103 * we intend to standardize. We encourage commercial
104 * re-implementations of code complying to this set of standards.
105 */
106
107 /*
108 * Anonymizing patch by lutz@as-node.jena.thur.de
109 * have a look into http-anon.c to get more informations.
110 */
111
112 #include "squid.h"
113
114 static const char *const crlf = "\r\n";
115
116 enum {
117 CCC_NOCACHE,
118 CCC_NOSTORE,
119 CCC_MAXAGE,
120 CCC_MAXSTALE,
121 CCC_MINFRESH,
122 CCC_ONLYIFCACHED,
123 CCC_ENUM_END
124 };
125
126 static CNCB httpConnectDone;
127 static CWCB httpSendComplete;
128 static CWCB httpSendRequestEntry;
129
130 static PF httpReadReply;
131 static PF httpSendRequest;
132 static PF httpStateFree;
133 static PF httpTimeout;
134 static void httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int);
135 static void httpCacheNegatively(StoreEntry *);
136 static void httpMakePrivate(StoreEntry *);
137 static void httpMakePublic(StoreEntry *);
138 static STABH httpAbort;
139 static HttpStateData *httpBuildState(int, StoreEntry *, request_t *, peer *);
140 static int httpSocketOpen(StoreEntry *, request_t *);
141 static void httpRestart(HttpStateData *);
142 static int httpTryRestart(HttpStateData *);
143 static int httpCachableReply(HttpStateData *);
144
145 static void
146 httpStateFree(int fdnotused, void *data)
147 {
148 HttpStateData *httpState = data;
149 if (httpState == NULL)
150 return;
151 storeUnregisterAbort(httpState->entry);
152 assert(httpState->entry->store_status != STORE_PENDING);
153 storeUnlockObject(httpState->entry);
154 if (httpState->reply_hdr) {
155 memFree(MEM_8K_BUF, httpState->reply_hdr);
156 httpState->reply_hdr = NULL;
157 }
158 requestUnlink(httpState->request);
159 requestUnlink(httpState->orig_request);
160 httpState->request = NULL;
161 httpState->orig_request = NULL;
162 cbdataFree(httpState);
163 }
164
165 int
166 httpCachable(method_t method)
167 {
168 /* GET and HEAD are cachable. Others are not. */
169 if (method != METHOD_GET && method != METHOD_HEAD)
170 return 0;
171 /* else cachable */
172 return 1;
173 }
174
175 static void
176 httpTimeout(int fd, void *data)
177 {
178 HttpStateData *httpState = data;
179 StoreEntry *entry = httpState->entry;
180 ErrorState *err;
181 debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
182 assert(entry->store_status == STORE_PENDING);
183 if (entry->mem_obj->inmem_hi == 0) {
184 err = errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT);
185 err->request = requestLink(httpState->orig_request);
186 errorAppendEntry(entry, err);
187 } else {
188 storeAbort(entry, 0);
189 }
190 comm_close(fd);
191 }
192
193 /* This object can be cached for a long time */
194 static void
195 httpMakePublic(StoreEntry * entry)
196 {
197 if (EBIT_TEST(entry->flag, ENTRY_CACHABLE))
198 storeSetPublicKey(entry);
199 }
200
201 /* This object should never be cached at all */
202 static void
203 httpMakePrivate(StoreEntry * entry)
204 {
205 storeExpireNow(entry);
206 EBIT_CLR(entry->flag, ENTRY_CACHABLE);
207 storeReleaseRequest(entry); /* delete object when not used */
208 }
209
210 /* This object may be negatively cached */
211 static void
212 httpCacheNegatively(StoreEntry * entry)
213 {
214 storeNegativeCache(entry);
215 if (EBIT_TEST(entry->flag, ENTRY_CACHABLE))
216 storeSetPublicKey(entry);
217 }
218
219 static int
220 httpCachableReply(HttpStateData * httpState)
221 {
222 HttpReply *rep = httpState->entry->mem_obj->reply;
223 HttpHeader *hdr = &rep->header;
224 const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0;
225 if (EBIT_TEST(cc_mask, CC_PRIVATE))
226 return 0;
227 if (EBIT_TEST(cc_mask, CC_NO_CACHE))
228 return 0;
229 if (EBIT_TEST(httpState->request->flags, REQ_AUTH))
230 if (!EBIT_TEST(cc_mask, CC_PROXY_REVALIDATE))
231 return 0;
232 /*
233 * Dealing with cookies is quite a bit more complicated
234 * than this. Ideally we should strip the cookie
235 * header from the reply but still cache the reply body.
236 * More confusion at draft-ietf-http-state-mgmt-05.txt.
237 */
238 /* With new headers the above stripping should be easy to do? @?@ */
239 if (httpHeaderHas(hdr, HDR_SET_COOKIE))
240 return 0;
241 switch (httpState->entry->mem_obj->reply->sline.status) {
242 /* Responses that are cacheable */
243 case 200: /* OK */
244 case 203: /* Non-Authoritative Information */
245 case 300: /* Multiple Choices */
246 case 301: /* Moved Permanently */
247 case 410: /* Gone */
248 /* don't cache objects from peers w/o LMT, Date, or Expires */
249 /* check that is it enough to check headers @?@ */
250 if (rep->date > -1)
251 return 1;
252 else if (rep->last_modified > -1)
253 return 1;
254 else if (!httpState->peer)
255 return 1;
256 /* @?@ (here and 302): invalid expires header compiles to squid_curtime */
257 else if (rep->expires > -1)
258 return 1;
259 else
260 return 0;
261 /* NOTREACHED */
262 break;
263 /* Responses that only are cacheable if the server says so */
264 case 302: /* Moved temporarily */
265 if (rep->expires > -1)
266 return 1;
267 else
268 return 0;
269 /* NOTREACHED */
270 break;
271 /* @?@ should we replace these magic numbers with http_status enums? */
272 /* Errors can be negatively cached */
273 case 204: /* No Content */
274 case 305: /* Use Proxy (proxy redirect) */
275 case 400: /* Bad Request */
276 case 403: /* Forbidden */
277 case 404: /* Not Found */
278 case 405: /* Method Now Allowed */
279 case 414: /* Request-URI Too Long */
280 case 500: /* Internal Server Error */
281 case 501: /* Not Implemented */
282 case 502: /* Bad Gateway */
283 case 503: /* Service Unavailable */
284 case 504: /* Gateway Timeout */
285 return -1;
286 /* NOTREACHED */
287 break;
288 /* Some responses can never be cached */
289 case 206: /* Partial Content -- Not yet supported */
290 case 303: /* See Other */
291 case 304: /* Not Modified */
292 case 401: /* Unauthorized */
293 case 407: /* Proxy Authentication Required */
294 case 600: /* Squid header parsing error */
295 default: /* Unknown status code */
296 return 0;
297 /* NOTREACHED */
298 break;
299 }
300 /* NOTREACHED */
301 }
302
303 /* rewrite this later using new interfaces @?@ */
304 void
305 httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
306 {
307 char *t = NULL;
308 StoreEntry *entry = httpState->entry;
309 int room;
310 int hdr_len;
311 HttpReply *reply = entry->mem_obj->reply;
312 debug(11, 3) ("httpProcessReplyHeader: key '%s'\n",
313 storeKeyText(entry->key));
314 if (httpState->reply_hdr == NULL)
315 httpState->reply_hdr = memAllocate(MEM_8K_BUF);
316 if (httpState->reply_hdr_state == 0) {
317 hdr_len = strlen(httpState->reply_hdr);
318 room = 8191 - hdr_len;
319 strncat(httpState->reply_hdr, buf, room < size ? room : size);
320 hdr_len += room < size ? room : size;
321 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
322 debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr);
323 httpState->reply_hdr_state += 2;
324 reply->sline.status = 555;
325 return;
326 }
327 t = httpState->reply_hdr + hdr_len;
328 /* headers can be incomplete only if object still arriving */
329 if (!httpState->eof) {
330 size_t k = headersEnd(httpState->reply_hdr, 8192);
331 if (0 == k)
332 return; /* headers not complete */
333 t = httpState->reply_hdr + k;
334 }
335 *t = '\0';
336 httpState->reply_hdr_state++;
337 }
338 if (httpState->reply_hdr_state == 1) {
339 const Ctx ctx = ctx_enter(entry->mem_obj->url);
340 httpState->reply_hdr_state++;
341 debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
342 httpState->reply_hdr);
343 /* Parse headers into reply structure */
344 /* Old code never parsed headers if headersEnd failed, was it intentional ? @?@ @?@ */
345 /* what happens if we fail to parse here? @?@ @?@ */
346 httpReplyParse(reply, httpState->reply_hdr); /* httpState->eof); */
347 storeTimestampsSet(entry);
348 /* Check if object is cacheable or not based on reply code */
349 debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status);
350 switch (httpCachableReply(httpState)) {
351 case 1:
352 httpMakePublic(entry);
353 break;
354 case 0:
355 httpMakePrivate(entry);
356 break;
357 case -1:
358 httpCacheNegatively(entry);
359 break;
360 default:
361 assert(0);
362 break;
363 }
364 if (reply->cache_control && EBIT_TEST(reply->cache_control->mask, CC_PROXY_REVALIDATE))
365 EBIT_SET(entry->flag, ENTRY_REVALIDATE);
366 if (EBIT_TEST(httpState->flags, HTTP_KEEPALIVE))
367 if (httpState->peer)
368 httpState->peer->stats.n_keepalives_sent++;
369 if (reply->proxy_keep_alive)
370 if (httpState->peer)
371 httpState->peer->stats.n_keepalives_recv++;
372 ctx_exit(ctx);
373 }
374 }
375
376 static int
377 httpPconnTransferDone(HttpStateData * httpState)
378 {
379 /* return 1 if we got the last of the data on a persistent connection */
380 MemObject *mem = httpState->entry->mem_obj;
381 HttpReply *reply = mem->reply;
382 debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd);
383 /*
384 * If we didn't send a Keepalive request header, then this
385 * can not be a persistent connection.
386 */
387 if (!EBIT_TEST(httpState->flags, HTTP_KEEPALIVE))
388 return 0;
389 debug(11, 5) ("httpPconnTransferDone: content_length=%d\n",
390 reply->content_length);
391 /*
392 * Deal with gross HTTP stuff
393 * - If we haven't seen the end of the reply headers, we can't
394 * be persistent.
395 * - For "200 OK" check the content-length in the next block.
396 * - For "204 No Content" (even with content-length) we're done.
397 * - For "304 Not Modified" (even with content-length) we're done.
398 * - 1XX replies never have a body; we're done.
399 * - For HEAD requests with content-length we're done.
400 * - For all other replies, check content length in next block.
401 */
402 if (httpState->reply_hdr_state < 2)
403 return 0;
404 else if (reply->sline.status == HTTP_OK)
405 (void) 0; /* common case, continue */
406 else if (reply->sline.status == HTTP_NO_CONTENT)
407 return 1;
408 else if (reply->sline.status == HTTP_NOT_MODIFIED)
409 return 1;
410 else if (reply->sline.status < HTTP_OK)
411 return 1;
412 else if (httpState->request->method == METHOD_HEAD)
413 return 1;
414 /*
415 * If there is no content-length, then we can't be
416 * persistent. If there is a content length, then we must
417 * wait until we've seen the end of the body.
418 */
419 if (reply->content_length < 0)
420 return 0;
421 else if (mem->inmem_hi < reply->content_length + reply->hdr_sz)
422 return 0;
423 else
424 return 1;
425 }
426
427 /* This will be called when data is ready to be read from fd. Read until
428 * error or connection closed. */
429 /* XXX this function is too long! */
430 static void
431 httpReadReply(int fd, void *data)
432 {
433 HttpStateData *httpState = data;
434 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
435 StoreEntry *entry = httpState->entry;
436 const request_t *request = httpState->request;
437 int len;
438 int bin;
439 int clen;
440 ErrorState *err;
441 if (protoAbortFetch(entry)) {
442 storeAbort(entry, 0);
443 comm_close(fd);
444 return;
445 }
446 /* check if we want to defer reading */
447 clen = entry->mem_obj->inmem_hi;
448 errno = 0;
449 len = read(fd, buf, SQUID_TCP_SO_RCVBUF);
450 debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len);
451 if (len > 0) {
452 fd_bytes(fd, len, FD_READ);
453 kb_incr(&Counter.server.all.kbytes_in, len);
454 kb_incr(&Counter.server.http.kbytes_in, len);
455 commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
456 IOStats.Http.reads++;
457 for (clen = len - 1, bin = 0; clen; bin++)
458 clen >>= 1;
459 IOStats.Http.read_hist[bin]++;
460 }
461 if (len < 0) {
462 debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n",
463 fd, xstrerror());
464 if (ignoreErrno(errno)) {
465 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
466 } else if (entry->mem_obj->inmem_hi == 0 && httpTryRestart(httpState)) {
467 httpRestart(httpState);
468 } else if (clen == 0) {
469 err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR);
470 err->xerrno = errno;
471 err->request = requestLink(httpState->orig_request);
472 errorAppendEntry(entry, err);
473 comm_close(fd);
474 } else {
475 storeAbort(entry, 0);
476 comm_close(fd);
477 }
478 } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
479 if (httpTryRestart(httpState)) {
480 httpRestart(httpState);
481 } else {
482 httpState->eof = 1;
483 err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE);
484 err->xerrno = errno;
485 err->request = requestLink(httpState->orig_request);
486 errorAppendEntry(entry, err);
487 comm_close(fd);
488 }
489 } else if (len == 0) {
490 /* Connection closed; retrieval done. */
491 httpState->eof = 1;
492 if (httpState->reply_hdr_state < 2)
493 /*
494 * Yes Henrik, there is a point to doing this. When we
495 * called httpProcessReplyHeader() before, we didn't find
496 * the end of headers, but now we are definately at EOF, so
497 * we want to process the reply headers.
498 */
499 httpProcessReplyHeader(httpState, buf, len);
500 storeComplete(entry); /* deallocates mem_obj->request */
501 comm_close(fd);
502 } else {
503 if (httpState->reply_hdr_state < 2)
504 httpProcessReplyHeader(httpState, buf, len);
505 storeAppend(entry, buf, len);
506 if (httpPconnTransferDone(httpState)) {
507 /* yes we have to clear all these! */
508 commSetDefer(fd, NULL, NULL);
509 commSetTimeout(fd, -1, NULL, NULL);
510 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
511 comm_remove_close_handler(fd, httpStateFree, httpState);
512 storeComplete(entry); /* deallocates mem_obj->request */
513 pconnPush(fd, request->host, request->port);
514 httpState->fd = -1;
515 httpStateFree(-1, httpState);
516 } else {
517 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
518 }
519 }
520 }
521
522 /* This will be called when request write is complete. Schedule read of
523 * reply. */
524 static void
525 httpSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
526 {
527 HttpStateData *httpState = data;
528 StoreEntry *entry = httpState->entry;
529 ErrorState *err;
530 debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n",
531 fd, size, errflag);
532 if (size > 0) {
533 fd_bytes(fd, size, FD_WRITE);
534 kb_incr(&Counter.server.all.kbytes_out, size);
535 kb_incr(&Counter.server.http.kbytes_out, size);
536 }
537 if (errflag == COMM_ERR_CLOSING)
538 return;
539 if (errflag) {
540 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
541 err->xerrno = errno;
542 err->request = requestLink(httpState->orig_request);
543 errorAppendEntry(entry, err);
544 comm_close(fd);
545 return;
546 } else {
547 /* Schedule read reply. */
548 commSetSelect(fd,
549 COMM_SELECT_READ,
550 httpReadReply,
551 httpState, 0);
552 commSetDefer(fd, protoCheckDeferRead, entry);
553 }
554 }
555
556 static void
557 httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int check)
558 {
559 size_t n = *sz + strlen(line) + 2;
560 if (n >= max)
561 return;
562 if (check) {
563 if (Config.onoff.anonymizer == ANONYMIZER_PARANOID) {
564 if (!httpAnonAllowed(line))
565 return;
566 } else if (Config.onoff.anonymizer == ANONYMIZER_STANDARD) {
567 if (httpAnonDenied(line))
568 return;
569 }
570 }
571 /* allowed header, explicitly known to be not dangerous */
572 debug(11, 5) ("httpAppendRequestHeader: %s\n", line);
573 strcpy(hdr + (*sz), line);
574 strcat(hdr + (*sz), crlf);
575 *sz = n;
576 }
577
578 #define YBUF_SZ (MAX_URL+32)
579 size_t
580 httpBuildRequestHeader(request_t * request,
581 request_t * orig_request,
582 StoreEntry * entry,
583 size_t * in_len,
584 char *hdr_out,
585 size_t out_sz,
586 int cfd,
587 int flags)
588 {
589 LOCAL_ARRAY(char, ybuf, YBUF_SZ);
590 LOCAL_ARRAY(char, no_forward, 1024);
591 char *xbuf = memAllocate(MEM_4K_BUF);
592 char *viabuf = memAllocate(MEM_4K_BUF);
593 char *fwdbuf = memAllocate(MEM_4K_BUF);
594 char *t = NULL;
595 char *s = NULL;
596 char *end = NULL;
597 size_t len = 0;
598 size_t hdr_len = 0;
599 size_t l;
600 int hdr_flags = 0;
601 int cc_flags = 0;
602 int n;
603 const char *url = NULL;
604 char *hdr_in;
605
606 assert(orig_request->headers != NULL);
607 hdr_in = orig_request->headers;
608 debug(11, 3) ("httpBuildRequestHeader: INPUT:\n%s\n", hdr_in);
609 xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096);
610 xstrncpy(viabuf, "Via: ", 4096);
611 snprintf(ybuf, YBUF_SZ, "%s %s HTTP/1.0",
612 RequestMethodStr[request->method],
613 strLen(request->urlpath) ? strBuf(request->urlpath) : "/");
614 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
615 /* Add IMS header */
616 if (entry && entry->lastmod && request->method == METHOD_GET) {
617 snprintf(ybuf, YBUF_SZ, "If-Modified-Since: %s", mkrfc1123(entry->lastmod));
618 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
619 EBIT_SET(hdr_flags, HDR_IMS);
620 }
621 assert(orig_request->headers_sz > 0);
622 end = orig_request->headers + orig_request->headers_sz;
623 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
624 hdr_len = t - hdr_in;
625 l = strcspn(t, crlf) + 1;
626 if (l > 4096)
627 l = 4096;
628 /* We might find a NULL before 'end' */
629 if (1 == l)
630 break;
631 xstrncpy(xbuf, t, l);
632 debug(11, 5) ("httpBuildRequestHeader: %s\n", xbuf);
633 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
634 continue;
635 if (strncasecmp(xbuf, "Proxy-authorization:", 20) == 0)
636 /* If we're not going to do proxy auth, then it must be passed on */
637 if (EBIT_TEST(request->flags, REQ_USED_PROXY_AUTH))
638 continue;
639 if (strncasecmp(xbuf, "Connection:", 11) == 0) {
640 handleConnectionHeader(0, no_forward, &xbuf[11]);
641 continue;
642 }
643 if (strncasecmp(xbuf, "Host:", 5) == 0) {
644 /* Don't use client's Host: header for redirected requests */
645 if (EBIT_TEST(request->flags, REQ_REDIRECTED))
646 continue;
647 EBIT_SET(hdr_flags, HDR_HOST);
648 } else if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) {
649 for (s = xbuf + 14; *s && isspace(*s); s++);
650 if (strncasecmp(s, "Max-age=", 8) == 0)
651 EBIT_SET(cc_flags, CCC_MAXAGE);
652 } else if (strncasecmp(xbuf, "Via:", 4) == 0) {
653 for (s = xbuf + 4; *s && isspace(*s); s++);
654 if ((int) strlen(viabuf) + (int) strlen(s) < 4000)
655 strcat(viabuf, s);
656 strcat(viabuf, ", ");
657 continue;
658 } else if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) {
659 for (s = xbuf + 16; *s && isspace(*s); s++);
660 if ((int) strlen(fwdbuf) + (int) strlen(s) < 4000)
661 strcat(fwdbuf, s);
662 strcat(fwdbuf, ", ");
663 continue;
664 } else if (strncasecmp(xbuf, "If-Modified-Since:", 18) == 0) {
665 if (EBIT_TEST(hdr_flags, HDR_IMS))
666 continue;
667 } else if (strncasecmp(xbuf, "Max-Forwards:", 13) == 0) {
668 if (orig_request->method == METHOD_TRACE) {
669 for (s = xbuf + 13; *s && isspace(*s); s++);
670 n = atoi(s);
671 snprintf(xbuf, 4096, "Max-Forwards: %d", n - 1);
672 }
673 }
674 if (!handleConnectionHeader(1, no_forward, xbuf))
675 httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512, 1);
676 }
677 hdr_len = t - hdr_in;
678 if (Config.fake_ua && strstr(hdr_out, "User-Agent") == NULL) {
679 snprintf(ybuf, YBUF_SZ, "User-Agent: %s", Config.fake_ua);
680 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 0);
681 }
682 /* Append Via: */
683 /* snprintf would fail here too */
684 snprintf(ybuf, YBUF_SZ, "%3.1f %s", orig_request->http_ver, ThisCache);
685 strcat(viabuf, ybuf);
686 httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz, 1);
687 /* Append to X-Forwarded-For: */
688 strcat(fwdbuf, cfd < 0 ? "unknown" : fd_table[cfd].ipaddr);
689 httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz, 1);
690 if (!EBIT_TEST(hdr_flags, HDR_HOST)) {
691 if (orig_request->port == urlDefaultPort(orig_request->protocol))
692 snprintf(ybuf, YBUF_SZ, "Host: %s", orig_request->host);
693 else
694 snprintf(ybuf, YBUF_SZ, "Host: %s:%d", orig_request->host,
695 orig_request->port);
696 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
697 }
698 if (!EBIT_TEST(cc_flags, CCC_MAXAGE)) {
699 url = entry ? storeUrl(entry) : urlCanonical(orig_request, NULL);
700 snprintf(ybuf, YBUF_SZ, "Cache-control: Max-age=%d", (int) getMaxAge(url));
701 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
702 if (strLen(request->urlpath))
703 assert(strstr(url, strBuf(request->urlpath)));
704 }
705 /* maybe append Connection: Keep-Alive */
706 if (EBIT_TEST(flags, HTTP_KEEPALIVE)) {
707 if (EBIT_TEST(flags, HTTP_PROXYING)) {
708 snprintf(ybuf, YBUF_SZ, "Proxy-Connection: Keep-Alive");
709 } else {
710 snprintf(ybuf, YBUF_SZ, "Connection: Keep-Alive");
711 }
712 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
713 }
714 httpAppendRequestHeader(hdr_out, null_string, &len, out_sz, 1);
715 memFree(MEM_4K_BUF, xbuf);
716 memFree(MEM_4K_BUF, viabuf);
717 memFree(MEM_4K_BUF, fwdbuf);
718 if (in_len)
719 *in_len = hdr_len;
720 if ((l = strlen(hdr_out)) != len) {
721 debug_trap("httpBuildRequestHeader: size mismatch");
722 len = l;
723 }
724 debug(11, 3) ("httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out);
725 return len;
726 }
727
728 /* This will be called when connect completes. Write request. */
729 static void
730 httpSendRequest(int fd, void *data)
731 {
732 HttpStateData *httpState = data;
733 char *buf = NULL;
734 int len = 0;
735 int buflen;
736 request_t *req = httpState->request;
737 int buftype = 0;
738 StoreEntry *entry = httpState->entry;
739 int cfd;
740 peer *p = httpState->peer;
741 CWCB *sendHeaderDone;
742
743 debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
744 buflen = strLen(req->urlpath);
745 if (req->headers)
746 buflen += req->headers_sz + 1;
747 buflen += 512; /* lots of extra */
748
749 if (pumpMethod(req->method))
750 sendHeaderDone = httpSendRequestEntry;
751 else
752 sendHeaderDone = httpSendComplete;
753
754 if (buflen < DISK_PAGE_SIZE) {
755 buf = memAllocate(MEM_8K_BUF);
756 buftype = BUF_TYPE_8K;
757 buflen = DISK_PAGE_SIZE;
758 } else {
759 buf = xcalloc(buflen, 1);
760 buftype = BUF_TYPE_MALLOC;
761 }
762 if (!opt_forwarded_for)
763 cfd = -1;
764 else if (entry->mem_obj == NULL)
765 cfd = -1;
766 else
767 cfd = entry->mem_obj->fd;
768 if (p != NULL)
769 EBIT_SET(httpState->flags, HTTP_PROXYING);
770 /*
771 * Is Keepalive okay for all request methods?
772 */
773 if (p == NULL)
774 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
775 else if (p->stats.n_keepalives_sent < 10)
776 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
777 else if ((double) p->stats.n_keepalives_recv / (double) p->stats.n_keepalives_sent > 0.50)
778 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
779 len = httpBuildRequestHeader(req,
780 httpState->orig_request,
781 entry,
782 NULL,
783 buf,
784 buflen,
785 cfd,
786 httpState->flags);
787 debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, buf);
788 comm_write(fd,
789 buf,
790 len,
791 sendHeaderDone,
792 httpState,
793 buftype == BUF_TYPE_8K ? memFree8K : xfree);
794 }
795
796 static int
797 httpSocketOpen(StoreEntry * entry, request_t * request)
798 {
799 int fd;
800 ErrorState *err;
801 fd = comm_open(SOCK_STREAM,
802 0,
803 Config.Addrs.tcp_outgoing,
804 0,
805 COMM_NONBLOCKING,
806 storeUrl(entry));
807 if (fd < 0) {
808 debug(50, 4) ("httpSocketOpen: %s\n", xstrerror());
809 err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
810 err->xerrno = errno;
811 err->request = requestLink(request);
812 errorAppendEntry(entry, err);
813 }
814 return fd;
815 }
816
817 static HttpStateData *
818 httpBuildState(int fd, StoreEntry * entry, request_t * orig_request, peer * e)
819 {
820 HttpStateData *httpState = memAllocate(MEM_HTTP_STATE_DATA);
821 request_t *request;
822 storeLockObject(entry);
823 cbdataAdd(httpState, MEM_HTTP_STATE_DATA);
824 httpState->entry = entry;
825 httpState->fd = fd;
826 if (e) {
827 request = memAllocate(MEM_REQUEST_T);
828 request->method = orig_request->method;
829 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
830 request->port = e->http_port;
831 stringReset(&request->urlpath, storeUrl(entry));
832 httpState->request = requestLink(request);
833 httpState->peer = e;
834 httpState->orig_request = requestLink(orig_request);
835 EBIT_SET(request->flags, REQ_PROXYING);
836 } else {
837 httpState->request = requestLink(orig_request);
838 httpState->orig_request = requestLink(orig_request);
839 }
840 /* register the handler to free HTTP state data when the FD closes */
841 comm_add_close_handler(httpState->fd, httpStateFree, httpState);
842 storeRegisterAbort(entry, httpAbort, httpState);
843 return httpState;
844 }
845
846 void
847 httpStart(request_t * request, StoreEntry * entry, peer * e)
848 {
849 HttpStateData *httpState;
850 int fd;
851 debug(11, 3) ("httpStart: \"%s %s\"\n",
852 RequestMethodStr[request->method], storeUrl(entry));
853 Counter.server.all.requests++;
854 Counter.server.http.requests++;
855 if (e) {
856 if (EBIT_TEST(e->options, NEIGHBOR_PROXY_ONLY))
857 storeReleaseRequest(entry);
858 if ((fd = pconnPop(e->host, e->http_port)) >= 0) {
859 debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd);
860 httpState = httpBuildState(fd, entry, request, e);
861 commSetTimeout(httpState->fd,
862 Config.Timeout.connect,
863 httpTimeout,
864 httpState);
865 httpConnectDone(fd, COMM_OK, httpState);
866 return;
867 }
868 } else {
869 if ((fd = pconnPop(request->host, request->port)) >= 0) {
870 debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd);
871 httpState = httpBuildState(fd, entry, request, e);
872 commSetTimeout(httpState->fd,
873 Config.Timeout.connect,
874 httpTimeout,
875 httpState);
876 httpConnectDone(fd, COMM_OK, httpState);
877 return;
878 }
879 }
880 if ((fd = httpSocketOpen(entry, request)) < 0)
881 return;
882 httpState = httpBuildState(fd, entry, request, e);
883 commSetTimeout(httpState->fd,
884 Config.Timeout.connect,
885 httpTimeout,
886 httpState);
887 commConnectStart(httpState->fd,
888 httpState->request->host,
889 httpState->request->port,
890 httpConnectDone,
891 httpState);
892 }
893
894 static int
895 httpTryRestart(HttpStateData * httpState)
896 {
897 /*
898 * We only retry the request if it looks like it was
899 * on a persistent/pipelined connection
900 */
901 if (fd_table[httpState->fd].uses < 2)
902 return 0;
903 if (pumpMethod(httpState->orig_request->method))
904 if (0 == pumpRestart(httpState->orig_request))
905 return 0;
906 return 1;
907 }
908
909 static void
910 httpRestart(HttpStateData * httpState)
911 {
912 /* restart a botched request from a persistent connection */
913 debug(11, 2) ("Retrying HTTP request for %s\n", storeUrl(httpState->entry));
914 if (pumpMethod(httpState->orig_request->method)) {
915 debug(11, 1) ("Potential Coredump: httpRestart %s %s\n",
916 RequestMethodStr[httpState->orig_request->method],
917 storeUrl(httpState->entry));
918 }
919 if (httpState->fd >= 0) {
920 comm_remove_close_handler(httpState->fd, httpStateFree, httpState);
921 comm_close(httpState->fd);
922 httpState->fd = -1;
923 }
924 httpState->fd = httpSocketOpen(httpState->entry, httpState->orig_request);
925 if (httpState->fd < 0)
926 return;
927 comm_add_close_handler(httpState->fd, httpStateFree, httpState);
928 commSetTimeout(httpState->fd,
929 Config.Timeout.connect,
930 httpTimeout,
931 httpState);
932 commConnectStart(httpState->fd,
933 httpState->request->host,
934 httpState->request->port,
935 httpConnectDone,
936 httpState);
937 }
938
939 static void
940 httpConnectDone(int fd, int status, void *data)
941 {
942 HttpStateData *httpState = data;
943 request_t *request = httpState->request;
944 StoreEntry *entry = httpState->entry;
945 ErrorState *err;
946 if (status == COMM_ERR_DNS) {
947 debug(11, 4) ("httpConnectDone: Unknown host: %s\n", request->host);
948 err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
949 err->dnsserver_msg = xstrdup(dns_error_message);
950 err->request = requestLink(httpState->orig_request);
951 errorAppendEntry(entry, err);
952 comm_close(fd);
953 } else if (status != COMM_OK) {
954 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
955 err->xerrno = errno;
956 err->host = xstrdup(request->host);
957 err->port = request->port;
958 err->request = requestLink(httpState->orig_request);
959 errorAppendEntry(entry, err);
960 if (httpState->peer)
961 peerCheckConnectStart(httpState->peer);
962 comm_close(fd);
963 } else {
964 fd_note(fd, storeUrl(entry));
965 fd_table[fd].uses++;
966 commSetSelect(fd, COMM_SELECT_WRITE, httpSendRequest, httpState, 0);
967 commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState);
968 }
969 }
970
971 static void
972 httpAbort(void *data)
973 {
974 HttpStateData *httpState = data;
975 debug(11, 2) ("httpAbort: %s\n", storeUrl(httpState->entry));
976 comm_close(httpState->fd);
977 }
978
979 static void
980 httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data)
981 {
982 HttpStateData *httpState = data;
983 StoreEntry *entry = httpState->entry;
984 ErrorState *err;
985 debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n",
986 fd, size, errflag);
987 if (size > 0) {
988 fd_bytes(fd, size, FD_WRITE);
989 kb_incr(&Counter.server.all.kbytes_out, size);
990 kb_incr(&Counter.server.http.kbytes_out, size);
991 }
992 if (errflag == COMM_ERR_CLOSING)
993 return;
994 if (errflag) {
995 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
996 err->xerrno = errno;
997 err->request = requestLink(httpState->orig_request);
998 errorAppendEntry(entry, err);
999 comm_close(fd);
1000 return;
1001 }
1002 pumpStart(fd, entry, httpState->orig_request, httpSendComplete, httpState);
1003 }