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