]>
Commit | Line | Data |
---|---|---|
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 | 43 | static const char *const crlf = "\r\n"; |
4db43fab | 44 | |
9e4ad609 | 45 | static CWCB httpSendComplete; |
54220df8 | 46 | static CWCB httpSendRequestEntry; |
47 | ||
9e4ad609 | 48 | static PF httpReadReply; |
b6a2f15e | 49 | static void httpSendRequest(HttpStateData *); |
9e4ad609 | 50 | static PF httpStateFree; |
51 | static PF httpTimeout; | |
f5b8bbc4 | 52 | static void httpCacheNegatively(StoreEntry *); |
53 | static void httpMakePrivate(StoreEntry *); | |
54 | static void httpMakePublic(StoreEntry *); | |
f8309b15 | 55 | static int httpCachableReply(HttpStateData *); |
f9cece6e | 56 | static void httpMaybeRemovePublic(StoreEntry *, http_status); |
b8d8561b | 57 | |
b177367b | 58 | static void |
59715b38 | 59 | httpStateFree(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 | 79 | int |
75e88d56 | 80 | httpCachable(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 | 89 | static void |
5c5783a2 | 90 | httpTimeout(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 | 105 | static void |
106 | httpMakePublic(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 | 113 | static void |
114 | httpMakePrivate(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 | 122 | static void |
123 | httpCacheNegatively(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 | 130 | static void |
131 | httpMaybeRemovePublic(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 | 216 | static int |
217 | httpCachableReply(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 | */ | |
327 | const char * | |
328 | httpMakeVaryMark(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 | 382 | void |
0ee4272b | 383 | httpProcessReplyHeader(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 | 484 | static int |
485 | httpPconnTransferDone(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 | 534 | static void |
b177367b | 535 | httpReadReply(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 | 673 | static void |
79a15e0a | 674 | httpSendComplete(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 | 719 | void |
6bf8443a | 720 | httpBuildRequestHeader(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 | 894 | mb_size_t |
99edd1c3 | 895 | httpBuildRequestPrefix(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 | 921 | static void |
b6a2f15e | 922 | httpSendRequest(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 | 975 | void |
db1cd23c | 976 | httpStart(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 | 1032 | static void |
94439e4e | 1033 | httpSendRequestEntryDone(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 | ||
1053 | static void | |
1054 | httpRequestBodyHandler(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 | ||
1070 | static void | |
94439e4e | 1071 | httpSendRequestEntry(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 | |
1100 | void | |
110eb4e5 | 1101 | httpBuildVersion(http_version_t * version, unsigned int major, unsigned int minor) |
1102 | { | |
1103 | version->major = major; | |
1104 | version->minor = minor; | |
ccf44862 | 1105 | } |