]> git.ipfire.org Git - thirdparty/squid.git/blob - src/http.cc
mkrelease: allow two digits for minor release numbers (#1837)
[thirdparty/squid.git] / src / http.cc
1 /*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 11 Hypertext Transfer Protocol (HTTP) */
10
11 /*
12 * Anonymizing patch by lutz@as-node.jena.thur.de
13 * have a look into http-anon.c to get more information.
14 */
15
16 #include "squid.h"
17 #include "acl/FilledChecklist.h"
18 #include "base/AsyncJobCalls.h"
19 #include "base/DelayedAsyncCalls.h"
20 #include "base/Raw.h"
21 #include "base/TextException.h"
22 #include "base64.h"
23 #include "CachePeer.h"
24 #include "client_side.h"
25 #include "comm/Connection.h"
26 #include "comm/Read.h"
27 #include "comm/Write.h"
28 #include "error/Detail.h"
29 #include "errorpage.h"
30 #include "fd.h"
31 #include "fde.h"
32 #include "globals.h"
33 #include "http.h"
34 #include "http/one/ResponseParser.h"
35 #include "http/one/TeChunkedParser.h"
36 #include "http/StatusCode.h"
37 #include "http/Stream.h"
38 #include "HttpControlMsg.h"
39 #include "HttpHdrCc.h"
40 #include "HttpHdrContRange.h"
41 #include "HttpHdrSc.h"
42 #include "HttpHdrScTarget.h"
43 #include "HttpHeaderTools.h"
44 #include "HttpReply.h"
45 #include "HttpRequest.h"
46 #include "HttpUpgradeProtocolAccess.h"
47 #include "log/access_log.h"
48 #include "MemBuf.h"
49 #include "MemObject.h"
50 #include "neighbors.h"
51 #include "pconn.h"
52 #include "peer_proxy_negotiate_auth.h"
53 #include "refresh.h"
54 #include "RefreshPattern.h"
55 #include "rfc1738.h"
56 #include "SquidConfig.h"
57 #include "SquidMath.h"
58 #include "StatCounters.h"
59 #include "Store.h"
60 #include "StrList.h"
61 #include "tools.h"
62 #include "util.h"
63
64 #if USE_AUTH
65 #include "auth/UserRequest.h"
66 #endif
67 #if USE_DELAY_POOLS
68 #include "DelayPools.h"
69 #endif
70
71 CBDATA_CLASS_INIT(HttpStateData);
72
73 static const char *const crlf = "\r\n";
74
75 static void httpMaybeRemovePublic(StoreEntry *, Http::StatusCode);
76 static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request,
77 HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &);
78
79 HttpStateData::HttpStateData(FwdState *theFwdState) :
80 AsyncJob("HttpStateData"),
81 Client(theFwdState)
82 {
83 debugs(11,5, "HttpStateData " << this << " created");
84 serverConnection = fwd->serverConnection();
85
86 if (fwd->serverConnection() != nullptr)
87 _peer = cbdataReference(fwd->serverConnection()->getPeer()); /* might be NULL */
88
89 flags.peering = _peer;
90 flags.tunneling = (_peer && request->flags.sslBumped);
91 flags.toOrigin = (!_peer || _peer->options.originserver || request->flags.sslBumped);
92
93 if (_peer) {
94 /*
95 * This NEIGHBOR_PROXY_ONLY check probably shouldn't be here.
96 * We might end up getting the object from somewhere else if,
97 * for example, the request to this neighbor fails.
98 */
99 if (!flags.tunneling && _peer->options.proxy_only)
100 entry->releaseRequest(true);
101
102 #if USE_DELAY_POOLS
103 entry->setNoDelay(_peer->options.no_delay);
104 #endif
105 }
106
107 /*
108 * register the handler to free HTTP state data when the FD closes
109 */
110 typedef CommCbMemFunT<HttpStateData, CommCloseCbParams> Dialer;
111 closeHandler = JobCallback(9, 5, Dialer, this, HttpStateData::httpStateConnClosed);
112 comm_add_close_handler(serverConnection->fd, closeHandler);
113 }
114
115 HttpStateData::~HttpStateData()
116 {
117 /*
118 * don't forget that ~Client() gets called automatically
119 */
120
121 if (httpChunkDecoder)
122 delete httpChunkDecoder;
123
124 cbdataReferenceDone(_peer);
125
126 delete upgradeHeaderOut;
127
128 debugs(11,5, "HttpStateData " << this << " destroyed; " << serverConnection);
129 }
130
131 const Comm::ConnectionPointer &
132 HttpStateData::dataConnection() const
133 {
134 return serverConnection;
135 }
136
137 void
138 HttpStateData::httpStateConnClosed(const CommCloseCbParams &params)
139 {
140 debugs(11, 5, "httpStateFree: FD " << params.fd << ", httpState=" << params.data);
141 doneWithFwd = "httpStateConnClosed()"; // assume FwdState is monitoring too
142 mustStop("HttpStateData::httpStateConnClosed");
143 }
144
145 void
146 HttpStateData::httpTimeout(const CommTimeoutCbParams &)
147 {
148 debugs(11, 4, serverConnection << ": '" << entry->url() << "'");
149
150 if (entry->store_status == STORE_PENDING) {
151 fwd->fail(new ErrorState(ERR_READ_TIMEOUT, Http::scGatewayTimeout, fwd->request, fwd->al));
152 }
153
154 closeServer();
155 mustStop("HttpStateData::httpTimeout");
156 }
157
158 static StoreEntry *
159 findPreviouslyCachedEntry(StoreEntry *newEntry) {
160 assert(newEntry->mem_obj);
161 return newEntry->mem_obj->request ?
162 storeGetPublicByRequest(newEntry->mem_obj->request.getRaw()) :
163 storeGetPublic(newEntry->mem_obj->storeId(), newEntry->mem_obj->method);
164 }
165
166 /// Remove an existing public store entry if the incoming response (to be
167 /// stored in a currently private entry) is going to invalidate it.
168 static void
169 httpMaybeRemovePublic(StoreEntry * e, Http::StatusCode status)
170 {
171 int remove = 0;
172 int forbidden = 0;
173
174 // If the incoming response already goes into a public entry, then there is
175 // nothing to remove. This protects ready-for-collapsing entries as well.
176 if (!EBIT_TEST(e->flags, KEY_PRIVATE))
177 return;
178
179 // If the new/incoming response cannot be stored, then it does not
180 // compete with the old stored response for the public key, and the
181 // old stored response should be left as is.
182 if (e->mem_obj->request && !e->mem_obj->request->flags.cachable)
183 return;
184
185 switch (status) {
186
187 case Http::scOkay:
188
189 case Http::scNonAuthoritativeInformation:
190
191 case Http::scMultipleChoices:
192
193 case Http::scMovedPermanently:
194
195 case Http::scFound:
196
197 case Http::scSeeOther:
198
199 case Http::scGone:
200
201 case Http::scNotFound:
202 remove = 1;
203
204 break;
205
206 case Http::scForbidden:
207
208 case Http::scMethodNotAllowed:
209 forbidden = 1;
210
211 break;
212
213 #if WORK_IN_PROGRESS
214
215 case Http::scUnauthorized:
216 forbidden = 1;
217
218 break;
219
220 #endif
221
222 default:
223 break;
224 }
225
226 if (!remove && !forbidden)
227 return;
228
229 StoreEntry *pe = findPreviouslyCachedEntry(e);
230
231 if (pe != nullptr) {
232 assert(e != pe);
233 #if USE_HTCP
234 neighborsHtcpClear(e, e->mem_obj->request.getRaw(), e->mem_obj->method, HTCP_CLR_INVALIDATION);
235 #endif
236 pe->release(true);
237 }
238
239 /** \par
240 * Also remove any cached HEAD response in case the object has
241 * changed.
242 */
243 if (e->mem_obj->request)
244 pe = storeGetPublicByRequestMethod(e->mem_obj->request.getRaw(), Http::METHOD_HEAD);
245 else
246 pe = storeGetPublic(e->mem_obj->storeId(), Http::METHOD_HEAD);
247
248 if (pe != nullptr) {
249 assert(e != pe);
250 #if USE_HTCP
251 neighborsHtcpClear(e, e->mem_obj->request.getRaw(), HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_INVALIDATION);
252 #endif
253 pe->release(true);
254 }
255 }
256
257 void
258 HttpStateData::processSurrogateControl(HttpReply *reply)
259 {
260 if (request->flags.accelerated && reply->surrogate_control) {
261 HttpHdrScTarget *sctusable = reply->surrogate_control->getMergedTarget(Config.Accel.surrogate_id);
262
263 if (sctusable) {
264 if (sctusable->hasNoStore() ||
265 (Config.onoff.surrogate_is_remote
266 && sctusable->noStoreRemote())) {
267 surrogateNoStore = true;
268 // Be conservative for now and make it non-shareable because
269 // there is no enough information here to make the decision.
270 entry->makePrivate(false);
271 }
272
273 /* The HttpHeader logic cannot tell if the header it's parsing is a reply to an
274 * accelerated request or not...
275 * Still, this is an abstraction breach. - RC
276 */
277 if (sctusable->hasMaxAge()) {
278 if (sctusable->maxAge() < sctusable->maxStale())
279 reply->expires = reply->date + sctusable->maxAge();
280 else
281 reply->expires = reply->date + sctusable->maxStale();
282
283 /* And update the timestamps */
284 entry->timestampsSet();
285 }
286
287 /* We ignore cache-control directives as per the Surrogate specification */
288 ignoreCacheControl = true;
289
290 delete sctusable;
291 }
292 }
293 }
294
295 HttpStateData::ReuseDecision::Answers
296 HttpStateData::reusableReply(HttpStateData::ReuseDecision &decision)
297 {
298 HttpReply const *rep = finalReply();
299 HttpHeader const *hdr = &rep->header;
300 const char *v;
301 #if USE_HTTP_VIOLATIONS
302
303 const RefreshPattern *R = nullptr;
304
305 /* This strange looking define first looks up the refresh pattern
306 * and then checks if the specified flag is set. The main purpose
307 * of this is to simplify the refresh pattern lookup and USE_HTTP_VIOLATIONS
308 * condition
309 */
310 #define REFRESH_OVERRIDE(flag) \
311 ((R = (R ? R : refreshLimits(entry->mem_obj->storeId()))) , \
312 (R && R->flags.flag))
313 #else
314 #define REFRESH_OVERRIDE(flag) 0
315 #endif
316
317 if (EBIT_TEST(entry->flags, RELEASE_REQUEST))
318 return decision.make(ReuseDecision::doNotCacheButShare, "the entry has been released");
319
320 // RFC 9111 section 4:
321 // "When more than one suitable response is stored,
322 // a cache MUST use the most recent one
323 // (as determined by the Date header field)."
324 // TODO: whether such responses could be shareable?
325 if (sawDateGoBack)
326 return decision.make(ReuseDecision::reuseNot, "the response has an older date header");
327
328 // Check for Surrogate/1.0 protocol conditions
329 // NP: reverse-proxy traffic our parent server has instructed us never to cache
330 if (surrogateNoStore)
331 return decision.make(ReuseDecision::reuseNot, "Surrogate-Control:no-store");
332
333 // RFC 2616: HTTP/1.1 Cache-Control conditions
334 if (!ignoreCacheControl) {
335 // XXX: check to see if the request headers alone were enough to prevent caching earlier
336 // (ie no-store request header) no need to check those all again here if so.
337 // for now we are not reliably doing that so we waste CPU re-checking request CC
338
339 // RFC 2616 section 14.9.2 - MUST NOT cache any response with request CC:no-store
340 if (request && request->cache_control && request->cache_control->hasNoStore() &&
341 !REFRESH_OVERRIDE(ignore_no_store))
342 return decision.make(ReuseDecision::reuseNot,
343 "client request Cache-Control:no-store");
344
345 // NP: request CC:no-cache only means cache READ is forbidden. STORE is permitted.
346 if (rep->cache_control && rep->cache_control->hasNoCacheWithParameters()) {
347 /* TODO: we are allowed to cache when no-cache= has parameters.
348 * Provided we strip away any of the listed headers unless they are revalidated
349 * successfully (ie, must revalidate AND these headers are prohibited on stale replies).
350 * That is a bit tricky for squid right now so we avoid caching entirely.
351 */
352 return decision.make(ReuseDecision::reuseNot,
353 "server reply Cache-Control:no-cache has parameters");
354 }
355
356 // NP: request CC:private is undefined. We ignore.
357 // NP: other request CC flags are limiters on HIT/MISS. We don't care about here.
358
359 // RFC 2616 section 14.9.2 - MUST NOT cache any response with CC:no-store
360 if (rep->cache_control && rep->cache_control->hasNoStore() &&
361 !REFRESH_OVERRIDE(ignore_no_store))
362 return decision.make(ReuseDecision::reuseNot,
363 "server reply Cache-Control:no-store");
364
365 // RFC 2616 section 14.9.1 - MUST NOT cache any response with CC:private in a shared cache like Squid.
366 // CC:private overrides CC:public when both are present in a response.
367 // TODO: add a shared/private cache configuration possibility.
368 if (rep->cache_control &&
369 rep->cache_control->hasPrivate() &&
370 !REFRESH_OVERRIDE(ignore_private)) {
371 /* TODO: we are allowed to cache when private= has parameters.
372 * Provided we strip away any of the listed headers unless they are revalidated
373 * successfully (ie, must revalidate AND these headers are prohibited on stale replies).
374 * That is a bit tricky for squid right now so we avoid caching entirely.
375 */
376 return decision.make(ReuseDecision::reuseNot,
377 "server reply Cache-Control:private");
378 }
379 }
380
381 // RFC 2068, sec 14.9.4 - MUST NOT cache any response with Authentication UNLESS certain CC controls are present
382 // allow HTTP violations to IGNORE those controls (ie re-block caching Auth)
383 if (request && (request->flags.auth || request->flags.authSent)) {
384 if (!rep->cache_control)
385 return decision.make(ReuseDecision::reuseNot,
386 "authenticated and server reply missing Cache-Control");
387
388 if (ignoreCacheControl)
389 return decision.make(ReuseDecision::reuseNot,
390 "authenticated and ignoring Cache-Control");
391
392 bool mayStore = false;
393 // HTTPbis pt6 section 3.2: a response CC:public is present
394 if (rep->cache_control->hasPublic()) {
395 debugs(22, 3, "Authenticated but server reply Cache-Control:public");
396 mayStore = true;
397
398 // HTTPbis pt6 section 3.2: a response CC:must-revalidate is present
399 } else if (rep->cache_control->hasMustRevalidate()) {
400 debugs(22, 3, "Authenticated but server reply Cache-Control:must-revalidate");
401 mayStore = true;
402
403 #if USE_HTTP_VIOLATIONS
404 // NP: given the must-revalidate exception we should also be able to exempt no-cache.
405 // HTTPbis WG verdict on this is that it is omitted from the spec due to being 'unexpected' by
406 // some. The caching+revalidate is not exactly unsafe though with Squids interpretation of no-cache
407 // (without parameters) as equivalent to must-revalidate in the reply.
408 } else if (rep->cache_control->hasNoCacheWithoutParameters()) {
409 debugs(22, 3, "Authenticated but server reply Cache-Control:no-cache (equivalent to must-revalidate)");
410 mayStore = true;
411 #endif
412
413 // HTTPbis pt6 section 3.2: a response CC:s-maxage is present
414 } else if (rep->cache_control->hasSMaxAge()) {
415 debugs(22, 3, "Authenticated but server reply Cache-Control:s-maxage");
416 mayStore = true;
417 }
418
419 if (!mayStore)
420 return decision.make(ReuseDecision::reuseNot, "authenticated transaction");
421
422 // NP: response CC:no-cache is equivalent to CC:must-revalidate,max-age=0. We MAY cache, and do so.
423 // NP: other request CC flags are limiters on HIT/MISS/REFRESH. We don't care about here.
424 }
425
426 /* HACK: The "multipart/x-mixed-replace" content type is used for
427 * continuous push replies. These are generally dynamic and
428 * probably should not be cachable
429 */
430 if ((v = hdr->getStr(Http::HdrType::CONTENT_TYPE)))
431 if (!strncasecmp(v, "multipart/x-mixed-replace", 25))
432 return decision.make(ReuseDecision::reuseNot, "Content-Type:multipart/x-mixed-replace");
433
434 // TODO: if possible, provide more specific message for each status code
435 static const char *shareableError = "shareable error status code";
436 static const char *nonShareableError = "non-shareable error status code";
437 ReuseDecision::Answers statusAnswer = ReuseDecision::reuseNot;
438 const char *statusReason = nonShareableError;
439
440 switch (rep->sline.status()) {
441
442 /* There are several situations when a non-cacheable response may be
443 * still shareable (e.g., among collapsed clients). We assume that these
444 * are 3xx and 5xx responses, indicating server problems and some of
445 * 4xx responses, common for all clients with a given cache key (e.g.,
446 * 404 Not Found or 414 URI Too Long). On the other hand, we should not
447 * share non-cacheable client-specific errors, such as 400 Bad Request
448 * or 406 Not Acceptable.
449 */
450
451 /* Responses that are cacheable */
452
453 case Http::scOkay:
454
455 case Http::scNonAuthoritativeInformation:
456
457 case Http::scMultipleChoices:
458
459 case Http::scMovedPermanently:
460 case Http::scPermanentRedirect:
461
462 case Http::scGone:
463 /*
464 * Don't cache objects that need to be refreshed on next request,
465 * unless we know how to refresh it.
466 */
467
468 if (refreshIsCachable(entry) || REFRESH_OVERRIDE(store_stale))
469 decision.make(ReuseDecision::cachePositively, "refresh check returned cacheable");
470 else
471 decision.make(ReuseDecision::doNotCacheButShare, "refresh check returned non-cacheable");
472 break;
473
474 /* Responses that only are cacheable if the server says so */
475
476 case Http::scFound:
477 case Http::scTemporaryRedirect:
478 if (rep->date <= 0)
479 decision.make(ReuseDecision::doNotCacheButShare, "Date is missing/invalid");
480 else if (rep->expires > rep->date)
481 decision.make(ReuseDecision::cachePositively, "Expires > Date");
482 else
483 decision.make(ReuseDecision::doNotCacheButShare, "Expires <= Date");
484 break;
485
486 /* These responses can be negatively cached. Most can also be shared. */
487 case Http::scNoContent:
488 case Http::scUseProxy:
489 case Http::scForbidden:
490 case Http::scNotFound:
491 case Http::scMethodNotAllowed:
492 case Http::scUriTooLong:
493 case Http::scInternalServerError:
494 case Http::scNotImplemented:
495 case Http::scBadGateway:
496 case Http::scServiceUnavailable:
497 case Http::scGatewayTimeout:
498 case Http::scMisdirectedRequest:
499 statusAnswer = ReuseDecision::doNotCacheButShare;
500 statusReason = shareableError;
501 [[fallthrough]]; // to the actual decision making below
502
503 case Http::scBadRequest: // no sharing; perhaps the server did not like something specific to this request
504 #if USE_HTTP_VIOLATIONS
505 if (Config.negativeTtl > 0)
506 decision.make(ReuseDecision::cacheNegatively, "Config.negativeTtl > 0");
507 else
508 #endif
509 decision.make(statusAnswer, statusReason);
510 break;
511
512 /* these responses can never be cached, some
513 of them can be shared though */
514 case Http::scSeeOther:
515 case Http::scNotModified:
516 case Http::scUnauthorized:
517 case Http::scProxyAuthenticationRequired:
518 case Http::scPaymentRequired:
519 case Http::scInsufficientStorage:
520 // TODO: use more specific reason for non-error status codes
521 decision.make(ReuseDecision::doNotCacheButShare, shareableError);
522 break;
523
524 case Http::scPartialContent: /* Not yet supported. TODO: make shareable for suitable ranges */
525 case Http::scNotAcceptable:
526 case Http::scRequestTimeout: // TODO: is this shareable?
527 case Http::scConflict: // TODO: is this shareable?
528 case Http::scLengthRequired:
529 case Http::scPreconditionFailed:
530 case Http::scContentTooLarge:
531 case Http::scUnsupportedMediaType:
532 case Http::scUnprocessableEntity:
533 case Http::scLocked: // TODO: is this shareable?
534 case Http::scFailedDependency:
535 case Http::scRequestedRangeNotSatisfied:
536 case Http::scExpectationFailed:
537 case Http::scInvalidHeader: /* Squid header parsing error */
538 case Http::scHeaderTooLarge:
539 decision.make(ReuseDecision::reuseNot, nonShareableError);
540 break;
541
542 default:
543 /* RFC 2616 section 6.1.1: an unrecognized response MUST NOT be cached. */
544 decision.make(ReuseDecision::reuseNot, "unknown status code");
545 break;
546 }
547
548 return decision.answer;
549 }
550
551 /// assemble a variant key (vary-mark) from the given Vary header and HTTP request
552 static void
553 assembleVaryKey(String &vary, SBuf &vstr, const HttpRequest &request)
554 {
555 static const SBuf asterisk("*");
556 const char *pos = nullptr;
557 const char *item = nullptr;
558 int ilen = 0;
559
560 while (strListGetItem(&vary, ',', &item, &ilen, &pos)) {
561 SBuf name(item, ilen);
562 if (name == asterisk) {
563 vstr = asterisk;
564 break;
565 }
566 name.toLower();
567 if (!vstr.isEmpty())
568 vstr.append(", ", 2);
569 vstr.append(name);
570 String hdr(request.header.getByName(name));
571 const char *value = hdr.termedBuf();
572 if (value) {
573 value = rfc1738_escape_part(value);
574 vstr.append("=\"", 2);
575 vstr.append(value);
576 vstr.append("\"", 1);
577 }
578
579 hdr.clean();
580 }
581 }
582
583 /*
584 * For Vary, store the relevant request headers as
585 * virtual headers in the reply
586 * Returns an empty SBuf if the variance cannot be stored
587 */
588 SBuf
589 httpMakeVaryMark(HttpRequest * request, HttpReply const * reply)
590 {
591 SBuf vstr;
592 String vary;
593
594 vary = reply->header.getList(Http::HdrType::VARY);
595 assembleVaryKey(vary, vstr, *request);
596
597 #if X_ACCELERATOR_VARY
598 vary.clean();
599 vary = reply->header.getList(Http::HdrType::HDR_X_ACCELERATOR_VARY);
600 assembleVaryKey(vary, vstr, *request);
601 #endif
602
603 debugs(11, 3, vstr);
604 return vstr;
605 }
606
607 void
608 HttpStateData::keepaliveAccounting(HttpReply *reply)
609 {
610 if (flags.keepalive)
611 if (flags.peering && !flags.tunneling)
612 ++ _peer->stats.n_keepalives_sent;
613
614 if (reply->keep_alive) {
615 if (flags.peering && !flags.tunneling)
616 ++ _peer->stats.n_keepalives_recv;
617
618 if (Config.onoff.detect_broken_server_pconns
619 && reply->bodySize(request->method) == -1 && !flags.chunked) {
620 debugs(11, DBG_IMPORTANT, "keepaliveAccounting: Impossible keep-alive header from '" << entry->url() << "'" );
621 // debugs(11, 2, "GOT HTTP REPLY HDR:\n---------\n" << readBuf->content() << "\n----------" );
622 flags.keepalive_broken = true;
623 }
624 }
625 }
626
627 void
628 HttpStateData::checkDateSkew(HttpReply *reply)
629 {
630 if (reply->date > -1 && flags.toOrigin) {
631 int skew = abs((int)(reply->date - squid_curtime));
632
633 if (skew > 86400)
634 debugs(11, 3, "" << request->url.host() << "'s clock is skewed by " << skew << " seconds!");
635 }
636 }
637
638 /**
639 * This creates the error page itself.. its likely
640 * that the forward ported reply header max size patch
641 * generates non http conformant error pages - in which
642 * case the errors where should be 'BAD_GATEWAY' etc
643 */
644 void
645 HttpStateData::processReplyHeader()
646 {
647 /** Creates a blank header. If this routine is made incremental, this will not do */
648
649 debugs(11, 3, "processReplyHeader: key '" << entry->getMD5Text() << "'");
650
651 assert(!flags.headers_parsed);
652
653 if (!inBuf.length())
654 return;
655
656 /* Attempt to parse the first line; this will define where the protocol, status, reason-phrase and header begin */
657 {
658 if (hp == nullptr)
659 hp = new Http1::ResponseParser;
660
661 bool parsedOk = hp->parse(inBuf);
662 // remember the actual received status-code before returning on errors,
663 // overwriting any previously stored value from earlier forwarding attempts
664 request->hier.peer_reply_status = hp->messageStatus(); // may still be scNone
665
666 // sync the buffers after parsing.
667 inBuf = hp->remaining();
668
669 if (hp->needsMoreData()) {
670 if (eof) { // no more data coming
671 assert(!parsedOk);
672 // fall through to handle this premature EOF as an error
673 } else {
674 debugs(33, 5, "Incomplete response, waiting for end of response headers");
675 return;
676 }
677 }
678
679 if (!parsedOk) {
680 // unrecoverable parsing error
681 // TODO: Use Raw! XXX: inBuf no longer has the [beginning of the] malformed header.
682 debugs(11, 3, "Non-HTTP-compliant header:\n---------\n" << inBuf << "\n----------");
683 flags.headers_parsed = true;
684 HttpReply *newrep = new HttpReply;
685 // hp->needsMoreData() means hp->parseStatusCode is unusable, but, here,
686 // it also means that the reply header got truncated by a premature EOF
687 assert(!hp->needsMoreData() || eof);
688 const auto scode = hp->needsMoreData() ? Http::scInvalidHeader : hp->parseStatusCode;
689 newrep->sline.set(Http::ProtocolVersion(), scode);
690 setVirginReply(newrep);
691 return;
692 }
693 }
694
695 /* We know the whole response is in parser now */
696 debugs(11, 2, "HTTP Server " << serverConnection);
697 debugs(11, 2, "HTTP Server RESPONSE:\n---------\n" <<
698 hp->messageProtocol() << " " << hp->messageStatus() << " " << hp->reasonPhrase() << "\n" <<
699 hp->mimeHeader() <<
700 "----------");
701
702 // reset payload tracking to begin after message headers
703 payloadSeen = inBuf.length();
704
705 const auto newrep = HttpReply::Pointer::Make();
706 // XXX: RFC 7230 indicates we MAY ignore the reason phrase,
707 // and use an empty string on unknown status.
708 // We do that now to avoid performance regression from using SBuf::c_str()
709 newrep->sline.set(hp->messageProtocol(), hp->messageStatus() /* , hp->reasonPhrase() */);
710
711 // parse headers
712 if (!newrep->parseHeader(*hp)) {
713 newrep->sline.set(hp->messageProtocol(), Http::scInvalidHeader);
714 debugs(11, 2, "error parsing response headers mime block");
715 }
716
717 // done with Parser, now process using the HttpReply
718 hp = nullptr;
719
720 newrep->sources |= request->url.getScheme() == AnyP::PROTO_HTTPS ? Http::Message::srcHttps : Http::Message::srcHttp;
721
722 if (newrep->sline.version.protocol == AnyP::PROTO_HTTP && Http::Is1xx(newrep->sline.status())) {
723 handle1xx(newrep.getRaw());
724 return;
725 }
726
727 flags.chunked = false;
728 if (newrep->sline.version.protocol == AnyP::PROTO_HTTP && newrep->header.chunked()) {
729 flags.chunked = true;
730 httpChunkDecoder = new Http1::TeChunkedParser;
731 }
732
733 if (!peerSupportsConnectionPinning())
734 request->flags.connectionAuthDisabled = true;
735
736 const auto vrep = setVirginReply(newrep.getRaw());
737 flags.headers_parsed = true;
738
739 keepaliveAccounting(vrep);
740
741 checkDateSkew(vrep);
742
743 processSurrogateControl (vrep);
744 }
745
746 /// ignore or start forwarding the 1xx response (a.k.a., control message)
747 void
748 HttpStateData::handle1xx(const HttpReply::Pointer &reply)
749 {
750 if (fwd->al)
751 fwd->al->reply = reply;
752
753 // one 1xx at a time: we must not be called while waiting for previous 1xx
754 Must(!flags.handling1xx);
755 flags.handling1xx = true;
756
757 const auto statusCode = reply->sline.status();
758
759 // drop1xx() needs to handle HTTP 101 (Switching Protocols) responses
760 // specially because they indicate that the server has stopped speaking HTTP
761 Must(!flags.serverSwitchedProtocols);
762 flags.serverSwitchedProtocols = (statusCode == Http::scSwitchingProtocols);
763
764 if (statusCode == Http::scContinue && request->forcedBodyContinuation)
765 return drop1xx("we have sent it already");
766
767 if (!request->canHandle1xx())
768 return drop1xx("the client does not support it");
769
770 #if USE_HTTP_VIOLATIONS
771 // check whether the 1xx response forwarding is allowed by squid.conf
772 if (Config.accessList.reply) {
773 ACLFilledChecklist ch(Config.accessList.reply, originalRequest().getRaw());
774 ch.updateAle(fwd->al);
775 ch.updateReply(reply);
776 ch.syncAle(originalRequest().getRaw(), nullptr);
777 if (!ch.fastCheck().allowed()) // TODO: support slow lookups?
778 return drop1xx("http_reply_access blocked it");
779 }
780 #endif // USE_HTTP_VIOLATIONS
781
782 if (flags.serverSwitchedProtocols) {
783 if (const auto reason = blockSwitchingProtocols(*reply))
784 return drop1xx(reason);
785 }
786
787 debugs(11, 2, "forwarding 1xx to client");
788
789 // the Sink will use this to call us back after writing 1xx to the client
790 typedef NullaryMemFunT<HttpStateData> CbDialer;
791 const AsyncCall::Pointer cb = JobCallback(11, 3, CbDialer, this,
792 HttpStateData::proceedAfter1xx);
793 CallJobHere1(11, 4, request->clientConnectionManager, ConnStateData,
794 ConnStateData::sendControlMsg, HttpControlMsg(reply, cb));
795 // If the call is not fired, then the Sink is gone, and HttpStateData
796 // will terminate due to an aborted store entry or another similar error.
797 // If we get stuck, it is not handle1xx fault if we could get stuck
798 // for similar reasons without a 1xx response.
799 }
800
801 /// if possible, safely ignores the received 1xx control message
802 /// otherwise, terminates the server connection
803 void
804 HttpStateData::drop1xx(const char *reason)
805 {
806 if (flags.serverSwitchedProtocols) {
807 debugs(11, 2, "bad 101 because " << reason);
808 const auto err = new ErrorState(ERR_INVALID_RESP, Http::scBadGateway, request.getRaw(), fwd->al);
809 fwd->fail(err);
810 closeServer();
811 mustStop("prohibited HTTP/101 response");
812 return;
813 }
814
815 debugs(11, 2, "ignoring 1xx because " << reason);
816 proceedAfter1xx();
817 }
818
819 /// \retval nil if the HTTP/101 (Switching Protocols) reply should be forwarded
820 /// \retval reason why an attempt to switch protocols should be stopped
821 const char *
822 HttpStateData::blockSwitchingProtocols(const HttpReply &reply) const
823 {
824 if (!upgradeHeaderOut)
825 return "Squid offered no Upgrade at all, but server switched to a tunnel";
826
827 // See RFC 7230 section 6.7 for the corresponding MUSTs
828
829 if (!reply.header.has(Http::HdrType::UPGRADE))
830 return "server did not send an Upgrade header field";
831
832 if (!reply.header.hasListMember(Http::HdrType::CONNECTION, "upgrade", ','))
833 return "server did not send 'Connection: upgrade'";
834
835 const auto acceptedProtos = reply.header.getList(Http::HdrType::UPGRADE);
836 const char *pos = nullptr;
837 const char *accepted = nullptr;
838 int acceptedLen = 0;
839 while (strListGetItem(&acceptedProtos, ',', &accepted, &acceptedLen, &pos)) {
840 debugs(11, 5, "server accepted at least" << Raw(nullptr, accepted, acceptedLen));
841 return nullptr; // OK: let the client validate server's selection
842 }
843
844 return "server sent an essentially empty Upgrade header field";
845 }
846
847 /// restores state and resumes processing after 1xx is ignored or forwarded
848 void
849 HttpStateData::proceedAfter1xx()
850 {
851 Must(flags.handling1xx);
852
853 if (flags.serverSwitchedProtocols) {
854 // pass server connection ownership to request->clientConnectionManager
855 ConnStateData::ServerConnectionContext scc(serverConnection, inBuf);
856 typedef UnaryMemFunT<ConnStateData, ConnStateData::ServerConnectionContext> MyDialer;
857 AsyncCall::Pointer call = asyncCall(11, 3, "ConnStateData::noteTakeServerConnectionControl",
858 MyDialer(request->clientConnectionManager,
859 &ConnStateData::noteTakeServerConnectionControl, scc));
860 ScheduleCallHere(call);
861 fwd->unregister(serverConnection);
862 comm_remove_close_handler(serverConnection->fd, closeHandler);
863 closeHandler = nullptr;
864 serverConnection = nullptr;
865 doneWithFwd = "switched protocols";
866 mustStop(doneWithFwd);
867 return;
868 }
869
870 debugs(11, 2, "continuing with " << payloadSeen << " bytes in buffer after 1xx");
871 CallJobHere(11, 3, this, HttpStateData, HttpStateData::processReply);
872 }
873
874 /**
875 * returns true if the peer can support connection pinning
876 */
877 bool
878 HttpStateData::peerSupportsConnectionPinning() const
879 {
880 if (!_peer)
881 return true;
882
883 // we are talking "through" rather than "to" our _peer
884 if (flags.tunneling)
885 return true;
886
887 /*If this peer does not support connection pinning (authenticated
888 connections) return false
889 */
890 if (!_peer->connection_auth)
891 return false;
892
893 const auto &rep = entry->mem().freshestReply();
894
895 /*The peer supports connection pinning and the http reply status
896 is not unauthorized, so the related connection can be pinned
897 */
898 if (rep.sline.status() != Http::scUnauthorized)
899 return true;
900
901 /*The server respond with Http::scUnauthorized and the peer configured
902 with "connection-auth=on" we know that the peer supports pinned
903 connections
904 */
905 if (_peer->connection_auth == 1)
906 return true;
907
908 /*At this point peer has configured with "connection-auth=auto"
909 parameter so we need some extra checks to decide if we are going
910 to allow pinned connections or not
911 */
912
913 /*if the peer configured with originserver just allow connection
914 pinning (squid 2.6 behaviour)
915 */
916 if (_peer->options.originserver)
917 return true;
918
919 /*if the connections it is already pinned it is OK*/
920 if (request->flags.pinned)
921 return true;
922
923 /*Allow pinned connections only if the Proxy-support header exists in
924 reply and has in its list the "Session-Based-Authentication"
925 which means that the peer supports connection pinning.
926 */
927 if (rep.header.hasListMember(Http::HdrType::PROXY_SUPPORT, "Session-Based-Authentication", ','))
928 return true;
929
930 return false;
931 }
932
933 // Called when we parsed (and possibly adapted) the headers but
934 // had not starting storing (a.k.a., sending) the body yet.
935 void
936 HttpStateData::haveParsedReplyHeaders()
937 {
938 Client::haveParsedReplyHeaders();
939
940 HttpReply *rep = finalReply();
941 const Http::StatusCode statusCode = rep->sline.status();
942
943 entry->timestampsSet();
944
945 /* Check if object is cacheable or not based on reply code */
946 debugs(11, 3, "HTTP CODE: " << statusCode);
947
948 if (StoreEntry *oldEntry = findPreviouslyCachedEntry(entry)) {
949 oldEntry->lock("HttpStateData::haveParsedReplyHeaders");
950 sawDateGoBack = rep->olderThan(oldEntry->hasFreshestReply());
951 oldEntry->unlock("HttpStateData::haveParsedReplyHeaders");
952 }
953
954 if (neighbors_do_private_keys && !sawDateGoBack)
955 httpMaybeRemovePublic(entry, rep->sline.status());
956
957 bool varyFailure = false;
958 if (rep->header.has(Http::HdrType::VARY)
959 #if X_ACCELERATOR_VARY
960 || rep->header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY)
961 #endif
962 ) {
963 const SBuf vary(httpMakeVaryMark(request.getRaw(), rep));
964
965 if (vary.isEmpty()) {
966 // TODO: check whether such responses are shareable.
967 // Do not share for now.
968 entry->makePrivate(false);
969 if (Http::IsReforwardableStatus(rep->sline.status()))
970 EBIT_SET(entry->flags, ENTRY_FWD_HDR_WAIT);
971 varyFailure = true;
972 } else {
973 entry->mem_obj->vary_headers = vary;
974
975 // RFC 7231 section 7.1.4
976 // Vary:* can be cached, but has mandatory revalidation
977 static const SBuf asterisk("*");
978 if (vary == asterisk)
979 EBIT_SET(entry->flags, ENTRY_REVALIDATE_ALWAYS);
980 }
981 }
982
983 if (!varyFailure) {
984 /*
985 * If its not a reply that we will re-forward, then
986 * allow the client to get it.
987 */
988 if (Http::IsReforwardableStatus(rep->sline.status()))
989 EBIT_SET(entry->flags, ENTRY_FWD_HDR_WAIT);
990
991 ReuseDecision decision(entry, statusCode);
992
993 switch (reusableReply(decision)) {
994
995 case ReuseDecision::reuseNot:
996 entry->makePrivate(false);
997 break;
998
999 case ReuseDecision::cachePositively:
1000 if (!entry->makePublic()) {
1001 decision.make(ReuseDecision::doNotCacheButShare, "public key creation error");
1002 entry->makePrivate(true);
1003 }
1004 break;
1005
1006 case ReuseDecision::cacheNegatively:
1007 if (!entry->cacheNegatively()) {
1008 decision.make(ReuseDecision::doNotCacheButShare, "public key creation error");
1009 entry->makePrivate(true);
1010 }
1011 break;
1012
1013 case ReuseDecision::doNotCacheButShare:
1014 entry->makePrivate(true);
1015 break;
1016
1017 default:
1018 assert(0);
1019 break;
1020 }
1021 debugs(11, 3, "decided: " << decision);
1022 }
1023
1024 if (!ignoreCacheControl) {
1025 if (rep->cache_control) {
1026 // We are required to revalidate on many conditions.
1027 // For security reasons we do so even if storage was caused by refresh_pattern ignore-* option
1028
1029 // CC:must-revalidate or CC:proxy-revalidate
1030 const bool ccMustRevalidate = (rep->cache_control->hasProxyRevalidate() || rep->cache_control->hasMustRevalidate());
1031
1032 // CC:no-cache (only if there are no parameters)
1033 const bool ccNoCacheNoParams = rep->cache_control->hasNoCacheWithoutParameters();
1034
1035 // CC:s-maxage=N
1036 const bool ccSMaxAge = rep->cache_control->hasSMaxAge();
1037
1038 // CC:private (yes, these can sometimes be stored)
1039 const bool ccPrivate = rep->cache_control->hasPrivate();
1040
1041 if (ccNoCacheNoParams || ccPrivate)
1042 EBIT_SET(entry->flags, ENTRY_REVALIDATE_ALWAYS);
1043 else if (ccMustRevalidate || ccSMaxAge)
1044 EBIT_SET(entry->flags, ENTRY_REVALIDATE_STALE);
1045 }
1046 #if USE_HTTP_VIOLATIONS // response header Pragma::no-cache is undefined in HTTP
1047 else {
1048 // Expensive calculation. So only do it IF the CC: header is not present.
1049
1050 /* HACK: Pragma: no-cache in _replies_ is not documented in HTTP,
1051 * but servers like "Active Imaging Webcast/2.0" sure do use it */
1052 if (rep->header.has(Http::HdrType::PRAGMA) &&
1053 rep->header.hasListMember(Http::HdrType::PRAGMA,"no-cache",','))
1054 EBIT_SET(entry->flags, ENTRY_REVALIDATE_ALWAYS);
1055 }
1056 #endif
1057 }
1058 }
1059
1060 HttpStateData::ConnectionStatus
1061 HttpStateData::statusIfComplete() const
1062 {
1063 const HttpReply *rep = virginReply();
1064 /** \par
1065 * If the reply wants to close the connection, it takes precedence */
1066
1067 static SBuf close("close", 5);
1068 if (httpHeaderHasConnDir(&rep->header, close))
1069 return COMPLETE_NONPERSISTENT_MSG;
1070
1071 /** \par
1072 * If we sent a Connection:close request header, then this
1073 * can not be a persistent connection.
1074 */
1075 if (!flags.keepalive)
1076 return COMPLETE_NONPERSISTENT_MSG;
1077
1078 /** \par
1079 * If we banned reuse, then this cannot be a persistent connection.
1080 */
1081 if (flags.forceClose)
1082 return COMPLETE_NONPERSISTENT_MSG;
1083
1084 /** \par
1085 * If we haven't sent the whole request then this can not be a persistent
1086 * connection.
1087 */
1088 if (!flags.request_sent) {
1089 debugs(11, 2, "Request not yet fully sent " << request->method << ' ' << entry->url());
1090 return COMPLETE_NONPERSISTENT_MSG;
1091 }
1092
1093 /** \par
1094 * What does the reply have to say about keep-alive?
1095 */
1096 /* XXX: BUG?
1097 * If the origin server (HTTP/1.0) does not send a keep-alive
1098 * header, but keeps the connection open anyway, what happens?
1099 * We'll return here and wait for an EOF before changing
1100 * store_status to STORE_OK. Combine this with ENTRY_FWD_HDR_WAIT
1101 * and an error status code, and we might have to wait until
1102 * the server times out the socket.
1103 */
1104 if (!rep->keep_alive)
1105 return COMPLETE_NONPERSISTENT_MSG;
1106
1107 return COMPLETE_PERSISTENT_MSG;
1108 }
1109
1110 HttpStateData::ConnectionStatus
1111 HttpStateData::persistentConnStatus() const
1112 {
1113 debugs(11, 3, serverConnection << " eof=" << eof);
1114 if (eof) // already reached EOF
1115 return COMPLETE_NONPERSISTENT_MSG;
1116
1117 /* If server fd is closing (but we have not been notified yet), stop Comm
1118 I/O to avoid assertions. TODO: Change Comm API to handle callers that
1119 want more I/O after async closing (usually initiated by others). */
1120 // XXX: add canReceive or s/canSend/canTalkToServer/
1121 if (!Comm::IsConnOpen(serverConnection))
1122 return COMPLETE_NONPERSISTENT_MSG;
1123
1124 /** \par
1125 * In chunked response we do not know the content length but we are absolutely
1126 * sure about the end of response, so we are calling the statusIfComplete to
1127 * decide if we can be persistent
1128 */
1129 if (lastChunk && flags.chunked)
1130 return statusIfComplete();
1131
1132 const HttpReply *vrep = virginReply();
1133 debugs(11, 5, "persistentConnStatus: content_length=" << vrep->content_length);
1134
1135 const int64_t clen = vrep->bodySize(request->method);
1136
1137 debugs(11, 5, "persistentConnStatus: clen=" << clen);
1138
1139 /* If the body size is unknown we must wait for EOF */
1140 if (clen < 0)
1141 return INCOMPLETE_MSG;
1142
1143 /** \par
1144 * If the body size is known, we must wait until we've gotten all of it. */
1145 if (clen > 0) {
1146 debugs(11,5, "payloadSeen=" << payloadSeen << " content_length=" << vrep->content_length);
1147
1148 if (payloadSeen < vrep->content_length)
1149 return INCOMPLETE_MSG;
1150
1151 if (payloadTruncated > 0) // already read more than needed
1152 return COMPLETE_NONPERSISTENT_MSG; // disable pconns
1153 }
1154
1155 /** \par
1156 * If there is no message body or we got it all, we can be persistent */
1157 return statusIfComplete();
1158 }
1159
1160 void
1161 HttpStateData::noteDelayAwareReadChance()
1162 {
1163 waitingForDelayAwareReadChance = false;
1164 maybeReadVirginBody();
1165 }
1166
1167 void
1168 HttpStateData::readReply(const CommIoCbParams &io)
1169 {
1170 debugs(11, 5, io.conn);
1171 waitingForCommRead = false;
1172
1173 // Bail out early on Comm::ERR_CLOSING - close handlers will tidy up for us
1174 if (io.flag == Comm::ERR_CLOSING) {
1175 debugs(11, 3, "http socket closing");
1176 return;
1177 }
1178
1179 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1180 abortTransaction("store entry aborted while reading reply");
1181 return;
1182 }
1183
1184 Must(Comm::IsConnOpen(serverConnection));
1185 Must(io.conn->fd == serverConnection->fd);
1186
1187 /*
1188 * Don't reset the timeout value here. The value should be
1189 * counting Config.Timeout.request and applies to the request
1190 * as a whole, not individual read() calls.
1191 * Plus, it breaks our lame *HalfClosed() detection
1192 */
1193
1194 const auto moreDataPermission = canBufferMoreReplyBytes();
1195 if (!moreDataPermission) {
1196 abortTransaction("ready to read required data, but the read buffer is full and cannot be drained");
1197 return;
1198 }
1199
1200 const auto readSizeMax = maybeMakeSpaceAvailable(moreDataPermission.value());
1201 // TODO: Move this logic inside maybeMakeSpaceAvailable():
1202 const auto readSizeWanted = readSizeMax ? entry->bytesWanted(Range<size_t>(0, readSizeMax)) : 0;
1203
1204 if (readSizeWanted <= 0) {
1205 // XXX: If we avoid Comm::ReadNow(), we should not Comm::Read() again
1206 // when the wait is over. We should go straight to readReply() instead.
1207
1208 #if USE_ADAPTATION
1209 // XXX: We are duplicating Client::calcBufferSpaceToReserve() logic.
1210 // XXX: Some other delayRead() cases may lack kickReads() guarantees.
1211 // TODO: Refactor maybeMakeSpaceAvailable() to properly treat each
1212 // no-read case instead of calling delayRead() for the remaining cases.
1213
1214 if (responseBodyBuffer) {
1215 debugs(11, 5, "avoid delayRead() to give adaptation a chance to drain overflow buffer: " << responseBodyBuffer->contentSize());
1216 return; // wait for Client::noteMoreBodySpaceAvailable()
1217 }
1218
1219 if (virginBodyDestination && virginBodyDestination->buf().hasContent()) {
1220 debugs(11, 5, "avoid delayRead() to give adaptation a chance to drain body pipe buffer: " << virginBodyDestination->buf().contentSize());
1221 return; // wait for Client::noteMoreBodySpaceAvailable()
1222 }
1223 #endif
1224
1225 delayRead(); /// wait for Client::noteDelayAwareReadChance()
1226 return;
1227 }
1228
1229 CommIoCbParams rd(this); // will be expanded with ReadNow results
1230 rd.conn = io.conn;
1231 rd.size = readSizeWanted;
1232 switch (Comm::ReadNow(rd, inBuf)) {
1233 case Comm::INPROGRESS:
1234 if (inBuf.isEmpty())
1235 debugs(33, 2, io.conn << ": no data to process, " << xstrerr(rd.xerrno));
1236 maybeReadVirginBody();
1237 return;
1238
1239 case Comm::OK:
1240 {
1241 payloadSeen += rd.size;
1242 #if USE_DELAY_POOLS
1243 DelayId delayId = entry->mem_obj->mostBytesAllowed();
1244 delayId.bytesIn(rd.size);
1245 #endif
1246
1247 statCounter.server.all.kbytes_in += rd.size;
1248 statCounter.server.http.kbytes_in += rd.size;
1249 ++ IOStats.Http.reads;
1250
1251 int bin = 0;
1252 for (int clen = rd.size - 1; clen; ++bin)
1253 clen >>= 1;
1254
1255 ++ IOStats.Http.read_hist[bin];
1256
1257 request->hier.notePeerRead();
1258 }
1259
1260 /* Continue to process previously read data */
1261 break;
1262
1263 case Comm::ENDFILE: // close detected by 0-byte read
1264 eof = 1;
1265
1266 /* Continue to process previously read data */
1267 break;
1268
1269 // case Comm::COMM_ERROR:
1270 default: // no other flags should ever occur
1271 debugs(11, 2, io.conn << ": read failure: " << xstrerr(rd.xerrno));
1272 const auto err = new ErrorState(ERR_READ_ERROR, Http::scBadGateway, fwd->request, fwd->al);
1273 err->xerrno = rd.xerrno;
1274 fwd->fail(err);
1275 closeServer();
1276 mustStop("HttpStateData::readReply");
1277 return;
1278 }
1279
1280 /* Process next response from buffer */
1281 processReply();
1282 }
1283
1284 /// processes the already read and buffered response data, possibly after
1285 /// waiting for asynchronous 1xx control message processing
1286 void
1287 HttpStateData::processReply()
1288 {
1289
1290 if (flags.handling1xx) { // we came back after handling a 1xx response
1291 debugs(11, 5, "done with 1xx handling");
1292 flags.handling1xx = false;
1293 Must(!flags.headers_parsed);
1294 }
1295
1296 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1297 abortTransaction("store entry aborted while we were waiting for processReply()");
1298 return;
1299 }
1300
1301 if (!flags.headers_parsed) { // have not parsed headers yet?
1302 processReplyHeader();
1303
1304 if (!continueAfterParsingHeader()) // parsing error or need more data
1305 return; // TODO: send errors to ICAP
1306
1307 adaptOrFinalizeReply(); // may write to, abort, or "close" the entry
1308 }
1309
1310 // kick more reads if needed and/or process the response body, if any
1311 processReplyBody(); // may call serverComplete()
1312 }
1313
1314 /**
1315 \retval true if we can continue with processing the body or doing ICAP.
1316 */
1317 bool
1318 HttpStateData::continueAfterParsingHeader()
1319 {
1320 if (flags.handling1xx) {
1321 debugs(11, 5, "wait for 1xx handling");
1322 Must(!flags.headers_parsed);
1323 return false;
1324 }
1325
1326 if (!flags.headers_parsed && !eof) {
1327 debugs(11, 9, "needs more at " << inBuf.length());
1328 /** \retval false If we have not finished parsing the headers and may get more data.
1329 * Schedules more reads to retrieve the missing data.
1330 */
1331 maybeReadVirginBody(); // schedules all kinds of reads; TODO: rename
1332 return false;
1333 }
1334
1335 /** If we are done with parsing, check for errors */
1336
1337 err_type error = ERR_NONE;
1338
1339 if (flags.headers_parsed) { // parsed headers, possibly with errors
1340 // check for header parsing errors
1341 if (HttpReply *vrep = virginReply()) {
1342 const Http::StatusCode s = vrep->sline.status();
1343 const AnyP::ProtocolVersion &v = vrep->sline.version;
1344 if (s == Http::scInvalidHeader && v != Http::ProtocolVersion(0,9)) {
1345 debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: Bad header encountered from " << entry->url() << " AKA " << request->url);
1346 error = ERR_INVALID_RESP;
1347 } else if (s == Http::scHeaderTooLarge) {
1348 fwd->dontRetry(true);
1349 error = ERR_TOO_BIG;
1350 } else if (vrep->header.conflictingContentLength()) {
1351 fwd->dontRetry(true);
1352 error = ERR_INVALID_RESP;
1353 } else if (vrep->header.unsupportedTe()) {
1354 fwd->dontRetry(true);
1355 error = ERR_INVALID_RESP;
1356 } else {
1357 return true; // done parsing, got reply, and no error
1358 }
1359 } else {
1360 // parsed headers but got no reply
1361 debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: No reply at all for " << entry->url() << " AKA " << request->url);
1362 error = ERR_INVALID_RESP;
1363 }
1364 } else {
1365 assert(eof);
1366 if (inBuf.length()) {
1367 error = ERR_INVALID_RESP;
1368 debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: Headers did not parse at all for " << entry->url() << " AKA " << request->url);
1369 } else {
1370 error = ERR_ZERO_SIZE_OBJECT;
1371 debugs(11, (request->flags.accelerated?DBG_IMPORTANT:2), "WARNING: HTTP: Invalid Response: No object data received for " << entry->url() << " AKA " << request->url);
1372 }
1373 }
1374
1375 assert(error != ERR_NONE);
1376 entry->reset();
1377 fwd->fail(new ErrorState(error, Http::scBadGateway, fwd->request, fwd->al));
1378 closeServer();
1379 mustStop("HttpStateData::continueAfterParsingHeader");
1380 return false; // quit on error
1381 }
1382
1383 /** truncate what we read if we read too much so that writeReplyBody()
1384 writes no more than what we should have read */
1385 void
1386 HttpStateData::truncateVirginBody()
1387 {
1388 assert(flags.headers_parsed);
1389
1390 HttpReply *vrep = virginReply();
1391 int64_t clen = -1;
1392 if (!vrep->expectingBody(request->method, clen) || clen < 0)
1393 return; // no body or a body of unknown size, including chunked
1394
1395 if (payloadSeen - payloadTruncated <= clen)
1396 return; // we did not read too much or already took care of the extras
1397
1398 if (const int64_t extras = payloadSeen - payloadTruncated - clen) {
1399 // server sent more that the advertised content length
1400 debugs(11, 5, "payloadSeen=" << payloadSeen <<
1401 " clen=" << clen << '/' << vrep->content_length <<
1402 " truncated=" << payloadTruncated << '+' << extras);
1403
1404 inBuf.chop(0, inBuf.length() - extras);
1405 payloadTruncated += extras;
1406 }
1407 }
1408
1409 /// called on a premature EOF discovered when reading response body
1410 void
1411 HttpStateData::markPrematureReplyBodyEofFailure()
1412 {
1413 const auto err = new ErrorState(ERR_READ_ERROR, Http::scBadGateway, fwd->request, fwd->al);
1414 static const auto d = MakeNamedErrorDetail("SRV_PREMATURE_EOF");
1415 err->detailError(d);
1416 fwd->fail(err);
1417 }
1418
1419 /**
1420 * Call this when there is data from the origin server
1421 * which should be sent to either StoreEntry, or to ICAP...
1422 */
1423 void
1424 HttpStateData::writeReplyBody()
1425 {
1426 truncateVirginBody(); // if needed
1427 const char *data = inBuf.rawContent();
1428 int len = inBuf.length();
1429 addVirginReplyBody(data, len);
1430 inBuf.consume(len);
1431
1432 // after addVirginReplyBody() wrote (when not adapting) everything we have
1433 // received to Store, check whether we have received/parsed the entire reply
1434 int64_t clen = -1;
1435 const char *parsedWhole = nullptr;
1436 if (!virginReply()->expectingBody(request->method, clen))
1437 parsedWhole = "http parsed header-only reply";
1438 else if (clen >= 0 && clen == payloadSeen - payloadTruncated)
1439 parsedWhole = "http parsed Content-Length body bytes";
1440 else if (clen < 0 && eof)
1441 parsedWhole = "http parsed body ending with expected/required EOF";
1442
1443 if (parsedWhole)
1444 markParsedVirginReplyAsWhole(parsedWhole);
1445 else if (eof)
1446 markPrematureReplyBodyEofFailure();
1447 }
1448
1449 bool
1450 HttpStateData::decodeAndWriteReplyBody()
1451 {
1452 assert(flags.chunked);
1453 assert(httpChunkDecoder);
1454 try {
1455 MemBuf decodedData;
1456 decodedData.init();
1457 httpChunkDecoder->setPayloadBuffer(&decodedData);
1458 const bool doneParsing = httpChunkDecoder->parse(inBuf);
1459 inBuf = httpChunkDecoder->remaining(); // sync buffers after parse
1460 addVirginReplyBody(decodedData.content(), decodedData.contentSize());
1461 if (doneParsing) {
1462 lastChunk = 1;
1463 markParsedVirginReplyAsWhole("http parsed last-chunk");
1464 } else if (eof) {
1465 markPrematureReplyBodyEofFailure();
1466 }
1467 return true;
1468 }
1469 catch (...) {
1470 debugs (11, 2, "de-chunking failure: " << CurrentException);
1471 }
1472 return false;
1473 }
1474
1475 /**
1476 * processReplyBody has two purposes:
1477 * 1 - take the reply body data, if any, and put it into either
1478 * the StoreEntry, or give it over to ICAP.
1479 * 2 - see if we made it to the end of the response (persistent
1480 * connections and such)
1481 */
1482 void
1483 HttpStateData::processReplyBody()
1484 {
1485 if (!flags.headers_parsed) {
1486 maybeReadVirginBody();
1487 return;
1488 }
1489
1490 #if USE_ADAPTATION
1491 debugs(11,5, "adaptationAccessCheckPending=" << adaptationAccessCheckPending);
1492 if (adaptationAccessCheckPending)
1493 return;
1494
1495 #endif
1496
1497 /*
1498 * At this point the reply headers have been parsed and consumed.
1499 * That means header content has been removed from readBuf and
1500 * it contains only body data.
1501 */
1502 if (entry->isAccepting()) {
1503 if (flags.chunked) {
1504 if (!decodeAndWriteReplyBody()) {
1505 serverComplete();
1506 return;
1507 }
1508 } else
1509 writeReplyBody();
1510 }
1511
1512 // storing/sending methods like earlier adaptOrFinalizeReply() or
1513 // above writeReplyBody() may release/abort the store entry.
1514 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1515 // TODO: In some cases (e.g., 304), we should keep persistent conn open.
1516 // Detect end-of-reply (and, hence, pool our idle pconn) earlier (ASAP).
1517 abortTransaction("store entry aborted while storing reply");
1518 return;
1519 } else
1520 switch (persistentConnStatus()) {
1521 case INCOMPLETE_MSG: {
1522 debugs(11, 5, "processReplyBody: INCOMPLETE_MSG from " << serverConnection);
1523 /* Wait for more data or EOF condition */
1524 AsyncCall::Pointer nil;
1525 if (flags.keepalive_broken) {
1526 commSetConnTimeout(serverConnection, 10, nil);
1527 } else {
1528 commSetConnTimeout(serverConnection, Config.Timeout.read, nil);
1529 }
1530 }
1531 break;
1532
1533 case COMPLETE_PERSISTENT_MSG: {
1534 debugs(11, 5, "processReplyBody: COMPLETE_PERSISTENT_MSG from " << serverConnection);
1535
1536 // TODO: Remove serverConnectionSaved but preserve exception safety.
1537
1538 commUnsetConnTimeout(serverConnection);
1539
1540 comm_remove_close_handler(serverConnection->fd, closeHandler);
1541 closeHandler = nullptr;
1542
1543 Ip::Address client_addr; // XXX: Remove as unused. Why was it added?
1544 if (request->flags.spoofClientIp)
1545 client_addr = request->client_addr;
1546
1547 auto serverConnectionSaved = serverConnection;
1548 fwd->unregister(serverConnection);
1549 serverConnection = nullptr;
1550
1551 bool ispinned = false; // TODO: Rename to isOrShouldBePinned
1552 if (request->flags.pinned) {
1553 ispinned = true;
1554 } else if (request->flags.connectionAuth && request->flags.authSent) {
1555 ispinned = true;
1556 }
1557
1558 if (ispinned) {
1559 if (request->clientConnectionManager.valid()) {
1560 CallJobHere1(11, 4, request->clientConnectionManager,
1561 ConnStateData,
1562 notePinnedConnectionBecameIdle,
1563 ConnStateData::PinnedIdleContext(serverConnectionSaved, request));
1564 } else {
1565 // must not pool/share ispinned connections, even orphaned ones
1566 serverConnectionSaved->close();
1567 }
1568 } else {
1569 fwdPconnPool->push(serverConnectionSaved, request->url.host());
1570 }
1571
1572 serverComplete();
1573 return;
1574 }
1575
1576 case COMPLETE_NONPERSISTENT_MSG:
1577 debugs(11, 5, "processReplyBody: COMPLETE_NONPERSISTENT_MSG from " << serverConnection);
1578
1579 serverComplete();
1580 return;
1581 }
1582
1583 maybeReadVirginBody();
1584 }
1585
1586 bool
1587 HttpStateData::mayReadVirginReplyBody() const
1588 {
1589 // TODO: Be more precise here. For example, if/when reading trailer, we may
1590 // not be doneWithServer() yet, but we should return false. Similarly, we
1591 // could still be writing the request body after receiving the whole reply.
1592 return !doneWithServer();
1593 }
1594
1595 void
1596 HttpStateData::maybeReadVirginBody()
1597 {
1598 if (!Comm::IsConnOpen(serverConnection) || fd_table[serverConnection->fd].closing()) {
1599 debugs(11, 3, "no, peer connection gone");
1600 return;
1601 }
1602
1603 if (eof) {
1604 // tolerate hypothetical calls between Comm::ENDFILE and closeServer()
1605 debugs(11, 3, "no, saw EOF");
1606 return;
1607 }
1608
1609 if (lastChunk) {
1610 // tolerate hypothetical calls between setting lastChunk and clearing serverConnection
1611 debugs(11, 3, "no, saw last-chunk");
1612 return;
1613 }
1614
1615 if (!canBufferMoreReplyBytes()) {
1616 abortTransaction("more response bytes required, but the read buffer is full and cannot be drained");
1617 return;
1618 }
1619
1620 if (waitingForDelayAwareReadChance) {
1621 debugs(11, 5, "no, still waiting for noteDelayAwareReadChance()");
1622 return;
1623 }
1624
1625 if (waitingForCommRead) {
1626 debugs(11, 3, "no, already waiting for readReply()");
1627 return;
1628 }
1629
1630 assert(!Comm::MonitorsRead(serverConnection->fd));
1631
1632 // wait for read(2) to be possible.
1633 typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
1634 AsyncCall::Pointer call = JobCallback(11, 5, Dialer, this, HttpStateData::readReply);
1635 Comm::Read(serverConnection, call);
1636 waitingForCommRead = true;
1637 }
1638
1639 /// Desired inBuf capacity based on various capacity preferences/limits:
1640 /// * a smaller buffer may not hold enough for look-ahead header/body parsers;
1641 /// * a smaller buffer may result in inefficient tiny network reads;
1642 /// * a bigger buffer may waste memory;
1643 /// * a bigger buffer may exceed SBuf storage capabilities (SBuf::maxSize);
1644 size_t
1645 HttpStateData::calcReadBufferCapacityLimit() const
1646 {
1647 if (!flags.headers_parsed)
1648 return Config.maxReplyHeaderSize;
1649
1650 // XXX: Our inBuf is not used to maintain the read-ahead gap, and using
1651 // Config.readAheadGap like this creates huge read buffers for large
1652 // read_ahead_gap values. TODO: Switch to using tcp_recv_bufsize as the
1653 // primary read buffer capacity factor.
1654 //
1655 // TODO: Cannot reuse throwing NaturalCast() here. Consider removing
1656 // .value() dereference in NaturalCast() or add/use NaturalCastOrMax().
1657 const auto configurationPreferences = NaturalSum<size_t>(Config.readAheadGap).value_or(SBuf::maxSize);
1658
1659 // TODO: Honor TeChunkedParser look-ahead and trailer parsing requirements
1660 // (when explicit configurationPreferences are set too low).
1661
1662 return std::min<size_t>(configurationPreferences, SBuf::maxSize);
1663 }
1664
1665 /// The maximum number of virgin reply bytes we may buffer before we violate
1666 /// the currently configured response buffering limits.
1667 /// \retval std::nullopt means that no more virgin response bytes can be read
1668 /// \retval 0 means that more virgin response bytes may be read later
1669 /// \retval >0 is the number of bytes that can be read now (subject to other constraints)
1670 std::optional<size_t>
1671 HttpStateData::canBufferMoreReplyBytes() const
1672 {
1673 #if USE_ADAPTATION
1674 // If we do not check this now, we may say the final "no" prematurely below
1675 // because inBuf.length() will decrease as adaptation drains buffered bytes.
1676 if (responseBodyBuffer) {
1677 debugs(11, 3, "yes, but waiting for adaptation to drain read buffer");
1678 return 0; // yes, we may be able to buffer more (but later)
1679 }
1680 #endif
1681
1682 const auto maxCapacity = calcReadBufferCapacityLimit();
1683 if (inBuf.length() >= maxCapacity) {
1684 debugs(11, 3, "no, due to a full buffer: " << inBuf.length() << '/' << inBuf.spaceSize() << "; limit: " << maxCapacity);
1685 return std::nullopt; // no, configuration prohibits buffering more
1686 }
1687
1688 const auto maxReadSize = maxCapacity - inBuf.length(); // positive
1689 debugs(11, 7, "yes, may read up to " << maxReadSize << " into " << inBuf.length() << '/' << inBuf.spaceSize());
1690 return maxReadSize; // yes, can read up to this many bytes (subject to other constraints)
1691 }
1692
1693 /// prepare read buffer for reading
1694 /// \return the maximum number of bytes the caller should attempt to read
1695 /// \retval 0 means that the caller should delay reading
1696 size_t
1697 HttpStateData::maybeMakeSpaceAvailable(const size_t maxReadSize)
1698 {
1699 // how much we want to read
1700 const size_t read_size = calcBufferSpaceToReserve(inBuf.spaceSize(), maxReadSize);
1701
1702 if (read_size < 2) {
1703 debugs(11, 7, "will not read up to " << read_size << " into buffer (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
1704 return 0;
1705 }
1706
1707 // we may need to grow the buffer
1708 inBuf.reserveSpace(read_size);
1709 debugs(11, 7, "may read up to " << read_size << " bytes info buffer (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
1710 return read_size;
1711 }
1712
1713 /// called after writing the very last request byte (body, last-chunk, etc)
1714 void
1715 HttpStateData::wroteLast(const CommIoCbParams &io)
1716 {
1717 debugs(11, 5, serverConnection << ": size " << io.size << ": errflag " << io.flag << ".");
1718 #if URL_CHECKSUM_DEBUG
1719
1720 entry->mem_obj->checkUrlChecksum();
1721 #endif
1722
1723 // XXX: Keep in sync with Client::sentRequestBody().
1724 // TODO: Extract common parts.
1725
1726 if (io.size > 0) {
1727 fd_bytes(io.fd, io.size, IoDirection::Write);
1728 statCounter.server.all.kbytes_out += io.size;
1729 statCounter.server.http.kbytes_out += io.size;
1730 }
1731
1732 if (io.flag == Comm::ERR_CLOSING)
1733 return;
1734
1735 // both successful and failed writes affect response times
1736 request->hier.notePeerWrite();
1737
1738 if (io.flag) {
1739 const auto err = new ErrorState(ERR_WRITE_ERROR, Http::scBadGateway, fwd->request, fwd->al);
1740 err->xerrno = io.xerrno;
1741 fwd->fail(err);
1742 closeServer();
1743 mustStop("HttpStateData::wroteLast");
1744 return;
1745 }
1746
1747 sendComplete();
1748 }
1749
1750 /// successfully wrote the entire request (including body, last-chunk, etc.)
1751 void
1752 HttpStateData::sendComplete()
1753 {
1754 /*
1755 * Set the read timeout here because it hasn't been set yet.
1756 * We only set the read timeout after the request has been
1757 * fully written to the peer. If we start the timeout
1758 * after connection establishment, then we are likely to hit
1759 * the timeout for POST/PUT requests that have very large
1760 * request bodies.
1761 */
1762 typedef CommCbMemFunT<HttpStateData, CommTimeoutCbParams> TimeoutDialer;
1763 AsyncCall::Pointer timeoutCall = JobCallback(11, 5,
1764 TimeoutDialer, this, HttpStateData::httpTimeout);
1765
1766 commSetConnTimeout(serverConnection, Config.Timeout.read, timeoutCall);
1767 flags.request_sent = true;
1768 }
1769
1770 void
1771 HttpStateData::closeServer()
1772 {
1773 debugs(11,5, "closing HTTP server " << serverConnection << " this " << this);
1774
1775 if (Comm::IsConnOpen(serverConnection)) {
1776 fwd->unregister(serverConnection);
1777 comm_remove_close_handler(serverConnection->fd, closeHandler);
1778 closeHandler = nullptr;
1779 serverConnection->close();
1780 }
1781 }
1782
1783 bool
1784 HttpStateData::doneWithServer() const
1785 {
1786 return !Comm::IsConnOpen(serverConnection);
1787 }
1788
1789 /*
1790 * Fixup authentication request headers for special cases
1791 */
1792 static void
1793 httpFixupAuthentication(HttpRequest * request, const HttpHeader * hdr_in, HttpHeader * hdr_out, const Http::StateFlags &flags)
1794 {
1795 /* Nothing to do unless we are forwarding to a peer */
1796 if (!flags.peering)
1797 return;
1798
1799 // This request is going "through" rather than "to" our _peer.
1800 if (flags.tunneling)
1801 return;
1802
1803 /* Needs to be explicitly enabled */
1804 if (!request->peer_login)
1805 return;
1806
1807 const auto header = flags.toOrigin ? Http::HdrType::AUTHORIZATION : Http::HdrType::PROXY_AUTHORIZATION;
1808 /* Maybe already dealt with? */
1809 if (hdr_out->has(header))
1810 return;
1811
1812 /* Nothing to do here for PASSTHRU */
1813 if (strcmp(request->peer_login, "PASSTHRU") == 0)
1814 return;
1815
1816 // Dangerous and undocumented PROXYPASS is a single-signon to servers with
1817 // the proxy password. Only Basic Authentication can work this way. This
1818 // statement forwards a "basic" Proxy-Authorization value from our client
1819 // to an originserver peer. Other PROXYPASS cases are handled lower.
1820 if (flags.toOrigin &&
1821 strcmp(request->peer_login, "PROXYPASS") == 0 &&
1822 hdr_in->has(Http::HdrType::PROXY_AUTHORIZATION)) {
1823
1824 const char *auth = hdr_in->getStr(Http::HdrType::PROXY_AUTHORIZATION);
1825
1826 if (auth && strncasecmp(auth, "basic ", 6) == 0) {
1827 hdr_out->putStr(header, auth);
1828 return;
1829 }
1830 }
1831
1832 char loginbuf[base64_encode_len(MAX_LOGIN_SZ)];
1833 size_t blen;
1834 struct base64_encode_ctx ctx;
1835 base64_encode_init(&ctx);
1836
1837 /* Special mode to pass the username to the upstream cache */
1838 if (*request->peer_login == '*') {
1839 const char *username = "-";
1840
1841 if (request->extacl_user.size())
1842 username = request->extacl_user.termedBuf();
1843 #if USE_AUTH
1844 else if (request->auth_user_request != nullptr)
1845 username = request->auth_user_request->username();
1846 #endif
1847
1848 blen = base64_encode_update(&ctx, loginbuf, strlen(username), reinterpret_cast<const uint8_t*>(username));
1849 blen += base64_encode_update(&ctx, loginbuf+blen, strlen(request->peer_login +1), reinterpret_cast<const uint8_t*>(request->peer_login +1));
1850 blen += base64_encode_final(&ctx, loginbuf+blen);
1851 httpHeaderPutStrf(hdr_out, header, "Basic %.*s", (int)blen, loginbuf);
1852 return;
1853 }
1854
1855 /* external_acl provided credentials */
1856 if (request->extacl_user.size() && request->extacl_passwd.size() &&
1857 (strcmp(request->peer_login, "PASS") == 0 ||
1858 strcmp(request->peer_login, "PROXYPASS") == 0)) {
1859
1860 blen = base64_encode_update(&ctx, loginbuf, request->extacl_user.size(), reinterpret_cast<const uint8_t*>(request->extacl_user.rawBuf()));
1861 blen += base64_encode_update(&ctx, loginbuf+blen, 1, reinterpret_cast<const uint8_t*>(":"));
1862 blen += base64_encode_update(&ctx, loginbuf+blen, request->extacl_passwd.size(), reinterpret_cast<const uint8_t*>(request->extacl_passwd.rawBuf()));
1863 blen += base64_encode_final(&ctx, loginbuf+blen);
1864 httpHeaderPutStrf(hdr_out, header, "Basic %.*s", (int)blen, loginbuf);
1865 return;
1866 }
1867 // if no external user credentials are available to fake authentication with PASS acts like PASSTHRU
1868 if (strcmp(request->peer_login, "PASS") == 0)
1869 return;
1870
1871 /* Kerberos login to peer */
1872 #if HAVE_AUTH_MODULE_NEGOTIATE && HAVE_KRB5 && HAVE_GSSAPI
1873 if (strncmp(request->peer_login, "NEGOTIATE",strlen("NEGOTIATE")) == 0) {
1874 char *Token=nullptr;
1875 char *PrincipalName=nullptr,*p;
1876 int negotiate_flags = 0;
1877
1878 if ((p=strchr(request->peer_login,':')) != nullptr ) {
1879 PrincipalName=++p;
1880 }
1881 if (request->flags.auth_no_keytab) {
1882 negotiate_flags |= PEER_PROXY_NEGOTIATE_NOKEYTAB;
1883 }
1884 Token = peer_proxy_negotiate_auth(PrincipalName, request->peer_host, negotiate_flags);
1885 if (Token) {
1886 httpHeaderPutStrf(hdr_out, header, "Negotiate %s",Token);
1887 }
1888 return;
1889 }
1890 #endif /* HAVE_KRB5 && HAVE_GSSAPI */
1891
1892 blen = base64_encode_update(&ctx, loginbuf, strlen(request->peer_login), reinterpret_cast<const uint8_t*>(request->peer_login));
1893 blen += base64_encode_final(&ctx, loginbuf+blen);
1894 httpHeaderPutStrf(hdr_out, header, "Basic %.*s", (int)blen, loginbuf);
1895 return;
1896 }
1897
1898 /*
1899 * build request headers and append them to a given MemBuf
1900 * used by buildRequestPrefix()
1901 * note: initialised the HttpHeader, the caller is responsible for Clean()-ing
1902 */
1903 void
1904 HttpStateData::httpBuildRequestHeader(HttpRequest * request,
1905 StoreEntry * entry,
1906 const AccessLogEntryPointer &al,
1907 HttpHeader * hdr_out,
1908 const Http::StateFlags &flags)
1909 {
1910 /* building buffer for complex strings */
1911 #define BBUF_SZ (MAX_URL+32)
1912 LOCAL_ARRAY(char, bbuf, BBUF_SZ);
1913 LOCAL_ARRAY(char, ntoabuf, MAX_IPSTRLEN);
1914 const HttpHeader *hdr_in = &request->header;
1915 const HttpHeaderEntry *e = nullptr;
1916 HttpHeaderPos pos = HttpHeaderInitPos;
1917 assert (hdr_out->owner == hoRequest);
1918
1919 /* use our IMS header if the cached entry has Last-Modified time */
1920 if (request->lastmod > -1)
1921 hdr_out->putTime(Http::HdrType::IF_MODIFIED_SINCE, request->lastmod);
1922
1923 // Add our own If-None-Match field if the cached entry has a strong ETag.
1924 // copyOneHeaderFromClientsideRequestToUpstreamRequest() adds client ones.
1925 if (request->etag.size() > 0) {
1926 hdr_out->addEntry(new HttpHeaderEntry(Http::HdrType::IF_NONE_MATCH, SBuf(),
1927 request->etag.termedBuf()));
1928 }
1929
1930 bool we_do_ranges = decideIfWeDoRanges (request);
1931
1932 String strConnection (hdr_in->getList(Http::HdrType::CONNECTION));
1933
1934 while ((e = hdr_in->getEntry(&pos)))
1935 copyOneHeaderFromClientsideRequestToUpstreamRequest(e, strConnection, request, hdr_out, we_do_ranges, flags);
1936
1937 /* Abstraction break: We should interpret multipart/byterange responses
1938 * into offset-length data, and this works around our inability to do so.
1939 */
1940 if (!we_do_ranges && request->multipartRangeRequest()) {
1941 /* don't cache the result */
1942 request->flags.cachable.veto();
1943 /* pretend it's not a range request */
1944 request->ignoreRange("want to request the whole object");
1945 request->flags.isRanged = false;
1946 }
1947
1948 hdr_out->addVia(request->http_ver, hdr_in);
1949
1950 if (request->flags.accelerated) {
1951 /* Append Surrogate-Capabilities */
1952 String strSurrogate(hdr_in->getList(Http::HdrType::SURROGATE_CAPABILITY));
1953 #if USE_SQUID_ESI
1954 snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0 ESI/1.0\"", Config.Accel.surrogate_id);
1955 #else
1956 snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0\"", Config.Accel.surrogate_id);
1957 #endif
1958 strListAdd(&strSurrogate, bbuf, ',');
1959 hdr_out->putStr(Http::HdrType::SURROGATE_CAPABILITY, strSurrogate.termedBuf());
1960 }
1961
1962 /** \pre Handle X-Forwarded-For */
1963 if (strcmp(opt_forwarded_for, "delete") != 0) {
1964
1965 String strFwd = hdr_in->getList(Http::HdrType::X_FORWARDED_FOR);
1966
1967 // Detect unreasonably long header values. And paranoidly check String
1968 // limits: a String ought to accommodate two reasonable-length values.
1969 if (strFwd.size() > 32*1024 || !strFwd.canGrowBy(strFwd.size())) {
1970 // There is probably a forwarding loop with Via detection disabled.
1971 // If we do nothing, String will assert on overflow soon.
1972 // TODO: Terminate all transactions with huge XFF?
1973 strFwd = "error";
1974
1975 static int warnedCount = 0;
1976 if (warnedCount++ < 100) {
1977 const SBuf url(entry ? SBuf(entry->url()) : request->effectiveRequestUri());
1978 debugs(11, DBG_IMPORTANT, "WARNING: likely forwarding loop with " << url);
1979 }
1980 }
1981
1982 if (strcmp(opt_forwarded_for, "on") == 0) {
1983 /** If set to ON - append client IP or 'unknown'. */
1984 if ( request->client_addr.isNoAddr() )
1985 strListAdd(&strFwd, "unknown", ',');
1986 else
1987 strListAdd(&strFwd, request->client_addr.toStr(ntoabuf, MAX_IPSTRLEN), ',');
1988 } else if (strcmp(opt_forwarded_for, "off") == 0) {
1989 /** If set to OFF - append 'unknown'. */
1990 strListAdd(&strFwd, "unknown", ',');
1991 } else if (strcmp(opt_forwarded_for, "transparent") == 0) {
1992 /** If set to TRANSPARENT - pass through unchanged. */
1993 } else if (strcmp(opt_forwarded_for, "truncate") == 0) {
1994 /** If set to TRUNCATE - drop existing list and replace with client IP or 'unknown'. */
1995 if ( request->client_addr.isNoAddr() )
1996 strFwd = "unknown";
1997 else
1998 strFwd = request->client_addr.toStr(ntoabuf, MAX_IPSTRLEN);
1999 }
2000 if (strFwd.size() > 0)
2001 hdr_out->putStr(Http::HdrType::X_FORWARDED_FOR, strFwd.termedBuf());
2002 }
2003 /** If set to DELETE - do not copy through. */
2004
2005 /* append Host if not there already */
2006 if (!hdr_out->has(Http::HdrType::HOST)) {
2007 if (request->peer_domain) {
2008 hdr_out->putStr(Http::HdrType::HOST, request->peer_domain);
2009 } else {
2010 SBuf authority = request->url.authority();
2011 hdr_out->putStr(Http::HdrType::HOST, authority.c_str());
2012 }
2013 }
2014
2015 /* append Authorization if known in URL, not in header and going direct */
2016 if (!hdr_out->has(Http::HdrType::AUTHORIZATION)) {
2017 if (flags.toOrigin && !request->url.userInfo().isEmpty()) {
2018 static char result[base64_encode_len(MAX_URL*2)]; // should be big enough for a single URI segment
2019 struct base64_encode_ctx ctx;
2020 base64_encode_init(&ctx);
2021 size_t blen = base64_encode_update(&ctx, result, request->url.userInfo().length(), reinterpret_cast<const uint8_t*>(request->url.userInfo().rawContent()));
2022 blen += base64_encode_final(&ctx, result+blen);
2023 result[blen] = '\0';
2024 if (blen)
2025 httpHeaderPutStrf(hdr_out, Http::HdrType::AUTHORIZATION, "Basic %.*s", (int)blen, result);
2026 }
2027 }
2028
2029 /* Fixup (Proxy-)Authorization special cases. Plain relaying dealt with above */
2030 httpFixupAuthentication(request, hdr_in, hdr_out, flags);
2031
2032 /* append Cache-Control, add max-age if not there already */
2033 {
2034 HttpHdrCc *cc = hdr_in->getCc();
2035
2036 if (!cc)
2037 cc = new HttpHdrCc();
2038
2039 /* Add max-age only without no-cache */
2040 if (!cc->hasMaxAge() && !cc->hasNoCache()) {
2041 // XXX: performance regression. c_str() reallocates
2042 SBuf tmp(request->effectiveRequestUri());
2043 cc->maxAge(getMaxAge(entry ? entry->url() : tmp.c_str()));
2044 }
2045
2046 /* Enforce sibling relations */
2047 if (flags.only_if_cached)
2048 cc->onlyIfCached(true);
2049
2050 hdr_out->putCc(*cc);
2051
2052 delete cc;
2053 }
2054
2055 // Always send Connection because HTTP/1.0 servers need explicit
2056 // "keep-alive", HTTP/1.1 servers need explicit "close", Upgrade recipients
2057 // need bare "upgrade", and we do not always know the server expectations.
2058 if (!hdr_out->has(Http::HdrType::CONNECTION)) // forwardUpgrade() may add it
2059 hdr_out->putStr(Http::HdrType::CONNECTION, flags.keepalive ? "keep-alive" : "close");
2060
2061 /* append Front-End-Https */
2062 if (flags.front_end_https) {
2063 if (flags.front_end_https == 1 || request->url.getScheme() == AnyP::PROTO_HTTPS)
2064 hdr_out->putStr(Http::HdrType::FRONT_END_HTTPS, "On");
2065 }
2066
2067 if (flags.chunked_request) {
2068 // Do not just copy the original value so that if the client-side
2069 // starts decode other encodings, this code may remain valid.
2070 hdr_out->putStr(Http::HdrType::TRANSFER_ENCODING, "chunked");
2071 }
2072
2073 /* Now mangle the headers. */
2074 httpHdrMangleList(hdr_out, request, al, ROR_REQUEST);
2075
2076 strConnection.clean();
2077 }
2078
2079 /// copies from-client Upgrade info into the given to-server header while
2080 /// honoring configuration filters and following HTTP requirements
2081 void
2082 HttpStateData::forwardUpgrade(HttpHeader &hdrOut)
2083 {
2084 if (!Config.http_upgrade_request_protocols)
2085 return; // forward nothing by default
2086
2087 /* RFC 7230 section 6.7 paragraph 10:
2088 * A server MUST ignore an Upgrade header field that is received in
2089 * an HTTP/1.0 request.
2090 */
2091 if (request->http_ver == Http::ProtocolVersion(1,0))
2092 return;
2093
2094 const auto &hdrIn = request->header;
2095 if (!hdrIn.has(Http::HdrType::UPGRADE))
2096 return;
2097 const auto upgradeIn = hdrIn.getList(Http::HdrType::UPGRADE);
2098
2099 String upgradeOut;
2100
2101 ACLFilledChecklist ch(nullptr, request.getRaw());
2102 ch.al = fwd->al;
2103 const char *pos = nullptr;
2104 const char *offeredStr = nullptr;
2105 int offeredStrLen = 0;
2106 while (strListGetItem(&upgradeIn, ',', &offeredStr, &offeredStrLen, &pos)) {
2107 const ProtocolView offeredProto(offeredStr, offeredStrLen);
2108 debugs(11, 5, "checks all rules applicable to " << offeredProto);
2109 Config.http_upgrade_request_protocols->forApplicable(offeredProto, [&ch, offeredStr, offeredStrLen, &upgradeOut] (const SBuf &cfgProto, const acl_access *guard) {
2110 debugs(11, 5, "checks " << cfgProto << " rule(s)");
2111 ch.changeAcl(guard);
2112 const auto answer = ch.fastCheck();
2113 if (answer.implicit)
2114 return false; // keep looking for an explicit rule match
2115 if (answer.allowed())
2116 strListAdd(upgradeOut, offeredStr, offeredStrLen);
2117 // else drop the offer (explicitly denied cases and ACL errors)
2118 return true; // stop after an explicit rule match or an error
2119 });
2120 }
2121
2122 if (upgradeOut.size()) {
2123 hdrOut.putStr(Http::HdrType::UPGRADE, upgradeOut.termedBuf());
2124
2125 /* RFC 7230 section 6.7 paragraph 10:
2126 * When Upgrade is sent, the sender MUST also send a Connection header
2127 * field that contains an "upgrade" connection option, in
2128 * order to prevent Upgrade from being accidentally forwarded by
2129 * intermediaries that might not implement the listed protocols.
2130 *
2131 * NP: Squid does not truly implement the protocol(s) in this Upgrade.
2132 * For now we are treating an explicit blind tunnel as "implemented"
2133 * regardless of the security implications.
2134 */
2135 hdrOut.putStr(Http::HdrType::CONNECTION, "upgrade");
2136
2137 // Connection:close and Connection:keepalive confuse some Upgrade
2138 // recipients, so we do not send those headers. Our Upgrade request
2139 // implicitly offers connection persistency per HTTP/1.1 defaults.
2140 // Update the keepalive flag to reflect that offer.
2141 // * If the server upgrades, then we would not be talking HTTP past the
2142 // HTTP 101 control message, and HTTP persistence would be irrelevant.
2143 // * Otherwise, our request will contradict onoff.server_pconns=off or
2144 // other no-keepalive conditions (if any). We compensate by copying
2145 // the original no-keepalive decision now and honoring it later.
2146 flags.forceClose = !flags.keepalive;
2147 flags.keepalive = true; // should already be true in most cases
2148 }
2149 }
2150
2151 /**
2152 * Decides whether a particular header may be cloned from the received Clients request
2153 * to our outgoing fetch request.
2154 */
2155 void
2156 copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request, HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &flags)
2157 {
2158 debugs(11, 5, "httpBuildRequestHeader: " << e->name << ": " << e->value );
2159
2160 switch (e->id) {
2161
2162 /** \par RFC 2616 sect 13.5.1 - Hop-by-Hop headers which Squid should not pass on. */
2163
2164 case Http::HdrType::PROXY_AUTHORIZATION:
2165 /** \par Proxy-Authorization:
2166 * Only pass on proxy authentication to peers for which
2167 * authentication forwarding is explicitly enabled
2168 */
2169 if (!flags.toOrigin && request->peer_login &&
2170 (strcmp(request->peer_login, "PASS") == 0 ||
2171 strcmp(request->peer_login, "PROXYPASS") == 0 ||
2172 strcmp(request->peer_login, "PASSTHRU") == 0)) {
2173 hdr_out->addEntry(e->clone());
2174 }
2175 break;
2176
2177 /** \par RFC 2616 sect 13.5.1 - Hop-by-Hop headers which Squid does not pass on. */
2178
2179 case Http::HdrType::CONNECTION: /** \par Connection: */
2180 case Http::HdrType::TE: /** \par TE: */
2181 case Http::HdrType::KEEP_ALIVE: /** \par Keep-Alive: */
2182 case Http::HdrType::PROXY_AUTHENTICATE: /** \par Proxy-Authenticate: */
2183 case Http::HdrType::TRAILER: /** \par Trailer: */
2184 case Http::HdrType::TRANSFER_ENCODING: /** \par Transfer-Encoding: */
2185 break;
2186
2187 /// \par Upgrade is hop-by-hop but forwardUpgrade() may send a filtered one
2188 case Http::HdrType::UPGRADE:
2189 break;
2190
2191 /** \par OTHER headers I haven't bothered to track down yet. */
2192
2193 case Http::HdrType::AUTHORIZATION:
2194 /** \par WWW-Authorization:
2195 * Pass on WWW authentication */
2196
2197 if (!flags.toOriginPeer()) {
2198 hdr_out->addEntry(e->clone());
2199 } else {
2200 /** \note Assume that talking to a cache_peer originserver makes
2201 * us a reverse proxy and only forward authentication if enabled
2202 * (see also httpFixupAuthentication for special cases)
2203 */
2204 if (request->peer_login &&
2205 (strcmp(request->peer_login, "PASS") == 0 ||
2206 strcmp(request->peer_login, "PASSTHRU") == 0 ||
2207 strcmp(request->peer_login, "PROXYPASS") == 0)) {
2208 hdr_out->addEntry(e->clone());
2209 }
2210 }
2211
2212 break;
2213
2214 case Http::HdrType::HOST:
2215 /** \par Host:
2216 * Normally Squid rewrites the Host: header.
2217 * However, there is one case when we don't: If the URL
2218 * went through our redirector and the admin configured
2219 * 'redir_rewrites_host' to be off.
2220 */
2221 if (request->peer_domain)
2222 hdr_out->putStr(Http::HdrType::HOST, request->peer_domain);
2223 else if (request->flags.redirected && !Config.onoff.redir_rewrites_host)
2224 hdr_out->addEntry(e->clone());
2225 else {
2226 SBuf authority = request->url.authority();
2227 hdr_out->putStr(Http::HdrType::HOST, authority.c_str());
2228 }
2229
2230 break;
2231
2232 case Http::HdrType::IF_MODIFIED_SINCE:
2233 /** \par If-Modified-Since:
2234 * append unless we added our own,
2235 * but only if cache_miss_revalidate is enabled, or
2236 * the request is not cacheable, or
2237 * the request contains authentication credentials.
2238 * \note at most one client's If-Modified-Since header can pass through
2239 */
2240 // XXX: need to check and cleanup the auth case so cacheable auth requests get cached.
2241 if (hdr_out->has(Http::HdrType::IF_MODIFIED_SINCE))
2242 break;
2243 else if (Config.onoff.cache_miss_revalidate || !request->flags.cachable || request->flags.auth)
2244 hdr_out->addEntry(e->clone());
2245 break;
2246
2247 case Http::HdrType::IF_NONE_MATCH:
2248 /** \par If-None-Match:
2249 * append if the wildcard '*' special case value is present, or
2250 * cache_miss_revalidate is disabled, or
2251 * the request is not cacheable in this proxy, or
2252 * the request contains authentication credentials.
2253 * \note this header lists a set of responses for the server to elide sending. Squid added values are extending that set.
2254 */
2255 // XXX: need to check and cleanup the auth case so cacheable auth requests get cached.
2256 if (hdr_out->hasListMember(Http::HdrType::IF_MATCH, "*", ',') || Config.onoff.cache_miss_revalidate || !request->flags.cachable || request->flags.auth)
2257 hdr_out->addEntry(e->clone());
2258 break;
2259
2260 case Http::HdrType::MAX_FORWARDS:
2261 /** \par Max-Forwards:
2262 * pass only on TRACE or OPTIONS requests */
2263 if (request->method == Http::METHOD_TRACE || request->method == Http::METHOD_OPTIONS) {
2264 const int64_t hops = e->getInt64();
2265
2266 if (hops > 0)
2267 hdr_out->putInt64(Http::HdrType::MAX_FORWARDS, hops - 1);
2268 }
2269
2270 break;
2271
2272 case Http::HdrType::VIA:
2273 /** \par Via:
2274 * If Via is disabled then forward any received header as-is.
2275 * Otherwise leave for explicit updated addition later. */
2276
2277 if (!Config.onoff.via)
2278 hdr_out->addEntry(e->clone());
2279
2280 break;
2281
2282 case Http::HdrType::RANGE:
2283
2284 case Http::HdrType::IF_RANGE:
2285
2286 case Http::HdrType::REQUEST_RANGE:
2287 /** \par Range:, If-Range:, Request-Range:
2288 * Only pass if we accept ranges */
2289 if (!we_do_ranges)
2290 hdr_out->addEntry(e->clone());
2291
2292 break;
2293
2294 case Http::HdrType::PROXY_CONNECTION: // SHOULD ignore. But doing so breaks things.
2295 break;
2296
2297 case Http::HdrType::CONTENT_LENGTH:
2298 // pass through unless we chunk; also, keeping this away from default
2299 // prevents request smuggling via Connection: Content-Length tricks
2300 if (!flags.chunked_request)
2301 hdr_out->addEntry(e->clone());
2302 break;
2303
2304 case Http::HdrType::X_FORWARDED_FOR:
2305
2306 case Http::HdrType::CACHE_CONTROL:
2307 /** \par X-Forwarded-For:, Cache-Control:
2308 * handled specially by Squid, so leave off for now.
2309 * append these after the loop if needed */
2310 break;
2311
2312 case Http::HdrType::FRONT_END_HTTPS:
2313 /** \par Front-End-Https:
2314 * Pass thru only if peer is configured with front-end-https */
2315 if (!flags.front_end_https)
2316 hdr_out->addEntry(e->clone());
2317
2318 break;
2319
2320 default:
2321 /** \par default.
2322 * pass on all other header fields
2323 * which are NOT listed by the special Connection: header. */
2324 if (strConnection.size()>0 && strListIsMember(&strConnection, e->name, ',')) {
2325 debugs(11, 2, "'" << e->name << "' header cropped by Connection: definition");
2326 return;
2327 }
2328
2329 hdr_out->addEntry(e->clone());
2330 }
2331 }
2332
2333 bool
2334 HttpStateData::decideIfWeDoRanges (HttpRequest * request)
2335 {
2336 bool result = true;
2337 /* decide if we want to do Ranges ourselves
2338 * and fetch the whole object now)
2339 * We want to handle Ranges ourselves iff
2340 * - we can actually parse client Range specs
2341 * - the specs are expected to be simple enough (e.g. no out-of-order ranges)
2342 * - reply will be cachable
2343 * (If the reply will be uncachable we have to throw it away after
2344 * serving this request, so it is better to forward ranges to
2345 * the server and fetch only the requested content)
2346 */
2347
2348 int64_t roffLimit = request->getRangeOffsetLimit();
2349
2350 if (nullptr == request->range || !request->flags.cachable
2351 || request->range->offsetLimitExceeded(roffLimit) || request->flags.connectionAuth)
2352 result = false;
2353
2354 debugs(11, 8, "decideIfWeDoRanges: range specs: " <<
2355 request->range << ", cachable: " <<
2356 request->flags.cachable << "; we_do_ranges: " << result);
2357
2358 return result;
2359 }
2360
2361 /* build request prefix and append it to a given MemBuf;
2362 * return the length of the prefix */
2363 mb_size_t
2364 HttpStateData::buildRequestPrefix(MemBuf * mb)
2365 {
2366 const int offset = mb->size;
2367 /* Uses a local httpver variable to print the HTTP label
2368 * since the HttpRequest may have an older version label.
2369 * XXX: This could create protocol bugs as the headers sent and
2370 * flow control should all be based on the HttpRequest version
2371 * not the one we are sending. Needs checking.
2372 */
2373 const AnyP::ProtocolVersion httpver = Http::ProtocolVersion();
2374 const SBuf url(flags.toOrigin ? request->url.path() : request->effectiveRequestUri());
2375 mb->appendf(SQUIDSBUFPH " " SQUIDSBUFPH " %s/%d.%d\r\n",
2376 SQUIDSBUFPRINT(request->method.image()),
2377 SQUIDSBUFPRINT(url),
2378 AnyP::ProtocolType_str[httpver.protocol],
2379 httpver.major,httpver.minor);
2380 /* build and pack headers */
2381 {
2382 HttpHeader hdr(hoRequest);
2383 forwardUpgrade(hdr); // before httpBuildRequestHeader() for CONNECTION
2384 httpBuildRequestHeader(request.getRaw(), entry, fwd->al, &hdr, flags);
2385
2386 if (request->flags.pinned && request->flags.connectionAuth)
2387 request->flags.authSent = true;
2388 else if (hdr.has(Http::HdrType::AUTHORIZATION))
2389 request->flags.authSent = true;
2390
2391 // The late placement of this check supports reply_header_add mangling,
2392 // but also complicates optimizing upgradeHeaderOut-like lookups.
2393 if (hdr.has(Http::HdrType::UPGRADE)) {
2394 assert(!upgradeHeaderOut);
2395 upgradeHeaderOut = new String(hdr.getList(Http::HdrType::UPGRADE));
2396 }
2397
2398 hdr.packInto(mb);
2399 hdr.clean();
2400 }
2401 /* append header terminator */
2402 mb->append(crlf, 2);
2403 return mb->size - offset;
2404 }
2405
2406 /* This will be called when connect completes. Write request. */
2407 bool
2408 HttpStateData::sendRequest()
2409 {
2410 MemBuf mb;
2411
2412 debugs(11, 5, serverConnection << ", request " << request << ", this " << this << ".");
2413
2414 if (!Comm::IsConnOpen(serverConnection)) {
2415 debugs(11,3, "cannot send request to closing " << serverConnection);
2416 assert(closeHandler != nullptr);
2417 return false;
2418 }
2419
2420 typedef CommCbMemFunT<HttpStateData, CommTimeoutCbParams> TimeoutDialer;
2421 AsyncCall::Pointer timeoutCall = JobCallback(11, 5,
2422 TimeoutDialer, this, HttpStateData::httpTimeout);
2423 commSetConnTimeout(serverConnection, Config.Timeout.lifetime, timeoutCall);
2424 maybeReadVirginBody();
2425
2426 if (request->body_pipe != nullptr) {
2427 if (!startRequestBodyFlow()) // register to receive body data
2428 return false;
2429 typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
2430 requestSender = JobCallback(11,5,
2431 Dialer, this, HttpStateData::sentRequestBody);
2432
2433 Must(!flags.chunked_request);
2434 // use chunked encoding if we do not know the length
2435 if (request->content_length < 0)
2436 flags.chunked_request = true;
2437 } else {
2438 assert(!requestBodySource);
2439 typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
2440 requestSender = JobCallback(11,5,
2441 Dialer, this, HttpStateData::wroteLast);
2442 }
2443
2444 /*
2445 * Is keep-alive okay for all request methods?
2446 */
2447 if (request->flags.mustKeepalive)
2448 flags.keepalive = true;
2449 else if (request->flags.pinned)
2450 flags.keepalive = request->persistent();
2451 else if (!Config.onoff.server_pconns)
2452 flags.keepalive = false;
2453 else if (flags.tunneling)
2454 // tunneled non pinned bumped requests must not keepalive
2455 flags.keepalive = !request->flags.sslBumped;
2456 else if (_peer == nullptr)
2457 flags.keepalive = true;
2458 else if (_peer->stats.n_keepalives_sent < 10)
2459 flags.keepalive = true;
2460 else if ((double) _peer->stats.n_keepalives_recv /
2461 (double) _peer->stats.n_keepalives_sent > 0.50)
2462 flags.keepalive = true;
2463
2464 if (_peer && !flags.tunneling) {
2465 /*The old code here was
2466 if (neighborType(_peer, request->url) == PEER_SIBLING && ...
2467 which is equivalent to:
2468 if (neighborType(_peer, URL()) == PEER_SIBLING && ...
2469 or better:
2470 if (((_peer->type == PEER_MULTICAST && p->options.mcast_siblings) ||
2471 _peer->type == PEER_SIBLINGS ) && _peer->options.allow_miss)
2472 flags.only_if_cached = 1;
2473
2474 But I suppose it was a bug
2475 */
2476 if (neighborType(_peer, request->url) == PEER_SIBLING && !_peer->options.allow_miss)
2477 flags.only_if_cached = true;
2478
2479 flags.front_end_https = _peer->front_end_https;
2480 }
2481
2482 mb.init();
2483 request->peer_host=_peer?_peer->host:nullptr;
2484 buildRequestPrefix(&mb);
2485
2486 debugs(11, 2, "HTTP Server " << serverConnection);
2487 debugs(11, 2, "HTTP Server REQUEST:\n---------\n" << mb.buf << "\n----------");
2488
2489 Comm::Write(serverConnection, &mb, requestSender);
2490 return true;
2491 }
2492
2493 bool
2494 HttpStateData::getMoreRequestBody(MemBuf &buf)
2495 {
2496 // parent's implementation can handle the no-encoding case
2497 if (!flags.chunked_request)
2498 return Client::getMoreRequestBody(buf);
2499
2500 MemBuf raw;
2501
2502 Must(requestBodySource != nullptr);
2503 if (!requestBodySource->getMoreData(raw))
2504 return false; // no request body bytes to chunk yet
2505
2506 // optimization: pre-allocate buffer size that should be enough
2507 const mb_size_t rawDataSize = raw.contentSize();
2508 // we may need to send: hex-chunk-size CRLF raw-data CRLF last-chunk
2509 buf.init(16 + 2 + rawDataSize + 2 + 5, raw.max_capacity);
2510
2511 buf.appendf("%x\r\n", static_cast<unsigned int>(rawDataSize));
2512 buf.append(raw.content(), rawDataSize);
2513 buf.append("\r\n", 2);
2514
2515 Must(rawDataSize > 0); // we did not accidentally created last-chunk above
2516
2517 // Do not send last-chunk unless we successfully received everything
2518 if (receivedWholeRequestBody) {
2519 Must(!flags.sentLastChunk);
2520 flags.sentLastChunk = true;
2521 buf.append("0\r\n\r\n", 5);
2522 }
2523
2524 return true;
2525 }
2526
2527 void
2528 httpStart(FwdState *fwd)
2529 {
2530 debugs(11, 3, fwd->request->method << ' ' << fwd->entry->url());
2531 AsyncJob::Start(new HttpStateData(fwd));
2532 }
2533
2534 void
2535 HttpStateData::start()
2536 {
2537 if (!sendRequest()) {
2538 debugs(11, 3, "httpStart: aborted");
2539 mustStop("HttpStateData::start failed");
2540 return;
2541 }
2542
2543 ++ statCounter.server.all.requests;
2544 ++ statCounter.server.http.requests;
2545
2546 /*
2547 * We used to set the read timeout here, but not any more.
2548 * Now its set in httpSendComplete() after the full request,
2549 * including request body, has been written to the server.
2550 */
2551 }
2552
2553 /// if broken posts are enabled for the request, try to fix and return true
2554 bool
2555 HttpStateData::finishingBrokenPost()
2556 {
2557 #if USE_HTTP_VIOLATIONS
2558 if (!Config.accessList.brokenPosts) {
2559 debugs(11, 5, "No brokenPosts list");
2560 return false;
2561 }
2562
2563 ACLFilledChecklist ch(Config.accessList.brokenPosts, originalRequest().getRaw());
2564 ch.al = fwd->al;
2565 ch.syncAle(originalRequest().getRaw(), nullptr);
2566 if (!ch.fastCheck().allowed()) {
2567 debugs(11, 5, "didn't match brokenPosts");
2568 return false;
2569 }
2570
2571 if (!Comm::IsConnOpen(serverConnection)) {
2572 debugs(11, 3, "ignoring broken POST for closed " << serverConnection);
2573 assert(closeHandler != nullptr);
2574 return true; // prevent caller from proceeding as if nothing happened
2575 }
2576
2577 debugs(11, 3, "finishingBrokenPost: fixing broken POST");
2578 typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
2579 requestSender = JobCallback(11,5,
2580 Dialer, this, HttpStateData::wroteLast);
2581 Comm::Write(serverConnection, "\r\n", 2, requestSender, nullptr);
2582 return true;
2583 #else
2584 return false;
2585 #endif /* USE_HTTP_VIOLATIONS */
2586 }
2587
2588 /// if needed, write last-chunk to end the request body and return true
2589 bool
2590 HttpStateData::finishingChunkedRequest()
2591 {
2592 if (flags.sentLastChunk) {
2593 debugs(11, 5, "already sent last-chunk");
2594 return false;
2595 }
2596
2597 Must(receivedWholeRequestBody); // or we should not be sending last-chunk
2598 flags.sentLastChunk = true;
2599
2600 typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
2601 requestSender = JobCallback(11,5, Dialer, this, HttpStateData::wroteLast);
2602 Comm::Write(serverConnection, "0\r\n\r\n", 5, requestSender, nullptr);
2603 return true;
2604 }
2605
2606 void
2607 HttpStateData::doneSendingRequestBody()
2608 {
2609 Client::doneSendingRequestBody();
2610 debugs(11,5, serverConnection);
2611
2612 // do we need to write something after the last body byte?
2613 if (flags.chunked_request && finishingChunkedRequest())
2614 return;
2615 if (!flags.chunked_request && finishingBrokenPost())
2616 return;
2617
2618 sendComplete();
2619 }
2620
2621 // more origin request body data is available
2622 void
2623 HttpStateData::handleMoreRequestBodyAvailable()
2624 {
2625 if (eof || !Comm::IsConnOpen(serverConnection)) {
2626 // XXX: we should check this condition in other callbacks then!
2627 // TODO: Check whether this can actually happen: We should unsubscribe
2628 // as a body consumer when the above condition(s) are detected.
2629 debugs(11, DBG_IMPORTANT, "Transaction aborted while reading HTTP body");
2630 return;
2631 }
2632
2633 assert(requestBodySource != nullptr);
2634
2635 if (requestBodySource->buf().hasContent()) {
2636 // XXX: why does not this trigger a debug message on every request?
2637
2638 if (flags.headers_parsed && !flags.abuse_detected) {
2639 flags.abuse_detected = true;
2640 debugs(11, DBG_IMPORTANT, "http handleMoreRequestBodyAvailable: Likely proxy abuse detected '" << request->client_addr << "' -> '" << entry->url() << "'" );
2641
2642 if (virginReply()->sline.status() == Http::scInvalidHeader) {
2643 closeServer();
2644 mustStop("HttpStateData::handleMoreRequestBodyAvailable");
2645 return;
2646 }
2647 }
2648 }
2649
2650 HttpStateData::handleMoreRequestBodyAvailable();
2651 }
2652
2653 // premature end of the request body
2654 void
2655 HttpStateData::handleRequestBodyProducerAborted()
2656 {
2657 Client::handleRequestBodyProducerAborted();
2658 if (entry->isEmpty()) {
2659 debugs(11, 3, "request body aborted: " << serverConnection);
2660 // We usually get here when ICAP REQMOD aborts during body processing.
2661 // We might also get here if client-side aborts, but then our response
2662 // should not matter because either client-side will provide its own or
2663 // there will be no response at all (e.g., if the the client has left).
2664 const auto err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, fwd->request, fwd->al);
2665 static const auto d = MakeNamedErrorDetail("SRV_REQMOD_REQ_BODY");
2666 err->detailError(d);
2667 fwd->fail(err);
2668 }
2669
2670 abortTransaction("request body producer aborted");
2671 }
2672
2673 // called when we wrote request headers(!) or a part of the body
2674 void
2675 HttpStateData::sentRequestBody(const CommIoCbParams &io)
2676 {
2677 if (io.size > 0)
2678 statCounter.server.http.kbytes_out += io.size;
2679
2680 Client::sentRequestBody(io);
2681 }
2682
2683 void
2684 HttpStateData::abortAll(const char *reason)
2685 {
2686 debugs(11,5, "aborting transaction for " << reason <<
2687 "; " << serverConnection << ", this " << this);
2688 mustStop(reason);
2689 }
2690
2691 HttpStateData::ReuseDecision::ReuseDecision(const StoreEntry *e, const Http::StatusCode code)
2692 : answer(HttpStateData::ReuseDecision::reuseNot), reason(nullptr), entry(e), statusCode(code) {}
2693
2694 HttpStateData::ReuseDecision::Answers
2695 HttpStateData::ReuseDecision::make(const HttpStateData::ReuseDecision::Answers ans, const char *why)
2696 {
2697 answer = ans;
2698 reason = why;
2699 return answer;
2700 }
2701
2702 std::ostream &operator <<(std::ostream &os, const HttpStateData::ReuseDecision &d)
2703 {
2704 static const char *ReuseMessages[] = {
2705 "do not cache and do not share", // reuseNot
2706 "cache positively and share", // cachePositively
2707 "cache negatively and share", // cacheNegatively
2708 "do not cache but share" // doNotCacheButShare
2709 };
2710
2711 assert(d.answer >= HttpStateData::ReuseDecision::reuseNot &&
2712 d.answer <= HttpStateData::ReuseDecision::doNotCacheButShare);
2713 return os << ReuseMessages[d.answer] << " because " << d.reason <<
2714 "; HTTP status " << d.statusCode << " " << *(d.entry);
2715 }
2716