]>
Commit | Line | Data |
---|---|---|
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 | 114 | static const char *const crlf = "\r\n"; |
4db43fab | 115 | |
9e4ad609 | 116 | static CNCB httpConnectDone; |
117 | static CWCB httpSendComplete; | |
54220df8 | 118 | static CWCB httpSendRequestEntry; |
119 | ||
9e4ad609 | 120 | static PF httpReadReply; |
121 | static PF httpSendRequest; | |
122 | static PF httpStateFree; | |
123 | static PF httpTimeout; | |
99edd1c3 | 124 | #if OLD_CODE |
f5b8bbc4 | 125 | static void httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int); |
99edd1c3 | 126 | #endif |
f5b8bbc4 | 127 | static void httpCacheNegatively(StoreEntry *); |
128 | static void httpMakePrivate(StoreEntry *); | |
129 | static void httpMakePublic(StoreEntry *); | |
f8309b15 | 130 | static int httpCachableReply(HttpStateData *); |
b8d8561b | 131 | |
b177367b | 132 | static void |
79d39a72 | 133 | httpStateFree(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 | 153 | int |
75e88d56 | 154 | httpCachable(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 | 163 | static void |
5c5783a2 | 164 | httpTimeout(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 | 182 | static void |
183 | httpMakePublic(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 | 190 | static void |
191 | httpMakePrivate(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 | 199 | static void |
200 | httpCacheNegatively(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 | 207 | static int |
208 | httpCachableReply(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 | 296 | void |
0ee4272b | 297 | httpProcessReplyHeader(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 | 367 | static int |
368 | httpPconnTransferDone(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 | 426 | static void |
b177367b | 427 | httpReadReply(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 | 521 | static void |
79a15e0a | 522 | httpSendComplete(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 | 554 | static void |
88738790 | 555 | httpAppendRequestHeader(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 | 582 | void |
6bf8443a | 583 | httpBuildRequestHeader(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 */ | |
721 | size_t | |
722 | httpBuildRequestPrefix(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 | 749 | static void |
b177367b | 750 | httpSendRequest(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 | 796 | void |
797 | httpStart(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 | ||
841 | static void | |
842 | httpConnectDone(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 | 873 | static void |
7db8b16d | 874 | httpSendRequestEntry(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 | } |