]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
debug levels
[thirdparty/squid.git] / src / http.cc
CommitLineData
da2b3a17 1
30a4f2a8 2/*
9d66d521 3 * $Id: http.cc,v 1.308 1998/08/18 22:42:19 wessels Exp $
30a4f2a8 4 *
5 * DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
6 * AUTHOR: Harvest Derived
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
e25c139f 13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * the CREDITS file for full details.
30a4f2a8 19 *
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.
24 *
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.
29 *
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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
30a4f2a8 34 */
019dd986 35
4a83b852 36/*
37 * Anonymizing patch by lutz@as-node.jena.thur.de
de3bdb4c 38 * have a look into http-anon.c to get more informations.
4a83b852 39 */
40
44a47c6e 41#include "squid.h"
090089c4 42
6bf8443a 43static const char *const crlf = "\r\n";
4db43fab 44
9e4ad609 45static CNCB httpConnectDone;
46static CWCB httpSendComplete;
54220df8 47static CWCB httpSendRequestEntry;
48
9e4ad609 49static PF httpReadReply;
50static PF httpSendRequest;
51static PF httpStateFree;
52static PF httpTimeout;
f5b8bbc4 53static void httpCacheNegatively(StoreEntry *);
54static void httpMakePrivate(StoreEntry *);
55static void httpMakePublic(StoreEntry *);
f8309b15 56static int httpCachableReply(HttpStateData *);
b8d8561b 57
b177367b 58static void
79d39a72 59httpStateFree(int fdnotused, void *data)
f5558c95 60{
b177367b 61 HttpStateData *httpState = data;
0d4d4170 62 if (httpState == NULL)
b177367b 63 return;
f88211e8 64 storeUnlockObject(httpState->entry);
0d4d4170 65 if (httpState->reply_hdr) {
3f6c0fb2 66 memFree(MEM_8K_BUF, httpState->reply_hdr);
0d4d4170 67 httpState->reply_hdr = NULL;
68 }
30a4f2a8 69 requestUnlink(httpState->request);
20cc1450 70 requestUnlink(httpState->orig_request);
7dd44885 71 httpState->request = NULL;
72 httpState->orig_request = NULL;
73 cbdataFree(httpState);
f5558c95 74}
75
b8d8561b 76int
75e88d56 77httpCachable(method_t method)
090089c4 78{
090089c4 79 /* GET and HEAD are cachable. Others are not. */
6eb42cae 80 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 81 return 0;
090089c4 82 /* else cachable */
83 return 1;
84}
85
b8d8561b 86static void
5c5783a2 87httpTimeout(int fd, void *data)
090089c4 88{
b177367b 89 HttpStateData *httpState = data;
593c9a75 90 StoreEntry *entry = httpState->entry;
9b312a19 91 ErrorState *err;
9fb13bb6 92 debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
8796b9e9 93 assert(entry->store_status == STORE_PENDING);
73a3014d 94 if (entry->mem_obj->inmem_hi == 0) {
fe40a877 95 err = errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT);
79a15e0a 96 err->request = requestLink(httpState->orig_request);
9b312a19 97 errorAppendEntry(entry, err);
b50179a6 98 } else {
b34ed725 99 storeAbort(entry, 0);
9b312a19 100 }
0d4d4170 101 comm_close(fd);
090089c4 102}
103
30a4f2a8 104/* This object can be cached for a long time */
b8d8561b 105static void
106httpMakePublic(StoreEntry * entry)
30a4f2a8 107{
79a15e0a 108 if (EBIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 109 storeSetPublicKey(entry);
110}
111
112/* This object should never be cached at all */
b8d8561b 113static void
114httpMakePrivate(StoreEntry * entry)
30a4f2a8 115{
30a4f2a8 116 storeExpireNow(entry);
79a15e0a 117 EBIT_CLR(entry->flag, ENTRY_CACHABLE);
30a4f2a8 118 storeReleaseRequest(entry); /* delete object when not used */
119}
120
121/* This object may be negatively cached */
b8d8561b 122static void
123httpCacheNegatively(StoreEntry * entry)
30a4f2a8 124{
79b5cc5f 125 storeNegativeCache(entry);
79a15e0a 126 if (EBIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 127 storeSetPublicKey(entry);
30a4f2a8 128}
129
f8309b15 130static int
131httpCachableReply(HttpStateData * httpState)
c54e9052 132{
d8b249ef 133 HttpReply *rep = httpState->entry->mem_obj->reply;
134 HttpHeader *hdr = &rep->header;
135 const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0;
7faf2bdb 136 if (EBIT_TEST(cc_mask, CC_PRIVATE))
f8309b15 137 return 0;
7faf2bdb 138 if (EBIT_TEST(cc_mask, CC_NO_CACHE))
f8309b15 139 return 0;
ed2f05a1 140 if (EBIT_TEST(cc_mask, CC_NO_STORE))
141 return 0;
a6dfe2d9 142 if (EBIT_TEST(httpState->request->flags, REQ_AUTH)) {
143 /*
144 * Responses to requests with authorization may be cached
68aefb7d 145 * only if a Cache-Control: public reply header is present.
a6dfe2d9 146 * RFC 2068, sec 14.9.4
147 */
148 if (!EBIT_TEST(cc_mask, CC_PUBLIC))
fee0cebb 149 return 0;
a6dfe2d9 150 }
f8309b15 151 /*
02fe0fbc 152 * We don't properly deal with Vary features yet, so we can't
153 * cache these
f8309b15 154 */
783e4699 155 if (httpHeaderHas(hdr, HDR_VARY))
156 return 0;
cb69b4c7 157 switch (httpState->entry->mem_obj->reply->sline.status) {
c54e9052 158 /* Responses that are cacheable */
19a04dac 159 case HTTP_OK:
160 case HTTP_NON_AUTHORITATIVE_INFORMATION:
161 case HTTP_MULTIPLE_CHOICES:
162 case HTTP_MOVED_PERMANENTLY:
163 case HTTP_GONE:
1294c0fc 164 /* don't cache objects from peers w/o LMT, Date, or Expires */
cb69b4c7 165 /* check that is it enough to check headers @?@ */
d8b249ef 166 if (rep->date > -1)
c54e9052 167 return 1;
d8b249ef 168 else if (rep->last_modified > -1)
c54e9052 169 return 1;
1294c0fc 170 else if (!httpState->peer)
c54e9052 171 return 1;
d8b249ef 172 /* @?@ (here and 302): invalid expires header compiles to squid_curtime */
173 else if (rep->expires > -1)
c54e9052 174 return 1;
c54e9052 175 else
176 return 0;
79d39a72 177 /* NOTREACHED */
c54e9052 178 break;
179 /* Responses that only are cacheable if the server says so */
19a04dac 180 case HTTP_MOVED_TEMPORARILY:
d8b249ef 181 if (rep->expires > -1)
c54e9052 182 return 1;
183 else
184 return 0;
79d39a72 185 /* NOTREACHED */
c54e9052 186 break;
187 /* Errors can be negatively cached */
19a04dac 188 case HTTP_NO_CONTENT:
189 case HTTP_USE_PROXY:
190 case HTTP_BAD_REQUEST:
191 case HTTP_FORBIDDEN:
192 case HTTP_NOT_FOUND:
193 case HTTP_METHOD_NOT_ALLOWED:
194 case HTTP_REQUEST_URI_TOO_LARGE:
195 case HTTP_INTERNAL_SERVER_ERROR:
196 case HTTP_NOT_IMPLEMENTED:
197 case HTTP_BAD_GATEWAY:
198 case HTTP_SERVICE_UNAVAILABLE:
199 case HTTP_GATEWAY_TIMEOUT:
c54e9052 200 return -1;
79d39a72 201 /* NOTREACHED */
c54e9052 202 break;
203 /* Some responses can never be cached */
0cdcddb9 204 case HTTP_PARTIAL_CONTENT: /* Not yet supported */
19a04dac 205 case HTTP_SEE_OTHER:
206 case HTTP_NOT_MODIFIED:
207 case HTTP_UNAUTHORIZED:
208 case HTTP_PROXY_AUTHENTICATION_REQUIRED:
0cdcddb9 209 case HTTP_INVALID_HEADER: /* Squid header parsing error */
c54e9052 210 default: /* Unknown status code */
211 return 0;
79d39a72 212 /* NOTREACHED */
c54e9052 213 break;
214 }
79d39a72 215 /* NOTREACHED */
c54e9052 216}
090089c4 217
cb69b4c7 218/* rewrite this later using new interfaces @?@ */
b8d8561b 219void
0ee4272b 220httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 221{
222 char *t = NULL;
30a4f2a8 223 StoreEntry *entry = httpState->entry;
d3fb4dea 224 int room;
225 int hdr_len;
cb69b4c7 226 HttpReply *reply = entry->mem_obj->reply;
b6cfb65c 227 debug(11, 3) ("httpProcessReplyHeader: key '%s'\n",
228 storeKeyText(entry->key));
e924600d 229 if (httpState->reply_hdr == NULL)
7021844c 230 httpState->reply_hdr = memAllocate(MEM_8K_BUF);
30a4f2a8 231 if (httpState->reply_hdr_state == 0) {
232 hdr_len = strlen(httpState->reply_hdr);
ed85b771 233 room = 8191 - hdr_len;
30a4f2a8 234 strncat(httpState->reply_hdr, buf, room < size ? room : size);
d3fb4dea 235 hdr_len += room < size ? room : size;
30a4f2a8 236 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
84fa351c 237 debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr);
30a4f2a8 238 httpState->reply_hdr_state += 2;
728da2ee 239 reply->sline.status = HTTP_INVALID_HEADER;
ed85b771 240 return;
d3fb4dea 241 }
d1a43e28 242 t = httpState->reply_hdr + hdr_len;
243 /* headers can be incomplete only if object still arriving */
2334c194 244 if (!httpState->eof) {
245 size_t k = headersEnd(httpState->reply_hdr, 8192);
246 if (0 == k)
d1a43e28 247 return; /* headers not complete */
2334c194 248 t = httpState->reply_hdr + k;
249 }
2285407f 250 *t = '\0';
30a4f2a8 251 httpState->reply_hdr_state++;
f5558c95 252 }
30a4f2a8 253 if (httpState->reply_hdr_state == 1) {
123abbe1 254 const Ctx ctx = ctx_enter(entry->mem_obj->url);
30a4f2a8 255 httpState->reply_hdr_state++;
a3d5953d 256 debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
30a4f2a8 257 httpState->reply_hdr);
258 /* Parse headers into reply structure */
2246b732 259 /* what happens if we fail to parse here? */
ee1679df 260 httpReplyParse(reply, httpState->reply_hdr); /* httpState->eof); */
ca98227c 261 storeTimestampsSet(entry);
30a4f2a8 262 /* Check if object is cacheable or not based on reply code */
cb69b4c7 263 debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status);
f8309b15 264 switch (httpCachableReply(httpState)) {
c54e9052 265 case 1:
266 httpMakePublic(entry);
30a4f2a8 267 break;
c54e9052 268 case 0:
269 httpMakePrivate(entry);
f5558c95 270 break;
c54e9052 271 case -1:
851eeef7 272 httpCacheNegatively(entry);
30a4f2a8 273 break;
c54e9052 274 default:
275 assert(0);
4e38e700 276 break;
f5558c95 277 }
0336304c 278 if (reply->cache_control) {
279 if (EBIT_TEST(reply->cache_control->mask, CC_PROXY_REVALIDATE))
0cdcddb9 280 EBIT_SET(entry->flag, ENTRY_REVALIDATE);
308e4a84 281 else if (EBIT_TEST(reply->cache_control->mask, CC_MUST_REVALIDATE))
0cdcddb9 282 EBIT_SET(entry->flag, ENTRY_REVALIDATE);
0336304c 283 }
9a47da71 284 if (EBIT_TEST(httpState->flags, HTTP_KEEPALIVE))
285 if (httpState->peer)
286 httpState->peer->stats.n_keepalives_sent++;
9f5a2895 287 if (reply->keep_alive)
1294c0fc 288 if (httpState->peer)
289 httpState->peer->stats.n_keepalives_recv++;
123abbe1 290 ctx_exit(ctx);
f5558c95 291 }
292}
293
603a02fd 294static int
295httpPconnTransferDone(HttpStateData * httpState)
296{
297 /* return 1 if we got the last of the data on a persistent connection */
298 MemObject *mem = httpState->entry->mem_obj;
cb69b4c7 299 HttpReply *reply = mem->reply;
51fdcbd5 300 debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd);
978e455f 301 /*
99edd1c3 302 * If we didn't send a keep-alive request header, then this
978e455f 303 * can not be a persistent connection.
304 */
79a15e0a 305 if (!EBIT_TEST(httpState->flags, HTTP_KEEPALIVE))
603a02fd 306 return 0;
9f5a2895 307 /*
308 * What does the reply have to say about keep-alive?
309 */
310 if (!reply->keep_alive)
311 return 0;
51fdcbd5 312 debug(11, 5) ("httpPconnTransferDone: content_length=%d\n",
d8b249ef 313 reply->content_length);
603a02fd 314 /*
978e455f 315 * Deal with gross HTTP stuff
316 * - If we haven't seen the end of the reply headers, we can't
317 * be persistent.
318 * - For "200 OK" check the content-length in the next block.
978e455f 319 * - For "204 No Content" (even with content-length) we're done.
320 * - For "304 Not Modified" (even with content-length) we're done.
a3c60429 321 * - 1XX replies never have a body; we're done.
978e455f 322 * - For HEAD requests with content-length we're done.
a3c60429 323 * - For all other replies, check content length in next block.
603a02fd 324 */
978e455f 325 if (httpState->reply_hdr_state < 2)
326 return 0;
cb69b4c7 327 else if (reply->sline.status == HTTP_OK)
a3c60429 328 (void) 0; /* common case, continue */
cb69b4c7 329 else if (reply->sline.status == HTTP_NO_CONTENT)
978e455f 330 return 1;
cb69b4c7 331 else if (reply->sline.status == HTTP_NOT_MODIFIED)
978e455f 332 return 1;
cb69b4c7 333 else if (reply->sline.status < HTTP_OK)
a3c60429 334 return 1;
978e455f 335 else if (httpState->request->method == METHOD_HEAD)
336 return 1;
603a02fd 337 /*
a3c60429 338 * If there is no content-length, then we can't be
978e455f 339 * persistent. If there is a content length, then we must
340 * wait until we've seen the end of the body.
603a02fd 341 */
d8b249ef 342 if (reply->content_length < 0)
603a02fd 343 return 0;
d8b249ef 344 else if (mem->inmem_hi < reply->content_length + reply->hdr_sz)
603a02fd 345 return 0;
978e455f 346 else
b34ed725 347 return 1;
603a02fd 348}
090089c4 349
350/* This will be called when data is ready to be read from fd. Read until
351 * error or connection closed. */
f5558c95 352/* XXX this function is too long! */
b8d8561b 353static void
b177367b 354httpReadReply(int fd, void *data)
090089c4 355{
b177367b 356 HttpStateData *httpState = data;
95d659f0 357 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
bfcaf585 358 StoreEntry *entry = httpState->entry;
603a02fd 359 const request_t *request = httpState->request;
090089c4 360 int len;
30a4f2a8 361 int bin;
090089c4 362 int clen;
447e176b 363 size_t read_sz;
364#if DELAY_POOLS
365 delay_id delay_id = delayMostBytesAllowed(entry->mem_obj);
366#endif
41462d93 367 if (fwdAbortFetch(entry)) {
9b312a19 368 storeAbort(entry, 0);
a3d5953d 369 comm_close(fd);
370 return;
234967c9 371 }
372 /* check if we want to defer reading */
1513873c 373 errno = 0;
447e176b 374 read_sz = SQUID_TCP_SO_RCVBUF;
375#if DELAY_POOLS
56e64999 376 read_sz = delayBytesWanted(delay_id, 1, read_sz);
447e176b 377#endif
378 len = read(fd, buf, read_sz);
a3d5953d 379 debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 380 if (len > 0) {
ee1679df 381 fd_bytes(fd, len, FD_READ);
447e176b 382#if DELAY_POOLS
383 delayBytesIn(delay_id, len);
384#endif
a0f32775 385 kb_incr(&Counter.server.all.kbytes_in, len);
386 kb_incr(&Counter.server.http.kbytes_in, len);
4f92c80c 387 commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
4a63c85f 388 IOStats.Http.reads++;
30a4f2a8 389 for (clen = len - 1, bin = 0; clen; bin++)
390 clen >>= 1;
391 IOStats.Http.read_hist[bin]++;
392 }
5ede6c8f 393 if (!httpState->reply_hdr && len > 0) {
394 /* Skip whitespace */
395 while (len > 0 && isspace(*buf))
396 xmemmove(buf, buf + 1, len--);
397 if (len == 0) {
398 /* Continue to read... */
399 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
400 return;
401 }
402 }
ba718c8f 403 if (len < 0) {
55cb44f1 404 debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n",
405 fd, xstrerror());
b224ea98 406 if (ignoreErrno(errno)) {
9b312a19 407 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
910169e5 408 } else if (entry->mem_obj->inmem_hi == 0) {
409 fwdFail(httpState->fwdState, ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR, errno);
1afe05c5 410 comm_close(fd);
090089c4 411 } else {
55cb44f1 412 storeAbort(entry, 0);
0d4d4170 413 comm_close(fd);
090089c4 414 }
8350fe9b 415 } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
910169e5 416 fwdFail(httpState->fwdState, ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE, errno);
417 httpState->eof = 1;
418 comm_close(fd);
090089c4 419 } else if (len == 0) {
420 /* Connection closed; retrieval done. */
f86a6a46 421 httpState->eof = 1;
d1a43e28 422 if (httpState->reply_hdr_state < 2)
b34ed725 423 /*
424 * Yes Henrik, there is a point to doing this. When we
425 * called httpProcessReplyHeader() before, we didn't find
426 * the end of headers, but now we are definately at EOF, so
427 * we want to process the reply headers.
428 */
d1a43e28 429 httpProcessReplyHeader(httpState, buf, len);
d1a43e28 430 storeComplete(entry); /* deallocates mem_obj->request */
0d4d4170 431 comm_close(fd);
090089c4 432 } else {
d1a43e28 433 if (httpState->reply_hdr_state < 2)
30a4f2a8 434 httpProcessReplyHeader(httpState, buf, len);
620da955 435 storeAppend(entry, buf, len);
9d66d521 436#ifdef OPTIMISTIC_IO
437 if (entry->store_status == STORE_ABORTED) {
438 /*
439 * the above storeAppend() call could ABORT this entry,
440 * in that case, the server FD should already be closed.
441 * there's nothing for us to do.
442 */
443 (void) 0;
444 } else
445#endif
603a02fd 446 if (httpPconnTransferDone(httpState)) {
5b29969a 447 /* yes we have to clear all these! */
8796b9e9 448 commSetDefer(fd, NULL, NULL);
5b29969a 449 commSetTimeout(fd, -1, NULL, NULL);
450 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
603a02fd 451 comm_remove_close_handler(fd, httpStateFree, httpState);
452 storeComplete(entry); /* deallocates mem_obj->request */
52f0d243 453 /* call storeComplete BEFORE fwdUnregister or else fwdUnregister
454 * will storeAbort */
455 fwdUnregister(fd, httpState->fwdState);
8796b9e9 456 pconnPush(fd, request->host, request->port);
603a02fd 457 httpState->fd = -1;
458 httpStateFree(-1, httpState);
459 } else {
9f5a2895 460 /* Wait for EOF condition */
603a02fd 461 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
462 }
090089c4 463 }
464}
465
466/* This will be called when request write is complete. Schedule read of
467 * reply. */
b8d8561b 468static void
79a15e0a 469httpSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
090089c4 470{
30a4f2a8 471 HttpStateData *httpState = data;
9b312a19 472 StoreEntry *entry = httpState->entry;
473 ErrorState *err;
a3d5953d 474 debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n",
090089c4 475 fd, size, errflag);
ee1679df 476 if (size > 0) {
477 fd_bytes(fd, size, FD_WRITE);
a0f32775 478 kb_incr(&Counter.server.all.kbytes_out, size);
399e85ea 479 kb_incr(&Counter.server.http.kbytes_out, size);
ee1679df 480 }
ea3a2a69 481 if (errflag == COMM_ERR_CLOSING)
482 return;
090089c4 483 if (errflag) {
fe40a877 484 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 485 err->xerrno = errno;
79a15e0a 486 err->request = requestLink(httpState->orig_request);
9b312a19 487 errorAppendEntry(entry, err);
0d4d4170 488 comm_close(fd);
090089c4 489 return;
490 } else {
491 /* Schedule read reply. */
b177367b 492 commSetSelect(fd,
019dd986 493 COMM_SELECT_READ,
b177367b 494 httpReadReply,
cd1fb0eb 495 httpState, 0);
41462d93 496 commSetDefer(fd, fwdCheckDeferRead, entry);
090089c4 497 }
498}
499
99edd1c3 500/*
501 * build request headers and append them to a given MemBuf
502 * used by httpBuildRequestPrefix()
503 * note: calls httpHeaderInit(), the caller is responsible for Clean()-ing
504 */
e1e72f06 505void
6bf8443a 506httpBuildRequestHeader(request_t * request,
507 request_t * orig_request,
508 StoreEntry * entry,
5999b776 509 HttpHeader * hdr_out,
603a02fd 510 int cfd,
511 int flags)
6bf8443a 512{
99edd1c3 513 /* building buffer for complex strings */
5999b776 514#define BBUF_SZ (MAX_URL+32)
99edd1c3 515 LOCAL_ARRAY(char, bbuf, BBUF_SZ);
516 String strConnection = StringNull;
517 const HttpHeader *hdr_in = &orig_request->header;
d192d11f 518 int filter_range;
99edd1c3 519 const HttpHeaderEntry *e;
520 HttpHeaderPos pos = HttpHeaderInitPos;
2246b732 521 httpHeaderInit(hdr_out, hoRequest);
99edd1c3 522 /* append our IMS header */
e17dc75c 523 if (entry && entry->lastmod > -1 && request->method == METHOD_GET)
99edd1c3 524 httpHeaderPutTime(hdr_out, HDR_IF_MODIFIED_SINCE, entry->lastmod);
525
137ee196 526 /* decide if we want to filter out Range specs
527 * no reason to filter out if the reply will not be cachable
528 * or if we cannot parse the specs */
d192d11f 529 filter_range =
530 orig_request->range && EBIT_TEST(orig_request->flags, REQ_CACHABLE);
137ee196 531
99edd1c3 532 strConnection = httpHeaderGetList(hdr_in, HDR_CONNECTION);
533 while ((e = httpHeaderGetEntry(hdr_in, &pos))) {
534 debug(11, 5) ("httpBuildRequestHeader: %s: %s\n",
535 strBuf(e->name), strBuf(e->value));
536 if (!httpRequestHdrAllowed(e, &strConnection))
6bf8443a 537 continue;
99edd1c3 538 switch (e->id) {
539 case HDR_PROXY_AUTHORIZATION:
afe95a7e 540 /* If we're not going to do proxy auth, then it must be passed on */
99edd1c3 541 if (!EBIT_TEST(request->flags, REQ_USED_PROXY_AUTH))
542 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
543 break;
544 case HDR_HOST:
77ed547a 545 /* Don't use client's Host: header for redirected requests */
99edd1c3 546 if (!EBIT_TEST(request->flags, REQ_REDIRECTED))
547 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
548 break;
549 case HDR_IF_MODIFIED_SINCE:
550 /* append unless we added our own;
551 * note: at most one client's ims header can pass through */
552 if (!httpHeaderHas(hdr_out, HDR_IF_MODIFIED_SINCE))
553 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
554 break;
555 case HDR_MAX_FORWARDS:
b3b64e58 556 if (orig_request->method == METHOD_TRACE) {
99edd1c3 557 /* sacrificing efficiency over clarity, etc. */
558 const int hops = httpHeaderGetInt(hdr_in, HDR_MAX_FORWARDS);
559 if (hops > 0)
5999b776 560 httpHeaderPutInt(hdr_out, HDR_MAX_FORWARDS, hops - 1);
b3b64e58 561 }
99edd1c3 562 break;
137ee196 563 case HDR_RANGE:
a9771e51 564 case HDR_IF_RANGE:
d192d11f 565 if (!filter_range)
137ee196 566 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
567 break;
99edd1c3 568 case HDR_PROXY_CONNECTION:
569 case HDR_CONNECTION:
570 case HDR_VIA:
571 case HDR_X_FORWARDED_FOR:
572 case HDR_CACHE_CONTROL:
573 /* append these after the loop if needed */
574 break;
575 default:
576 /* pass on all other header fields */
577 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
66f7337b 578 }
88738790 579 }
99edd1c3 580
581 /* append fake user agent if configured and
582 * the real one is not supplied by the client */
583 if (Config.fake_ua && !httpHeaderHas(hdr_out, HDR_USER_AGENT))
584 httpHeaderPutStr(hdr_out, HDR_USER_AGENT, Config.fake_ua);
585
586 /* append Via */
587 {
588 String strVia = httpHeaderGetList(hdr_in, HDR_VIA);
589 snprintf(bbuf, BBUF_SZ, "%3.1f %s", orig_request->http_ver, ThisCache);
590 strListAdd(&strVia, bbuf, ',');
591 httpHeaderPutStr(hdr_out, HDR_VIA, strBuf(strVia));
592 stringClean(&strVia);
6bf8443a 593 }
99edd1c3 594 /* append X-Forwarded-For */
595 {
596 String strFwd = httpHeaderGetList(hdr_in, HDR_X_FORWARDED_FOR);
597 strListAdd(&strFwd, (cfd < 0 ? "unknown" : fd_table[cfd].ipaddr), ',');
598 httpHeaderPutStr(hdr_out, HDR_X_FORWARDED_FOR, strBuf(strFwd));
599 stringClean(&strFwd);
600 }
601 /* append Host if not there already */
602 if (!httpHeaderHas(hdr_out, HDR_HOST)) {
603 /* use port# only if not default */
604 if (orig_request->port == urlDefaultPort(orig_request->protocol)) {
605 httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->host);
606 } else {
2246b732 607 httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d",
99edd1c3 608 orig_request->host, (int) orig_request->port);
99edd1c3 609 }
6bf8443a 610 }
99edd1c3 611 /* append Cache-Control, add max-age if not there already */
612 {
613 HttpHdrCc *cc = httpHeaderGetCc(hdr_in);
614 if (!cc)
615 cc = httpHdrCcCreate();
616 if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) {
9b5d1d21 617 const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request);
99edd1c3 618 httpHdrCcSetMaxAge(cc, getMaxAge(url));
619 if (strLen(request->urlpath))
620 assert(strstr(url, strBuf(request->urlpath)));
621 }
622 httpHeaderPutCc(hdr_out, cc);
623 httpHdrCcDestroy(cc);
6bf8443a 624 }
99edd1c3 625 /* maybe append Connection: keep-alive */
79a15e0a 626 if (EBIT_TEST(flags, HTTP_KEEPALIVE)) {
627 if (EBIT_TEST(flags, HTTP_PROXYING)) {
99edd1c3 628 httpHeaderPutStr(hdr_out, HDR_PROXY_CONNECTION, "keep-alive");
603a02fd 629 } else {
99edd1c3 630 httpHeaderPutStr(hdr_out, HDR_CONNECTION, "keep-alive");
603a02fd 631 }
603a02fd 632 }
99edd1c3 633 stringClean(&strConnection);
634}
635
636/* build request prefix and append it to a given MemBuf;
637 * return the length of the prefix */
638size_t
639httpBuildRequestPrefix(request_t * request,
640 request_t * orig_request,
641 StoreEntry * entry,
5999b776 642 MemBuf * mb,
99edd1c3 643 int cfd,
644 int flags)
645{
646 const int offset = mb->size;
647 memBufPrintf(mb, "%s %s HTTP/1.0\r\n",
648 RequestMethodStr[request->method],
649 strLen(request->urlpath) ? strBuf(request->urlpath) : "/");
650 /* build and pack headers */
651 {
652 HttpHeader hdr;
653 Packer p;
654 httpBuildRequestHeader(request, orig_request, entry, &hdr, cfd, flags);
655 packerToMemInit(&p, mb);
656 httpHeaderPackInto(&hdr, &p);
657 httpHeaderClean(&hdr);
658 packerClean(&p);
9d9d144b 659 }
99edd1c3 660 /* append header terminator */
661 memBufAppend(mb, "\r\n", 2);
662 return mb->size - offset;
6bf8443a 663}
664
090089c4 665/* This will be called when connect completes. Write request. */
b8d8561b 666static void
b177367b 667httpSendRequest(int fd, void *data)
090089c4 668{
b177367b 669 HttpStateData *httpState = data;
99edd1c3 670 MemBuf mb;
30a4f2a8 671 request_t *req = httpState->request;
620da955 672 StoreEntry *entry = httpState->entry;
2a26c096 673 int cfd;
1294c0fc 674 peer *p = httpState->peer;
901e234d 675 CWCB *sendHeaderDone;
090089c4 676
a3d5953d 677 debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
090089c4 678
efb9218c 679 if (pumpMethod(req->method))
7db8b16d 680 sendHeaderDone = httpSendRequestEntry;
681 else
682 sendHeaderDone = httpSendComplete;
54220df8 683
2a26c096 684 if (!opt_forwarded_for)
6bf8443a 685 cfd = -1;
2a26c096 686 else if (entry->mem_obj == NULL)
6bf8443a 687 cfd = -1;
2a26c096 688 else
382d851a 689 cfd = entry->mem_obj->fd;
b0a1e5bf 690 assert(-1 == cfd || FD_SOCKET == fd_table[cfd].type);
1294c0fc 691 if (p != NULL)
79a15e0a 692 EBIT_SET(httpState->flags, HTTP_PROXYING);
efb9218c 693 /*
99edd1c3 694 * Is keep-alive okay for all request methods?
efb9218c 695 */
696 if (p == NULL)
697 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
698 else if (p->stats.n_keepalives_sent < 10)
699 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
700 else if ((double) p->stats.n_keepalives_recv / (double) p->stats.n_keepalives_sent > 0.50)
701 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
99edd1c3 702 memBufDefInit(&mb);
703 httpBuildRequestPrefix(req,
79a15e0a 704 httpState->orig_request,
6bf8443a 705 entry,
99edd1c3 706 &mb,
603a02fd 707 cfd,
708 httpState->flags);
99edd1c3 709 debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, mb.buf);
710 comm_write_mbuf(fd, mb, sendHeaderDone, httpState);
090089c4 711}
712
910169e5 713void
714httpStart(FwdState * fwdState, int fd)
603a02fd 715{
cb87dab6 716 HttpStateData *httpState = memAllocate(MEM_HTTP_STATE_DATA);
910169e5 717 request_t *proxy_req;
718 request_t *orig_req = fwdState->request;
719 debug(11, 3) ("httpStart: \"%s %s\"\n",
720 RequestMethodStr[orig_req->method],
721 storeUrl(fwdState->entry));
cb87dab6 722 cbdataAdd(httpState, MEM_HTTP_STATE_DATA);
910169e5 723 storeLockObject(fwdState->entry);
724 httpState->fwdState = fwdState;
725 httpState->entry = fwdState->entry;
9e4ad609 726 httpState->fd = fd;
910169e5 727 if (fwdState->servers)
728 httpState->peer = fwdState->servers->peer; /* might be NULL */
729 if (httpState->peer) {
730 proxy_req = requestCreate(orig_req->method,
731 PROTO_NONE, storeUrl(httpState->entry));
732 xstrncpy(proxy_req->host, httpState->peer->host, SQUIDHOSTNAMELEN);
733 proxy_req->port = httpState->peer->http_port;
23e8446b 734 proxy_req->flags = orig_req->flags;
910169e5 735 httpState->request = requestLink(proxy_req);
910169e5 736 httpState->orig_request = requestLink(orig_req);
737 EBIT_SET(proxy_req->flags, REQ_PROXYING);
738 /*
739 * This NEIGHBOR_PROXY_ONLY check probably shouldn't be here.
740 * We might end up getting the object from somewhere else if,
741 * for example, the request to this neighbor fails.
742 */
743 if (EBIT_TEST(httpState->peer->options, NEIGHBOR_PROXY_ONLY))
744 storeReleaseRequest(httpState->entry);
95e36d02 745#if DELAY_POOLS
746 if (EBIT_TEST(httpState->peer->options, NEIGHBOR_NO_DELAY)) {
447e176b 747 proxy_req->delay_id = 0;
95e36d02 748 } else {
447e176b 749 proxy_req->delay_id = orig_req->delay_id;
95e36d02 750 }
751#endif
603a02fd 752 } else {
910169e5 753 httpState->request = requestLink(orig_req);
754 httpState->orig_request = requestLink(orig_req);
603a02fd 755 }
910169e5 756 /*
757 * register the handler to free HTTP state data when the FD closes
758 */
759 comm_add_close_handler(fd, httpStateFree, httpState);
a0f32775 760 Counter.server.all.requests++;
761 Counter.server.http.requests++;
41462d93 762 httpConnectDone(fd, COMM_OK, httpState);
e5f6c5c2 763}
764
765static void
766httpConnectDone(int fd, int status, void *data)
767{
768 HttpStateData *httpState = data;
769 request_t *request = httpState->request;
770 StoreEntry *entry = httpState->entry;
9b312a19 771 ErrorState *err;
edeb28fd 772 if (status == COMM_ERR_DNS) {
a3d5953d 773 debug(11, 4) ("httpConnectDone: Unknown host: %s\n", request->host);
fe40a877 774 err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
9b312a19 775 err->dnsserver_msg = xstrdup(dns_error_message);
79a15e0a 776 err->request = requestLink(httpState->orig_request);
9b312a19 777 errorAppendEntry(entry, err);
edeb28fd 778 comm_close(fd);
779 } else if (status != COMM_OK) {
fe40a877 780 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
c45ed9ad 781 err->xerrno = errno;
9b312a19 782 err->host = xstrdup(request->host);
783 err->port = request->port;
79a15e0a 784 err->request = requestLink(httpState->orig_request);
9b312a19 785 errorAppendEntry(entry, err);
1294c0fc 786 if (httpState->peer)
787 peerCheckConnectStart(httpState->peer);
e5f6c5c2 788 comm_close(fd);
789 } else {
bfcaf585 790 commSetSelect(fd, COMM_SELECT_WRITE, httpSendRequest, httpState, 0);
86cf9987 791 commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState);
090089c4 792 }
090089c4 793}
794
54220df8 795static void
7db8b16d 796httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data)
54220df8 797{
798 HttpStateData *httpState = data;
799 StoreEntry *entry = httpState->entry;
800 ErrorState *err;
801 debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n",
7db8b16d 802 fd, size, errflag);
54220df8 803 if (size > 0) {
7db8b16d 804 fd_bytes(fd, size, FD_WRITE);
54220df8 805 kb_incr(&Counter.server.all.kbytes_out, size);
806 kb_incr(&Counter.server.http.kbytes_out, size);
807 }
808 if (errflag == COMM_ERR_CLOSING)
7db8b16d 809 return;
54220df8 810 if (errflag) {
7db8b16d 811 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
812 err->xerrno = errno;
813 err->request = requestLink(httpState->orig_request);
814 errorAppendEntry(entry, err);
815 comm_close(fd);
816 return;
54220df8 817 }
7db8b16d 818 pumpStart(fd, entry, httpState->orig_request, httpSendComplete, httpState);
54220df8 819}