3 * $Id: http.cc,v 1.266 1998/04/27 19:52:48 wessels Exp $
5 * DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
6 * AUTHOR: Harvest Derived
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * --------------------------------------------------------
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.
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.
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.
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.
33 * Copyright (c) 1994, 1995. All rights reserved.
35 * The Harvest software was developed by the Internet Research Task
36 * Force Research Group on Resource Discovery (IRTF-RD):
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.
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.
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.
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.
72 * Users may make derivative works from the Harvest software, subject
73 * to the following constraints:
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.
81 * - You must notify IRTF-RD regarding your distribution of
82 * the derivative work.
84 * - You must clearly notify users that your are distributing
85 * a modified version and not the original Harvest software.
87 * - Any derivative product is also subject to these copyright
88 * and use restrictions.
90 * Note that the Harvest software is NOT in the public domain. We
91 * retain copyright, as specified above.
93 * HISTORY OF FREE SOFTWARE STATUS
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.
108 * Anonymizing patch by lutz@as-node.jena.thur.de
109 * have a look into http-anon.c to get more informations.
114 static const char *const crlf
= "\r\n";
126 static CNCB httpConnectDone
;
127 static CWCB httpSendComplete
;
128 static CWCB httpSendRequestEntry
;
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
*);
146 httpStateFree(int fdnotused
, void *data
)
148 HttpStateData
*httpState
= data
;
149 if (httpState
== NULL
)
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
;
158 requestUnlink(httpState
->request
);
159 requestUnlink(httpState
->orig_request
);
160 httpState
->request
= NULL
;
161 httpState
->orig_request
= NULL
;
162 cbdataFree(httpState
);
166 httpCachable(method_t method
)
168 /* GET and HEAD are cachable. Others are not. */
169 if (method
!= METHOD_GET
&& method
!= METHOD_HEAD
)
176 httpTimeout(int fd
, void *data
)
178 HttpStateData
*httpState
= data
;
179 StoreEntry
*entry
= httpState
->entry
;
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
);
188 storeAbort(entry
, 0);
193 /* This object can be cached for a long time */
195 httpMakePublic(StoreEntry
* entry
)
197 if (EBIT_TEST(entry
->flag
, ENTRY_CACHABLE
))
198 storeSetPublicKey(entry
);
201 /* This object should never be cached at all */
203 httpMakePrivate(StoreEntry
* entry
)
205 storeExpireNow(entry
);
206 EBIT_CLR(entry
->flag
, ENTRY_CACHABLE
);
207 storeReleaseRequest(entry
); /* delete object when not used */
210 /* This object may be negatively cached */
212 httpCacheNegatively(StoreEntry
* entry
)
214 storeNegativeCache(entry
);
215 if (EBIT_TEST(entry
->flag
, ENTRY_CACHABLE
))
216 storeSetPublicKey(entry
);
220 httpCachableReply(HttpStateData
* httpState
)
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
))
227 if (EBIT_TEST(cc_mask
, CC_NO_CACHE
))
229 if (EBIT_TEST(httpState
->request
->flags
, REQ_AUTH
))
230 if (!EBIT_TEST(cc_mask
, CC_PROXY_REVALIDATE
))
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.
238 /* With new headers the above stripping should be easy to do? @?@ */
239 if (httpHeaderHas(hdr
, HDR_SET_COOKIE
))
241 switch (httpState
->entry
->mem_obj
->reply
->sline
.status
) {
242 /* Responses that are cacheable */
244 case 203: /* Non-Authoritative Information */
245 case 300: /* Multiple Choices */
246 case 301: /* Moved Permanently */
248 /* don't cache objects from peers w/o LMT, Date, or Expires */
249 /* check that is it enough to check headers @?@ */
252 else if (rep
->last_modified
> -1)
254 else if (!httpState
->peer
)
256 /* @?@ (here and 302): invalid expires header compiles to squid_curtime */
257 else if (rep
->expires
> -1)
263 /* Responses that only are cacheable if the server says so */
264 case 302: /* Moved temporarily */
265 if (rep
->expires
> -1)
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 */
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 */
303 /* rewrite this later using new interfaces @?@ */
305 httpProcessReplyHeader(HttpStateData
* httpState
, const char *buf
, int size
)
308 StoreEntry
*entry
= httpState
->entry
;
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;
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);
332 return; /* headers not complete */
333 t
= httpState
->reply_hdr
+ k
;
336 httpState
->reply_hdr_state
++;
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
)) {
352 httpMakePublic(entry
);
355 httpMakePrivate(entry
);
358 httpCacheNegatively(entry
);
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
))
368 httpState
->peer
->stats
.n_keepalives_sent
++;
369 if (reply
->proxy_keep_alive
)
371 httpState
->peer
->stats
.n_keepalives_recv
++;
377 httpPconnTransferDone(HttpStateData
* httpState
)
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
);
384 * If we didn't send a Keepalive request header, then this
385 * can not be a persistent connection.
387 if (!EBIT_TEST(httpState
->flags
, HTTP_KEEPALIVE
))
389 debug(11, 5) ("httpPconnTransferDone: content_length=%d\n",
390 reply
->content_length
);
392 * Deal with gross HTTP stuff
393 * - If we haven't seen the end of the reply headers, we can't
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.
402 if (httpState
->reply_hdr_state
< 2)
404 else if (reply
->sline
.status
== HTTP_OK
)
405 (void) 0; /* common case, continue */
406 else if (reply
->sline
.status
== HTTP_NO_CONTENT
)
408 else if (reply
->sline
.status
== HTTP_NOT_MODIFIED
)
410 else if (reply
->sline
.status
< HTTP_OK
)
412 else if (httpState
->request
->method
== METHOD_HEAD
)
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.
419 if (reply
->content_length
< 0)
421 else if (mem
->inmem_hi
< reply
->content_length
+ reply
->hdr_sz
)
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! */
431 httpReadReply(int fd
, void *data
)
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
;
441 if (protoAbortFetch(entry
)) {
442 storeAbort(entry
, 0);
446 /* check if we want to defer reading */
447 clen
= entry
->mem_obj
->inmem_hi
;
449 len
= read(fd
, buf
, SQUID_TCP_SO_RCVBUF
);
450 debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd
, len
);
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
++)
459 IOStats
.Http
.read_hist
[bin
]++;
462 debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n",
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
);
471 err
->request
= requestLink(httpState
->orig_request
);
472 errorAppendEntry(entry
, err
);
475 storeAbort(entry
, 0);
478 } else if (len
== 0 && entry
->mem_obj
->inmem_hi
== 0) {
479 if (httpTryRestart(httpState
)) {
480 httpRestart(httpState
);
483 err
= errorCon(ERR_ZERO_SIZE_OBJECT
, HTTP_SERVICE_UNAVAILABLE
);
485 err
->request
= requestLink(httpState
->orig_request
);
486 errorAppendEntry(entry
, err
);
489 } else if (len
== 0) {
490 /* Connection closed; retrieval done. */
492 if (httpState
->reply_hdr_state
< 2)
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.
499 httpProcessReplyHeader(httpState
, buf
, len
);
500 storeComplete(entry
); /* deallocates mem_obj->request */
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
);
515 httpStateFree(-1, httpState
);
517 commSetSelect(fd
, COMM_SELECT_READ
, httpReadReply
, httpState
, 0);
522 /* This will be called when request write is complete. Schedule read of
525 httpSendComplete(int fd
, char *bufnotused
, size_t size
, int errflag
, void *data
)
527 HttpStateData
*httpState
= data
;
528 StoreEntry
*entry
= httpState
->entry
;
530 debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n",
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
);
537 if (errflag
== COMM_ERR_CLOSING
)
540 err
= errorCon(ERR_WRITE_ERROR
, HTTP_INTERNAL_SERVER_ERROR
);
542 err
->request
= requestLink(httpState
->orig_request
);
543 errorAppendEntry(entry
, err
);
547 /* Schedule read reply. */
552 commSetDefer(fd
, protoCheckDeferRead
, entry
);
557 httpAppendRequestHeader(char *hdr
, const char *line
, size_t * sz
, size_t max
, int check
)
559 size_t n
= *sz
+ strlen(line
) + 2;
563 if (Config
.onoff
.anonymizer
== ANONYMIZER_PARANOID
) {
564 if (!httpAnonAllowed(line
))
566 } else if (Config
.onoff
.anonymizer
== ANONYMIZER_STANDARD
) {
567 if (httpAnonDenied(line
))
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
);
578 #define YBUF_SZ (MAX_URL+32)
580 httpBuildRequestHeader(request_t
* request
,
581 request_t
* orig_request
,
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
);
603 const char *url
= NULL
;
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);
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
);
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;
628 /* We might find a NULL before 'end' */
631 xstrncpy(xbuf
, t
, l
);
632 debug(11, 5) ("httpBuildRequestHeader: %s\n", xbuf
);
633 if (strncasecmp(xbuf
, "Proxy-Connection:", 17) == 0)
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
))
639 if (strncasecmp(xbuf
, "Connection:", 11) == 0) {
640 handleConnectionHeader(0, no_forward
, &xbuf
[11]);
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
))
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)
656 strcat(viabuf
, ", ");
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)
662 strcat(fwdbuf
, ", ");
664 } else if (strncasecmp(xbuf
, "If-Modified-Since:", 18) == 0) {
665 if (EBIT_TEST(hdr_flags
, HDR_IMS
))
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
++);
671 snprintf(xbuf
, 4096, "Max-Forwards: %d", n
- 1);
674 if (!handleConnectionHeader(1, no_forward
, xbuf
))
675 httpAppendRequestHeader(hdr_out
, xbuf
, &len
, out_sz
- 512, 1);
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);
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
);
694 snprintf(ybuf
, YBUF_SZ
, "Host: %s:%d", orig_request
->host
,
696 httpAppendRequestHeader(hdr_out
, ybuf
, &len
, out_sz
, 1);
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
)));
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");
710 snprintf(ybuf
, YBUF_SZ
, "Connection: Keep-Alive");
712 httpAppendRequestHeader(hdr_out
, ybuf
, &len
, out_sz
, 1);
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
);
720 if ((l
= strlen(hdr_out
)) != len
) {
721 debug_trap("httpBuildRequestHeader: size mismatch");
724 debug(11, 3) ("httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out
);
728 /* This will be called when connect completes. Write request. */
730 httpSendRequest(int fd
, void *data
)
732 HttpStateData
*httpState
= data
;
736 request_t
*req
= httpState
->request
;
738 StoreEntry
*entry
= httpState
->entry
;
740 peer
*p
= httpState
->peer
;
741 CWCB
*sendHeaderDone
;
743 debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd
, httpState
);
744 buflen
= strLen(req
->urlpath
);
746 buflen
+= req
->headers_sz
+ 1;
747 buflen
+= 512; /* lots of extra */
749 if (pumpMethod(req
->method
))
750 sendHeaderDone
= httpSendRequestEntry
;
752 sendHeaderDone
= httpSendComplete
;
754 if (buflen
< DISK_PAGE_SIZE
) {
755 buf
= memAllocate(MEM_8K_BUF
);
756 buftype
= BUF_TYPE_8K
;
757 buflen
= DISK_PAGE_SIZE
;
759 buf
= xcalloc(buflen
, 1);
760 buftype
= BUF_TYPE_MALLOC
;
762 if (!opt_forwarded_for
)
764 else if (entry
->mem_obj
== NULL
)
767 cfd
= entry
->mem_obj
->fd
;
769 EBIT_SET(httpState
->flags
, HTTP_PROXYING
);
771 * Is Keepalive okay for all request methods?
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
,
787 debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd
, buf
);
793 buftype
== BUF_TYPE_8K
? memFree8K
: xfree
);
797 httpSocketOpen(StoreEntry
* entry
, request_t
* request
)
801 fd
= comm_open(SOCK_STREAM
,
803 Config
.Addrs
.tcp_outgoing
,
808 debug(50, 4) ("httpSocketOpen: %s\n", xstrerror());
809 err
= errorCon(ERR_SOCKET_FAILURE
, HTTP_INTERNAL_SERVER_ERROR
);
811 err
->request
= requestLink(request
);
812 errorAppendEntry(entry
, err
);
817 static HttpStateData
*
818 httpBuildState(int fd
, StoreEntry
* entry
, request_t
* orig_request
, peer
* e
)
820 HttpStateData
*httpState
= memAllocate(MEM_HTTP_STATE_DATA
);
822 storeLockObject(entry
);
823 cbdataAdd(httpState
, MEM_HTTP_STATE_DATA
);
824 httpState
->entry
= entry
;
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
);
834 httpState
->orig_request
= requestLink(orig_request
);
835 EBIT_SET(request
->flags
, REQ_PROXYING
);
837 httpState
->request
= requestLink(orig_request
);
838 httpState
->orig_request
= requestLink(orig_request
);
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
);
847 httpStart(request_t
* request
, StoreEntry
* entry
, peer
* e
)
849 HttpStateData
*httpState
;
851 debug(11, 3) ("httpStart: \"%s %s\"\n",
852 RequestMethodStr
[request
->method
], storeUrl(entry
));
853 Counter
.server
.all
.requests
++;
854 Counter
.server
.http
.requests
++;
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
,
865 httpConnectDone(fd
, COMM_OK
, httpState
);
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
,
876 httpConnectDone(fd
, COMM_OK
, httpState
);
880 if ((fd
= httpSocketOpen(entry
, request
)) < 0)
882 httpState
= httpBuildState(fd
, entry
, request
, e
);
883 commSetTimeout(httpState
->fd
,
884 Config
.Timeout
.connect
,
887 commConnectStart(httpState
->fd
,
888 httpState
->request
->host
,
889 httpState
->request
->port
,
895 httpTryRestart(HttpStateData
* httpState
)
898 * We only retry the request if it looks like it was
899 * on a persistent/pipelined connection
901 if (fd_table
[httpState
->fd
].uses
< 2)
903 if (pumpMethod(httpState
->orig_request
->method
))
904 if (0 == pumpRestart(httpState
->orig_request
))
910 httpRestart(HttpStateData
* httpState
)
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
));
919 if (httpState
->fd
>= 0) {
920 comm_remove_close_handler(httpState
->fd
, httpStateFree
, httpState
);
921 comm_close(httpState
->fd
);
924 httpState
->fd
= httpSocketOpen(httpState
->entry
, httpState
->orig_request
);
925 if (httpState
->fd
< 0)
927 comm_add_close_handler(httpState
->fd
, httpStateFree
, httpState
);
928 commSetTimeout(httpState
->fd
,
929 Config
.Timeout
.connect
,
932 commConnectStart(httpState
->fd
,
933 httpState
->request
->host
,
934 httpState
->request
->port
,
940 httpConnectDone(int fd
, int status
, void *data
)
942 HttpStateData
*httpState
= data
;
943 request_t
*request
= httpState
->request
;
944 StoreEntry
*entry
= httpState
->entry
;
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
);
953 } else if (status
!= COMM_OK
) {
954 err
= errorCon(ERR_CONNECT_FAIL
, HTTP_SERVICE_UNAVAILABLE
);
956 err
->host
= xstrdup(request
->host
);
957 err
->port
= request
->port
;
958 err
->request
= requestLink(httpState
->orig_request
);
959 errorAppendEntry(entry
, err
);
961 peerCheckConnectStart(httpState
->peer
);
964 fd_note(fd
, storeUrl(entry
));
966 commSetSelect(fd
, COMM_SELECT_WRITE
, httpSendRequest
, httpState
, 0);
967 commSetTimeout(fd
, Config
.Timeout
.read
, httpTimeout
, httpState
);
972 httpAbort(void *data
)
974 HttpStateData
*httpState
= data
;
975 debug(11, 2) ("httpAbort: %s\n", storeUrl(httpState
->entry
));
976 comm_close(httpState
->fd
);
980 httpSendRequestEntry(int fd
, char *bufnotused
, size_t size
, int errflag
, void *data
)
982 HttpStateData
*httpState
= data
;
983 StoreEntry
*entry
= httpState
->entry
;
985 debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n",
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
);
992 if (errflag
== COMM_ERR_CLOSING
)
995 err
= errorCon(ERR_WRITE_ERROR
, HTTP_INTERNAL_SERVER_ERROR
);
997 err
->request
= requestLink(httpState
->orig_request
);
998 errorAppendEntry(entry
, err
);
1002 pumpStart(fd
, entry
, httpState
->orig_request
, httpSendComplete
, httpState
);