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