3 * $Id: http.cc,v 1.377 2001/01/12 00:37:18 wessels Exp $
5 * DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
6 * AUTHOR: Harvest Derived
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
37 * Anonymizing patch by lutz@as-node.jena.thur.de
38 * have a look into http-anon.c to get more informations.
43 static const char *const crlf
= "\r\n";
45 static CWCB httpSendComplete
;
46 static CWCB httpSendRequestEntry
;
48 static PF httpReadReply
;
49 static void httpSendRequest(HttpStateData
*);
50 static PF httpStateFree
;
51 static PF httpTimeout
;
52 static void httpCacheNegatively(StoreEntry
*);
53 static void httpMakePrivate(StoreEntry
*);
54 static void httpMakePublic(StoreEntry
*);
55 static int httpCachableReply(HttpStateData
*);
56 static void httpMaybeRemovePublic(StoreEntry
*, http_status
);
59 httpStateFree(int fd
, void *data
)
61 HttpStateData
*httpState
= data
;
63 delayClearNoDelay(fd
);
65 if (httpState
== NULL
)
67 storeUnlockObject(httpState
->entry
);
68 if (httpState
->reply_hdr
) {
69 memFree(httpState
->reply_hdr
, MEM_8K_BUF
);
70 httpState
->reply_hdr
= NULL
;
72 requestUnlink(httpState
->request
);
73 requestUnlink(httpState
->orig_request
);
74 httpState
->request
= NULL
;
75 httpState
->orig_request
= NULL
;
76 cbdataFree(httpState
);
80 httpCachable(method_t method
)
82 /* GET and HEAD are cachable. Others are not. */
83 if (method
!= METHOD_GET
&& method
!= METHOD_HEAD
)
90 httpTimeout(int fd
, void *data
)
92 HttpStateData
*httpState
= data
;
93 StoreEntry
*entry
= httpState
->entry
;
94 debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd
, storeUrl(entry
));
95 if (entry
->store_status
== STORE_PENDING
) {
96 if (entry
->mem_obj
->inmem_hi
== 0) {
97 fwdFail(httpState
->fwd
,
98 errorCon(ERR_READ_TIMEOUT
, HTTP_GATEWAY_TIMEOUT
));
104 /* This object can be cached for a long time */
106 httpMakePublic(StoreEntry
* entry
)
108 if (EBIT_TEST(entry
->flags
, ENTRY_CACHABLE
))
109 storeSetPublicKey(entry
);
112 /* This object should never be cached at all */
114 httpMakePrivate(StoreEntry
* entry
)
116 storeExpireNow(entry
);
117 storeReleaseRequest(entry
); /* delete object when not used */
118 /* storeReleaseRequest clears ENTRY_CACHABLE flag */
121 /* This object may be negatively cached */
123 httpCacheNegatively(StoreEntry
* entry
)
125 storeNegativeCache(entry
);
126 if (EBIT_TEST(entry
->flags
, ENTRY_CACHABLE
))
127 storeSetPublicKey(entry
);
131 httpMaybeRemovePublic(StoreEntry
* e
, http_status status
)
136 if (!EBIT_TEST(e
->flags
, KEY_PRIVATE
))
140 case HTTP_NON_AUTHORITATIVE_INFORMATION
:
141 case HTTP_MULTIPLE_CHOICES
:
142 case HTTP_MOVED_PERMANENTLY
:
143 case HTTP_MOVED_TEMPORARILY
:
149 case HTTP_METHOD_NOT_ALLOWED
:
153 case HTTP_UNAUTHORIZED
:
160 * Any 2xx response should eject previously cached entities...
162 if (status
>= 200 && status
< 300)
167 if (!remove
&& !forbidden
)
170 if ((pe
= storeGetPublic(e
->mem_obj
->url
, e
->mem_obj
->method
)) != NULL
) {
175 * Also remove any cached HEAD response in case the object has
178 if ((pe
= storeGetPublic(e
->mem_obj
->url
, METHOD_HEAD
)) != NULL
) {
184 switch (e
->mem_obj
->method
) {
187 case METHOD_PROPPATCH
:
193 * Remove any cached GET object if it is beleived that the
194 * object may have changed as a result of other methods
196 if ((pe
= storeGetPublic(e
->mem_obj
->url
, METHOD_GET
)) != NULL
) {
205 httpCachableReply(HttpStateData
* httpState
)
207 HttpReply
*rep
= httpState
->entry
->mem_obj
->reply
;
208 HttpHeader
*hdr
= &rep
->header
;
209 const int cc_mask
= (rep
->cache_control
) ? rep
->cache_control
->mask
: 0;
211 if (EBIT_TEST(cc_mask
, CC_PRIVATE
))
213 if (EBIT_TEST(cc_mask
, CC_NO_CACHE
))
215 if (EBIT_TEST(cc_mask
, CC_NO_STORE
))
217 if (httpState
->request
->flags
.auth
) {
219 * Responses to requests with authorization may be cached
220 * only if a Cache-Control: public reply header is present.
221 * RFC 2068, sec 14.9.4
223 if (!EBIT_TEST(cc_mask
, CC_PUBLIC
))
227 * We don't properly deal with Vary features yet, so we can't
230 if (httpHeaderHas(hdr
, HDR_VARY
))
232 /* Pragma: no-cache in _replies_ is not documented in HTTP,
233 * but servers like "Active Imaging Webcast/2.0" sure do use it */
234 if (httpHeaderHas(hdr
, HDR_PRAGMA
)) {
235 String s
= httpHeaderGetList(hdr
, HDR_PRAGMA
);
236 const int no_cache
= strListIsMember(&s
, "no-cache", ',');
242 * The "multipart/x-mixed-replace" content type is used for
243 * continuous push replies. These are generally dynamic and
244 * probably should not be cachable
246 if ((v
= httpHeaderGetStr(hdr
, HDR_CONTENT_TYPE
)))
247 if (!strncasecmp(v
, "multipart/x-mixed-replace", 25))
249 switch (httpState
->entry
->mem_obj
->reply
->sline
.status
) {
250 /* Responses that are cacheable */
252 case HTTP_NON_AUTHORITATIVE_INFORMATION
:
253 case HTTP_MULTIPLE_CHOICES
:
254 case HTTP_MOVED_PERMANENTLY
:
257 * Don't cache objects that need to be refreshed on next request,
258 * unless we know how to refresh it.
260 if (!refreshIsCachable(httpState
->entry
))
262 /* don't cache objects from peers w/o LMT, Date, or Expires */
263 /* check that is it enough to check headers @?@ */
266 else if (rep
->last_modified
> -1)
268 else if (!httpState
->peer
)
270 /* @?@ (here and 302): invalid expires header compiles to squid_curtime */
271 else if (rep
->expires
> -1)
277 /* Responses that only are cacheable if the server says so */
278 case HTTP_MOVED_TEMPORARILY
:
279 if (rep
->expires
> -1)
285 /* Errors can be negatively cached */
286 case HTTP_NO_CONTENT
:
288 case HTTP_BAD_REQUEST
:
291 case HTTP_METHOD_NOT_ALLOWED
:
292 case HTTP_REQUEST_URI_TOO_LARGE
:
293 case HTTP_INTERNAL_SERVER_ERROR
:
294 case HTTP_NOT_IMPLEMENTED
:
295 case HTTP_BAD_GATEWAY
:
296 case HTTP_SERVICE_UNAVAILABLE
:
297 case HTTP_GATEWAY_TIMEOUT
:
301 /* Some responses can never be cached */
302 case HTTP_PARTIAL_CONTENT
: /* Not yet supported */
304 case HTTP_NOT_MODIFIED
:
305 case HTTP_UNAUTHORIZED
:
306 case HTTP_PROXY_AUTHENTICATION_REQUIRED
:
307 case HTTP_INVALID_HEADER
: /* Squid header parsing error */
308 default: /* Unknown status code */
316 /* rewrite this later using new interfaces @?@ */
318 httpProcessReplyHeader(HttpStateData
* httpState
, const char *buf
, int size
)
321 StoreEntry
*entry
= httpState
->entry
;
324 HttpReply
*reply
= entry
->mem_obj
->reply
;
326 debug(11, 3) ("httpProcessReplyHeader: key '%s'\n",
327 storeKeyText(entry
->hash
.key
));
328 if (httpState
->reply_hdr
== NULL
)
329 httpState
->reply_hdr
= memAllocate(MEM_8K_BUF
);
330 assert(httpState
->reply_hdr_state
== 0);
331 hdr_len
= httpState
->reply_hdr_size
;
332 room
= 8191 - hdr_len
;
333 xmemcpy(httpState
->reply_hdr
+ hdr_len
, buf
, room
< size
? room
: size
);
334 hdr_len
+= room
< size
? room
: size
;
335 httpState
->reply_hdr
[hdr_len
] = '\0';
336 httpState
->reply_hdr_size
= hdr_len
;
337 if (hdr_len
> 4 && strncmp(httpState
->reply_hdr
, "HTTP/", 5)) {
338 debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState
->reply_hdr
);
339 httpState
->reply_hdr_state
+= 2;
340 reply
->sline
.status
= HTTP_INVALID_HEADER
;
343 t
= httpState
->reply_hdr
+ hdr_len
;
344 /* headers can be incomplete only if object still arriving */
345 if (!httpState
->eof
) {
346 size_t k
= headersEnd(httpState
->reply_hdr
, 8192);
348 return; /* headers not complete */
349 t
= httpState
->reply_hdr
+ k
;
352 httpState
->reply_hdr_state
++;
353 assert(httpState
->reply_hdr_state
== 1);
354 ctx
= ctx_enter(entry
->mem_obj
->url
);
355 httpState
->reply_hdr_state
++;
356 debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
357 httpState
->reply_hdr
);
358 /* Parse headers into reply structure */
359 /* what happens if we fail to parse here? */
360 httpReplyParse(reply
, httpState
->reply_hdr
, hdr_len
);
361 storeTimestampsSet(entry
);
362 /* Check if object is cacheable or not based on reply code */
363 debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply
->sline
.status
);
364 if (neighbors_do_private_keys
)
365 httpMaybeRemovePublic(entry
, reply
->sline
.status
);
366 switch (httpCachableReply(httpState
)) {
368 httpMakePublic(entry
);
371 httpMakePrivate(entry
);
374 httpCacheNegatively(entry
);
380 if (reply
->cache_control
) {
381 if (EBIT_TEST(reply
->cache_control
->mask
, CC_PROXY_REVALIDATE
))
382 EBIT_SET(entry
->flags
, ENTRY_REVALIDATE
);
383 else if (EBIT_TEST(reply
->cache_control
->mask
, CC_MUST_REVALIDATE
))
384 EBIT_SET(entry
->flags
, ENTRY_REVALIDATE
);
386 if (httpState
->flags
.keepalive
)
388 httpState
->peer
->stats
.n_keepalives_sent
++;
389 if (reply
->keep_alive
)
391 httpState
->peer
->stats
.n_keepalives_recv
++;
392 if (reply
->date
> -1 && !httpState
->peer
) {
393 int skew
= abs(reply
->date
- squid_curtime
);
395 debug(11, 3) ("%s's clock is skewed by %d seconds!\n",
396 httpState
->request
->host
, skew
);
400 headersLog(1, 0, httpState
->request
->method
, reply
);
405 httpPconnTransferDone(HttpStateData
* httpState
)
407 /* return 1 if we got the last of the data on a persistent connection */
408 MemObject
*mem
= httpState
->entry
->mem_obj
;
409 HttpReply
*reply
= mem
->reply
;
411 debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState
->fd
);
413 * If we didn't send a keep-alive request header, then this
414 * can not be a persistent connection.
416 if (!httpState
->flags
.keepalive
)
419 * What does the reply have to say about keep-alive?
423 * If the origin server (HTTP/1.0) does not send a keep-alive
424 * header, but keeps the connection open anyway, what happens?
425 * We'll return here and http.c waits for an EOF before changing
426 * store_status to STORE_OK. Combine this with ENTRY_FWD_HDR_WAIT
427 * and an error status code, and we might have to wait until
428 * the server times out the socket.
430 if (!reply
->keep_alive
)
432 debug(11, 5) ("httpPconnTransferDone: content_length=%d\n",
433 reply
->content_length
);
434 /* If we haven't seen the end of reply headers, we are not done */
435 if (httpState
->reply_hdr_state
< 2)
437 clen
= httpReplyBodySize(httpState
->request
->method
, reply
);
438 /* If there is no message body, we can be persistent */
441 /* If the body size is unknown we must wait for EOF */
444 /* If the body size is known, we must wait until we've gotten all of it. */
445 if (mem
->inmem_hi
< reply
->content_length
+ reply
->hdr_sz
)
451 /* This will be called when data is ready to be read from fd. Read until
452 * error or connection closed. */
453 /* XXX this function is too long! */
455 httpReadReply(int fd
, void *data
)
457 HttpStateData
*httpState
= data
;
458 LOCAL_ARRAY(char, buf
, SQUID_TCP_SO_RCVBUF
);
459 StoreEntry
*entry
= httpState
->entry
;
460 const request_t
*request
= httpState
->request
;
468 /* special "if" only for http (for nodelay proxy conns) */
469 if (delayIsNoDelay(fd
))
472 delay_id
= delayMostBytesAllowed(entry
->mem_obj
);
474 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
478 /* check if we want to defer reading */
480 read_sz
= SQUID_TCP_SO_RCVBUF
;
482 read_sz
= delayBytesWanted(delay_id
, 1, read_sz
);
484 statCounter
.syscalls
.sock
.reads
++;
485 len
= read(fd
, buf
, read_sz
);
486 debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd
, len
);
488 fd_bytes(fd
, len
, FD_READ
);
490 delayBytesIn(delay_id
, len
);
492 kb_incr(&statCounter
.server
.all
.kbytes_in
, len
);
493 kb_incr(&statCounter
.server
.http
.kbytes_in
, len
);
494 commSetTimeout(fd
, Config
.Timeout
.read
, NULL
, NULL
);
495 IOStats
.Http
.reads
++;
496 for (clen
= len
- 1, bin
= 0; clen
; bin
++)
498 IOStats
.Http
.read_hist
[bin
]++;
500 if (!httpState
->reply_hdr
&& len
> 0) {
501 /* Skip whitespace */
502 while (len
> 0 && xisspace(*buf
))
503 xmemmove(buf
, buf
+ 1, len
--);
505 /* Continue to read... */
506 commSetSelect(fd
, COMM_SELECT_READ
, httpReadReply
, httpState
, 0);
511 debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n",
513 if (ignoreErrno(errno
)) {
514 commSetSelect(fd
, COMM_SELECT_READ
, httpReadReply
, httpState
, 0);
515 } else if (entry
->mem_obj
->inmem_hi
== 0) {
517 err
= errorCon(ERR_READ_ERROR
, HTTP_INTERNAL_SERVER_ERROR
);
519 fwdFail(httpState
->fwd
, err
);
524 } else if (len
== 0 && entry
->mem_obj
->inmem_hi
== 0) {
526 err
= errorCon(ERR_ZERO_SIZE_OBJECT
, HTTP_SERVICE_UNAVAILABLE
);
528 fwdFail(httpState
->fwd
, err
);
531 } else if (len
== 0) {
532 /* Connection closed; retrieval done. */
534 if (httpState
->reply_hdr_state
< 2)
536 * Yes Henrik, there is a point to doing this. When we
537 * called httpProcessReplyHeader() before, we didn't find
538 * the end of headers, but now we are definately at EOF, so
539 * we want to process the reply headers.
541 httpProcessReplyHeader(httpState
, buf
, len
);
542 fwdComplete(httpState
->fwd
);
545 if (httpState
->reply_hdr_state
< 2) {
546 httpProcessReplyHeader(httpState
, buf
, len
);
547 if (httpState
->reply_hdr_state
== 2) {
548 http_status s
= entry
->mem_obj
->reply
->sline
.status
;
550 fwdStatus(httpState
->fwd
, s
);
553 * If its not a reply that we will re-forward, then
554 * allow the client to get it.
556 if (!fwdReforwardableStatus(s
))
557 EBIT_CLR(entry
->flags
, ENTRY_FWD_HDR_WAIT
);
560 storeAppend(entry
, buf
, len
);
561 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
563 * the above storeAppend() call could ABORT this entry,
564 * in that case, the server FD should already be closed.
565 * there's nothing for us to do.
568 } else if (httpPconnTransferDone(httpState
)) {
569 /* yes we have to clear all these! */
570 commSetDefer(fd
, NULL
, NULL
);
571 commSetTimeout(fd
, -1, NULL
, NULL
);
572 commSetSelect(fd
, COMM_SELECT_READ
, NULL
, NULL
, 0);
574 delayClearNoDelay(fd
);
576 comm_remove_close_handler(fd
, httpStateFree
, httpState
);
577 fwdUnregister(fd
, httpState
->fwd
);
578 pconnPush(fd
, request
->host
, request
->port
);
579 fwdComplete(httpState
->fwd
);
581 httpStateFree(fd
, httpState
);
583 /* Wait for EOF condition */
584 commSetSelect(fd
, COMM_SELECT_READ
, httpReadReply
, httpState
, 0);
589 /* This will be called when request write is complete. Schedule read of
592 httpSendComplete(int fd
, char *bufnotused
, size_t size
, int errflag
, void *data
)
594 HttpStateData
*httpState
= data
;
595 StoreEntry
*entry
= httpState
->entry
;
597 debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n",
599 #if URL_CHECKSUM_DEBUG
600 assert(entry
->mem_obj
->chksum
== url_checksum(entry
->mem_obj
->url
));
603 fd_bytes(fd
, size
, FD_WRITE
);
604 kb_incr(&statCounter
.server
.all
.kbytes_out
, size
);
605 kb_incr(&statCounter
.server
.http
.kbytes_out
, size
);
607 if (errflag
== COMM_ERR_CLOSING
)
610 err
= errorCon(ERR_WRITE_ERROR
, HTTP_INTERNAL_SERVER_ERROR
);
612 err
->request
= requestLink(httpState
->orig_request
);
613 errorAppendEntry(entry
, err
);
617 /* Schedule read reply. */
618 commSetSelect(fd
, COMM_SELECT_READ
, httpReadReply
, httpState
, 0);
620 * Set the read timeout here because it hasn't been set yet.
621 * We only set the read timeout after the request has been
622 * fully written to the server-side. If we start the timeout
623 * after connection establishment, then we are likely to hit
624 * the timeout for POST/PUT requests that have very large
627 commSetTimeout(fd
, Config
.Timeout
.read
, httpTimeout
, httpState
);
628 commSetDefer(fd
, fwdCheckDeferRead
, entry
);
633 * build request headers and append them to a given MemBuf
634 * used by httpBuildRequestPrefix()
635 * note: calls httpHeaderInit(), the caller is responsible for Clean()-ing
638 httpBuildRequestHeader(request_t
* request
,
639 request_t
* orig_request
,
641 HttpHeader
* hdr_out
,
643 http_state_flags flags
)
645 /* building buffer for complex strings */
646 #define BBUF_SZ (MAX_URL+32)
647 LOCAL_ARRAY(char, bbuf
, BBUF_SZ
);
648 String strConnection
= StringNull
;
649 const HttpHeader
*hdr_in
= &orig_request
->header
;
651 const HttpHeaderEntry
*e
;
654 HttpHeaderPos pos
= HttpHeaderInitPos
;
655 httpHeaderInit(hdr_out
, hoRequest
);
656 /* append our IMS header */
657 if (request
->lastmod
> -1 && request
->method
== METHOD_GET
)
658 httpHeaderPutTime(hdr_out
, HDR_IF_MODIFIED_SINCE
, request
->lastmod
);
660 /* decide if we want to do Ranges ourselves
661 * (and fetch the whole object now)
662 * We want to handle Ranges ourselves iff
663 * - we can actually parse client Range specs
664 * - the specs are expected to be simple enough (e.g. no out-of-order ranges)
665 * - reply will be cachable
666 * (If the reply will be uncachable we have to throw it away after
667 * serving this request, so it is better to forward ranges to
668 * the server and fetch only the requested content)
670 if (NULL
== orig_request
->range
)
672 else if (!orig_request
->flags
.cachable
)
674 else if (httpHdrRangeOffsetLimit(orig_request
->range
))
678 debug(11, 8) ("httpBuildRequestHeader: range specs: %p, cachable: %d; we_do_ranges: %d\n",
679 orig_request
->range
, orig_request
->flags
.cachable
, we_do_ranges
);
681 strConnection
= httpHeaderGetList(hdr_in
, HDR_CONNECTION
);
682 while ((e
= httpHeaderGetEntry(hdr_in
, &pos
))) {
683 debug(11, 5) ("httpBuildRequestHeader: %s: %s\n",
684 strBuf(e
->name
), strBuf(e
->value
));
685 if (!httpRequestHdrAllowed(e
, &strConnection
)) {
686 debug(11, 2) ("'%s' header denied by anonymize_headers configuration\n",
691 case HDR_PROXY_AUTHORIZATION
:
692 /* Only pass on proxy authentication to peers for which
693 * authentication forwarding is explicitly enabled
695 if (request
->flags
.proxying
&& orig_request
->peer_login
&&
696 strcmp(orig_request
->peer_login
, "PASS") == 0) {
697 httpHeaderAddEntry(hdr_out
, httpHeaderEntryClone(e
));
700 case HDR_AUTHORIZATION
:
701 /* Pass on WWW authentication even if used locally. If this is
702 * not wanted in an accelerator then the header can be removed
703 * using the anonymization functions
705 httpHeaderAddEntry(hdr_out
, httpHeaderEntryClone(e
));
706 /* XXX Some accelerators might want to strip the header
707 * and regard the reply as cacheable, but authentication
708 * is not normally enabled for accelerators without reading
709 * the code, so there is not much use in adding logics here
710 * without first defining the concept of having authentication
711 * in the accelerator...
716 * Normally Squid does not copy the Host: header from
717 * a client request into the forwarded request headers.
718 * However, there is one case when we do: If the URL
719 * went through our redirector and the admin configured
720 * 'redir_rewrites_host' to be off.
722 if (request
->flags
.redirected
)
723 if (!Config
.onoff
.redir_rewrites_host
)
724 httpHeaderAddEntry(hdr_out
, httpHeaderEntryClone(e
));
726 case HDR_IF_MODIFIED_SINCE
:
727 /* append unless we added our own;
728 * note: at most one client's ims header can pass through */
729 if (!httpHeaderHas(hdr_out
, HDR_IF_MODIFIED_SINCE
))
730 httpHeaderAddEntry(hdr_out
, httpHeaderEntryClone(e
));
732 case HDR_MAX_FORWARDS
:
733 if (orig_request
->method
== METHOD_TRACE
) {
734 /* sacrificing efficiency over clarity, etc. */
735 const int hops
= httpHeaderGetInt(hdr_in
, HDR_MAX_FORWARDS
);
737 httpHeaderPutInt(hdr_out
, HDR_MAX_FORWARDS
, hops
- 1);
742 case HDR_REQUEST_RANGE
:
744 httpHeaderAddEntry(hdr_out
, httpHeaderEntryClone(e
));
746 case HDR_PROXY_CONNECTION
:
749 case HDR_X_FORWARDED_FOR
:
750 case HDR_CACHE_CONTROL
:
751 /* append these after the loop if needed */
754 /* pass on all other header fields */
755 httpHeaderAddEntry(hdr_out
, httpHeaderEntryClone(e
));
760 strVia
= httpHeaderGetList(hdr_in
, HDR_VIA
);
761 snprintf(bbuf
, BBUF_SZ
, "%d.%d %s",
762 orig_request
->http_ver
.major
,
763 orig_request
->http_ver
.minor
, ThisCache
);
764 strListAdd(&strVia
, bbuf
, ',');
765 httpHeaderPutStr(hdr_out
, HDR_VIA
, strBuf(strVia
));
766 stringClean(&strVia
);
768 /* append X-Forwarded-For */
769 strFwd
= httpHeaderGetList(hdr_in
, HDR_X_FORWARDED_FOR
);
770 strListAdd(&strFwd
, (cfd
< 0 ? "unknown" : fd_table
[cfd
].ipaddr
), ',');
771 httpHeaderPutStr(hdr_out
, HDR_X_FORWARDED_FOR
, strBuf(strFwd
));
772 stringClean(&strFwd
);
774 /* append Host if not there already */
775 if (!httpHeaderHas(hdr_out
, HDR_HOST
)) {
776 /* use port# only if not default */
777 if (orig_request
->port
== urlDefaultPort(orig_request
->protocol
)) {
778 httpHeaderPutStr(hdr_out
, HDR_HOST
, orig_request
->host
);
780 httpHeaderPutStrf(hdr_out
, HDR_HOST
, "%s:%d",
781 orig_request
->host
, (int) orig_request
->port
);
784 /* append Authorization if known in URL, not in header and going direct */
785 if (!httpHeaderHas(hdr_out
, HDR_AUTHORIZATION
)) {
786 if (!request
->flags
.proxying
&& *request
->login
) {
787 httpHeaderPutStrf(hdr_out
, HDR_AUTHORIZATION
, "Basic %s",
788 base64_encode(request
->login
));
791 /* append Proxy-Authorization if configured for peer, and proxying */
792 if (request
->flags
.proxying
&& orig_request
->peer_login
&&
793 !httpHeaderHas(hdr_out
, HDR_PROXY_AUTHORIZATION
) &&
794 strcmp(orig_request
->peer_login
, "PASS") != 0) {
795 if (*orig_request
->peer_login
== '*') {
796 /* Special mode, to pass the username to the upstream cache */
798 char *username
= "-";
799 if (orig_request
->auth_user_request
)
800 username
= authenticateUserRequestUsername(orig_request
->auth_user_request
);
801 snprintf(loginbuf
, sizeof(loginbuf
), "%s%s", username
, orig_request
->peer_login
+ 1);
802 httpHeaderPutStrf(hdr_out
, HDR_PROXY_AUTHORIZATION
, "Basic %s",
803 base64_encode(loginbuf
));
805 httpHeaderPutStrf(hdr_out
, HDR_PROXY_AUTHORIZATION
, "Basic %s",
806 base64_encode(orig_request
->peer_login
));
809 /* append Cache-Control, add max-age if not there already */
811 HttpHdrCc
*cc
= httpHeaderGetCc(hdr_in
);
813 cc
= httpHdrCcCreate();
814 if (!EBIT_TEST(cc
->mask
, CC_MAX_AGE
)) {
815 const char *url
= entry
? storeUrl(entry
) : urlCanonical(orig_request
);
816 httpHdrCcSetMaxAge(cc
, getMaxAge(url
));
817 if (strLen(request
->urlpath
))
818 assert(strstr(url
, strBuf(request
->urlpath
)));
820 if (flags
.only_if_cached
)
821 EBIT_SET(cc
->mask
, CC_ONLY_IF_CACHED
);
822 httpHeaderPutCc(hdr_out
, cc
);
823 httpHdrCcDestroy(cc
);
825 /* maybe append Connection: keep-alive */
826 if (flags
.keepalive
) {
827 if (flags
.proxying
) {
828 httpHeaderPutStr(hdr_out
, HDR_PROXY_CONNECTION
, "keep-alive");
830 httpHeaderPutStr(hdr_out
, HDR_CONNECTION
, "keep-alive");
833 /* Now mangle the headers. */
834 httpHdrMangleList(hdr_out
, request
);
835 stringClean(&strConnection
);
838 /* build request prefix and append it to a given MemBuf;
839 * return the length of the prefix */
841 httpBuildRequestPrefix(request_t
* request
,
842 request_t
* orig_request
,
846 http_state_flags flags
)
848 const int offset
= mb
->size
;
849 memBufPrintf(mb
, "%s %s HTTP/1.0\r\n",
850 RequestMethodStr
[request
->method
],
851 strLen(request
->urlpath
) ? strBuf(request
->urlpath
) : "/");
852 /* build and pack headers */
856 httpBuildRequestHeader(request
, orig_request
, entry
, &hdr
, cfd
, flags
);
857 packerToMemInit(&p
, mb
);
858 httpHeaderPackInto(&hdr
, &p
);
859 httpHeaderClean(&hdr
);
862 /* append header terminator */
863 memBufAppend(mb
, crlf
, 2);
864 return mb
->size
- offset
;
866 /* This will be called when connect completes. Write request. */
868 httpSendRequest(HttpStateData
* httpState
)
871 request_t
*req
= httpState
->request
;
872 StoreEntry
*entry
= httpState
->entry
;
874 peer
*p
= httpState
->peer
;
875 CWCB
*sendHeaderDone
;
877 debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", httpState
->fd
, httpState
);
879 if (httpState
->orig_request
->body_connection
)
880 sendHeaderDone
= httpSendRequestEntry
;
882 sendHeaderDone
= httpSendComplete
;
884 if (!opt_forwarded_for
)
886 else if (entry
->mem_obj
== NULL
)
889 cfd
= entry
->mem_obj
->fd
;
890 assert(-1 == cfd
|| FD_SOCKET
== fd_table
[cfd
].type
);
892 httpState
->flags
.proxying
= 1;
894 httpState
->flags
.proxying
= 0;
896 * Is keep-alive okay for all request methods?
898 if (!Config
.onoff
.server_pconns
)
899 httpState
->flags
.keepalive
= 0;
901 httpState
->flags
.keepalive
= 1;
902 else if (p
->stats
.n_keepalives_sent
< 10)
903 httpState
->flags
.keepalive
= 1;
904 else if ((double) p
->stats
.n_keepalives_recv
/ (double) p
->stats
.n_keepalives_sent
> 0.50)
905 httpState
->flags
.keepalive
= 1;
907 if (neighborType(httpState
->peer
, httpState
->request
) == PEER_SIBLING
&&
908 !httpState
->peer
->options
.allow_miss
)
909 httpState
->flags
.only_if_cached
= 1;
911 httpBuildRequestPrefix(req
,
912 httpState
->orig_request
,
917 debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", httpState
->fd
, mb
.buf
);
918 comm_write_mbuf(httpState
->fd
, mb
, sendHeaderDone
, httpState
);
922 httpStart(FwdState
* fwd
)
924 int fd
= fwd
->server_fd
;
925 HttpStateData
*httpState
;
926 request_t
*proxy_req
;
927 request_t
*orig_req
= fwd
->request
;
928 debug(11, 3) ("httpStart: \"%s %s\"\n",
929 RequestMethodStr
[orig_req
->method
],
930 storeUrl(fwd
->entry
));
931 httpState
= CBDATA_ALLOC(HttpStateData
, NULL
);
932 storeLockObject(fwd
->entry
);
933 httpState
->fwd
= fwd
;
934 httpState
->entry
= fwd
->entry
;
937 httpState
->peer
= fwd
->servers
->peer
; /* might be NULL */
938 if (httpState
->peer
) {
939 proxy_req
= requestCreate(orig_req
->method
,
940 PROTO_NONE
, storeUrl(httpState
->entry
));
941 xstrncpy(proxy_req
->host
, httpState
->peer
->host
, SQUIDHOSTNAMELEN
);
942 proxy_req
->port
= httpState
->peer
->http_port
;
943 proxy_req
->flags
= orig_req
->flags
;
944 proxy_req
->lastmod
= orig_req
->lastmod
;
945 httpState
->request
= requestLink(proxy_req
);
946 httpState
->orig_request
= requestLink(orig_req
);
947 proxy_req
->flags
.proxying
= 1;
949 * This NEIGHBOR_PROXY_ONLY check probably shouldn't be here.
950 * We might end up getting the object from somewhere else if,
951 * for example, the request to this neighbor fails.
953 if (httpState
->peer
->options
.proxy_only
)
954 storeReleaseRequest(httpState
->entry
);
956 assert(delayIsNoDelay(fd
) == 0);
957 if (httpState
->peer
->options
.no_delay
)
961 httpState
->request
= requestLink(orig_req
);
962 httpState
->orig_request
= requestLink(orig_req
);
965 * register the handler to free HTTP state data when the FD closes
967 comm_add_close_handler(fd
, httpStateFree
, httpState
);
968 statCounter
.server
.all
.requests
++;
969 statCounter
.server
.http
.requests
++;
970 httpSendRequest(httpState
);
972 * We used to set the read timeout here, but not any more.
973 * Now its set in httpSendComplete() after the full request,
974 * including request body, has been written to the server.
979 httpSendRequestEntryDone(int fd
, void *data
)
981 HttpStateData
*httpState
= data
;
983 debug(11, 5) ("httpSendRequestEntryDone: FD %d\n",
985 memset(&ch
, '\0', sizeof(ch
));
986 ch
.request
= httpState
->request
;
987 if (!Config
.accessList
.brokenPosts
) {
988 debug(11, 5) ("httpSendRequestEntryDone: No brokenPosts list\n");
989 httpSendComplete(fd
, NULL
, 0, 0, data
);
990 } else if (!aclCheckFast(Config
.accessList
.brokenPosts
, &ch
)) {
991 debug(11, 5) ("httpSendRequestEntryDone: didn't match brokenPosts\n");
992 httpSendComplete(fd
, NULL
, 0, 0, data
);
994 debug(11, 2) ("httpSendRequestEntryDone: matched brokenPosts\n");
995 comm_write(fd
, "\r\n", 2, httpSendComplete
, data
, NULL
);
1000 httpRequestBodyHandler(char *buf
, size_t size
, void *data
)
1002 HttpStateData
*httpState
= (HttpStateData
*) data
;
1004 comm_write(httpState
->fd
, buf
, size
, httpSendRequestEntry
, data
, memFree8K
);
1005 } else if (size
== 0) {
1008 httpSendRequestEntryDone(httpState
->fd
, data
);
1010 /* Failed to get whole body, probably aborted */
1012 httpSendComplete(httpState
->fd
, NULL
, 0, COMM_ERR_CLOSING
, data
);
1017 httpSendRequestEntry(int fd
, char *bufnotused
, size_t size
, int errflag
, void *data
)
1019 HttpStateData
*httpState
= data
;
1020 StoreEntry
*entry
= httpState
->entry
;
1022 debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n",
1025 fd_bytes(fd
, size
, FD_WRITE
);
1026 kb_incr(&statCounter
.server
.all
.kbytes_out
, size
);
1027 kb_incr(&statCounter
.server
.http
.kbytes_out
, size
);
1029 if (errflag
== COMM_ERR_CLOSING
)
1032 err
= errorCon(ERR_WRITE_ERROR
, HTTP_INTERNAL_SERVER_ERROR
);
1033 err
->xerrno
= errno
;
1034 err
->request
= requestLink(httpState
->orig_request
);
1035 errorAppendEntry(entry
, err
);
1039 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
1043 clientReadBody(httpState
->orig_request
, memAllocate(MEM_8K_BUF
), 8192, httpRequestBodyHandler
, httpState
);
1047 httpBuildVersion(http_version_t
* version
, unsigned int major
, unsigned int minor
)
1049 version
->major
= major
;
1050 version
->minor
= minor
;