]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | /* | |
3 | * $Id: http.cc,v 1.516 2007/04/24 23:13:25 wessels Exp $ | |
4 | * | |
5 | * DEBUG: section 11 Hypertext Transfer Protocol (HTTP) | |
6 | * AUTHOR: Harvest Derived | |
7 | * | |
8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
9 | * ---------------------------------------------------------- | |
10 | * | |
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. | |
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 | |
32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
33 | * | |
34 | */ | |
35 | ||
36 | /* | |
37 | * Anonymizing patch by lutz@as-node.jena.thur.de | |
38 | * have a look into http-anon.c to get more informations. | |
39 | */ | |
40 | ||
41 | #include "squid.h" | |
42 | #include "errorpage.h" | |
43 | #include "MemBuf.h" | |
44 | #include "http.h" | |
45 | #include "AuthUserRequest.h" | |
46 | #include "Store.h" | |
47 | #include "HttpReply.h" | |
48 | #include "HttpRequest.h" | |
49 | #include "MemObject.h" | |
50 | #include "HttpHdrContRange.h" | |
51 | #include "HttpHdrSc.h" | |
52 | #include "HttpHdrScTarget.h" | |
53 | #include "ACLChecklist.h" | |
54 | #include "fde.h" | |
55 | #if DELAY_POOLS | |
56 | #include "DelayPools.h" | |
57 | #endif | |
58 | #if ICAP_CLIENT | |
59 | #include "ICAP/ICAPConfig.h" | |
60 | extern ICAPConfig TheICAPConfig; | |
61 | #endif | |
62 | #include "SquidTime.h" | |
63 | ||
64 | CBDATA_CLASS_INIT(HttpStateData); | |
65 | ||
66 | static const char *const crlf = "\r\n"; | |
67 | ||
68 | static PF httpStateFree; | |
69 | static PF httpTimeout; | |
70 | static void httpMaybeRemovePublic(StoreEntry *, http_status); | |
71 | static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, String strConnection, HttpRequest * request, HttpRequest * orig_request, | |
72 | HttpHeader * hdr_out, int we_do_ranges, http_state_flags); | |
73 | #if ICAP_CLIENT | |
74 | static void icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data); | |
75 | #endif | |
76 | ||
77 | HttpStateData::HttpStateData(FwdState *theFwdState) : ServerStateData(theFwdState), | |
78 | header_bytes_read(0), reply_bytes_read(0) | |
79 | { | |
80 | debugs(11,5,HERE << "HttpStateData " << this << " created"); | |
81 | ignoreCacheControl = false; | |
82 | surrogateNoStore = false; | |
83 | fd = fwd->server_fd; | |
84 | readBuf = new MemBuf; | |
85 | readBuf->init(4096, SQUID_TCP_SO_RCVBUF); | |
86 | orig_request = HTTPMSGLOCK(fwd->request); | |
87 | ||
88 | if (fwd->servers) | |
89 | _peer = fwd->servers->_peer; /* might be NULL */ | |
90 | ||
91 | if (_peer) { | |
92 | const char *url; | |
93 | ||
94 | if (_peer->options.originserver) | |
95 | url = orig_request->urlpath.buf(); | |
96 | else | |
97 | url = entry->url(); | |
98 | ||
99 | HttpRequest * proxy_req = new HttpRequest(orig_request->method, | |
100 | orig_request->protocol, url); | |
101 | ||
102 | xstrncpy(proxy_req->host, _peer->host, SQUIDHOSTNAMELEN); | |
103 | ||
104 | proxy_req->port = _peer->http_port; | |
105 | ||
106 | proxy_req->flags = orig_request->flags; | |
107 | ||
108 | proxy_req->lastmod = orig_request->lastmod; | |
109 | ||
110 | proxy_req->flags.proxying = 1; | |
111 | ||
112 | HTTPMSGUNLOCK(request); | |
113 | ||
114 | request = HTTPMSGLOCK(proxy_req); | |
115 | ||
116 | /* | |
117 | * This NEIGHBOR_PROXY_ONLY check probably shouldn't be here. | |
118 | * We might end up getting the object from somewhere else if, | |
119 | * for example, the request to this neighbor fails. | |
120 | */ | |
121 | if (_peer->options.proxy_only) | |
122 | entry->releaseRequest(); | |
123 | ||
124 | #if DELAY_POOLS | |
125 | ||
126 | entry->setNoDelay(_peer->options.no_delay); | |
127 | ||
128 | #endif | |
129 | ||
130 | } | |
131 | ||
132 | /* | |
133 | * register the handler to free HTTP state data when the FD closes | |
134 | */ | |
135 | comm_add_close_handler(fd, httpStateFree, this); | |
136 | } | |
137 | ||
138 | HttpStateData::~HttpStateData() | |
139 | { | |
140 | /* | |
141 | * don't forget that ~ServerStateData() gets called automatically | |
142 | */ | |
143 | ||
144 | if (!readBuf->isNull()) | |
145 | readBuf->clean(); | |
146 | ||
147 | delete readBuf; | |
148 | ||
149 | HTTPMSGUNLOCK(orig_request); | |
150 | ||
151 | debugs(11,5, HERE << "HttpStateData " << this << " destroyed; FD " << fd); | |
152 | } | |
153 | ||
154 | int | |
155 | HttpStateData::dataDescriptor() const | |
156 | { | |
157 | return fd; | |
158 | } | |
159 | ||
160 | static void | |
161 | httpStateFree(int fd, void *data) | |
162 | { | |
163 | HttpStateData *httpState = static_cast<HttpStateData *>(data); | |
164 | debug(11,5)("httpStateFree: FD %d, httpState=%p\n", fd, data); | |
165 | delete httpState; | |
166 | } | |
167 | ||
168 | int | |
169 | httpCachable(method_t method) | |
170 | { | |
171 | /* GET and HEAD are cachable. Others are not. */ | |
172 | ||
173 | if (method != METHOD_GET && method != METHOD_HEAD) | |
174 | return 0; | |
175 | ||
176 | /* else cachable */ | |
177 | return 1; | |
178 | } | |
179 | ||
180 | static void | |
181 | httpTimeout(int fd, void *data) | |
182 | { | |
183 | HttpStateData *httpState = static_cast<HttpStateData *>(data); | |
184 | StoreEntry *entry = httpState->entry; | |
185 | debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, entry->url()); | |
186 | ||
187 | if (entry->store_status == STORE_PENDING) { | |
188 | httpState->fwd->fail(errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT, httpState->fwd->request)); | |
189 | } | |
190 | ||
191 | comm_close(fd); | |
192 | } | |
193 | ||
194 | static void | |
195 | httpMaybeRemovePublic(StoreEntry * e, http_status status) | |
196 | { | |
197 | ||
198 | int remove | |
199 | = 0; | |
200 | ||
201 | int forbidden = 0; | |
202 | ||
203 | StoreEntry *pe; | |
204 | ||
205 | if (!EBIT_TEST(e->flags, KEY_PRIVATE)) | |
206 | return; | |
207 | ||
208 | switch (status) { | |
209 | ||
210 | case HTTP_OK: | |
211 | ||
212 | case HTTP_NON_AUTHORITATIVE_INFORMATION: | |
213 | ||
214 | case HTTP_MULTIPLE_CHOICES: | |
215 | ||
216 | case HTTP_MOVED_PERMANENTLY: | |
217 | ||
218 | case HTTP_MOVED_TEMPORARILY: | |
219 | ||
220 | case HTTP_GONE: | |
221 | ||
222 | case HTTP_NOT_FOUND: | |
223 | ||
224 | remove | |
225 | = 1; | |
226 | ||
227 | break; | |
228 | ||
229 | case HTTP_FORBIDDEN: | |
230 | ||
231 | case HTTP_METHOD_NOT_ALLOWED: | |
232 | forbidden = 1; | |
233 | ||
234 | break; | |
235 | ||
236 | #if WORK_IN_PROGRESS | |
237 | ||
238 | case HTTP_UNAUTHORIZED: | |
239 | forbidden = 1; | |
240 | ||
241 | break; | |
242 | ||
243 | #endif | |
244 | ||
245 | default: | |
246 | #if QUESTIONABLE | |
247 | /* | |
248 | * Any 2xx response should eject previously cached entities... | |
249 | */ | |
250 | ||
251 | if (status >= 200 && status < 300) | |
252 | remove | |
253 | = 1; | |
254 | ||
255 | #endif | |
256 | ||
257 | break; | |
258 | } | |
259 | ||
260 | if (!remove | |
261 | && !forbidden) | |
262 | return; | |
263 | ||
264 | assert(e->mem_obj); | |
265 | ||
266 | if (e->mem_obj->request) | |
267 | pe = storeGetPublicByRequest(e->mem_obj->request); | |
268 | else | |
269 | pe = storeGetPublic(e->mem_obj->url, e->mem_obj->method); | |
270 | ||
271 | if (pe != NULL) { | |
272 | assert(e != pe); | |
273 | pe->release(); | |
274 | } | |
275 | ||
276 | /* | |
277 | * Also remove any cached HEAD response in case the object has | |
278 | * changed. | |
279 | */ | |
280 | if (e->mem_obj->request) | |
281 | pe = storeGetPublicByRequestMethod(e->mem_obj->request, METHOD_HEAD); | |
282 | else | |
283 | pe = storeGetPublic(e->mem_obj->url, METHOD_HEAD); | |
284 | ||
285 | if (pe != NULL) { | |
286 | assert(e != pe); | |
287 | pe->release(); | |
288 | } | |
289 | ||
290 | if (forbidden) | |
291 | return; | |
292 | ||
293 | switch (e->mem_obj->method) { | |
294 | ||
295 | case METHOD_PUT: | |
296 | ||
297 | case METHOD_DELETE: | |
298 | ||
299 | case METHOD_PROPPATCH: | |
300 | ||
301 | case METHOD_MKCOL: | |
302 | ||
303 | case METHOD_MOVE: | |
304 | ||
305 | case METHOD_BMOVE: | |
306 | ||
307 | case METHOD_BDELETE: | |
308 | /* | |
309 | * Remove any cached GET object if it is beleived that the | |
310 | * object may have changed as a result of other methods | |
311 | */ | |
312 | ||
313 | if (e->mem_obj->request) | |
314 | pe = storeGetPublicByRequestMethod(e->mem_obj->request, METHOD_GET); | |
315 | else | |
316 | pe = storeGetPublic(e->mem_obj->url, METHOD_GET); | |
317 | ||
318 | if (pe != NULL) { | |
319 | assert(e != pe); | |
320 | pe->release(); | |
321 | } | |
322 | ||
323 | break; | |
324 | ||
325 | default: | |
326 | /* Keep GCC happy. The methods above are all mutating HTTP methods | |
327 | */ | |
328 | break; | |
329 | } | |
330 | } | |
331 | ||
332 | void | |
333 | HttpStateData::processSurrogateControl(HttpReply *reply) | |
334 | { | |
335 | #if ESI | |
336 | ||
337 | if (request->flags.accelerated && reply->surrogate_control) { | |
338 | HttpHdrScTarget *sctusable = | |
339 | httpHdrScGetMergedTarget(reply->surrogate_control, | |
340 | Config.Accel.surrogate_id); | |
341 | ||
342 | if (sctusable) { | |
343 | if (EBIT_TEST(sctusable->mask, SC_NO_STORE) || | |
344 | (Config.onoff.surrogate_is_remote | |
345 | && EBIT_TEST(sctusable->mask, SC_NO_STORE_REMOTE))) { | |
346 | surrogateNoStore = true; | |
347 | entry->makePrivate(); | |
348 | } | |
349 | ||
350 | /* The HttpHeader logic cannot tell if the header it's parsing is a reply to an | |
351 | * accelerated request or not... | |
352 | * Still, this is an abtraction breach. - RC | |
353 | */ | |
354 | if (sctusable->max_age != -1) { | |
355 | if (sctusable->max_age < sctusable->max_stale) | |
356 | reply->expires = reply->date + sctusable->max_age; | |
357 | else | |
358 | reply->expires = reply->date + sctusable->max_stale; | |
359 | ||
360 | /* And update the timestamps */ | |
361 | entry->timestampsSet(); | |
362 | } | |
363 | ||
364 | /* We ignore cache-control directives as per the Surrogate specification */ | |
365 | ignoreCacheControl = true; | |
366 | ||
367 | httpHdrScTargetDestroy(sctusable); | |
368 | } | |
369 | } | |
370 | ||
371 | #endif | |
372 | } | |
373 | ||
374 | int | |
375 | HttpStateData::cacheableReply() | |
376 | { | |
377 | HttpReply const *rep = getReply(); | |
378 | HttpHeader const *hdr = &rep->header; | |
379 | const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0; | |
380 | const char *v; | |
381 | #if HTTP_VIOLATIONS | |
382 | ||
383 | const refresh_t *R = NULL; | |
384 | ||
385 | /* This strange looking define first looks up the refresh pattern | |
386 | * and then checks if the specified flag is set. The main purpose | |
387 | * of this is to simplify the refresh pattern lookup and HTTP_VIOLATIONS | |
388 | * condition | |
389 | */ | |
390 | #define REFRESH_OVERRIDE(flag) \ | |
391 | ((R = (R ? R : refreshLimits(entry->mem_obj->url))) , \ | |
392 | (R && R->flags.flag)) | |
393 | #else | |
394 | #define REFRESH_OVERRIDE(flag) 0 | |
395 | #endif | |
396 | ||
397 | if (surrogateNoStore) | |
398 | return 0; | |
399 | ||
400 | if (!ignoreCacheControl) { | |
401 | if (EBIT_TEST(cc_mask, CC_PRIVATE)) { | |
402 | if (!REFRESH_OVERRIDE(ignore_private)) | |
403 | return 0; | |
404 | } | |
405 | ||
406 | if (EBIT_TEST(cc_mask, CC_NO_CACHE)) { | |
407 | if (!REFRESH_OVERRIDE(ignore_no_cache)) | |
408 | return 0; | |
409 | } | |
410 | ||
411 | if (EBIT_TEST(cc_mask, CC_NO_STORE)) { | |
412 | if (!REFRESH_OVERRIDE(ignore_no_store)) | |
413 | return 0; | |
414 | } | |
415 | } | |
416 | ||
417 | if (request->flags.auth) { | |
418 | /* | |
419 | * Responses to requests with authorization may be cached | |
420 | * only if a Cache-Control: public reply header is present. | |
421 | * RFC 2068, sec 14.9.4 | |
422 | */ | |
423 | ||
424 | if (!EBIT_TEST(cc_mask, CC_PUBLIC)) { | |
425 | if (!REFRESH_OVERRIDE(ignore_auth)) | |
426 | return 0; | |
427 | } | |
428 | } | |
429 | ||
430 | /* Pragma: no-cache in _replies_ is not documented in HTTP, | |
431 | * but servers like "Active Imaging Webcast/2.0" sure do use it */ | |
432 | if (hdr->has(HDR_PRAGMA)) { | |
433 | String s = hdr->getList(HDR_PRAGMA); | |
434 | const int no_cache = strListIsMember(&s, "no-cache", ','); | |
435 | s.clean(); | |
436 | ||
437 | if (no_cache) { | |
438 | if (!REFRESH_OVERRIDE(ignore_no_cache)) | |
439 | return 0; | |
440 | } | |
441 | } | |
442 | ||
443 | /* | |
444 | * The "multipart/x-mixed-replace" content type is used for | |
445 | * continuous push replies. These are generally dynamic and | |
446 | * probably should not be cachable | |
447 | */ | |
448 | if ((v = hdr->getStr(HDR_CONTENT_TYPE))) | |
449 | if (!strncasecmp(v, "multipart/x-mixed-replace", 25)) | |
450 | return 0; | |
451 | ||
452 | switch (getReply()->sline.status) { | |
453 | /* Responses that are cacheable */ | |
454 | ||
455 | case HTTP_OK: | |
456 | ||
457 | case HTTP_NON_AUTHORITATIVE_INFORMATION: | |
458 | ||
459 | case HTTP_MULTIPLE_CHOICES: | |
460 | ||
461 | case HTTP_MOVED_PERMANENTLY: | |
462 | ||
463 | case HTTP_GONE: | |
464 | /* | |
465 | * Don't cache objects that need to be refreshed on next request, | |
466 | * unless we know how to refresh it. | |
467 | */ | |
468 | ||
469 | if (!refreshIsCachable(entry)) { | |
470 | debug(22, 3) ("refreshIsCachable() returned non-cacheable..\n"); | |
471 | return 0; | |
472 | } | |
473 | ||
474 | /* don't cache objects from peers w/o LMT, Date, or Expires */ | |
475 | /* check that is it enough to check headers @?@ */ | |
476 | if (rep->date > -1) | |
477 | return 1; | |
478 | else if (rep->last_modified > -1) | |
479 | return 1; | |
480 | else if (!_peer) | |
481 | return 1; | |
482 | ||
483 | /* @?@ (here and 302): invalid expires header compiles to squid_curtime */ | |
484 | else if (rep->expires > -1) | |
485 | return 1; | |
486 | else | |
487 | return 0; | |
488 | ||
489 | /* NOTREACHED */ | |
490 | break; | |
491 | ||
492 | /* Responses that only are cacheable if the server says so */ | |
493 | ||
494 | case HTTP_MOVED_TEMPORARILY: | |
495 | if (rep->expires > rep->date && rep->date > 0) | |
496 | return 1; | |
497 | else | |
498 | return 0; | |
499 | ||
500 | /* NOTREACHED */ | |
501 | break; | |
502 | ||
503 | /* Errors can be negatively cached */ | |
504 | ||
505 | case HTTP_NO_CONTENT: | |
506 | ||
507 | case HTTP_USE_PROXY: | |
508 | ||
509 | case HTTP_BAD_REQUEST: | |
510 | ||
511 | case HTTP_FORBIDDEN: | |
512 | ||
513 | case HTTP_NOT_FOUND: | |
514 | ||
515 | case HTTP_METHOD_NOT_ALLOWED: | |
516 | ||
517 | case HTTP_REQUEST_URI_TOO_LARGE: | |
518 | ||
519 | case HTTP_INTERNAL_SERVER_ERROR: | |
520 | ||
521 | case HTTP_NOT_IMPLEMENTED: | |
522 | ||
523 | case HTTP_BAD_GATEWAY: | |
524 | ||
525 | case HTTP_SERVICE_UNAVAILABLE: | |
526 | ||
527 | case HTTP_GATEWAY_TIMEOUT: | |
528 | return -1; | |
529 | ||
530 | /* NOTREACHED */ | |
531 | break; | |
532 | ||
533 | /* Some responses can never be cached */ | |
534 | ||
535 | case HTTP_PARTIAL_CONTENT: /* Not yet supported */ | |
536 | ||
537 | case HTTP_SEE_OTHER: | |
538 | ||
539 | case HTTP_NOT_MODIFIED: | |
540 | ||
541 | case HTTP_UNAUTHORIZED: | |
542 | ||
543 | case HTTP_PROXY_AUTHENTICATION_REQUIRED: | |
544 | ||
545 | case HTTP_INVALID_HEADER: /* Squid header parsing error */ | |
546 | ||
547 | case HTTP_HEADER_TOO_LARGE: | |
548 | ||
549 | case HTTP_PAYMENT_REQUIRED: | |
550 | case HTTP_NOT_ACCEPTABLE: | |
551 | case HTTP_REQUEST_TIMEOUT: | |
552 | case HTTP_CONFLICT: | |
553 | case HTTP_LENGTH_REQUIRED: | |
554 | case HTTP_PRECONDITION_FAILED: | |
555 | case HTTP_REQUEST_ENTITY_TOO_LARGE: | |
556 | case HTTP_UNSUPPORTED_MEDIA_TYPE: | |
557 | case HTTP_UNPROCESSABLE_ENTITY: | |
558 | case HTTP_LOCKED: | |
559 | case HTTP_FAILED_DEPENDENCY: | |
560 | case HTTP_INSUFFICIENT_STORAGE: | |
561 | ||
562 | return 0; | |
563 | ||
564 | default: /* Unknown status code */ | |
565 | debugs (11, 0, HERE << "HttpStateData::cacheableReply: unexpected http status code " << getReply()->sline.status); | |
566 | ||
567 | return 0; | |
568 | ||
569 | /* NOTREACHED */ | |
570 | break; | |
571 | } | |
572 | ||
573 | /* NOTREACHED */ | |
574 | } | |
575 | ||
576 | /* | |
577 | * For Vary, store the relevant request headers as | |
578 | * virtual headers in the reply | |
579 | * Returns false if the variance cannot be stored | |
580 | */ | |
581 | const char * | |
582 | httpMakeVaryMark(HttpRequest * request, HttpReply const * reply) | |
583 | { | |
584 | String vary, hdr; | |
585 | const char *pos = NULL; | |
586 | const char *item; | |
587 | const char *value; | |
588 | int ilen; | |
589 | static String vstr; | |
590 | ||
591 | vstr.clean(); | |
592 | vary = reply->header.getList(HDR_VARY); | |
593 | ||
594 | while (strListGetItem(&vary, ',', &item, &ilen, &pos)) { | |
595 | char *name = (char *)xmalloc(ilen + 1); | |
596 | xstrncpy(name, item, ilen + 1); | |
597 | Tolower(name); | |
598 | ||
599 | if (strcmp(name, "*") == 0) { | |
600 | /* Can not handle "Vary: *" withtout ETag support */ | |
601 | safe_free(name); | |
602 | vstr.clean(); | |
603 | break; | |
604 | } | |
605 | ||
606 | strListAdd(&vstr, name, ','); | |
607 | hdr = request->header.getByName(name); | |
608 | safe_free(name); | |
609 | value = hdr.buf(); | |
610 | ||
611 | if (value) { | |
612 | value = rfc1738_escape_part(value); | |
613 | vstr.append("=\"", 2); | |
614 | vstr.append(value); | |
615 | vstr.append("\"", 1); | |
616 | } | |
617 | ||
618 | hdr.clean(); | |
619 | } | |
620 | ||
621 | vary.clean(); | |
622 | #if X_ACCELERATOR_VARY | |
623 | ||
624 | pos = NULL; | |
625 | vary = reply->header.getList(HDR_X_ACCELERATOR_VARY); | |
626 | ||
627 | while (strListGetItem(&vary, ',', &item, &ilen, &pos)) { | |
628 | char *name = (char *)xmalloc(ilen + 1); | |
629 | xstrncpy(name, item, ilen + 1); | |
630 | Tolower(name); | |
631 | strListAdd(&vstr, name, ','); | |
632 | hdr = request->header.getByName(name); | |
633 | safe_free(name); | |
634 | value = hdr.buf(); | |
635 | ||
636 | if (value) { | |
637 | value = rfc1738_escape_part(value); | |
638 | vstr.append("=\"", 2); | |
639 | vstr.append(value); | |
640 | vstr.append("\"", 1); | |
641 | } | |
642 | ||
643 | hdr.clean(); | |
644 | } | |
645 | ||
646 | vary.clean(); | |
647 | #endif | |
648 | ||
649 | debug(11, 3) ("httpMakeVaryMark: %s\n", vstr.buf()); | |
650 | return vstr.buf(); | |
651 | } | |
652 | ||
653 | void | |
654 | HttpStateData::failReply(HttpReply *reply, http_status const & status) | |
655 | { | |
656 | reply->sline.version = HttpVersion(1, 0); | |
657 | reply->sline.status = status; | |
658 | entry->replaceHttpReply(reply); | |
659 | ||
660 | if (eof == 1) { | |
661 | serverComplete(); | |
662 | } | |
663 | } | |
664 | ||
665 | void | |
666 | HttpStateData::keepaliveAccounting(HttpReply *reply) | |
667 | { | |
668 | if (flags.keepalive) | |
669 | if (_peer) | |
670 | _peer->stats.n_keepalives_sent++; | |
671 | ||
672 | if (reply->keep_alive) { | |
673 | if (_peer) | |
674 | _peer->stats.n_keepalives_recv++; | |
675 | ||
676 | if (Config.onoff.detect_broken_server_pconns && reply->bodySize(request->method) == -1) { | |
677 | debug(11, 1) ("keepaliveAccounting: Impossible keep-alive header from '%s'\n", entry->url()); | |
678 | // debug(11, 2) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", readBuf->content()); | |
679 | flags.keepalive_broken = 1; | |
680 | } | |
681 | } | |
682 | } | |
683 | ||
684 | void | |
685 | HttpStateData::checkDateSkew(HttpReply *reply) | |
686 | { | |
687 | if (reply->date > -1 && !_peer) { | |
688 | int skew = abs((int)(reply->date - squid_curtime)); | |
689 | ||
690 | if (skew > 86400) | |
691 | debug(11, 3) ("%s's clock is skewed by %d seconds!\n", | |
692 | request->host, skew); | |
693 | } | |
694 | } | |
695 | ||
696 | /* | |
697 | * This creates the error page itself.. its likely | |
698 | * that the forward ported reply header max size patch | |
699 | * generates non http conformant error pages - in which | |
700 | * case the errors where should be 'BAD_GATEWAY' etc | |
701 | */ | |
702 | void | |
703 | HttpStateData::processReplyHeader() | |
704 | { | |
705 | /* Creates a blank header. If this routine is made incremental, this will | |
706 | * not do | |
707 | */ | |
708 | HttpReply *newrep = new HttpReply; | |
709 | Ctx ctx = ctx_enter(entry->mem_obj->url); | |
710 | debug(11, 3) ("processReplyHeader: key '%s'\n", entry->getMD5Text()); | |
711 | ||
712 | assert(!flags.headers_parsed); | |
713 | ||
714 | http_status error = HTTP_STATUS_NONE; | |
715 | ||
716 | const bool parsed = newrep->parse(readBuf, eof, &error); | |
717 | ||
718 | if (!parsed && error > 0) { // unrecoverable parsing error | |
719 | debugs(11, 3, "processReplyHeader: Non-HTTP-compliant header: '" << readBuf->content() << "'"); | |
720 | flags.headers_parsed = 1; | |
721 | // negated result yields http_status | |
722 | failReply (newrep, error); | |
723 | ctx_exit(ctx); | |
724 | return; | |
725 | } | |
726 | ||
727 | if (!parsed) { // need more data | |
728 | assert(!error); | |
729 | assert(!eof); | |
730 | delete newrep; | |
731 | ctx_exit(ctx); | |
732 | return; | |
733 | } | |
734 | ||
735 | reply = HTTPMSGLOCK(newrep); | |
736 | ||
737 | debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", | |
738 | readBuf->content()); | |
739 | ||
740 | header_bytes_read = headersEnd(readBuf->content(), readBuf->contentSize()); | |
741 | readBuf->consume(header_bytes_read); | |
742 | ||
743 | flags.headers_parsed = 1; | |
744 | ||
745 | keepaliveAccounting(reply); | |
746 | ||
747 | checkDateSkew(reply); | |
748 | ||
749 | processSurrogateControl (reply); | |
750 | ||
751 | /* TODO: IF the reply is a 1.0 reply, AND it has a Connection: Header | |
752 | * Parse the header and remove all referenced headers | |
753 | */ | |
754 | ||
755 | #if ICAP_CLIENT | |
756 | ||
757 | if (TheICAPConfig.onoff) { | |
758 | ICAPAccessCheck *icap_access_check = | |
759 | new ICAPAccessCheck(ICAP::methodRespmod, ICAP::pointPreCache, request, reply, icapAclCheckDoneWrapper, this); | |
760 | ||
761 | icapAccessCheckPending = true; | |
762 | icap_access_check->check(); // will eventually delete self | |
763 | ctx_exit(ctx); | |
764 | return; | |
765 | } | |
766 | ||
767 | #endif | |
768 | ||
769 | entry->replaceHttpReply(reply); | |
770 | ||
771 | haveParsedReplyHeaders(); | |
772 | ||
773 | if (eof == 1) { | |
774 | serverComplete(); | |
775 | } | |
776 | ||
777 | ctx_exit(ctx); | |
778 | } | |
779 | ||
780 | // Called when we parsed (and possibly adapted) the headers but | |
781 | // had not starting storing (a.k.a., sending) the body yet. | |
782 | void | |
783 | HttpStateData::haveParsedReplyHeaders() | |
784 | { | |
785 | Ctx ctx = ctx_enter(entry->mem_obj->url); | |
786 | ||
787 | if (getReply()->sline.status == HTTP_PARTIAL_CONTENT && | |
788 | getReply()->content_range) | |
789 | currentOffset = getReply()->content_range->spec.offset; | |
790 | ||
791 | entry->timestampsSet(); | |
792 | ||
793 | /* Check if object is cacheable or not based on reply code */ | |
794 | debug(11, 3) ("haveParsedReplyHeaders: HTTP CODE: %d\n", getReply()->sline.status); | |
795 | ||
796 | if (neighbors_do_private_keys) | |
797 | httpMaybeRemovePublic(entry, getReply()->sline.status); | |
798 | ||
799 | if (getReply()->header.has(HDR_VARY) | |
800 | #if X_ACCELERATOR_VARY | |
801 | || getReply()->header.has(HDR_X_ACCELERATOR_VARY) | |
802 | #endif | |
803 | ) { | |
804 | const char *vary = httpMakeVaryMark(orig_request, getReply()); | |
805 | ||
806 | if (!vary) { | |
807 | entry->makePrivate(); | |
808 | goto no_cache; | |
809 | ||
810 | } | |
811 | ||
812 | entry->mem_obj->vary_headers = xstrdup(vary); | |
813 | } | |
814 | ||
815 | #if WIP_FWD_LOG | |
816 | fwdStatus(fwd, s); | |
817 | ||
818 | #endif | |
819 | /* | |
820 | * If its not a reply that we will re-forward, then | |
821 | * allow the client to get it. | |
822 | */ | |
823 | if (!fwd->reforwardableStatus(getReply()->sline.status)) | |
824 | EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); | |
825 | ||
826 | switch (cacheableReply()) { | |
827 | ||
828 | case 1: | |
829 | entry->makePublic(); | |
830 | break; | |
831 | ||
832 | case 0: | |
833 | entry->makePrivate(); | |
834 | break; | |
835 | ||
836 | case -1: | |
837 | ||
838 | if (Config.negativeTtl > 0) | |
839 | entry->cacheNegatively(); | |
840 | else | |
841 | entry->makePrivate(); | |
842 | ||
843 | break; | |
844 | ||
845 | default: | |
846 | assert(0); | |
847 | ||
848 | break; | |
849 | } | |
850 | ||
851 | no_cache: | |
852 | ||
853 | if (!ignoreCacheControl && getReply()->cache_control) { | |
854 | if (EBIT_TEST(getReply()->cache_control->mask, CC_PROXY_REVALIDATE)) | |
855 | EBIT_SET(entry->flags, ENTRY_REVALIDATE); | |
856 | else if (EBIT_TEST(getReply()->cache_control->mask, CC_MUST_REVALIDATE)) | |
857 | EBIT_SET(entry->flags, ENTRY_REVALIDATE); | |
858 | } | |
859 | ||
860 | #if HEADERS_LOG | |
861 | headersLog(1, 0, request->method, getReply()); | |
862 | ||
863 | #endif | |
864 | ||
865 | ctx_exit(ctx); | |
866 | } | |
867 | ||
868 | HttpStateData::ConnectionStatus | |
869 | HttpStateData::statusIfComplete() const | |
870 | { | |
871 | HttpReply const *rep = getReply(); | |
872 | /* If the reply wants to close the connection, it takes precedence */ | |
873 | ||
874 | if (httpHeaderHasConnDir(&rep->header, "close")) | |
875 | return COMPLETE_NONPERSISTENT_MSG; | |
876 | ||
877 | /* If we didn't send a keep-alive request header, then this | |
878 | * can not be a persistent connection. | |
879 | */ | |
880 | if (!flags.keepalive) | |
881 | return COMPLETE_NONPERSISTENT_MSG; | |
882 | ||
883 | /* | |
884 | * If we haven't sent the whole request then this can not be a persistent | |
885 | * connection. | |
886 | */ | |
887 | if (!flags.request_sent) { | |
888 | debug(11, 1) ("statusIfComplete: Request not yet fully sent \"%s %s\"\n", | |
889 | RequestMethodStr[orig_request->method], | |
890 | entry->url()); | |
891 | return COMPLETE_NONPERSISTENT_MSG; | |
892 | } | |
893 | ||
894 | /* | |
895 | * What does the reply have to say about keep-alive? | |
896 | */ | |
897 | /* | |
898 | * XXX BUG? | |
899 | * If the origin server (HTTP/1.0) does not send a keep-alive | |
900 | * header, but keeps the connection open anyway, what happens? | |
901 | * We'll return here and http.c waits for an EOF before changing | |
902 | * store_status to STORE_OK. Combine this with ENTRY_FWD_HDR_WAIT | |
903 | * and an error status code, and we might have to wait until | |
904 | * the server times out the socket. | |
905 | */ | |
906 | if (!rep->keep_alive) | |
907 | return COMPLETE_NONPERSISTENT_MSG; | |
908 | ||
909 | return COMPLETE_PERSISTENT_MSG; | |
910 | } | |
911 | ||
912 | HttpStateData::ConnectionStatus | |
913 | HttpStateData::persistentConnStatus() const | |
914 | { | |
915 | debug(11, 3) ("persistentConnStatus: FD %d\n", fd); | |
916 | debug(11, 5) ("persistentConnStatus: content_length=%d\n", | |
917 | reply->content_length); | |
918 | ||
919 | /* If we haven't seen the end of reply headers, we are not done */ | |
920 | debug(11,5)("persistentConnStatus: flags.headers_parsed=%d\n", flags.headers_parsed); | |
921 | ||
922 | if (!flags.headers_parsed) | |
923 | return INCOMPLETE_MSG; | |
924 | ||
925 | const int clen = reply->bodySize(request->method); | |
926 | ||
927 | debug(11,5)("persistentConnStatus: clen=%d\n", clen); | |
928 | ||
929 | /* If the body size is unknown we must wait for EOF */ | |
930 | if (clen < 0) | |
931 | return INCOMPLETE_MSG; | |
932 | ||
933 | /* If the body size is known, we must wait until we've gotten all of it. */ | |
934 | if (clen > 0) { | |
935 | // old technique: | |
936 | // if (entry->mem_obj->endOffset() < reply->content_length + reply->hdr_sz) | |
937 | const int body_bytes_read = reply_bytes_read - header_bytes_read; | |
938 | debugs(11,5, "persistentConnStatus: body_bytes_read=" << | |
939 | body_bytes_read << " content_length=" << reply->content_length); | |
940 | ||
941 | if (body_bytes_read < reply->content_length) | |
942 | return INCOMPLETE_MSG; | |
943 | } | |
944 | ||
945 | /* If there is no message body or we got it all, we can be persistent */ | |
946 | return statusIfComplete(); | |
947 | } | |
948 | ||
949 | /* | |
950 | * This is the callback after some data has been read from the network | |
951 | */ | |
952 | void | |
953 | HttpStateData::ReadReplyWrapper(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data) | |
954 | { | |
955 | HttpStateData *httpState = static_cast<HttpStateData *>(data); | |
956 | assert (fd == httpState->fd); | |
957 | // assert(buf == readBuf->content()); | |
958 | PROF_start(HttpStateData_readReply); | |
959 | httpState->readReply (len, flag, xerrno); | |
960 | PROF_stop(HttpStateData_readReply); | |
961 | } | |
962 | ||
963 | /* XXX this function is too long! */ | |
964 | void | |
965 | HttpStateData::readReply (size_t len, comm_err_t flag, int xerrno) | |
966 | { | |
967 | int bin; | |
968 | int clen; | |
969 | flags.do_next_read = 0; | |
970 | ||
971 | /* | |
972 | * Bail out early on COMM_ERR_CLOSING - close handlers will tidy up for us | |
973 | */ | |
974 | ||
975 | if (flag == COMM_ERR_CLOSING) { | |
976 | debug (11,3)("http socket closing\n"); | |
977 | return; | |
978 | } | |
979 | ||
980 | if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { | |
981 | maybeReadVirginBody(); | |
982 | return; | |
983 | } | |
984 | ||
985 | errno = 0; | |
986 | /* prepare the read size for the next read (if any) */ | |
987 | ||
988 | debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, (int)len); | |
989 | ||
990 | if (flag == COMM_OK && len > 0) { | |
991 | readBuf->appended(len); | |
992 | reply_bytes_read += len; | |
993 | #if DELAY_POOLS | |
994 | ||
995 | DelayId delayId = entry->mem_obj->mostBytesAllowed(); | |
996 | delayId.bytesIn(len); | |
997 | #endif | |
998 | ||
999 | kb_incr(&statCounter.server.all.kbytes_in, len); | |
1000 | kb_incr(&statCounter.server.http.kbytes_in, len); | |
1001 | IOStats.Http.reads++; | |
1002 | ||
1003 | for (clen = len - 1, bin = 0; clen; bin++) | |
1004 | clen >>= 1; | |
1005 | ||
1006 | IOStats.Http.read_hist[bin]++; | |
1007 | } | |
1008 | ||
1009 | /* here the RFC says we should ignore whitespace between replies, but we can't as | |
1010 | * doing so breaks HTTP/0.9 replies beginning with witespace, and in addition | |
1011 | * the response splitting countermeasures is extremely likely to trigger on this, | |
1012 | * not allowing connection reuse in the first place. | |
1013 | */ | |
1014 | #if DONT_DO_THIS | |
1015 | if (!flags.headers_parsed && flag == COMM_OK && len > 0 && fd_table[fd].uses > 1) { | |
1016 | /* Skip whitespace between replies */ | |
1017 | ||
1018 | while (len > 0 && xisspace(*buf)) | |
1019 | xmemmove(buf, buf + 1, len--); | |
1020 | ||
1021 | if (len == 0) { | |
1022 | /* Continue to read... */ | |
1023 | /* Timeout NOT increased. This whitespace was from previous reply */ | |
1024 | flags.do_next_read = 1; | |
1025 | maybeReadVirginBody(); | |
1026 | return; | |
1027 | } | |
1028 | } | |
1029 | ||
1030 | #endif | |
1031 | ||
1032 | if (flag != COMM_OK || len < 0) { | |
1033 | debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n", | |
1034 | fd, xstrerror()); | |
1035 | ||
1036 | if (ignoreErrno(errno)) { | |
1037 | flags.do_next_read = 1; | |
1038 | } else { | |
1039 | ErrorState *err; | |
1040 | err = errorCon(ERR_READ_ERROR, HTTP_BAD_GATEWAY, fwd->request); | |
1041 | err->xerrno = errno; | |
1042 | fwd->fail(err); | |
1043 | flags.do_next_read = 0; | |
1044 | comm_close(fd); | |
1045 | } | |
1046 | } else if (flag == COMM_OK && len == 0 && !flags.headers_parsed) { | |
1047 | fwd->fail(errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_BAD_GATEWAY, fwd->request)); | |
1048 | eof = 1; | |
1049 | flags.do_next_read = 0; | |
1050 | comm_close(fd); | |
1051 | } else if (flag == COMM_OK && len == 0) { | |
1052 | /* Connection closed; retrieval done. */ | |
1053 | eof = 1; | |
1054 | ||
1055 | if (!flags.headers_parsed) { | |
1056 | /* | |
1057 | * When we called processReplyHeader() before, we | |
1058 | * didn't find the end of headers, but now we are | |
1059 | * definately at EOF, so we want to process the reply | |
1060 | * headers. | |
1061 | */ | |
1062 | PROF_start(HttpStateData_processReplyHeader); | |
1063 | processReplyHeader(); | |
1064 | PROF_stop(HttpStateData_processReplyHeader); | |
1065 | } else if (getReply()->sline.status == HTTP_INVALID_HEADER && HttpVersion(0,9) != getReply()->sline.version) { | |
1066 | fwd->fail(errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, fwd->request)); | |
1067 | flags.do_next_read = 0; | |
1068 | } else { | |
1069 | if (entry->mem_obj->getReply()->sline.status == HTTP_HEADER_TOO_LARGE) { | |
1070 | entry->reset(); | |
1071 | fwd->fail( errorCon(ERR_TOO_BIG, HTTP_BAD_GATEWAY, fwd->request)); | |
1072 | fwd->dontRetry(true); | |
1073 | flags.do_next_read = 0; | |
1074 | comm_close(fd); | |
1075 | } else { | |
1076 | serverComplete(); | |
1077 | } | |
1078 | } | |
1079 | } else { | |
1080 | if (!flags.headers_parsed) { | |
1081 | PROF_start(HttpStateData_processReplyHeader); | |
1082 | processReplyHeader(); | |
1083 | PROF_stop(HttpStateData_processReplyHeader); | |
1084 | ||
1085 | if (flags.headers_parsed) { | |
1086 | bool fail = reply == NULL; | |
1087 | ||
1088 | if (!fail) { | |
1089 | http_status s = getReply()->sline.status; | |
1090 | HttpVersion httpver = getReply()->sline.version; | |
1091 | fail = s == HTTP_INVALID_HEADER && httpver != HttpVersion(0,9); | |
1092 | } | |
1093 | ||
1094 | if (fail) { | |
1095 | entry->reset(); | |
1096 | fwd->fail( errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, fwd->request)); | |
1097 | comm_close(fd); | |
1098 | return; | |
1099 | } | |
1100 | ||
1101 | } | |
1102 | } | |
1103 | ||
1104 | PROF_start(HttpStateData_processReplyBody); | |
1105 | processReplyBody(); | |
1106 | PROF_stop(HttpStateData_processReplyBody); | |
1107 | } | |
1108 | } | |
1109 | ||
1110 | /* | |
1111 | * Call this when there is data from the origin server | |
1112 | * which should be sent to either StoreEntry, or to ICAP... | |
1113 | */ | |
1114 | void | |
1115 | HttpStateData::writeReplyBody() | |
1116 | { | |
1117 | const char *data = readBuf->content(); | |
1118 | int len = readBuf->contentSize(); | |
1119 | ||
1120 | #if ICAP_CLIENT | |
1121 | ||
1122 | if (virginBodyDestination != NULL) { | |
1123 | const size_t putSize = virginBodyDestination->putMoreData(data, len); | |
1124 | readBuf->consume(putSize); | |
1125 | return; | |
1126 | } | |
1127 | ||
1128 | // Even if we are done with sending the virgin body to ICAP, we may still | |
1129 | // be waiting for adapted headers. We need them before writing to store. | |
1130 | if (adaptedHeadSource != NULL) { | |
1131 | debugs(11,5, HERE << "need adapted head from " << adaptedHeadSource); | |
1132 | return; | |
1133 | } | |
1134 | ||
1135 | #endif | |
1136 | ||
1137 | entry->write (StoreIOBuffer(len, currentOffset, (char*)data)); | |
1138 | ||
1139 | readBuf->consume(len); | |
1140 | ||
1141 | currentOffset += len; | |
1142 | } | |
1143 | ||
1144 | /* | |
1145 | * processReplyBody has two purposes: | |
1146 | * 1 - take the reply body data, if any, and put it into either | |
1147 | * the StoreEntry, or give it over to ICAP. | |
1148 | * 2 - see if we made it to the end of the response (persistent | |
1149 | * connections and such) | |
1150 | */ | |
1151 | void | |
1152 | HttpStateData::processReplyBody() | |
1153 | { | |
1154 | ||
1155 | struct IN_ADDR *client_addr = NULL; | |
1156 | ||
1157 | if (!flags.headers_parsed) { | |
1158 | flags.do_next_read = 1; | |
1159 | maybeReadVirginBody(); | |
1160 | return; | |
1161 | } | |
1162 | ||
1163 | #if ICAP_CLIENT | |
1164 | if (icapAccessCheckPending) | |
1165 | return; | |
1166 | ||
1167 | #endif | |
1168 | ||
1169 | /* | |
1170 | * At this point the reply headers have been parsed and consumed. | |
1171 | * That means header content has been removed from readBuf and | |
1172 | * it contains only body data. | |
1173 | */ | |
1174 | writeReplyBody(); | |
1175 | ||
1176 | if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { | |
1177 | /* | |
1178 | * the above writeReplyBody() call could ABORT this entry, | |
1179 | * in that case, the server FD should already be closed. | |
1180 | * there's nothing for us to do. | |
1181 | */ | |
1182 | (void) 0; | |
1183 | } else | |
1184 | switch (persistentConnStatus()) { | |
1185 | ||
1186 | case INCOMPLETE_MSG: | |
1187 | debug(11,5)("processReplyBody: INCOMPLETE_MSG\n"); | |
1188 | /* Wait for more data or EOF condition */ | |
1189 | ||
1190 | if (flags.keepalive_broken) { | |
1191 | commSetTimeout(fd, 10, NULL, NULL); | |
1192 | } else { | |
1193 | commSetTimeout(fd, Config.Timeout.read, NULL, NULL); | |
1194 | } | |
1195 | ||
1196 | flags.do_next_read = 1; | |
1197 | break; | |
1198 | ||
1199 | case COMPLETE_PERSISTENT_MSG: | |
1200 | debug(11,5)("processReplyBody: COMPLETE_PERSISTENT_MSG\n"); | |
1201 | /* yes we have to clear all these! */ | |
1202 | commSetTimeout(fd, -1, NULL, NULL); | |
1203 | flags.do_next_read = 0; | |
1204 | ||
1205 | comm_remove_close_handler(fd, httpStateFree, this); | |
1206 | fwd->unregister(fd); | |
1207 | #if LINUX_TPROXY | |
1208 | ||
1209 | if (orig_request->flags.tproxy) | |
1210 | client_addr = &orig_request->client_addr; | |
1211 | ||
1212 | #endif | |
1213 | ||
1214 | if (_peer) { | |
1215 | if (_peer->options.originserver) | |
1216 | fwd->pconnPush(fd, _peer->name, orig_request->port, orig_request->host, client_addr); | |
1217 | else | |
1218 | fwd->pconnPush(fd, _peer->name, _peer->http_port, NULL, client_addr); | |
1219 | } else { | |
1220 | fwd->pconnPush(fd, request->host, request->port, NULL, client_addr); | |
1221 | } | |
1222 | ||
1223 | fd = -1; | |
1224 | ||
1225 | serverComplete(); | |
1226 | return; | |
1227 | ||
1228 | case COMPLETE_NONPERSISTENT_MSG: | |
1229 | debug(11,5)("processReplyBody: COMPLETE_NONPERSISTENT_MSG\n"); | |
1230 | serverComplete(); | |
1231 | return; | |
1232 | } | |
1233 | ||
1234 | maybeReadVirginBody(); | |
1235 | } | |
1236 | ||
1237 | void | |
1238 | HttpStateData::maybeReadVirginBody() | |
1239 | { | |
1240 | int read_sz = readBuf->spaceSize(); | |
1241 | ||
1242 | #if ICAP_CLIENT | |
1243 | #if RE_ENABLE_THIS_IF_NEEDED_OR_DELETE | |
1244 | // This code is not broken, but is probably not needed because we | |
1245 | // probably can read more than will fit into the BodyPipe buffer. | |
1246 | ||
1247 | if (virginBodyDestination != NULL) { | |
1248 | /* | |
1249 | * BodyPipe buffer has a finite size limit. We | |
1250 | * should not read more data from the network than will fit | |
1251 | * into the pipe buffer. If totally full, don't register | |
1252 | * the read handler at all. The ICAP side will call our | |
1253 | * icapSpaceAvailable() method when it has free space again. | |
1254 | */ | |
1255 | int icap_space = virginBodyDestination->buf().potentialSpaceSize(); | |
1256 | ||
1257 | debugs(11,9, "HttpStateData may read up to min(" << icap_space << | |
1258 | ", " << read_sz << ") bytes"); | |
1259 | ||
1260 | if (icap_space < read_sz) | |
1261 | read_sz = icap_space; | |
1262 | } | |
1263 | ||
1264 | #endif | |
1265 | #endif | |
1266 | ||
1267 | debugs(11,9, HERE << (flags.do_next_read ? "may" : "wont") << | |
1268 | " read up to " << read_sz << " bytes from FD " << fd); | |
1269 | ||
1270 | /* | |
1271 | * why <2? Because delayAwareRead() won't actually read if | |
1272 | * you ask it to read 1 byte. The delayed read request | |
1273 | * just gets re-queued until the client side drains, then | |
1274 | * the I/O thread hangs. Better to not register any read | |
1275 | * handler until we get a notification from someone that | |
1276 | * its okay to read again. | |
1277 | */ | |
1278 | if (read_sz < 2) | |
1279 | return; | |
1280 | ||
1281 | if (flags.do_next_read) { | |
1282 | flags.do_next_read = 0; | |
1283 | entry->delayAwareRead(fd, readBuf->space(), read_sz, ReadReplyWrapper, this); | |
1284 | } | |
1285 | } | |
1286 | ||
1287 | /* | |
1288 | * This will be called when request write is complete. | |
1289 | */ | |
1290 | void | |
1291 | HttpStateData::SendComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data) | |
1292 | { | |
1293 | HttpStateData *httpState = static_cast<HttpStateData *>(data); | |
1294 | debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n", | |
1295 | fd, (int) size, errflag); | |
1296 | #if URL_CHECKSUM_DEBUG | |
1297 | ||
1298 | entry->mem_obj->checkUrlChecksum(); | |
1299 | #endif | |
1300 | ||
1301 | if (size > 0) { | |
1302 | fd_bytes(fd, size, FD_WRITE); | |
1303 | kb_incr(&statCounter.server.all.kbytes_out, size); | |
1304 | kb_incr(&statCounter.server.http.kbytes_out, size); | |
1305 | } | |
1306 | ||
1307 | if (errflag == COMM_ERR_CLOSING) | |
1308 | return; | |
1309 | ||
1310 | if (errflag) { | |
1311 | ErrorState *err; | |
1312 | err = errorCon(ERR_WRITE_ERROR, HTTP_BAD_GATEWAY, httpState->fwd->request); | |
1313 | err->xerrno = xerrno; | |
1314 | httpState->fwd->fail(err); | |
1315 | comm_close(fd); | |
1316 | return; | |
1317 | } | |
1318 | ||
1319 | /* | |
1320 | * Set the read timeout here because it hasn't been set yet. | |
1321 | * We only set the read timeout after the request has been | |
1322 | * fully written to the server-side. If we start the timeout | |
1323 | * after connection establishment, then we are likely to hit | |
1324 | * the timeout for POST/PUT requests that have very large | |
1325 | * request bodies. | |
1326 | */ | |
1327 | commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); | |
1328 | ||
1329 | httpState->flags.request_sent = 1; | |
1330 | } | |
1331 | ||
1332 | // Close the HTTP server connection. Used by serverComplete(). | |
1333 | void | |
1334 | HttpStateData::closeServer() | |
1335 | { | |
1336 | debugs(11,5, HERE << "closing HTTP server FD " << fd << " this " << this); | |
1337 | ||
1338 | if (fd >= 0) { | |
1339 | fwd->unregister(fd); | |
1340 | comm_remove_close_handler(fd, httpStateFree, this); | |
1341 | comm_close(fd); | |
1342 | fd = -1; | |
1343 | } | |
1344 | } | |
1345 | ||
1346 | bool | |
1347 | HttpStateData::doneWithServer() const | |
1348 | { | |
1349 | return fd < 0; | |
1350 | } | |
1351 | ||
1352 | /* | |
1353 | * build request headers and append them to a given MemBuf | |
1354 | * used by buildRequestPrefix() | |
1355 | * note: initialised the HttpHeader, the caller is responsible for Clean()-ing | |
1356 | */ | |
1357 | void | |
1358 | HttpStateData::httpBuildRequestHeader(HttpRequest * request, | |
1359 | HttpRequest * orig_request, | |
1360 | StoreEntry * entry, | |
1361 | HttpHeader * hdr_out, | |
1362 | http_state_flags flags) | |
1363 | { | |
1364 | /* building buffer for complex strings */ | |
1365 | #define BBUF_SZ (MAX_URL+32) | |
1366 | LOCAL_ARRAY(char, bbuf, BBUF_SZ); | |
1367 | const HttpHeader *hdr_in = &orig_request->header; | |
1368 | const HttpHeaderEntry *e; | |
1369 | String strFwd; | |
1370 | HttpHeaderPos pos = HttpHeaderInitPos; | |
1371 | assert (hdr_out->owner == hoRequest); | |
1372 | /* append our IMS header */ | |
1373 | ||
1374 | if (request->lastmod > -1) | |
1375 | hdr_out->putTime(HDR_IF_MODIFIED_SINCE, request->lastmod); | |
1376 | ||
1377 | bool we_do_ranges = decideIfWeDoRanges (orig_request); | |
1378 | ||
1379 | String strConnection (hdr_in->getList(HDR_CONNECTION)); | |
1380 | ||
1381 | while ((e = hdr_in->getEntry(&pos))) | |
1382 | copyOneHeaderFromClientsideRequestToUpstreamRequest(e, strConnection, request, orig_request, hdr_out, we_do_ranges, flags); | |
1383 | ||
1384 | /* Abstraction break: We should interpret multipart/byterange responses | |
1385 | * into offset-length data, and this works around our inability to do so. | |
1386 | */ | |
1387 | if (!we_do_ranges && orig_request->multipartRangeRequest()) { | |
1388 | /* don't cache the result */ | |
1389 | orig_request->flags.cachable = 0; | |
1390 | /* pretend it's not a range request */ | |
1391 | delete orig_request->range; | |
1392 | orig_request->range = NULL; | |
1393 | orig_request->flags.range = 0; | |
1394 | } | |
1395 | ||
1396 | /* append Via */ | |
1397 | if (Config.onoff.via) { | |
1398 | String strVia; | |
1399 | strVia = hdr_in->getList(HDR_VIA); | |
1400 | snprintf(bbuf, BBUF_SZ, "%d.%d %s", | |
1401 | orig_request->http_ver.major, | |
1402 | orig_request->http_ver.minor, ThisCache); | |
1403 | strListAdd(&strVia, bbuf, ','); | |
1404 | hdr_out->putStr(HDR_VIA, strVia.buf()); | |
1405 | strVia.clean(); | |
1406 | } | |
1407 | ||
1408 | #if ESI | |
1409 | { | |
1410 | /* Append Surrogate-Capabilities */ | |
1411 | String strSurrogate (hdr_in->getList(HDR_SURROGATE_CAPABILITY)); | |
1412 | snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0 ESI/1.0\"", | |
1413 | Config.Accel.surrogate_id); | |
1414 | strListAdd(&strSurrogate, bbuf, ','); | |
1415 | hdr_out->putStr(HDR_SURROGATE_CAPABILITY, strSurrogate.buf()); | |
1416 | } | |
1417 | #endif | |
1418 | ||
1419 | /* append X-Forwarded-For */ | |
1420 | strFwd = hdr_in->getList(HDR_X_FORWARDED_FOR); | |
1421 | ||
1422 | if (opt_forwarded_for && orig_request->client_addr.s_addr != no_addr.s_addr) | |
1423 | strListAdd(&strFwd, inet_ntoa(orig_request->client_addr), ','); | |
1424 | else | |
1425 | strListAdd(&strFwd, "unknown", ','); | |
1426 | ||
1427 | hdr_out->putStr(HDR_X_FORWARDED_FOR, strFwd.buf()); | |
1428 | ||
1429 | strFwd.clean(); | |
1430 | ||
1431 | /* append Host if not there already */ | |
1432 | if (!hdr_out->has(HDR_HOST)) { | |
1433 | if (orig_request->peer_domain) { | |
1434 | hdr_out->putStr(HDR_HOST, orig_request->peer_domain); | |
1435 | } else if (orig_request->port == urlDefaultPort(orig_request->protocol)) { | |
1436 | /* use port# only if not default */ | |
1437 | hdr_out->putStr(HDR_HOST, orig_request->host); | |
1438 | } else { | |
1439 | httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d", | |
1440 | orig_request->host, (int) orig_request->port); | |
1441 | } | |
1442 | } | |
1443 | ||
1444 | /* append Authorization if known in URL, not in header and going direct */ | |
1445 | if (!hdr_out->has(HDR_AUTHORIZATION)) { | |
1446 | if (!request->flags.proxying && *request->login) { | |
1447 | httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", | |
1448 | base64_encode(request->login)); | |
1449 | } | |
1450 | } | |
1451 | ||
1452 | /* append Proxy-Authorization if configured for peer, and proxying */ | |
1453 | if (request->flags.proxying && orig_request->peer_login && | |
1454 | !hdr_out->has(HDR_PROXY_AUTHORIZATION)) { | |
1455 | if (*orig_request->peer_login == '*') { | |
1456 | /* Special mode, to pass the username to the upstream cache */ | |
1457 | char loginbuf[256]; | |
1458 | const char *username = "-"; | |
1459 | ||
1460 | if (orig_request->auth_user_request) | |
1461 | username = orig_request->auth_user_request->username(); | |
1462 | else if (orig_request->extacl_user.size()) | |
1463 | username = orig_request->extacl_user.buf(); | |
1464 | ||
1465 | snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1); | |
1466 | ||
1467 | httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s", | |
1468 | base64_encode(loginbuf)); | |
1469 | } else if (strcmp(orig_request->peer_login, "PASS") == 0) { | |
1470 | if (orig_request->extacl_user.size() && orig_request->extacl_passwd.size()) { | |
1471 | char loginbuf[256]; | |
1472 | snprintf(loginbuf, sizeof(loginbuf), "%s:%s", orig_request->extacl_user.buf(), orig_request->extacl_passwd.buf()); | |
1473 | httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s", | |
1474 | base64_encode(loginbuf)); | |
1475 | } | |
1476 | } else if (strcmp(orig_request->peer_login, "PROXYPASS") == 0) { | |
1477 | /* Nothing to do */ | |
1478 | } else { | |
1479 | httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s", | |
1480 | base64_encode(orig_request->peer_login)); | |
1481 | } | |
1482 | } | |
1483 | ||
1484 | /* append WWW-Authorization if configured for peer */ | |
1485 | if (flags.originpeer && orig_request->peer_login && | |
1486 | !hdr_out->has(HDR_AUTHORIZATION)) { | |
1487 | if (strcmp(orig_request->peer_login, "PASS") == 0) { | |
1488 | /* No credentials to forward.. (should have been done above if available) */ | |
1489 | } else if (strcmp(orig_request->peer_login, "PROXYPASS") == 0) { | |
1490 | /* Special mode, convert proxy authentication to WWW authentication | |
1491 | * (also applies to authentication provided by external acl) | |
1492 | */ | |
1493 | const char *auth = hdr_in->getStr(HDR_PROXY_AUTHORIZATION); | |
1494 | ||
1495 | if (auth && strncasecmp(auth, "basic ", 6) == 0) { | |
1496 | hdr_out->putStr(HDR_AUTHORIZATION, auth); | |
1497 | } else if (orig_request->extacl_user.size() && orig_request->extacl_passwd.size()) { | |
1498 | char loginbuf[256]; | |
1499 | snprintf(loginbuf, sizeof(loginbuf), "%s:%s", orig_request->extacl_user.buf(), orig_request->extacl_passwd.buf()); | |
1500 | httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", | |
1501 | base64_encode(loginbuf)); | |
1502 | } | |
1503 | } else if (*orig_request->peer_login == '*') { | |
1504 | /* Special mode, to pass the username to the upstream cache */ | |
1505 | char loginbuf[256]; | |
1506 | const char *username = "-"; | |
1507 | ||
1508 | if (orig_request->auth_user_request) | |
1509 | username = orig_request->auth_user_request->username(); | |
1510 | else if (orig_request->extacl_user.size()) | |
1511 | username = orig_request->extacl_user.buf(); | |
1512 | ||
1513 | snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1); | |
1514 | ||
1515 | httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", | |
1516 | base64_encode(loginbuf)); | |
1517 | } else { | |
1518 | /* Fixed login string */ | |
1519 | httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", | |
1520 | base64_encode(orig_request->peer_login)); | |
1521 | } | |
1522 | } | |
1523 | ||
1524 | /* append Cache-Control, add max-age if not there already */ { | |
1525 | HttpHdrCc *cc = hdr_in->getCc(); | |
1526 | ||
1527 | if (!cc) | |
1528 | cc = httpHdrCcCreate(); | |
1529 | ||
1530 | if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { | |
1531 | const char *url = | |
1532 | entry ? entry->url() : urlCanonical(orig_request); | |
1533 | httpHdrCcSetMaxAge(cc, getMaxAge(url)); | |
1534 | ||
1535 | if (request->urlpath.size()) | |
1536 | assert(strstr(url, request->urlpath.buf())); | |
1537 | } | |
1538 | ||
1539 | /* Set no-cache if determined needed but not found */ | |
1540 | if (orig_request->flags.nocache && !hdr_in->has(HDR_PRAGMA)) | |
1541 | EBIT_SET(cc->mask, CC_NO_CACHE); | |
1542 | ||
1543 | /* Enforce sibling relations */ | |
1544 | if (flags.only_if_cached) | |
1545 | EBIT_SET(cc->mask, CC_ONLY_IF_CACHED); | |
1546 | ||
1547 | hdr_out->putCc(cc); | |
1548 | ||
1549 | httpHdrCcDestroy(cc); | |
1550 | } | |
1551 | ||
1552 | /* maybe append Connection: keep-alive */ | |
1553 | if (flags.keepalive) { | |
1554 | if (flags.proxying) { | |
1555 | hdr_out->putStr(HDR_PROXY_CONNECTION, "keep-alive"); | |
1556 | } else { | |
1557 | hdr_out->putStr(HDR_CONNECTION, "keep-alive"); | |
1558 | } | |
1559 | } | |
1560 | ||
1561 | /* append Front-End-Https */ | |
1562 | if (flags.front_end_https) { | |
1563 | if (flags.front_end_https == 1 || request->protocol == PROTO_HTTPS) | |
1564 | hdr_out->putStr(HDR_FRONT_END_HTTPS, "On"); | |
1565 | } | |
1566 | ||
1567 | /* Now mangle the headers. */ | |
1568 | if (Config2.onoff.mangle_request_headers) | |
1569 | httpHdrMangleList(hdr_out, request, ROR_REQUEST); | |
1570 | ||
1571 | strConnection.clean(); | |
1572 | } | |
1573 | ||
1574 | void | |
1575 | copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, String strConnection, HttpRequest * request, HttpRequest * orig_request, HttpHeader * hdr_out, int we_do_ranges, http_state_flags flags) | |
1576 | { | |
1577 | debug(11, 5) ("httpBuildRequestHeader: %s: %s\n", | |
1578 | e->name.buf(), e->value.buf()); | |
1579 | ||
1580 | if (!httpRequestHdrAllowed(e, &strConnection)) { | |
1581 | debug(11, 2) ("'%s' header denied by anonymize_headers configuration\n",+ e->name.buf()); | |
1582 | return; | |
1583 | } | |
1584 | ||
1585 | switch (e->id) { | |
1586 | ||
1587 | case HDR_PROXY_AUTHORIZATION: | |
1588 | /* Only pass on proxy authentication to peers for which | |
1589 | * authentication forwarding is explicitly enabled | |
1590 | */ | |
1591 | ||
1592 | if (flags.proxying && orig_request->peer_login && | |
1593 | (strcmp(orig_request->peer_login, "PASS") == 0 || | |
1594 | strcmp(orig_request->peer_login, "PROXYPASS") == 0)) { | |
1595 | hdr_out->addEntry(e->clone()); | |
1596 | } | |
1597 | ||
1598 | break; | |
1599 | ||
1600 | case HDR_AUTHORIZATION: | |
1601 | /* Pass on WWW authentication */ | |
1602 | ||
1603 | if (!flags.originpeer) { | |
1604 | hdr_out->addEntry(e->clone()); | |
1605 | } else { | |
1606 | /* In accelerators, only forward authentication if enabled | |
1607 | * (see also below for proxy->server authentication) | |
1608 | */ | |
1609 | ||
1610 | if (orig_request->peer_login && | |
1611 | (strcmp(orig_request->peer_login, "PASS") == 0 || | |
1612 | strcmp(orig_request->peer_login, "PROXYPASS") == 0)) { | |
1613 | hdr_out->addEntry(e->clone()); | |
1614 | } | |
1615 | } | |
1616 | ||
1617 | break; | |
1618 | ||
1619 | case HDR_HOST: | |
1620 | /* | |
1621 | * Normally Squid rewrites the Host: header. | |
1622 | * However, there is one case when we don't: If the URL | |
1623 | * went through our redirector and the admin configured | |
1624 | * 'redir_rewrites_host' to be off. | |
1625 | */ | |
1626 | ||
1627 | if (request->flags.redirected && !Config.onoff.redir_rewrites_host) | |
1628 | hdr_out->addEntry(e->clone()); | |
1629 | else { | |
1630 | /* use port# only if not default */ | |
1631 | ||
1632 | if (orig_request->port == urlDefaultPort(orig_request->protocol)) { | |
1633 | hdr_out->putStr(HDR_HOST, orig_request->host); | |
1634 | } else { | |
1635 | httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d", | |
1636 | orig_request->host, (int) orig_request->port); | |
1637 | } | |
1638 | } | |
1639 | ||
1640 | break; | |
1641 | ||
1642 | case HDR_IF_MODIFIED_SINCE: | |
1643 | /* append unless we added our own; | |
1644 | * note: at most one client's ims header can pass through */ | |
1645 | ||
1646 | if (!hdr_out->has(HDR_IF_MODIFIED_SINCE)) | |
1647 | hdr_out->addEntry(e->clone()); | |
1648 | ||
1649 | break; | |
1650 | ||
1651 | case HDR_MAX_FORWARDS: | |
1652 | if (orig_request->method == METHOD_TRACE) { | |
1653 | const int hops = e->getInt(); | |
1654 | ||
1655 | if (hops > 0) | |
1656 | hdr_out->putInt(HDR_MAX_FORWARDS, hops - 1); | |
1657 | } | |
1658 | ||
1659 | break; | |
1660 | ||
1661 | case HDR_VIA: | |
1662 | /* If Via is disabled then forward any received header as-is */ | |
1663 | ||
1664 | if (!Config.onoff.via) | |
1665 | hdr_out->addEntry(e->clone()); | |
1666 | ||
1667 | break; | |
1668 | ||
1669 | case HDR_RANGE: | |
1670 | ||
1671 | case HDR_IF_RANGE: | |
1672 | ||
1673 | case HDR_REQUEST_RANGE: | |
1674 | if (!we_do_ranges) | |
1675 | hdr_out->addEntry(e->clone()); | |
1676 | ||
1677 | break; | |
1678 | ||
1679 | case HDR_PROXY_CONNECTION: | |
1680 | ||
1681 | case HDR_CONNECTION: | |
1682 | ||
1683 | case HDR_X_FORWARDED_FOR: | |
1684 | ||
1685 | case HDR_CACHE_CONTROL: | |
1686 | /* append these after the loop if needed */ | |
1687 | break; | |
1688 | ||
1689 | case HDR_FRONT_END_HTTPS: | |
1690 | if (!flags.front_end_https) | |
1691 | hdr_out->addEntry(e->clone()); | |
1692 | ||
1693 | break; | |
1694 | ||
1695 | default: | |
1696 | /* pass on all other header fields */ | |
1697 | hdr_out->addEntry(e->clone()); | |
1698 | } | |
1699 | } | |
1700 | ||
1701 | bool | |
1702 | HttpStateData::decideIfWeDoRanges (HttpRequest * orig_request) | |
1703 | { | |
1704 | bool result = true; | |
1705 | /* decide if we want to do Ranges ourselves | |
1706 | * and fetch the whole object now) | |
1707 | * We want to handle Ranges ourselves iff | |
1708 | * - we can actually parse client Range specs | |
1709 | * - the specs are expected to be simple enough (e.g. no out-of-order ranges) | |
1710 | * - reply will be cachable | |
1711 | * (If the reply will be uncachable we have to throw it away after | |
1712 | * serving this request, so it is better to forward ranges to | |
1713 | * the server and fetch only the requested content) | |
1714 | */ | |
1715 | ||
1716 | if (NULL == orig_request->range || !orig_request->flags.cachable | |
1717 | || orig_request->range->offsetLimitExceeded()) | |
1718 | result = false; | |
1719 | ||
1720 | debug(11, 8) ("decideIfWeDoRanges: range specs: %p, cachable: %d; we_do_ranges: %d\n", | |
1721 | orig_request->range, orig_request->flags.cachable, result); | |
1722 | ||
1723 | return result; | |
1724 | } | |
1725 | ||
1726 | /* build request prefix and append it to a given MemBuf; | |
1727 | * return the length of the prefix */ | |
1728 | mb_size_t | |
1729 | HttpStateData::buildRequestPrefix(HttpRequest * request, | |
1730 | HttpRequest * orig_request, | |
1731 | StoreEntry * entry, | |
1732 | MemBuf * mb, | |
1733 | http_state_flags flags) | |
1734 | { | |
1735 | const int offset = mb->size; | |
1736 | HttpVersion httpver(1, 0); | |
1737 | mb->Printf("%s %s HTTP/%d.%d\r\n", | |
1738 | RequestMethodStr[request->method], | |
1739 | request->urlpath.size() ? request->urlpath.buf() : "/", | |
1740 | httpver.major,httpver.minor); | |
1741 | /* build and pack headers */ | |
1742 | { | |
1743 | HttpHeader hdr(hoRequest); | |
1744 | Packer p; | |
1745 | httpBuildRequestHeader(request, orig_request, entry, &hdr, flags); | |
1746 | packerToMemInit(&p, mb); | |
1747 | hdr.packInto(&p); | |
1748 | hdr.clean(); | |
1749 | packerClean(&p); | |
1750 | } | |
1751 | /* append header terminator */ | |
1752 | mb->append(crlf, 2); | |
1753 | return mb->size - offset; | |
1754 | } | |
1755 | ||
1756 | /* This will be called when connect completes. Write request. */ | |
1757 | bool | |
1758 | HttpStateData::sendRequest() | |
1759 | { | |
1760 | MemBuf mb; | |
1761 | ||
1762 | debug(11, 5) ("httpSendRequest: FD %d, request %p, this %p.\n", fd, request, this); | |
1763 | ||
1764 | commSetTimeout(fd, Config.Timeout.lifetime, httpTimeout, this); | |
1765 | flags.do_next_read = 1; | |
1766 | maybeReadVirginBody(); | |
1767 | ||
1768 | if (orig_request->body_pipe != NULL) { | |
1769 | requestBodySource = orig_request->body_pipe; | |
1770 | ||
1771 | if (!requestBodySource->setConsumerIfNotLate(this)) { | |
1772 | debugs(32,3, HERE << "aborting on partially consumed body"); | |
1773 | requestBodySource = NULL; | |
1774 | return false; | |
1775 | } | |
1776 | ||
1777 | requestSender = HttpStateData::sentRequestBodyWrapper; | |
1778 | debugs(32,3, HERE << "expecting request body on pipe " << requestBodySource); | |
1779 | } else { | |
1780 | assert(!requestBodySource); | |
1781 | requestSender = HttpStateData::SendComplete; | |
1782 | } | |
1783 | ||
1784 | if (_peer != NULL) { | |
1785 | if (_peer->options.originserver) { | |
1786 | flags.proxying = 0; | |
1787 | flags.originpeer = 1; | |
1788 | } else { | |
1789 | flags.proxying = 1; | |
1790 | flags.originpeer = 0; | |
1791 | } | |
1792 | } else { | |
1793 | flags.proxying = 0; | |
1794 | flags.originpeer = 0; | |
1795 | } | |
1796 | ||
1797 | /* | |
1798 | * Is keep-alive okay for all request methods? | |
1799 | */ | |
1800 | if (!Config.onoff.server_pconns) | |
1801 | flags.keepalive = 0; | |
1802 | else if (_peer == NULL) | |
1803 | flags.keepalive = 1; | |
1804 | else if (_peer->stats.n_keepalives_sent < 10) | |
1805 | flags.keepalive = 1; | |
1806 | else if ((double) _peer->stats.n_keepalives_recv / | |
1807 | (double) _peer->stats.n_keepalives_sent > 0.50) | |
1808 | flags.keepalive = 1; | |
1809 | ||
1810 | if (_peer) { | |
1811 | if (neighborType(_peer, request) == PEER_SIBLING && | |
1812 | !_peer->options.allow_miss) | |
1813 | flags.only_if_cached = 1; | |
1814 | ||
1815 | flags.front_end_https = _peer->front_end_https; | |
1816 | } | |
1817 | ||
1818 | mb.init(); | |
1819 | buildRequestPrefix(request, orig_request, entry, &mb, flags); | |
1820 | debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, mb.buf); | |
1821 | comm_write_mbuf(fd, &mb, requestSender, this); | |
1822 | ||
1823 | return true; | |
1824 | } | |
1825 | ||
1826 | void | |
1827 | httpStart(FwdState *fwd) | |
1828 | { | |
1829 | debug(11, 3) ("httpStart: \"%s %s\"\n", | |
1830 | RequestMethodStr[fwd->request->method], | |
1831 | fwd->entry->url()); | |
1832 | HttpStateData *httpState = new HttpStateData(fwd); | |
1833 | ||
1834 | if (!httpState->sendRequest()) { | |
1835 | debug(11, 3) ("httpStart: aborted"); | |
1836 | delete httpState; | |
1837 | return; | |
1838 | } | |
1839 | ||
1840 | statCounter.server.all.requests++; | |
1841 | statCounter.server.http.requests++; | |
1842 | ||
1843 | /* | |
1844 | * We used to set the read timeout here, but not any more. | |
1845 | * Now its set in httpSendComplete() after the full request, | |
1846 | * including request body, has been written to the server. | |
1847 | */ | |
1848 | } | |
1849 | ||
1850 | void | |
1851 | HttpStateData::doneSendingRequestBody() | |
1852 | { | |
1853 | ACLChecklist ch; | |
1854 | debugs(11,5, HERE << "doneSendingRequestBody: FD " << fd); | |
1855 | ch.request = HTTPMSGLOCK(request); | |
1856 | ||
1857 | if (Config.accessList.brokenPosts) | |
1858 | ch.accessList = cbdataReference(Config.accessList.brokenPosts); | |
1859 | ||
1860 | /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */ | |
1861 | ||
1862 | if (!Config.accessList.brokenPosts) { | |
1863 | debug(11, 5) ("doneSendingRequestBody: No brokenPosts list\n"); | |
1864 | HttpStateData::SendComplete(fd, NULL, 0, COMM_OK, 0, this); | |
1865 | } else if (!ch.fastCheck()) { | |
1866 | debug(11, 5) ("doneSendingRequestBody: didn't match brokenPosts\n"); | |
1867 | HttpStateData::SendComplete(fd, NULL, 0, COMM_OK, 0, this); | |
1868 | } else { | |
1869 | debug(11, 2) ("doneSendingRequestBody: matched brokenPosts\n"); | |
1870 | comm_write(fd, "\r\n", 2, HttpStateData::SendComplete, this, NULL); | |
1871 | } | |
1872 | } | |
1873 | ||
1874 | // more origin request body data is available | |
1875 | void | |
1876 | HttpStateData::handleMoreRequestBodyAvailable() | |
1877 | { | |
1878 | if (eof || fd < 0) { | |
1879 | // XXX: we should check this condition in other callbacks then! | |
1880 | // TODO: Check whether this can actually happen: We should unsubscribe | |
1881 | // as a body consumer when the above condition(s) are detected. | |
1882 | debugs(11, 1, HERE << "Transaction aborted while reading HTTP body"); | |
1883 | return; | |
1884 | } | |
1885 | ||
1886 | assert(requestBodySource != NULL); | |
1887 | ||
1888 | if (requestBodySource->buf().hasContent()) { | |
1889 | // XXX: why does not this trigger a debug message on every request? | |
1890 | ||
1891 | if (flags.headers_parsed && !flags.abuse_detected) { | |
1892 | flags.abuse_detected = 1; | |
1893 | debug(11, 1) ("http handleMoreRequestBodyAvailable: Likely proxy abuse detected '%s' -> '%s'\n", | |
1894 | inet_ntoa(orig_request->client_addr), | |
1895 | entry->url()); | |
1896 | ||
1897 | if (getReply()->sline.status == HTTP_INVALID_HEADER) { | |
1898 | comm_close(fd); | |
1899 | return; | |
1900 | } | |
1901 | } | |
1902 | } | |
1903 | ||
1904 | HttpStateData::handleMoreRequestBodyAvailable(); | |
1905 | } | |
1906 | ||
1907 | // premature end of the request body | |
1908 | void | |
1909 | HttpStateData::handleRequestBodyProducerAborted() | |
1910 | { | |
1911 | ServerStateData::handleRequestBodyProducerAborted(); | |
1912 | // XXX: SendComplete(COMM_ERR_CLOSING) does little. Is it enough? | |
1913 | SendComplete(fd, NULL, 0, COMM_ERR_CLOSING, 0, this); | |
1914 | } | |
1915 | ||
1916 | // called when we wrote request headers(!) or a part of the body | |
1917 | void | |
1918 | HttpStateData::sentRequestBody(int fd, size_t size, comm_err_t errflag) | |
1919 | { | |
1920 | if (size > 0) | |
1921 | kb_incr(&statCounter.server.http.kbytes_out, size); | |
1922 | ||
1923 | ServerStateData::sentRequestBody(fd, size, errflag); | |
1924 | } | |
1925 | ||
1926 | // Quickly abort the transaction | |
1927 | // TODO: destruction should be sufficient as the destructor should cleanup, | |
1928 | // including canceling close handlers | |
1929 | void | |
1930 | HttpStateData::abortTransaction(const char *reason) | |
1931 | { | |
1932 | debugs(11,5, HERE << "aborting transaction for " << reason << | |
1933 | "; FD " << fd << ", this " << this); | |
1934 | ||
1935 | if (fd >= 0) | |
1936 | comm_close(fd); | |
1937 | else | |
1938 | delete this; | |
1939 | } | |
1940 | ||
1941 | void | |
1942 | httpBuildVersion(HttpVersion * version, unsigned int major, unsigned int minor) | |
1943 | { | |
1944 | version->major = major; | |
1945 | version->minor = minor; | |
1946 | } | |
1947 | ||
1948 | #if ICAP_CLIENT | |
1949 | ||
1950 | static void | |
1951 | icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data) | |
1952 | { | |
1953 | HttpStateData *http = (HttpStateData *)data; | |
1954 | http->icapAclCheckDone(service); | |
1955 | } | |
1956 | ||
1957 | void | |
1958 | HttpStateData::icapAclCheckDone(ICAPServiceRep::Pointer service) | |
1959 | { | |
1960 | icapAccessCheckPending = false; | |
1961 | ||
1962 | const bool startedIcap = startIcap(service, orig_request); | |
1963 | ||
1964 | if (!startedIcap && (!service || service->bypass)) { | |
1965 | // handle ICAP start failure when no service was selected | |
1966 | // or where the selected service was optional | |
1967 | entry->replaceHttpReply(reply); | |
1968 | ||
1969 | haveParsedReplyHeaders(); | |
1970 | processReplyBody(); | |
1971 | ||
1972 | if (eof == 1) | |
1973 | serverComplete(); | |
1974 | ||
1975 | return; | |
1976 | } | |
1977 | ||
1978 | if (!startedIcap) { | |
1979 | // handle start failure for an essential ICAP service | |
1980 | ErrorState *err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, orig_request); | |
1981 | err->xerrno = errno; | |
1982 | errorAppendEntry(entry, err); | |
1983 | comm_close(fd); | |
1984 | return; | |
1985 | } | |
1986 | ||
1987 | processReplyBody(); | |
1988 | } | |
1989 | ||
1990 | #endif |