]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
Fix a minor compiler warning
[thirdparty/squid.git] / src / http.cc
CommitLineData
da2b3a17 1
30a4f2a8 2/*
add2192d 3 * $Id: http.cc,v 1.387 2002/02/26 15:48:14 adrian Exp $
30a4f2a8 4 *
5 * DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
6 * AUTHOR: Harvest Derived
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
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 CWCB httpSendComplete;
54220df8 46static CWCB httpSendRequestEntry;
47
9e4ad609 48static PF httpReadReply;
b6a2f15e 49static void httpSendRequest(HttpStateData *);
9e4ad609 50static PF httpStateFree;
51static PF httpTimeout;
f5b8bbc4 52static void httpCacheNegatively(StoreEntry *);
53static void httpMakePrivate(StoreEntry *);
54static void httpMakePublic(StoreEntry *);
f8309b15 55static int httpCachableReply(HttpStateData *);
f9cece6e 56static void httpMaybeRemovePublic(StoreEntry *, http_status);
b8d8561b 57
b177367b 58static void
59715b38 59httpStateFree(int fd, void *data)
f5558c95 60{
b177367b 61 HttpStateData *httpState = data;
59715b38 62#if DELAY_POOLS
63 delayClearNoDelay(fd);
64#endif
0d4d4170 65 if (httpState == NULL)
b177367b 66 return;
f88211e8 67 storeUnlockObject(httpState->entry);
0d4d4170 68 if (httpState->reply_hdr) {
db1cd23c 69 memFree(httpState->reply_hdr, MEM_8K_BUF);
0d4d4170 70 httpState->reply_hdr = NULL;
71 }
30a4f2a8 72 requestUnlink(httpState->request);
20cc1450 73 requestUnlink(httpState->orig_request);
7dd44885 74 httpState->request = NULL;
75 httpState->orig_request = NULL;
76 cbdataFree(httpState);
f5558c95 77}
78
b8d8561b 79int
75e88d56 80httpCachable(method_t method)
090089c4 81{
090089c4 82 /* GET and HEAD are cachable. Others are not. */
6eb42cae 83 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 84 return 0;
090089c4 85 /* else cachable */
86 return 1;
87}
88
b8d8561b 89static void
5c5783a2 90httpTimeout(int fd, void *data)
090089c4 91{
b177367b 92 HttpStateData *httpState = data;
593c9a75 93 StoreEntry *entry = httpState->entry;
9fb13bb6 94 debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
12158bdc 95 if (entry->store_status == STORE_PENDING) {
96 if (entry->mem_obj->inmem_hi == 0) {
97 fwdFail(httpState->fwd,
98 errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT));
99 }
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{
d46a87a8 108 if (EBIT_TEST(entry->flags, 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);
30a4f2a8 117 storeReleaseRequest(entry); /* delete object when not used */
f3e570e9 118 /* storeReleaseRequest clears ENTRY_CACHABLE flag */
30a4f2a8 119}
120
121/* This object may be negatively cached */
b8d8561b 122static void
123httpCacheNegatively(StoreEntry * entry)
30a4f2a8 124{
79b5cc5f 125 storeNegativeCache(entry);
d46a87a8 126 if (EBIT_TEST(entry->flags, ENTRY_CACHABLE))
30a4f2a8 127 storeSetPublicKey(entry);
30a4f2a8 128}
129
f9cece6e 130static void
131httpMaybeRemovePublic(StoreEntry * e, http_status status)
132{
133 int remove = 0;
7e3ce7b9 134 int forbidden = 0;
f9cece6e 135 StoreEntry *pe;
d46a87a8 136 if (!EBIT_TEST(e->flags, KEY_PRIVATE))
9dc1202d 137 return;
f9cece6e 138 switch (status) {
139 case HTTP_OK:
140 case HTTP_NON_AUTHORITATIVE_INFORMATION:
141 case HTTP_MULTIPLE_CHOICES:
142 case HTTP_MOVED_PERMANENTLY:
143 case HTTP_MOVED_TEMPORARILY:
f9cece6e 144 case HTTP_GONE:
7e3ce7b9 145 case HTTP_NOT_FOUND:
f9cece6e 146 remove = 1;
147 break;
7e3ce7b9 148 case HTTP_FORBIDDEN:
149 case HTTP_METHOD_NOT_ALLOWED:
150 forbidden = 1;
151 break;
f9cece6e 152#if WORK_IN_PROGRESS
c8fd0193 153 case HTTP_UNAUTHORIZED:
7e3ce7b9 154 forbidden = 1;
f9cece6e 155 break;
156#endif
157 default:
7e3ce7b9 158#if QUESTIONABLE
159 /*
160 * Any 2xx response should eject previously cached entities...
161 */
162 if (status >= 200 && status < 300)
163 remove = 1;
164#endif
f9cece6e 165 break;
166 }
7e3ce7b9 167 if (!remove && !forbidden)
f9cece6e 168 return;
169 assert(e->mem_obj);
f66a9ef4 170 if (e->mem_obj->request)
171 pe = storeGetPublicByRequest(e->mem_obj->request);
172 else
173 pe = storeGetPublic(e->mem_obj->url, e->mem_obj->method);
174 if (pe != NULL) {
0856d155 175 assert(e != pe);
176 storeRelease(pe);
177 }
7e3ce7b9 178 /*
179 * Also remove any cached HEAD response in case the object has
180 * changed.
181 */
f66a9ef4 182 if (e->mem_obj->request)
183 pe = storeGetPublicByRequestMethod(e->mem_obj->request, METHOD_HEAD);
184 else
185 pe = storeGetPublic(e->mem_obj->url, METHOD_HEAD);
186 if (pe != NULL) {
7e3ce7b9 187 assert(e != pe);
188 storeRelease(pe);
189 }
190 if (forbidden)
191 return;
192 switch (e->mem_obj->method) {
193 case METHOD_PUT:
194 case METHOD_DELETE:
195 case METHOD_PROPPATCH:
196 case METHOD_MKCOL:
197 case METHOD_MOVE:
42b51993 198 case METHOD_BMOVE:
199 case METHOD_BDELETE:
7e3ce7b9 200 /*
201 * Remove any cached GET object if it is beleived that the
202 * object may have changed as a result of other methods
203 */
f66a9ef4 204 if (e->mem_obj->request)
205 pe = storeGetPublicByRequestMethod(e->mem_obj->request, METHOD_GET);
206 else
207 pe = storeGetPublic(e->mem_obj->url, METHOD_GET);
208 if (pe != NULL) {
0856d155 209 assert(e != pe);
210 storeRelease(pe);
211 }
7e3ce7b9 212 break;
0856d155 213 }
f9cece6e 214}
215
f8309b15 216static int
217httpCachableReply(HttpStateData * httpState)
c54e9052 218{
d8b249ef 219 HttpReply *rep = httpState->entry->mem_obj->reply;
220 HttpHeader *hdr = &rep->header;
221 const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0;
c68e9c6b 222 const char *v;
7faf2bdb 223 if (EBIT_TEST(cc_mask, CC_PRIVATE))
f8309b15 224 return 0;
7faf2bdb 225 if (EBIT_TEST(cc_mask, CC_NO_CACHE))
f8309b15 226 return 0;
ed2f05a1 227 if (EBIT_TEST(cc_mask, CC_NO_STORE))
228 return 0;
92695e5e 229 if (httpState->request->flags.auth) {
a6dfe2d9 230 /*
231 * Responses to requests with authorization may be cached
68aefb7d 232 * only if a Cache-Control: public reply header is present.
a6dfe2d9 233 * RFC 2068, sec 14.9.4
234 */
235 if (!EBIT_TEST(cc_mask, CC_PUBLIC))
fee0cebb 236 return 0;
a6dfe2d9 237 }
c68e9c6b 238 /* Pragma: no-cache in _replies_ is not documented in HTTP,
239 * but servers like "Active Imaging Webcast/2.0" sure do use it */
240 if (httpHeaderHas(hdr, HDR_PRAGMA)) {
241 String s = httpHeaderGetList(hdr, HDR_PRAGMA);
242 const int no_cache = strListIsMember(&s, "no-cache", ',');
243 stringClean(&s);
244 if (no_cache)
245 return 0;
246 }
247 /*
248 * The "multipart/x-mixed-replace" content type is used for
249 * continuous push replies. These are generally dynamic and
250 * probably should not be cachable
251 */
252 if ((v = httpHeaderGetStr(hdr, HDR_CONTENT_TYPE)))
253 if (!strncasecmp(v, "multipart/x-mixed-replace", 25))
254 return 0;
cb69b4c7 255 switch (httpState->entry->mem_obj->reply->sline.status) {
c54e9052 256 /* Responses that are cacheable */
19a04dac 257 case HTTP_OK:
258 case HTTP_NON_AUTHORITATIVE_INFORMATION:
259 case HTTP_MULTIPLE_CHOICES:
260 case HTTP_MOVED_PERMANENTLY:
261 case HTTP_GONE:
cfa9f1cb 262 /*
263 * Don't cache objects that need to be refreshed on next request,
264 * unless we know how to refresh it.
265 */
266 if (!refreshIsCachable(httpState->entry))
267 return 0;
1294c0fc 268 /* don't cache objects from peers w/o LMT, Date, or Expires */
cb69b4c7 269 /* check that is it enough to check headers @?@ */
d8b249ef 270 if (rep->date > -1)
c54e9052 271 return 1;
d8b249ef 272 else if (rep->last_modified > -1)
c54e9052 273 return 1;
1294c0fc 274 else if (!httpState->peer)
c54e9052 275 return 1;
d8b249ef 276 /* @?@ (here and 302): invalid expires header compiles to squid_curtime */
277 else if (rep->expires > -1)
c54e9052 278 return 1;
c54e9052 279 else
280 return 0;
79d39a72 281 /* NOTREACHED */
c54e9052 282 break;
283 /* Responses that only are cacheable if the server says so */
19a04dac 284 case HTTP_MOVED_TEMPORARILY:
d8b249ef 285 if (rep->expires > -1)
c54e9052 286 return 1;
287 else
288 return 0;
79d39a72 289 /* NOTREACHED */
c54e9052 290 break;
291 /* Errors can be negatively cached */
19a04dac 292 case HTTP_NO_CONTENT:
293 case HTTP_USE_PROXY:
294 case HTTP_BAD_REQUEST:
295 case HTTP_FORBIDDEN:
296 case HTTP_NOT_FOUND:
297 case HTTP_METHOD_NOT_ALLOWED:
298 case HTTP_REQUEST_URI_TOO_LARGE:
299 case HTTP_INTERNAL_SERVER_ERROR:
300 case HTTP_NOT_IMPLEMENTED:
301 case HTTP_BAD_GATEWAY:
302 case HTTP_SERVICE_UNAVAILABLE:
303 case HTTP_GATEWAY_TIMEOUT:
c54e9052 304 return -1;
79d39a72 305 /* NOTREACHED */
c54e9052 306 break;
307 /* Some responses can never be cached */
0cdcddb9 308 case HTTP_PARTIAL_CONTENT: /* Not yet supported */
19a04dac 309 case HTTP_SEE_OTHER:
310 case HTTP_NOT_MODIFIED:
311 case HTTP_UNAUTHORIZED:
312 case HTTP_PROXY_AUTHENTICATION_REQUIRED:
0cdcddb9 313 case HTTP_INVALID_HEADER: /* Squid header parsing error */
c54e9052 314 default: /* Unknown status code */
315 return 0;
79d39a72 316 /* NOTREACHED */
c54e9052 317 break;
318 }
79d39a72 319 /* NOTREACHED */
c54e9052 320}
090089c4 321
f66a9ef4 322/*
323 * For Vary, store the relevant request headers as
324 * virtual headers in the reply
325 * Returns false if the variance cannot be stored
326 */
327const char *
328httpMakeVaryMark(request_t * request, HttpReply * reply)
329{
f66a9ef4 330 String vary, hdr;
331 const char *pos = NULL;
332 const char *item;
333 const char *value;
334 int ilen;
335 static String vstr =
336 {0, 0, NULL};
337
338 stringClean(&vstr);
339 vary = httpHeaderGetList(&reply->header, HDR_VARY);
340 while (strListGetItem(&vary, ',', &item, &ilen, &pos)) {
341 char *name = xmalloc(ilen + 1);
342 xstrncpy(name, item, ilen + 1);
343 Tolower(name);
344 strListAdd(&vstr, name, ',');
345 hdr = httpHeaderGetByName(&request->header, name);
346 safe_free(name);
347 value = strBuf(hdr);
348 if (value) {
349 value = rfc1738_escape(value);
350 stringAppend(&vstr, "=\"", 2);
351 stringAppend(&vstr, value, strlen(value));
352 stringAppend(&vstr, "\"", 1);
353 }
354 stringClean(&hdr);
355 }
356 stringClean(&vary);
357#if X_ACCELERATOR_VARY
358 vary = httpHeaderGetList(&reply->header, HDR_X_ACCELERATOR_VARY);
359 while (strListGetItem(&vary, ',', &item, &ilen, &pos)) {
360 char *name = xmalloc(ilen + 1);
361 xstrncpy(name, item, ilen + 1);
362 Tolower(name);
363 strListAdd(&vstr, name, ',');
364 hdr = httpHeaderGetByName(&request->header, name);
365 safe_free(name);
366 value = strBuf(hdr);
367 if (value) {
368 value = rfc1738_escape(value);
369 stringAppend(&vstr, "=\"", 2);
370 stringAppend(&vstr, value, strlen(value));
371 stringAppend(&vstr, "\"", 1);
372 }
373 stringClean(&hdr);
374 }
375 stringClean(&vary);
376#endif
3a114cc1 377 debug(11, 3) ("httpMakeVaryMark: %s\n", strBuf(vstr));
f66a9ef4 378 return strBuf(vstr);
379}
380
cb69b4c7 381/* rewrite this later using new interfaces @?@ */
b8d8561b 382void
0ee4272b 383httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 384{
385 char *t = NULL;
30a4f2a8 386 StoreEntry *entry = httpState->entry;
d3fb4dea 387 int room;
9bc73deb 388 size_t hdr_len;
cb69b4c7 389 HttpReply *reply = entry->mem_obj->reply;
9bc73deb 390 Ctx ctx;
b6cfb65c 391 debug(11, 3) ("httpProcessReplyHeader: key '%s'\n",
186477c1 392 storeKeyText(entry->hash.key));
e924600d 393 if (httpState->reply_hdr == NULL)
7021844c 394 httpState->reply_hdr = memAllocate(MEM_8K_BUF);
9bc73deb 395 assert(httpState->reply_hdr_state == 0);
304d289e 396 hdr_len = httpState->reply_hdr_size;
9bc73deb 397 room = 8191 - hdr_len;
cac03dd2 398 xmemcpy(httpState->reply_hdr + hdr_len, buf, room < size ? room : size);
9bc73deb 399 hdr_len += room < size ? room : size;
304d289e 400 httpState->reply_hdr[hdr_len] = '\0';
401 httpState->reply_hdr_size = hdr_len;
9bc73deb 402 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
403 debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr);
404 httpState->reply_hdr_state += 2;
405 reply->sline.status = HTTP_INVALID_HEADER;
406 return;
f5558c95 407 }
9bc73deb 408 t = httpState->reply_hdr + hdr_len;
409 /* headers can be incomplete only if object still arriving */
410 if (!httpState->eof) {
d20b1cd0 411 size_t k = headersEnd(httpState->reply_hdr, 8192);
412 if (0 == k)
413 return; /* headers not complete */
9bc73deb 414 t = httpState->reply_hdr + k;
415 }
416 *t = '\0';
417 httpState->reply_hdr_state++;
418 assert(httpState->reply_hdr_state == 1);
419 ctx = ctx_enter(entry->mem_obj->url);
420 httpState->reply_hdr_state++;
421 debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
422 httpState->reply_hdr);
423 /* Parse headers into reply structure */
424 /* what happens if we fail to parse here? */
425 httpReplyParse(reply, httpState->reply_hdr, hdr_len);
426 storeTimestampsSet(entry);
427 /* Check if object is cacheable or not based on reply code */
428 debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status);
429 if (neighbors_do_private_keys)
430 httpMaybeRemovePublic(entry, reply->sline.status);
431 switch (httpCachableReply(httpState)) {
432 case 1:
f66a9ef4 433 if (httpHeaderHas(&reply->header, HDR_VARY)
434#if X_ACCELERATOR_VARY
435 || httpHeaderHas(&reply->header, HDR_X_ACCELERATOR_VARY)
436#endif
437 ) {
d9e8616c 438 const char *vary = httpMakeVaryMark(httpState->orig_request, reply);
f66a9ef4 439 if (vary) {
440 entry->mem_obj->vary_headers = xstrdup(vary);
441 /* Kill the old base object if a change in variance is detected */
442 httpMakePublic(entry);
443 } else {
444 httpMakePrivate(entry);
445 }
446 } else {
447 httpMakePublic(entry);
448 }
9bc73deb 449 break;
450 case 0:
451 httpMakePrivate(entry);
452 break;
453 case -1:
454 httpCacheNegatively(entry);
455 break;
456 default:
457 assert(0);
458 break;
459 }
460 if (reply->cache_control) {
461 if (EBIT_TEST(reply->cache_control->mask, CC_PROXY_REVALIDATE))
462 EBIT_SET(entry->flags, ENTRY_REVALIDATE);
463 else if (EBIT_TEST(reply->cache_control->mask, CC_MUST_REVALIDATE))
464 EBIT_SET(entry->flags, ENTRY_REVALIDATE);
465 }
466 if (httpState->flags.keepalive)
467 if (httpState->peer)
468 httpState->peer->stats.n_keepalives_sent++;
469 if (reply->keep_alive)
470 if (httpState->peer)
471 httpState->peer->stats.n_keepalives_recv++;
472 if (reply->date > -1 && !httpState->peer) {
473 int skew = abs(reply->date - squid_curtime);
474 if (skew > 86400)
475 debug(11, 3) ("%s's clock is skewed by %d seconds!\n",
476 httpState->request->host, skew);
f5558c95 477 }
9bc73deb 478 ctx_exit(ctx);
c3609322 479#if HEADERS_LOG
480 headersLog(1, 0, httpState->request->method, reply);
481#endif
f5558c95 482}
483
603a02fd 484static int
485httpPconnTransferDone(HttpStateData * httpState)
486{
487 /* return 1 if we got the last of the data on a persistent connection */
488 MemObject *mem = httpState->entry->mem_obj;
cb69b4c7 489 HttpReply *reply = mem->reply;
35282fbf 490 int clen;
51fdcbd5 491 debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd);
978e455f 492 /*
99edd1c3 493 * If we didn't send a keep-alive request header, then this
978e455f 494 * can not be a persistent connection.
495 */
b515fc11 496 if (!httpState->flags.keepalive)
603a02fd 497 return 0;
9f5a2895 498 /*
499 * What does the reply have to say about keep-alive?
500 */
b6a2f15e 501 /*
502 * XXX BUG?
503 * If the origin server (HTTP/1.0) does not send a keep-alive
504 * header, but keeps the connection open anyway, what happens?
505 * We'll return here and http.c waits for an EOF before changing
506 * store_status to STORE_OK. Combine this with ENTRY_FWD_HDR_WAIT
507 * and an error status code, and we might have to wait until
508 * the server times out the socket.
509 */
9f5a2895 510 if (!reply->keep_alive)
511 return 0;
51fdcbd5 512 debug(11, 5) ("httpPconnTransferDone: content_length=%d\n",
d8b249ef 513 reply->content_length);
35282fbf 514 /* If we haven't seen the end of reply headers, we are not done */
978e455f 515 if (httpState->reply_hdr_state < 2)
516 return 0;
35282fbf 517 clen = httpReplyBodySize(httpState->request->method, reply);
518 /* If there is no message body, we can be persistent */
519 if (0 == clen)
a3c60429 520 return 1;
35282fbf 521 /* If the body size is unknown we must wait for EOF */
522 if (clen < 0)
603a02fd 523 return 0;
35282fbf 524 /* If the body size is known, we must wait until we've gotten all of it. */
525 if (mem->inmem_hi < reply->content_length + reply->hdr_sz)
603a02fd 526 return 0;
35282fbf 527 /* We got it all */
528 return 1;
603a02fd 529}
090089c4 530
531/* This will be called when data is ready to be read from fd. Read until
532 * error or connection closed. */
f5558c95 533/* XXX this function is too long! */
b8d8561b 534static void
b177367b 535httpReadReply(int fd, void *data)
090089c4 536{
b177367b 537 HttpStateData *httpState = data;
95d659f0 538 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
bfcaf585 539 StoreEntry *entry = httpState->entry;
603a02fd 540 const request_t *request = httpState->request;
090089c4 541 int len;
30a4f2a8 542 int bin;
090089c4 543 int clen;
447e176b 544 size_t read_sz;
545#if DELAY_POOLS
59715b38 546 delay_id delay_id;
547
548 /* special "if" only for http (for nodelay proxy conns) */
549 if (delayIsNoDelay(fd))
550 delay_id = 0;
551 else
552 delay_id = delayMostBytesAllowed(entry->mem_obj);
447e176b 553#endif
e92e4e44 554 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
555 comm_close(fd);
556 return;
557 }
234967c9 558 /* check if we want to defer reading */
1513873c 559 errno = 0;
447e176b 560 read_sz = SQUID_TCP_SO_RCVBUF;
561#if DELAY_POOLS
56e64999 562 read_sz = delayBytesWanted(delay_id, 1, read_sz);
447e176b 563#endif
83704487 564 statCounter.syscalls.sock.reads++;
1f7c9178 565 len = FD_READ_METHOD(fd, buf, read_sz);
a3d5953d 566 debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 567 if (len > 0) {
ee1679df 568 fd_bytes(fd, len, FD_READ);
447e176b 569#if DELAY_POOLS
570 delayBytesIn(delay_id, len);
571#endif
83704487 572 kb_incr(&statCounter.server.all.kbytes_in, len);
573 kb_incr(&statCounter.server.http.kbytes_in, len);
4f92c80c 574 commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
4a63c85f 575 IOStats.Http.reads++;
30a4f2a8 576 for (clen = len - 1, bin = 0; clen; bin++)
577 clen >>= 1;
578 IOStats.Http.read_hist[bin]++;
579 }
5ede6c8f 580 if (!httpState->reply_hdr && len > 0) {
581 /* Skip whitespace */
b6a2f15e 582 while (len > 0 && xisspace(*buf))
5ede6c8f 583 xmemmove(buf, buf + 1, len--);
584 if (len == 0) {
585 /* Continue to read... */
586 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
587 return;
588 }
589 }
ba718c8f 590 if (len < 0) {
55cb44f1 591 debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n",
592 fd, xstrerror());
b224ea98 593 if (ignoreErrno(errno)) {
9b312a19 594 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
910169e5 595 } else if (entry->mem_obj->inmem_hi == 0) {
ec250dfd 596 ErrorState *err;
597 err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR);
b16e464a 598 err->request = requestLink((request_t *) request);
ec250dfd 599 err->xerrno = errno;
600 fwdFail(httpState->fwd, err);
1afe05c5 601 comm_close(fd);
090089c4 602 } else {
0d4d4170 603 comm_close(fd);
090089c4 604 }
8350fe9b 605 } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
ec250dfd 606 ErrorState *err;
607 err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE);
608 err->xerrno = errno;
b16e464a 609 err->request = requestLink((request_t *) request);
ec250dfd 610 fwdFail(httpState->fwd, err);
910169e5 611 httpState->eof = 1;
612 comm_close(fd);
090089c4 613 } else if (len == 0) {
614 /* Connection closed; retrieval done. */
f86a6a46 615 httpState->eof = 1;
d1a43e28 616 if (httpState->reply_hdr_state < 2)
b34ed725 617 /*
618 * Yes Henrik, there is a point to doing this. When we
619 * called httpProcessReplyHeader() before, we didn't find
620 * the end of headers, but now we are definately at EOF, so
621 * we want to process the reply headers.
622 */
d1a43e28 623 httpProcessReplyHeader(httpState, buf, len);
db1cd23c 624 fwdComplete(httpState->fwd);
0d4d4170 625 comm_close(fd);
090089c4 626 } else {
7e3e1d01 627 if (httpState->reply_hdr_state < 2) {
30a4f2a8 628 httpProcessReplyHeader(httpState, buf, len);
db1cd23c 629 if (httpState->reply_hdr_state == 2) {
630 http_status s = entry->mem_obj->reply->sline.status;
225644d7 631#if WIP_FWD_LOG
632 fwdStatus(httpState->fwd, s);
633#endif
b6a2f15e 634 /*
635 * If its not a reply that we will re-forward, then
636 * allow the client to get it.
db1cd23c 637 */
b6a2f15e 638 if (!fwdReforwardableStatus(s))
db1cd23c 639 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
640 }
7e3e1d01 641 }
620da955 642 storeAppend(entry, buf, len);
b7fe0ab0 643 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
9d66d521 644 /*
645 * the above storeAppend() call could ABORT this entry,
646 * in that case, the server FD should already be closed.
647 * there's nothing for us to do.
648 */
649 (void) 0;
d9627979 650 } else if (httpPconnTransferDone(httpState)) {
5b29969a 651 /* yes we have to clear all these! */
8796b9e9 652 commSetDefer(fd, NULL, NULL);
5b29969a 653 commSetTimeout(fd, -1, NULL, NULL);
654 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
ec603b25 655#if DELAY_POOLS
656 delayClearNoDelay(fd);
657#endif
603a02fd 658 comm_remove_close_handler(fd, httpStateFree, httpState);
db1cd23c 659 fwdUnregister(fd, httpState->fwd);
8796b9e9 660 pconnPush(fd, request->host, request->port);
8a28f65f 661 fwdComplete(httpState->fwd);
603a02fd 662 httpState->fd = -1;
59715b38 663 httpStateFree(fd, httpState);
603a02fd 664 } else {
9f5a2895 665 /* Wait for EOF condition */
603a02fd 666 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
667 }
090089c4 668 }
669}
670
671/* This will be called when request write is complete. Schedule read of
672 * reply. */
b8d8561b 673static void
79a15e0a 674httpSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
090089c4 675{
30a4f2a8 676 HttpStateData *httpState = data;
9b312a19 677 StoreEntry *entry = httpState->entry;
678 ErrorState *err;
a3d5953d 679 debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n",
ed19251a 680 fd, (int) size, errflag);
bc87dc25 681#if URL_CHECKSUM_DEBUG
682 assert(entry->mem_obj->chksum == url_checksum(entry->mem_obj->url));
683#endif
ee1679df 684 if (size > 0) {
685 fd_bytes(fd, size, FD_WRITE);
83704487 686 kb_incr(&statCounter.server.all.kbytes_out, size);
687 kb_incr(&statCounter.server.http.kbytes_out, size);
ee1679df 688 }
ea3a2a69 689 if (errflag == COMM_ERR_CLOSING)
690 return;
090089c4 691 if (errflag) {
fe40a877 692 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 693 err->xerrno = errno;
79a15e0a 694 err->request = requestLink(httpState->orig_request);
9b312a19 695 errorAppendEntry(entry, err);
0d4d4170 696 comm_close(fd);
090089c4 697 return;
698 } else {
699 /* Schedule read reply. */
b6a2f15e 700 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
701 /*
702 * Set the read timeout here because it hasn't been set yet.
703 * We only set the read timeout after the request has been
704 * fully written to the server-side. If we start the timeout
705 * after connection establishment, then we are likely to hit
706 * the timeout for POST/PUT requests that have very large
707 * request bodies.
708 */
709 commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState);
41462d93 710 commSetDefer(fd, fwdCheckDeferRead, entry);
090089c4 711 }
712}
713
99edd1c3 714/*
715 * build request headers and append them to a given MemBuf
716 * used by httpBuildRequestPrefix()
717 * note: calls httpHeaderInit(), the caller is responsible for Clean()-ing
718 */
e1e72f06 719void
6bf8443a 720httpBuildRequestHeader(request_t * request,
721 request_t * orig_request,
722 StoreEntry * entry,
5999b776 723 HttpHeader * hdr_out,
603a02fd 724 int cfd,
b515fc11 725 http_state_flags flags)
6bf8443a 726{
99edd1c3 727 /* building buffer for complex strings */
5999b776 728#define BBUF_SZ (MAX_URL+32)
99edd1c3 729 LOCAL_ARRAY(char, bbuf, BBUF_SZ);
730 String strConnection = StringNull;
731 const HttpHeader *hdr_in = &orig_request->header;
732 const HttpHeaderEntry *e;
6bccf575 733 String strVia;
734 String strFwd;
99edd1c3 735 HttpHeaderPos pos = HttpHeaderInitPos;
2246b732 736 httpHeaderInit(hdr_out, hoRequest);
99edd1c3 737 /* append our IMS header */
9bc73deb 738 if (request->lastmod > -1 && request->method == METHOD_GET)
739 httpHeaderPutTime(hdr_out, HDR_IF_MODIFIED_SINCE, request->lastmod);
99edd1c3 740
741 strConnection = httpHeaderGetList(hdr_in, HDR_CONNECTION);
742 while ((e = httpHeaderGetEntry(hdr_in, &pos))) {
743 debug(11, 5) ("httpBuildRequestHeader: %s: %s\n",
744 strBuf(e->name), strBuf(e->value));
b7677233 745 if (!httpRequestHdrAllowed(e, &strConnection)) {
746 debug(11, 2) ("'%s' header denied by anonymize_headers configuration\n",
747 strBuf(e->name));
6bf8443a 748 continue;
b7677233 749 }
99edd1c3 750 switch (e->id) {
751 case HDR_PROXY_AUTHORIZATION:
94439e4e 752 /* Only pass on proxy authentication to peers for which
753 * authentication forwarding is explicitly enabled
754 */
755 if (request->flags.proxying && orig_request->peer_login &&
756 strcmp(orig_request->peer_login, "PASS") == 0) {
99edd1c3 757 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
94439e4e 758 }
99edd1c3 759 break;
c68e9c6b 760 case HDR_AUTHORIZATION:
94439e4e 761 /* Pass on WWW authentication even if used locally. If this is
762 * not wanted in an accelerator then the header can be removed
763 * using the anonymization functions
764 */
765 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
766 /* XXX Some accelerators might want to strip the header
767 * and regard the reply as cacheable, but authentication
768 * is not normally enabled for accelerators without reading
769 * the code, so there is not much use in adding logics here
770 * without first defining the concept of having authentication
771 * in the accelerator...
772 */
c68e9c6b 773 break;
99edd1c3 774 case HDR_HOST:
7e3ce7b9 775 /*
776 * Normally Squid does not copy the Host: header from
777 * a client request into the forwarded request headers.
778 * However, there is one case when we do: If the URL
779 * went through our redirector and the admin configured
780 * 'redir_rewrites_host' to be off.
781 */
782 if (request->flags.redirected)
783 if (!Config.onoff.redir_rewrites_host)
784 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
99edd1c3 785 break;
786 case HDR_IF_MODIFIED_SINCE:
787 /* append unless we added our own;
788 * note: at most one client's ims header can pass through */
789 if (!httpHeaderHas(hdr_out, HDR_IF_MODIFIED_SINCE))
790 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
791 break;
792 case HDR_MAX_FORWARDS:
b3b64e58 793 if (orig_request->method == METHOD_TRACE) {
99edd1c3 794 /* sacrificing efficiency over clarity, etc. */
795 const int hops = httpHeaderGetInt(hdr_in, HDR_MAX_FORWARDS);
796 if (hops > 0)
5999b776 797 httpHeaderPutInt(hdr_out, HDR_MAX_FORWARDS, hops - 1);
b3b64e58 798 }
99edd1c3 799 break;
800 case HDR_PROXY_CONNECTION:
801 case HDR_CONNECTION:
802 case HDR_VIA:
803 case HDR_X_FORWARDED_FOR:
804 case HDR_CACHE_CONTROL:
805 /* append these after the loop if needed */
806 break;
807 default:
808 /* pass on all other header fields */
809 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
66f7337b 810 }
88738790 811 }
99edd1c3 812
99edd1c3 813 /* append Via */
6bccf575 814 strVia = httpHeaderGetList(hdr_in, HDR_VIA);
815 snprintf(bbuf, BBUF_SZ, "%d.%d %s",
816 orig_request->http_ver.major,
817 orig_request->http_ver.minor, ThisCache);
818 strListAdd(&strVia, bbuf, ',');
819 httpHeaderPutStr(hdr_out, HDR_VIA, strBuf(strVia));
820 stringClean(&strVia);
821
99edd1c3 822 /* append X-Forwarded-For */
6bccf575 823 strFwd = httpHeaderGetList(hdr_in, HDR_X_FORWARDED_FOR);
824 strListAdd(&strFwd, (cfd < 0 ? "unknown" : fd_table[cfd].ipaddr), ',');
825 httpHeaderPutStr(hdr_out, HDR_X_FORWARDED_FOR, strBuf(strFwd));
826 stringClean(&strFwd);
827
99edd1c3 828 /* append Host if not there already */
829 if (!httpHeaderHas(hdr_out, HDR_HOST)) {
830 /* use port# only if not default */
831 if (orig_request->port == urlDefaultPort(orig_request->protocol)) {
832 httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->host);
833 } else {
2246b732 834 httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d",
99edd1c3 835 orig_request->host, (int) orig_request->port);
99edd1c3 836 }
6bf8443a 837 }
c68e9c6b 838 /* append Authorization if known in URL, not in header and going direct */
839 if (!httpHeaderHas(hdr_out, HDR_AUTHORIZATION)) {
840 if (!request->flags.proxying && *request->login) {
841 httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s",
842 base64_encode(request->login));
843 }
844 }
845 /* append Proxy-Authorization if configured for peer, and proxying */
c3b33cb7 846 if (request->flags.proxying && orig_request->peer_login &&
2b6662ba 847 !httpHeaderHas(hdr_out, HDR_PROXY_AUTHORIZATION) &&
848 strcmp(orig_request->peer_login, "PASS") != 0) {
c3b33cb7 849 if (*orig_request->peer_login == '*') {
850 /* Special mode, to pass the username to the upstream cache */
851 char loginbuf[256];
a2c963ae 852 const char *username = "-";
c3b33cb7 853 if (orig_request->auth_user_request)
854 username = authenticateUserRequestUsername(orig_request->auth_user_request);
2b6662ba 855 snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1);
c3b33cb7 856 httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s",
857 base64_encode(loginbuf));
858 } else {
c68e9c6b 859 httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s",
1f38f50a 860 base64_encode(orig_request->peer_login));
c68e9c6b 861 }
862 }
99edd1c3 863 /* append Cache-Control, add max-age if not there already */
864 {
865 HttpHdrCc *cc = httpHeaderGetCc(hdr_in);
866 if (!cc)
867 cc = httpHdrCcCreate();
868 if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) {
9b5d1d21 869 const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request);
99edd1c3 870 httpHdrCcSetMaxAge(cc, getMaxAge(url));
871 if (strLen(request->urlpath))
872 assert(strstr(url, strBuf(request->urlpath)));
873 }
db1cd23c 874 if (flags.only_if_cached)
875 EBIT_SET(cc->mask, CC_ONLY_IF_CACHED);
99edd1c3 876 httpHeaderPutCc(hdr_out, cc);
877 httpHdrCcDestroy(cc);
6bf8443a 878 }
99edd1c3 879 /* maybe append Connection: keep-alive */
b515fc11 880 if (flags.keepalive) {
881 if (flags.proxying) {
99edd1c3 882 httpHeaderPutStr(hdr_out, HDR_PROXY_CONNECTION, "keep-alive");
603a02fd 883 } else {
99edd1c3 884 httpHeaderPutStr(hdr_out, HDR_CONNECTION, "keep-alive");
603a02fd 885 }
603a02fd 886 }
6bccf575 887 /* Now mangle the headers. */
888 httpHdrMangleList(hdr_out, request);
99edd1c3 889 stringClean(&strConnection);
890}
891
892/* build request prefix and append it to a given MemBuf;
893 * return the length of the prefix */
9bc73deb 894mb_size_t
99edd1c3 895httpBuildRequestPrefix(request_t * request,
896 request_t * orig_request,
897 StoreEntry * entry,
5999b776 898 MemBuf * mb,
99edd1c3 899 int cfd,
b515fc11 900 http_state_flags flags)
99edd1c3 901{
902 const int offset = mb->size;
903 memBufPrintf(mb, "%s %s HTTP/1.0\r\n",
904 RequestMethodStr[request->method],
905 strLen(request->urlpath) ? strBuf(request->urlpath) : "/");
906 /* build and pack headers */
907 {
908 HttpHeader hdr;
909 Packer p;
910 httpBuildRequestHeader(request, orig_request, entry, &hdr, cfd, flags);
911 packerToMemInit(&p, mb);
912 httpHeaderPackInto(&hdr, &p);
913 httpHeaderClean(&hdr);
914 packerClean(&p);
9d9d144b 915 }
99edd1c3 916 /* append header terminator */
b8890359 917 memBufAppend(mb, crlf, 2);
99edd1c3 918 return mb->size - offset;
6bf8443a 919}
090089c4 920/* This will be called when connect completes. Write request. */
b8d8561b 921static void
b6a2f15e 922httpSendRequest(HttpStateData * httpState)
090089c4 923{
99edd1c3 924 MemBuf mb;
30a4f2a8 925 request_t *req = httpState->request;
620da955 926 StoreEntry *entry = httpState->entry;
2a26c096 927 int cfd;
1294c0fc 928 peer *p = httpState->peer;
901e234d 929 CWCB *sendHeaderDone;
090089c4 930
b6a2f15e 931 debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", httpState->fd, httpState);
090089c4 932
94439e4e 933 if (httpState->orig_request->body_connection)
7db8b16d 934 sendHeaderDone = httpSendRequestEntry;
935 else
936 sendHeaderDone = httpSendComplete;
54220df8 937
2a26c096 938 if (!opt_forwarded_for)
6bf8443a 939 cfd = -1;
2a26c096 940 else if (entry->mem_obj == NULL)
6bf8443a 941 cfd = -1;
2a26c096 942 else
382d851a 943 cfd = entry->mem_obj->fd;
b0a1e5bf 944 assert(-1 == cfd || FD_SOCKET == fd_table[cfd].type);
1294c0fc 945 if (p != NULL)
b515fc11 946 httpState->flags.proxying = 1;
94439e4e 947 else
948 httpState->flags.proxying = 0;
efb9218c 949 /*
99edd1c3 950 * Is keep-alive okay for all request methods?
efb9218c 951 */
efd900cb 952 if (!Config.onoff.server_pconns)
953 httpState->flags.keepalive = 0;
954 else if (p == NULL)
b515fc11 955 httpState->flags.keepalive = 1;
efb9218c 956 else if (p->stats.n_keepalives_sent < 10)
b515fc11 957 httpState->flags.keepalive = 1;
efb9218c 958 else if ((double) p->stats.n_keepalives_recv / (double) p->stats.n_keepalives_sent > 0.50)
b515fc11 959 httpState->flags.keepalive = 1;
db1cd23c 960 if (httpState->peer)
987de783 961 if (neighborType(httpState->peer, httpState->request) == PEER_SIBLING &&
a4b8110e 962 !httpState->peer->options.allow_miss)
db1cd23c 963 httpState->flags.only_if_cached = 1;
99edd1c3 964 memBufDefInit(&mb);
965 httpBuildRequestPrefix(req,
79a15e0a 966 httpState->orig_request,
6bf8443a 967 entry,
99edd1c3 968 &mb,
603a02fd 969 cfd,
970 httpState->flags);
b6a2f15e 971 debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", httpState->fd, mb.buf);
972 comm_write_mbuf(httpState->fd, mb, sendHeaderDone, httpState);
090089c4 973}
b6a2f15e 974
910169e5 975void
db1cd23c 976httpStart(FwdState * fwd)
603a02fd 977{
db1cd23c 978 int fd = fwd->server_fd;
28c60158 979 HttpStateData *httpState;
910169e5 980 request_t *proxy_req;
db1cd23c 981 request_t *orig_req = fwd->request;
910169e5 982 debug(11, 3) ("httpStart: \"%s %s\"\n",
983 RequestMethodStr[orig_req->method],
db1cd23c 984 storeUrl(fwd->entry));
72711e31 985 httpState = cbdataAlloc(HttpStateData);
db1cd23c 986 storeLockObject(fwd->entry);
987 httpState->fwd = fwd;
988 httpState->entry = fwd->entry;
9e4ad609 989 httpState->fd = fd;
db1cd23c 990 if (fwd->servers)
991 httpState->peer = fwd->servers->peer; /* might be NULL */
910169e5 992 if (httpState->peer) {
993 proxy_req = requestCreate(orig_req->method,
994 PROTO_NONE, storeUrl(httpState->entry));
995 xstrncpy(proxy_req->host, httpState->peer->host, SQUIDHOSTNAMELEN);
996 proxy_req->port = httpState->peer->http_port;
23e8446b 997 proxy_req->flags = orig_req->flags;
9bc73deb 998 proxy_req->lastmod = orig_req->lastmod;
910169e5 999 httpState->request = requestLink(proxy_req);
910169e5 1000 httpState->orig_request = requestLink(orig_req);
92695e5e 1001 proxy_req->flags.proxying = 1;
910169e5 1002 /*
1003 * This NEIGHBOR_PROXY_ONLY check probably shouldn't be here.
1004 * We might end up getting the object from somewhere else if,
1005 * for example, the request to this neighbor fails.
1006 */
cd196bc8 1007 if (httpState->peer->options.proxy_only)
910169e5 1008 storeReleaseRequest(httpState->entry);
95e36d02 1009#if DELAY_POOLS
59715b38 1010 assert(delayIsNoDelay(fd) == 0);
1011 if (httpState->peer->options.no_delay)
1012 delaySetNoDelay(fd);
95e36d02 1013#endif
603a02fd 1014 } else {
910169e5 1015 httpState->request = requestLink(orig_req);
1016 httpState->orig_request = requestLink(orig_req);
603a02fd 1017 }
910169e5 1018 /*
1019 * register the handler to free HTTP state data when the FD closes
1020 */
1021 comm_add_close_handler(fd, httpStateFree, httpState);
83704487 1022 statCounter.server.all.requests++;
1023 statCounter.server.http.requests++;
b6a2f15e 1024 httpSendRequest(httpState);
1025 /*
1026 * We used to set the read timeout here, but not any more.
1027 * Now its set in httpSendComplete() after the full request,
1028 * including request body, has been written to the server.
1029 */
090089c4 1030}
1031
54220df8 1032static void
94439e4e 1033httpSendRequestEntryDone(int fd, void *data)
54220df8 1034{
1035 HttpStateData *httpState = data;
94439e4e 1036 aclCheck_t ch;
1037 debug(11, 5) ("httpSendRequestEntryDone: FD %d\n",
1038 fd);
1039 memset(&ch, '\0', sizeof(ch));
1040 ch.request = httpState->request;
1041 if (!Config.accessList.brokenPosts) {
1042 debug(11, 5) ("httpSendRequestEntryDone: No brokenPosts list\n");
1043 httpSendComplete(fd, NULL, 0, 0, data);
1044 } else if (!aclCheckFast(Config.accessList.brokenPosts, &ch)) {
1045 debug(11, 5) ("httpSendRequestEntryDone: didn't match brokenPosts\n");
1046 httpSendComplete(fd, NULL, 0, 0, data);
1047 } else {
1048 debug(11, 2) ("httpSendRequestEntryDone: matched brokenPosts\n");
1049 comm_write(fd, "\r\n", 2, httpSendComplete, data, NULL);
54220df8 1050 }
94439e4e 1051}
1052
1053static void
1054httpRequestBodyHandler(char *buf, size_t size, void *data)
1055{
1056 HttpStateData *httpState = (HttpStateData *) data;
1057 if (size > 0) {
1058 comm_write(httpState->fd, buf, size, httpSendRequestEntry, data, memFree8K);
1059 } else if (size == 0) {
1060 /* End of body */
1061 memFree8K(buf);
1062 httpSendRequestEntryDone(httpState->fd, data);
1063 } else {
1064 /* Failed to get whole body, probably aborted */
1065 memFree8K(buf);
1066 httpSendComplete(httpState->fd, NULL, 0, COMM_ERR_CLOSING, data);
b6a2f15e 1067 }
376bb137 1068}
1069
1070static void
94439e4e 1071httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data)
376bb137 1072{
1073 HttpStateData *httpState = data;
1074 StoreEntry *entry = httpState->entry;
1075 ErrorState *err;
94439e4e 1076 debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n",
ed19251a 1077 fd, (int) size, errflag);
376bb137 1078 if (size > 0) {
1079 fd_bytes(fd, size, FD_WRITE);
83704487 1080 kb_incr(&statCounter.server.all.kbytes_out, size);
1081 kb_incr(&statCounter.server.http.kbytes_out, size);
376bb137 1082 }
1083 if (errflag == COMM_ERR_CLOSING)
1084 return;
1085 if (errflag) {
1086 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
1087 err->xerrno = errno;
1088 err->request = requestLink(httpState->orig_request);
1089 errorAppendEntry(entry, err);
1090 comm_close(fd);
1091 return;
1092 }
94439e4e 1093 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1094 comm_close(fd);
1095 return;
376bb137 1096 }
94439e4e 1097 clientReadBody(httpState->orig_request, memAllocate(MEM_8K_BUF), 8192, httpRequestBodyHandler, httpState);
54220df8 1098}
ccf44862 1099
1100void
110eb4e5 1101httpBuildVersion(http_version_t * version, unsigned int major, unsigned int minor)
1102{
1103 version->major = major;
1104 version->minor = minor;
ccf44862 1105}