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