]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_side_request.cc
9a78af4e07184d8bd08e3f30d2bfdca6f7e65e01
[thirdparty/squid.git] / src / client_side_request.cc
1 /*
2 * Copyright (C) 1996-2026 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 85 Client-side Request Routines */
10
11 /*
12 * General logic of request processing:
13 *
14 * We run a series of tests to determine if access will be permitted, and to do
15 * any redirection. Then we call into the result clientStream to retrieve data.
16 * From that point on it's up to reply management.
17 */
18
19 #include "squid.h"
20 #include "acl/FilledChecklist.h"
21 #include "acl/Gadgets.h"
22 #include "anyp/PortCfg.h"
23 #include "base/AsyncJobCalls.h"
24 #include "client_side.h"
25 #include "client_side_reply.h"
26 #include "client_side_request.h"
27 #include "ClientRequestContext.h"
28 #include "clientStream.h"
29 #include "comm/Connection.h"
30 #include "comm/Write.h"
31 #include "debug/Messages.h"
32 #include "error/Detail.h"
33 #include "errorpage.h"
34 #include "fd.h"
35 #include "fde.h"
36 #include "format/Token.h"
37 #include "FwdState.h"
38 #include "helper.h"
39 #include "helper/Reply.h"
40 #include "http.h"
41 #include "http/Stream.h"
42 #include "HttpHdrCc.h"
43 #include "HttpReply.h"
44 #include "HttpRequest.h"
45 #include "internal.h"
46 #include "ip/NfMarkConfig.h"
47 #include "ip/QosConfig.h"
48 #include "ipcache.h"
49 #include "log/access_log.h"
50 #include "MemObject.h"
51 #include "Parsing.h"
52 #include "proxyp/Header.h"
53 #include "redirect.h"
54 #include "rfc1738.h"
55 #include "sbuf/StringConvert.h"
56 #include "SquidConfig.h"
57 #include "Store.h"
58 #include "StrList.h"
59 #include "tools.h"
60 #include "wordlist.h"
61 #if USE_AUTH
62 #include "auth/UserRequest.h"
63 #endif
64 #if USE_ADAPTATION
65 #include "adaptation/AccessCheck.h"
66 #include "adaptation/Answer.h"
67 #include "adaptation/Iterator.h"
68 #include "adaptation/Service.h"
69 #if ICAP_CLIENT
70 #include "adaptation/icap/History.h"
71 #endif
72 #endif
73 #if USE_OPENSSL
74 #include "ssl/ServerBump.h"
75 #include "ssl/support.h"
76 #endif
77
78 #if FOLLOW_X_FORWARDED_FOR
79
80 #if !defined(SQUID_X_FORWARDED_FOR_HOP_MAX)
81 #define SQUID_X_FORWARDED_FOR_HOP_MAX 64
82 #endif
83
84 static void clientFollowXForwardedForCheck(Acl::Answer answer, void *data);
85 #endif /* FOLLOW_X_FORWARDED_FOR */
86
87 ErrorState *clientBuildError(err_type, Http::StatusCode, char const *url, const ConnStateData *, HttpRequest *, const AccessLogEntry::Pointer &);
88
89 CBDATA_CLASS_INIT(ClientRequestContext);
90
91 /* Local functions */
92 /* other */
93 static void clientAccessCheckDoneWrapper(Acl::Answer, void *);
94 #if USE_OPENSSL
95 static void sslBumpAccessCheckDoneWrapper(Acl::Answer, void *);
96 #endif
97 static int clientHierarchical(ClientHttpRequest * http);
98 static void clientInterpretRequestHeaders(ClientHttpRequest * http);
99 static HLPCB clientRedirectDoneWrapper;
100 static HLPCB clientStoreIdDoneWrapper;
101 static void checkNoCacheDoneWrapper(Acl::Answer, void *);
102 CSR clientGetMoreData;
103 CSS clientReplyStatus;
104 CSD clientReplyDetach;
105 static void checkFailureRatio(err_type, hier_code);
106
107 ClientRequestContext::~ClientRequestContext()
108 {
109 cbdataReferenceDone(http);
110
111 delete error;
112 debugs(85,3, "ClientRequestContext destructed, this=" << this);
113 }
114
115 ClientRequestContext::ClientRequestContext(ClientHttpRequest *anHttp) :
116 http(cbdataReference(anHttp))
117 {
118 debugs(85, 3, "ClientRequestContext constructed, this=" << this);
119 }
120
121 CBDATA_CLASS_INIT(ClientHttpRequest);
122
123 ClientHttpRequest::ClientHttpRequest(ConnStateData * aConn) :
124 #if USE_ADAPTATION
125 AsyncJob("ClientHttpRequest"),
126 #endif
127 al(new AccessLogEntry()),
128 conn_(cbdataReference(aConn))
129 {
130 CodeContext::Reset(al);
131 al->cache.start_time = current_time;
132 if (aConn) {
133 al->tcpClient = aConn->clientConnection;
134 al->cache.port = aConn->port;
135 al->cache.caddr = aConn->log_addr;
136 al->proxyProtocolHeader = aConn->proxyProtocolHeader();
137 al->updateError(aConn->bareError);
138
139 #if USE_OPENSSL
140 if (aConn->clientConnection != nullptr && aConn->clientConnection->isOpen()) {
141 if (auto ssl = fd_table[aConn->clientConnection->fd].ssl.get())
142 al->cache.sslClientCert.resetWithoutLocking(SSL_get_peer_certificate(ssl));
143 }
144 #endif
145 }
146 dlinkAdd(this, &active, &ClientActiveRequests);
147 }
148
149 /*
150 * returns true if client specified that the object must come from the cache
151 * without contacting origin server
152 */
153 bool
154 ClientHttpRequest::onlyIfCached()const
155 {
156 assert(request);
157 return request->cache_control &&
158 request->cache_control->hasOnlyIfCached();
159 }
160
161 /**
162 * This function is designed to serve a fairly specific purpose.
163 * Occasionally our vBNS-connected caches can talk to each other, but not
164 * the rest of the world. Here we try to detect frequent failures which
165 * make the cache unusable (e.g. DNS lookup and connect() failures). If
166 * the failure:success ratio goes above 1.0 then we go into "hit only"
167 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
168 * will only fetch HITs from us if they are using the ICP protocol. We
169 * stay in this mode for 5 minutes.
170 *
171 * Duane W., Sept 16, 1996
172 */
173 static void
174 checkFailureRatio(err_type etype, hier_code hcode)
175 {
176 // Can be set at compile time with -D compiler flag
177 #ifndef FAILURE_MODE_TIME
178 #define FAILURE_MODE_TIME 300
179 #endif
180
181 if (hcode == HIER_NONE)
182 return;
183
184 // don't bother when ICP is disabled.
185 if (Config.Port.icp <= 0)
186 return;
187
188 static double magic_factor = 100.0;
189 double n_good;
190 double n_bad;
191
192 n_good = magic_factor / (1.0 + request_failure_ratio);
193
194 n_bad = magic_factor - n_good;
195
196 switch (etype) {
197
198 case ERR_DNS_FAIL:
199
200 case ERR_CONNECT_FAIL:
201 case ERR_SECURE_CONNECT_FAIL:
202
203 case ERR_READ_ERROR:
204 ++n_bad;
205 break;
206
207 default:
208 ++n_good;
209 }
210
211 request_failure_ratio = n_bad / n_good;
212
213 if (hit_only_mode_until > squid_curtime)
214 return;
215
216 if (request_failure_ratio < 1.0)
217 return;
218
219 debugs(33, DBG_CRITICAL, "WARNING: Failure Ratio at "<< std::setw(4)<<
220 std::setprecision(3) << request_failure_ratio);
221
222 debugs(33, DBG_CRITICAL, "WARNING: ICP going into HIT-only mode for " <<
223 FAILURE_MODE_TIME / 60 << " minutes...");
224
225 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
226
227 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
228 }
229
230 ClientHttpRequest::~ClientHttpRequest()
231 {
232 debugs(33, 3, "httpRequestFree: " << uri);
233
234 // Even though freeResources() below may destroy the request,
235 // we no longer set request->body_pipe to NULL here
236 // because we did not initiate that pipe (ConnStateData did)
237
238 /* the ICP check here was erroneous
239 * - StoreEntry::releaseRequest was always called if entry was valid
240 */
241
242 logRequest();
243
244 loggingEntry(nullptr);
245
246 if (request)
247 checkFailureRatio(request->error.category, al->hier.code);
248
249 freeResources();
250
251 #if USE_ADAPTATION
252 announceInitiatorAbort(virginHeadSource);
253
254 if (adaptedBodySource != nullptr)
255 stopConsumingFrom(adaptedBodySource);
256 #endif
257
258 delete calloutContext;
259
260 cbdataReferenceDone(conn_);
261
262 /* moving to the next connection is handled by the context free */
263 dlinkDelete(&active, &ClientActiveRequests);
264 }
265
266 #if FOLLOW_X_FORWARDED_FOR
267 /**
268 * clientFollowXForwardedForCheck() checks the content of X-Forwarded-For:
269 * against the followXFF ACL, or cleans up and passes control to
270 * clientAccessCheck().
271 *
272 * The trust model here is a little ambiguous. So to clarify the logic:
273 * - we may always use the direct client address as the client IP.
274 * - these trust tests merey tell whether we trust given IP enough to believe the
275 * IP string which it appended to the X-Forwarded-For: header.
276 * - if at any point we don't trust what an IP adds we stop looking.
277 * - at that point the current contents of indirect_client_addr are the value set
278 * by the last previously trusted IP.
279 * ++ indirect_client_addr contains the remote direct client from the trusted peers viewpoint.
280 */
281 static void
282 clientFollowXForwardedForCheck(Acl::Answer answer, void *data)
283 {
284 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
285 ClientHttpRequest *http = calloutContext->http;
286 HttpRequest *request = http->request;
287
288 if (answer.allowed() && request->x_forwarded_for_iterator.size() != 0) {
289
290 /*
291 * Remove the last comma-delimited element from the
292 * x_forwarded_for_iterator and use it to repeat the cycle.
293 */
294 const char *p;
295 const char *asciiaddr;
296 int l;
297 Ip::Address addr;
298 p = request->x_forwarded_for_iterator.termedBuf();
299 l = request->x_forwarded_for_iterator.size();
300
301 /*
302 * XXX x_forwarded_for_iterator should really be a list of
303 * IP addresses, but it's a String instead. We have to
304 * walk backwards through the String, biting off the last
305 * comma-delimited part each time. As long as the data is in
306 * a String, we should probably implement and use a variant of
307 * strListGetItem() that walks backwards instead of forwards
308 * through a comma-separated list. But we don't even do that;
309 * we just do the work in-line here.
310 */
311 /* skip trailing space and commas */
312 while (l > 0 && (p[l-1] == ',' || xisspace(p[l-1])))
313 --l;
314 request->x_forwarded_for_iterator.cut(l);
315 /* look for start of last item in list */
316 while (l > 0 && ! (p[l-1] == ',' || xisspace(p[l-1])))
317 --l;
318 asciiaddr = p+l;
319 if ((addr = asciiaddr)) {
320 request->indirect_client_addr = addr;
321 request->x_forwarded_for_iterator.cut(l);
322 auto ch = clientAclChecklistCreate(Config.accessList.followXFF, http);
323 if (!Config.onoff.acl_uses_indirect_client) {
324 /* override the default src_addr tested if we have to go deeper than one level into XFF */
325 ch->src_addr = request->indirect_client_addr;
326 }
327 if (++calloutContext->currentXffHopNumber < SQUID_X_FORWARDED_FOR_HOP_MAX) {
328 ACLFilledChecklist::NonBlockingCheck(std::move(ch), clientFollowXForwardedForCheck, data);
329 return;
330 }
331 const auto headerName = Http::HeaderLookupTable.lookup(Http::HdrType::X_FORWARDED_FOR).name;
332 debugs(28, DBG_CRITICAL, "ERROR: Ignoring trailing " << headerName << " addresses" <<
333 Debug::Extra << "addresses allowed by follow_x_forwarded_for: " << calloutContext->currentXffHopNumber <<
334 Debug::Extra << "last/accepted address: " << request->indirect_client_addr <<
335 Debug::Extra << "ignored trailing addresses: " << request->x_forwarded_for_iterator);
336 // fall through to resume clientAccessCheck() processing
337 }
338 }
339
340 /* clean up, and pass control to clientAccessCheck */
341 if (Config.onoff.log_uses_indirect_client) {
342 /*
343 * Ensure that the access log shows the indirect client
344 * instead of the direct client.
345 */
346 http->al->cache.caddr = request->indirect_client_addr;
347 if (ConnStateData *conn = http->getConn())
348 conn->log_addr = request->indirect_client_addr;
349 }
350 request->x_forwarded_for_iterator.clean();
351 request->flags.done_follow_x_forwarded_for = true;
352
353 if (answer.conflicted()) {
354 debugs(28, DBG_CRITICAL, "ERROR: Processing X-Forwarded-For. Stopping at IP address: " << request->indirect_client_addr );
355 }
356
357 /* process actual access ACL as normal. */
358 calloutContext->clientAccessCheck();
359 }
360 #endif /* FOLLOW_X_FORWARDED_FOR */
361
362 static void
363 hostHeaderIpVerifyWrapper(const ipcache_addrs* ia, const Dns::LookupDetails &dns, void *data)
364 {
365 ClientRequestContext *c = static_cast<ClientRequestContext*>(data);
366 c->hostHeaderIpVerify(ia, dns);
367 }
368
369 void
370 ClientRequestContext::hostHeaderIpVerify(const ipcache_addrs* ia, const Dns::LookupDetails &dns)
371 {
372 Comm::ConnectionPointer clientConn = http->getConn()->clientConnection;
373
374 // note the DNS details for the transaction stats.
375 http->request->recordLookup(dns);
376
377 // Is the NAT destination IP in DNS?
378 if (ia && ia->have(clientConn->local)) {
379 debugs(85, 3, "validate IP " << clientConn->local << " possible from Host:");
380 http->request->flags.hostVerified = true;
381 http->doCallouts();
382 return;
383 }
384 debugs(85, 3, "FAIL: validate IP " << clientConn->local << " possible from Host:");
385 hostHeaderVerifyFailed("local IP", "any domain IP");
386 }
387
388 void
389 ClientRequestContext::hostHeaderVerifyFailed(const char *A, const char *B)
390 {
391 // IP address validation for Host: failed. Admin wants to ignore them.
392 // NP: we do not yet handle CONNECT tunnels well, so ignore for them
393 if (!Config.onoff.hostStrictVerify && http->request->method != Http::METHOD_CONNECT) {
394 debugs(85, 3, "SECURITY ALERT: Host header forgery detected on " << http->getConn()->clientConnection <<
395 " (" << A << " does not match " << B << ") on URL: " << http->request->effectiveRequestUri());
396
397 // MUST NOT cache (for now). It is tempting to set flags.noCache, but
398 // that flag is about satisfying _this_ request. We are actually OK with
399 // satisfying this request from the cache, but want to prevent _other_
400 // requests from being satisfied using this response.
401 http->request->flags.cachable.veto();
402
403 // XXX: when we have updated the cache key to base on raw-IP + URI this cacheable limit can go.
404 http->request->flags.hierarchical = false; // MUST NOT pass to peers (for now)
405 // XXX: when we have sorted out the best way to relay requests properly to peers this hierarchical limit can go.
406 http->doCallouts();
407 return;
408 }
409
410 debugs(85, DBG_IMPORTANT, "SECURITY ALERT: Host header forgery detected on " <<
411 http->getConn()->clientConnection << " (" << A << " does not match " << B << ")");
412 if (const char *ua = http->request->header.getStr(Http::HdrType::USER_AGENT))
413 debugs(85, DBG_IMPORTANT, "SECURITY ALERT: By user agent: " << ua);
414 debugs(85, DBG_IMPORTANT, "SECURITY ALERT: on URL: " << http->request->effectiveRequestUri());
415
416 // IP address validation for Host: failed. reject the connection.
417 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
418 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
419 assert (repContext);
420 repContext->setReplyToError(ERR_CONFLICT_HOST, Http::scConflict,
421 nullptr,
422 http->getConn(),
423 http->request,
424 nullptr,
425 #if USE_AUTH
426 http->getConn() != nullptr && http->getConn()->getAuth() != nullptr ?
427 http->getConn()->getAuth() : http->request->auth_user_request);
428 #else
429 nullptr);
430 #endif
431 node = (clientStreamNode *)http->client_stream.tail->data;
432 clientStreamRead(node, http, node->readBuffer);
433 }
434
435 void
436 ClientRequestContext::hostHeaderVerify()
437 {
438 // Require a Host: header.
439 const char *host = http->request->header.getStr(Http::HdrType::HOST);
440
441 if (!host) {
442 // TODO: dump out the HTTP/1.1 error about missing host header.
443 // otherwise this is fine, can't forge a header value when its not even set.
444 debugs(85, 3, "validate skipped with no Host: header present.");
445 http->doCallouts();
446 return;
447 }
448
449 if (http->request->flags.internal) {
450 // TODO: kill this when URL handling allows partial URLs out of accel mode
451 // and we no longer screw with the URL just to add our internal host there
452 debugs(85, 6, "validate skipped due to internal composite URL.");
453 http->doCallouts();
454 return;
455 }
456
457 // TODO: Unify Host value parsing below with AnyP::Uri authority parsing
458 // Locate if there is a port attached, strip ready for IP lookup
459 char *portStr = nullptr;
460 char *hostB = xstrdup(host);
461 host = hostB;
462 if (host[0] == '[') {
463 // IPv6 literal.
464 portStr = strchr(hostB, ']');
465 if (portStr && *(++portStr) != ':') {
466 portStr = nullptr;
467 }
468 } else {
469 // Domain or IPv4 literal with port
470 portStr = strrchr(hostB, ':');
471 }
472
473 uint16_t port = 0;
474 if (portStr) {
475 *portStr = '\0'; // strip the ':'
476 if (*(++portStr) != '\0') {
477 char *end = nullptr;
478 int64_t ret = strtoll(portStr, &end, 10);
479 if (end == portStr || *end != '\0' || ret < 1 || ret > 0xFFFF) {
480 // invalid port details. Replace the ':'
481 *(--portStr) = ':';
482 portStr = nullptr;
483 } else
484 port = (ret & 0xFFFF);
485 }
486 }
487
488 debugs(85, 3, "validate host=" << host << ", port=" << port << ", portStr=" << (portStr?portStr:"NULL"));
489 if (http->request->flags.intercepted || http->request->flags.interceptTproxy) {
490 // verify the Host: port (if any) matches the apparent destination
491 if (portStr && port != http->getConn()->clientConnection->local.port()) {
492 debugs(85, 3, "FAIL on validate port " << http->getConn()->clientConnection->local.port() <<
493 " matches Host: port " << port << " (" << portStr << ")");
494 hostHeaderVerifyFailed("intercepted port", portStr);
495 } else {
496 // XXX: match the scheme default port against the apparent destination
497
498 // verify the destination DNS is one of the Host: headers IPs
499 ipcache_nbgethostbyname(host, hostHeaderIpVerifyWrapper, this);
500 }
501 } else if (!Config.onoff.hostStrictVerify) {
502 debugs(85, 3, "validate skipped.");
503 http->doCallouts();
504 } else if (strlen(host) != strlen(http->request->url.host())) {
505 // Verify forward-proxy requested URL domain matches the Host: header
506 debugs(85, 3, "FAIL on validate URL domain length " << http->request->url.host() << " matches Host: " << host);
507 hostHeaderVerifyFailed(host, http->request->url.host());
508 } else if (matchDomainName(host, http->request->url.host()) != 0) {
509 // Verify forward-proxy requested URL domain matches the Host: header
510 debugs(85, 3, "FAIL on validate URL domain " << http->request->url.host() << " matches Host: " << host);
511 hostHeaderVerifyFailed(host, http->request->url.host());
512 } else if (portStr && !http->request->url.port()) {
513 debugs(85, 3, "FAIL on validate portless URI matches Host: " << portStr);
514 hostHeaderVerifyFailed("portless URI", portStr);
515 } else if (portStr && port != *http->request->url.port()) {
516 // Verify forward-proxy requested URL domain matches the Host: header
517 debugs(85, 3, "FAIL on validate URL port " << *http->request->url.port() << " matches Host: port " << portStr);
518 hostHeaderVerifyFailed("URL port", portStr);
519 } else if (!portStr && http->request->method != Http::METHOD_CONNECT && http->request->url.port() != http->request->url.getScheme().defaultPort()) {
520 // Verify forward-proxy requested URL domain matches the Host: header
521 // Special case: we don't have a default-port to check for CONNECT. Assume URL is correct.
522 debugs(85, 3, "FAIL on validate URL port " << http->request->url.port().value_or(0) << " matches Host: default port " << http->request->url.getScheme().defaultPort().value_or(0));
523 hostHeaderVerifyFailed("URL port", "default port");
524 } else {
525 // Okay no problem.
526 debugs(85, 3, "validate passed.");
527 http->request->flags.hostVerified = true;
528 http->doCallouts();
529 }
530 safe_free(hostB);
531 }
532
533 /* This is the entry point for external users of the client_side routines */
534 void
535 ClientRequestContext::clientAccessCheck()
536 {
537 #if FOLLOW_X_FORWARDED_FOR
538 if (!http->request->flags.doneFollowXff() &&
539 Config.accessList.followXFF &&
540 http->request->header.has(Http::HdrType::X_FORWARDED_FOR)) {
541
542 /* we always trust the direct client address for actual use */
543 http->request->indirect_client_addr = http->request->client_addr;
544 http->request->indirect_client_addr.port(0);
545
546 /* setup the XFF iterator for processing */
547 http->request->x_forwarded_for_iterator = http->request->header.getList(Http::HdrType::X_FORWARDED_FOR);
548
549 /* begin by checking to see if we trust direct client enough to walk XFF */
550 auto acl_checklist = clientAclChecklistCreate(Config.accessList.followXFF, http);
551 ACLFilledChecklist::NonBlockingCheck(std::move(acl_checklist), clientFollowXForwardedForCheck, this);
552 return;
553 }
554 #endif
555
556 if (Config.accessList.http) {
557 auto acl_checklist = clientAclChecklistCreate(Config.accessList.http, http);
558 ACLFilledChecklist::NonBlockingCheck(std::move(acl_checklist), clientAccessCheckDoneWrapper, this);
559 } else {
560 debugs(0, DBG_CRITICAL, "No http_access configuration found. This will block ALL traffic");
561 clientAccessCheckDone(ACCESS_DENIED);
562 }
563 }
564
565 /**
566 * Identical in operation to clientAccessCheck() but performed later using different configured ACL list.
567 * The default here is to allow all. Since the earlier http_access should do a default deny all.
568 * This check is just for a last-minute denial based on adapted request headers.
569 */
570 void
571 ClientRequestContext::clientAccessCheck2()
572 {
573 if (Config.accessList.adapted_http) {
574 auto acl_checklist = clientAclChecklistCreate(Config.accessList.adapted_http, http);
575 ACLFilledChecklist::NonBlockingCheck(std::move(acl_checklist), clientAccessCheckDoneWrapper, this);
576 } else {
577 debugs(85, 2, "No adapted_http_access configuration. default: ALLOW");
578 clientAccessCheckDone(ACCESS_ALLOWED);
579 }
580 }
581
582 void
583 clientAccessCheckDoneWrapper(Acl::Answer answer, void *data)
584 {
585 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
586 calloutContext->clientAccessCheckDone(answer);
587 }
588
589 void
590 ClientRequestContext::clientAccessCheckDone(const Acl::Answer &answer)
591 {
592 Http::StatusCode status;
593 debugs(85, 2, "The request " << http->request->method << ' ' <<
594 http->uri << " is " << answer <<
595 "; last ACL checked: " << answer.lastCheckDescription());
596
597 #if USE_AUTH
598 char const *proxy_auth_msg = "<null>";
599 if (http->getConn() != nullptr && http->getConn()->getAuth() != nullptr)
600 proxy_auth_msg = http->getConn()->getAuth()->denyMessage("<null>");
601 else if (http->request->auth_user_request != nullptr)
602 proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
603 #endif
604
605 if (!answer.allowed()) {
606 // auth has a grace period where credentials can be expired but okay not to challenge.
607
608 /* Send an auth challenge or error */
609 // XXX: do we still need aclIsProxyAuth() ?
610 const auto auth_challenge = (answer == ACCESS_AUTH_REQUIRED || aclIsProxyAuth(answer.lastCheckedName));
611 debugs(85, 5, "Access Denied: " << http->uri);
612 #if USE_AUTH
613 if (auth_challenge)
614 debugs(33, 5, "Proxy Auth Message = " << (proxy_auth_msg ? proxy_auth_msg : "<null>"));
615 #endif
616
617 auto page_id = FindDenyInfoPage(answer, answer != ACCESS_AUTH_REQUIRED);
618
619 http->updateLoggingTags(LOG_TCP_DENIED);
620
621 if (auth_challenge) {
622 #if USE_AUTH
623 if (http->request->flags.sslBumped) {
624 /*SSL Bumped request, authentication is not possible*/
625 status = Http::scForbidden;
626 } else if (!http->flags.accel) {
627 /* Proxy authorisation needed */
628 status = Http::scProxyAuthenticationRequired;
629 } else {
630 /* WWW authorisation needed */
631 status = Http::scUnauthorized;
632 }
633 #else
634 // need auth, but not possible to do.
635 status = Http::scForbidden;
636 #endif
637 if (page_id == ERR_NONE)
638 page_id = (status == Http::scForbidden) ? ERR_ACCESS_DENIED : ERR_CACHE_ACCESS_DENIED;
639 } else {
640 status = Http::scForbidden;
641
642 if (page_id == ERR_NONE)
643 page_id = ERR_ACCESS_DENIED;
644 }
645
646 error = clientBuildError(page_id, status, nullptr, http->getConn(), http->request, http->al);
647
648 #if USE_AUTH
649 error->auth_user_request =
650 http->getConn() != nullptr && http->getConn()->getAuth() != nullptr ?
651 http->getConn()->getAuth() : http->request->auth_user_request;
652 #endif
653
654 readNextRequest = true;
655 }
656
657 /* ACCESS_ALLOWED continues here ... */
658 xfree(http->uri);
659 http->uri = SBufToCstring(http->request->effectiveRequestUri());
660 http->doCallouts();
661 }
662
663 #if USE_ADAPTATION
664 void
665 ClientHttpRequest::noteAdaptationAclCheckDone(Adaptation::ServiceGroupPointer g)
666 {
667 debugs(93,3, this << " adaptationAclCheckDone called");
668
669 #if ICAP_CLIENT
670 Adaptation::Icap::History::Pointer ih = request->icapHistory();
671 if (ih != nullptr) {
672 if (getConn() != nullptr && getConn()->clientConnection != nullptr) {
673 #if USE_OPENSSL
674 if (getConn()->clientConnection->isOpen()) {
675 ih->ssluser = sslGetUserEmail(fd_table[getConn()->clientConnection->fd].ssl.get());
676 }
677 #endif
678 }
679 ih->log_uri = log_uri;
680 ih->req_sz = req_sz;
681 }
682 #endif
683
684 if (!g) {
685 debugs(85,3, "no adaptation needed");
686 doCallouts();
687 return;
688 }
689
690 startAdaptation(g);
691 }
692
693 #endif
694
695 static void
696 clientRedirectAccessCheckDone(Acl::Answer answer, void *data)
697 {
698 ClientRequestContext *context = (ClientRequestContext *)data;
699 ClientHttpRequest *http = context->http;
700
701 if (answer.allowed())
702 redirectStart(http, clientRedirectDoneWrapper, context);
703 else {
704 Helper::Reply const nilReply(Helper::Error);
705 context->clientRedirectDone(nilReply);
706 }
707 }
708
709 void
710 ClientRequestContext::clientRedirectStart()
711 {
712 debugs(33, 5, "'" << http->uri << "'");
713 http->al->syncNotes(http->request);
714 if (Config.accessList.redirector) {
715 auto acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
716 ACLFilledChecklist::NonBlockingCheck(std::move(acl_checklist), clientRedirectAccessCheckDone, this);
717 } else
718 redirectStart(http, clientRedirectDoneWrapper, this);
719 }
720
721 /**
722 * This methods handles Access checks result of StoreId access list.
723 * Will handle as "ERR" (no change) in a case Access is not allowed.
724 */
725 static void
726 clientStoreIdAccessCheckDone(Acl::Answer answer, void *data)
727 {
728 ClientRequestContext *context = static_cast<ClientRequestContext *>(data);
729 ClientHttpRequest *http = context->http;
730
731 if (answer.allowed())
732 storeIdStart(http, clientStoreIdDoneWrapper, context);
733 else {
734 debugs(85, 3, "access denied expected ERR reply handling: " << answer);
735 Helper::Reply const nilReply(Helper::Error);
736 context->clientStoreIdDone(nilReply);
737 }
738 }
739
740 /**
741 * Start locating an alternative storage ID string (if any) from admin
742 * configured helper program. This is an asynchronous operation terminating in
743 * ClientRequestContext::clientStoreIdDone() when completed.
744 */
745 void
746 ClientRequestContext::clientStoreIdStart()
747 {
748 debugs(33, 5,"'" << http->uri << "'");
749
750 if (Config.accessList.store_id) {
751 auto acl_checklist = clientAclChecklistCreate(Config.accessList.store_id, http);
752 ACLFilledChecklist::NonBlockingCheck(std::move(acl_checklist), clientStoreIdAccessCheckDone, this);
753 } else
754 storeIdStart(http, clientStoreIdDoneWrapper, this);
755 }
756
757 static int
758 clientHierarchical(ClientHttpRequest * http)
759 {
760 HttpRequest *request = http->request;
761 HttpRequestMethod method = request->method;
762
763 // intercepted requests MUST NOT (yet) be sent to peers unless verified
764 if (!request->flags.hostVerified && (request->flags.intercepted || request->flags.interceptTproxy))
765 return 0;
766
767 /*
768 * IMS needs a private key, so we can use the hierarchy for IMS only if our
769 * neighbors support private keys
770 */
771
772 if (request->flags.ims && !neighbors_do_private_keys)
773 return 0;
774
775 /*
776 * This is incorrect: authenticating requests can be sent via a hierarchy
777 * (they can even be cached if the correct headers are set on the reply)
778 */
779 if (request->flags.auth)
780 return 0;
781
782 if (method == Http::METHOD_TRACE)
783 return 1;
784
785 if (method != Http::METHOD_GET)
786 return 0;
787
788 if (request->flags.loopDetected)
789 return 0;
790
791 if (request->url.getScheme() == AnyP::PROTO_HTTP)
792 return method.respMaybeCacheable();
793
794 return 1;
795 }
796
797 static void
798 clientCheckPinning(ClientHttpRequest * http)
799 {
800 HttpRequest *request = http->request;
801 HttpHeader *req_hdr = &request->header;
802 ConnStateData *http_conn = http->getConn();
803
804 // Internal requests may be without a client connection
805 if (!http_conn)
806 return;
807
808 request->flags.connectionAuthDisabled = http_conn->port->connection_auth_disabled;
809 if (!request->flags.connectionAuthDisabled) {
810 if (Comm::IsConnOpen(http_conn->pinning.serverConnection)) {
811 if (http_conn->pinning.auth) {
812 request->flags.connectionAuth = true;
813 request->flags.auth = true;
814 } else {
815 request->flags.connectionProxyAuth = true;
816 }
817 // These should already be linked correctly.
818 assert(request->clientConnectionManager == http_conn);
819 }
820 }
821
822 /* check if connection auth is used, and flag as candidate for pinning
823 * in such case.
824 * Note: we may need to set flags.connectionAuth even if the connection
825 * is already pinned if it was pinned earlier due to proxy auth
826 */
827 if (!request->flags.connectionAuth) {
828 if (req_hdr->has(Http::HdrType::AUTHORIZATION) || req_hdr->has(Http::HdrType::PROXY_AUTHORIZATION)) {
829 HttpHeaderPos pos = HttpHeaderInitPos;
830 HttpHeaderEntry *e;
831 int may_pin = 0;
832 while ((e = req_hdr->getEntry(&pos))) {
833 if (e->id == Http::HdrType::AUTHORIZATION || e->id == Http::HdrType::PROXY_AUTHORIZATION) {
834 const char *value = e->value.rawBuf();
835 if (strncasecmp(value, "NTLM ", 5) == 0
836 ||
837 strncasecmp(value, "Negotiate ", 10) == 0
838 ||
839 strncasecmp(value, "Kerberos ", 9) == 0) {
840 if (e->id == Http::HdrType::AUTHORIZATION) {
841 request->flags.connectionAuth = true;
842 may_pin = 1;
843 } else {
844 request->flags.connectionProxyAuth = true;
845 may_pin = 1;
846 }
847 }
848 }
849 }
850 if (may_pin && !request->pinnedConnection()) {
851 // These should already be linked correctly. Just need the ServerConnection to pinn.
852 assert(request->clientConnectionManager == http_conn);
853 }
854 }
855 }
856 }
857
858 static void
859 clientInterpretRequestHeaders(ClientHttpRequest * http)
860 {
861 HttpRequest *request = http->request;
862 HttpHeader *req_hdr = &request->header;
863 bool no_cache = false;
864
865 request->imslen = -1;
866 request->ims = req_hdr->getTime(Http::HdrType::IF_MODIFIED_SINCE);
867
868 if (request->ims > 0)
869 request->flags.ims = true;
870
871 if (!request->flags.ignoreCc) {
872 if (request->cache_control) {
873 if (request->cache_control->hasNoCache())
874 no_cache=true;
875
876 // RFC 2616: treat Pragma:no-cache as if it was Cache-Control:no-cache when Cache-Control is missing
877 } else if (req_hdr->has(Http::HdrType::PRAGMA))
878 no_cache = req_hdr->hasListMember(Http::HdrType::PRAGMA,"no-cache",',');
879 }
880
881 if (request->method == Http::METHOD_OTHER) {
882 no_cache=true;
883 }
884
885 if (no_cache) {
886 #if USE_HTTP_VIOLATIONS
887
888 if (Config.onoff.reload_into_ims)
889 request->flags.nocacheHack = true;
890 else if (refresh_nocache_hack)
891 request->flags.nocacheHack = true;
892 else
893 #endif
894
895 request->flags.noCache = true;
896 }
897
898 /* ignore range header in non-GETs or non-HEADs */
899 if (request->method == Http::METHOD_GET || request->method == Http::METHOD_HEAD) {
900 // XXX: initialize if we got here without HttpRequest::parseHeader()
901 if (!request->range)
902 request->range = req_hdr->getRange();
903
904 if (request->range) {
905 request->flags.isRanged = true;
906 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
907 /* XXX: This is suboptimal. We should give the stream the range set,
908 * and thereby let the top of the stream set the offset when the
909 * size becomes known. As it is, we will end up requesting from 0
910 * for every -X range specification.
911 * RBC - this may be somewhat wrong. We should probably set the range
912 * iter up at this point.
913 */
914 node->readBuffer.offset = request->range->lowestOffset(0);
915 }
916 }
917
918 /* Only HEAD and GET requests permit a Range or Request-Range header.
919 * If these headers appear on any other type of request, delete them now.
920 */
921 else {
922 req_hdr->delById(Http::HdrType::RANGE);
923 req_hdr->delById(Http::HdrType::REQUEST_RANGE);
924 request->ignoreRange("neither HEAD nor GET");
925 }
926
927 if (req_hdr->has(Http::HdrType::AUTHORIZATION))
928 request->flags.auth = true;
929
930 clientCheckPinning(http);
931
932 if (!request->url.userInfo().isEmpty())
933 request->flags.auth = true;
934
935 if (req_hdr->has(Http::HdrType::VIA)) {
936 String s = req_hdr->getList(Http::HdrType::VIA);
937 /*
938 * ThisCache cannot be a member of Via header, "1.1 ThisCache" can.
939 * Note ThisCache2 has a space prepended to the hostname so we don't
940 * accidentally match super-domains.
941 */
942
943 if (strListIsSubstr(&s, ThisCache2, ',')) {
944 request->flags.loopDetected = true;
945 }
946
947 #if USE_FORW_VIA_DB
948 fvdbCountVia(StringToSBuf(s));
949
950 #endif
951
952 s.clean();
953 }
954
955 // headers only relevant to reverse-proxy
956 if (request->flags.accelerated) {
957 // check for a cdn-info member with a cdn-id matching surrogate_id
958 // XXX: HttpHeader::hasListMember() does not handle OWS around ";" yet
959 if (req_hdr->hasListMember(Http::HdrType::CDN_LOOP, Config.Accel.surrogate_id, ','))
960 request->flags.loopDetected = true;
961 }
962
963 if (request->flags.loopDetected) {
964 debugObj(33, DBG_IMPORTANT, "WARNING: Forwarding loop detected for:\n",
965 request, (ObjPackMethod) & httpRequestPack);
966 }
967
968 #if USE_FORW_VIA_DB
969
970 if (req_hdr->has(Http::HdrType::X_FORWARDED_FOR)) {
971 String s = req_hdr->getList(Http::HdrType::X_FORWARDED_FOR);
972 fvdbCountForwarded(StringToSBuf(s));
973 s.clean();
974 }
975
976 #endif
977
978 if (http->request->maybeCacheable())
979 request->flags.cachable.support();
980 else
981 request->flags.cachable.veto();
982
983 if (clientHierarchical(http))
984 request->flags.hierarchical = true;
985
986 debugs(85, 5, "clientInterpretRequestHeaders: REQ_NOCACHE = " <<
987 (request->flags.noCache ? "SET" : "NOT SET"));
988 debugs(85, 5, "clientInterpretRequestHeaders: REQ_CACHABLE = " <<
989 (request->flags.cachable ? "SET" : "NOT SET"));
990 debugs(85, 5, "clientInterpretRequestHeaders: REQ_HIERARCHICAL = " <<
991 (request->flags.hierarchical ? "SET" : "NOT SET"));
992
993 }
994
995 void
996 clientRedirectDoneWrapper(void *data, const Helper::Reply &result)
997 {
998 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
999 calloutContext->clientRedirectDone(result);
1000 }
1001
1002 void
1003 clientStoreIdDoneWrapper(void *data, const Helper::Reply &result)
1004 {
1005 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
1006 calloutContext->clientStoreIdDone(result);
1007 }
1008
1009 void
1010 ClientRequestContext::clientRedirectDone(const Helper::Reply &reply)
1011 {
1012 HttpRequest *old_request = http->request;
1013 debugs(85, 5, "'" << http->uri << "' result=" << reply);
1014 assert(redirect_state == REDIRECT_PENDING);
1015 redirect_state = REDIRECT_DONE;
1016
1017 // Put helper response Notes into the transaction state record (ALE) eventually
1018 // do it early to ensure that no matter what the outcome the notes are present.
1019 if (http->al)
1020 http->al->syncNotes(old_request);
1021
1022 UpdateRequestNotes(http->getConn(), *old_request, reply.notes);
1023
1024 switch (reply.result) {
1025 case Helper::TimedOut:
1026 if (Config.onUrlRewriteTimeout.action != toutActBypass) {
1027 static const auto d = MakeNamedErrorDetail("REDIRECTOR_TIMEDOUT");
1028 http->calloutsError(ERR_GATEWAY_FAILURE, d);
1029 debugs(85, DBG_IMPORTANT, "ERROR: URL rewrite helper: Timedout");
1030 }
1031 break;
1032
1033 case Helper::Unknown:
1034 case Helper::TT:
1035 // Handler in redirect.cc should have already mapped Unknown
1036 // IF it contained valid entry for the old URL-rewrite helper protocol
1037 debugs(85, DBG_IMPORTANT, "ERROR: URL rewrite helper returned invalid result code. Wrong helper? " << reply);
1038 break;
1039
1040 case Helper::BrokenHelper:
1041 debugs(85, DBG_IMPORTANT, "ERROR: URL rewrite helper: " << reply);
1042 break;
1043
1044 case Helper::Error:
1045 // no change to be done.
1046 break;
1047
1048 case Helper::Okay: {
1049 // #1: redirect with a specific status code OK status=NNN url="..."
1050 // #2: redirect with a default status code OK url="..."
1051 // #3: re-write the URL OK rewrite-url="..."
1052
1053 const char *statusNote = reply.notes.findFirst("status");
1054 const char *urlNote = reply.notes.findFirst("url");
1055
1056 if (urlNote != nullptr) {
1057 // HTTP protocol redirect to be done.
1058
1059 // TODO: change default redirect status for appropriate requests
1060 // Squid defaults to 302 status for now for better compatibility with old clients.
1061 // HTTP/1.0 client should get 302 (Http::scFound)
1062 // HTTP/1.1 client contacting reverse-proxy should get 307 (Http::scTemporaryRedirect)
1063 // HTTP/1.1 client being diverted by forward-proxy should get 303 (Http::scSeeOther)
1064 Http::StatusCode status = Http::scFound;
1065 if (statusNote != nullptr) {
1066 const char * result = statusNote;
1067 status = static_cast<Http::StatusCode>(atoi(result));
1068 }
1069
1070 if (status == Http::scMovedPermanently
1071 || status == Http::scFound
1072 || status == Http::scSeeOther
1073 || status == Http::scPermanentRedirect
1074 || status == Http::scTemporaryRedirect) {
1075 http->redirect.status = status;
1076 http->redirect.location = xstrdup(urlNote);
1077 // TODO: validate the URL produced here is RFC 2616 compliant absolute URI
1078 } else {
1079 debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid " << status << " redirect Location: " << urlNote);
1080 }
1081 } else {
1082 // URL-rewrite wanted. Ew.
1083 urlNote = reply.notes.findFirst("rewrite-url");
1084
1085 // prevent broken helpers causing too much damage. If old URL == new URL skip the re-write.
1086 if (urlNote != nullptr && strcmp(urlNote, http->uri)) {
1087 AnyP::Uri tmpUrl;
1088 if (tmpUrl.parse(old_request->method, SBuf(urlNote))) {
1089 HttpRequest *new_request = old_request->clone();
1090 new_request->url = tmpUrl;
1091 debugs(61, 2, "URL-rewriter diverts URL from " << old_request->effectiveRequestUri() << " to " << new_request->effectiveRequestUri());
1092
1093 // unlink bodypipe from the old request. Not needed there any longer.
1094 if (old_request->body_pipe != nullptr) {
1095 old_request->body_pipe = nullptr;
1096 debugs(61,2, "URL-rewriter diverts body_pipe " << new_request->body_pipe <<
1097 " from request " << old_request << " to " << new_request);
1098 }
1099
1100 http->resetRequestXXX(new_request, true);
1101 old_request = nullptr;
1102 } else {
1103 debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid request: " <<
1104 old_request->method << " " << urlNote << " " << old_request->http_ver);
1105 }
1106 }
1107 }
1108 }
1109 break;
1110 }
1111
1112 /* XXX PIPELINE: This is inaccurate during pipelining */
1113
1114 if (http->getConn() != nullptr && Comm::IsConnOpen(http->getConn()->clientConnection))
1115 fd_note(http->getConn()->clientConnection->fd, http->uri);
1116
1117 assert(http->uri);
1118
1119 http->doCallouts();
1120 }
1121
1122 /**
1123 * This method handles the different replies from StoreID helper.
1124 */
1125 void
1126 ClientRequestContext::clientStoreIdDone(const Helper::Reply &reply)
1127 {
1128 HttpRequest *old_request = http->request;
1129 debugs(85, 5, "'" << http->uri << "' result=" << reply);
1130 assert(store_id_state == REDIRECT_PENDING);
1131 store_id_state = REDIRECT_DONE;
1132
1133 // Put helper response Notes into the transaction state record (ALE) eventually
1134 // do it early to ensure that no matter what the outcome the notes are present.
1135 if (http->al)
1136 http->al->syncNotes(old_request);
1137
1138 UpdateRequestNotes(http->getConn(), *old_request, reply.notes);
1139
1140 switch (reply.result) {
1141 case Helper::Unknown:
1142 case Helper::TT:
1143 // Handler in redirect.cc should have already mapped Unknown
1144 // IF it contained valid entry for the old helper protocol
1145 debugs(85, DBG_IMPORTANT, "ERROR: storeID helper returned invalid result code. Wrong helper? " << reply);
1146 break;
1147
1148 case Helper::TimedOut:
1149 // Timeouts for storeID are not implemented
1150 case Helper::BrokenHelper:
1151 debugs(85, DBG_IMPORTANT, "ERROR: storeID helper: " << reply);
1152 break;
1153
1154 case Helper::Error:
1155 // no change to be done.
1156 break;
1157
1158 case Helper::Okay: {
1159 const char *urlNote = reply.notes.findFirst("store-id");
1160
1161 // prevent broken helpers causing too much damage. If old URL == new URL skip the re-write.
1162 if (urlNote != nullptr && strcmp(urlNote, http->uri) ) {
1163 // Debug section required for some very specific cases.
1164 debugs(85, 9, "Setting storeID with: " << urlNote );
1165 http->request->store_id = urlNote;
1166 http->store_id = urlNote;
1167 }
1168 }
1169 break;
1170 }
1171
1172 http->doCallouts();
1173 }
1174
1175 /// applies "cache allow/deny" rules, asynchronously if needed
1176 void
1177 ClientRequestContext::checkNoCache()
1178 {
1179 if (Config.accessList.noCache) {
1180 auto acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
1181 ACLFilledChecklist::NonBlockingCheck(std::move(acl_checklist), checkNoCacheDoneWrapper, this);
1182 } else {
1183 /* unless otherwise specified, we try to cache. */
1184 checkNoCacheDone(ACCESS_ALLOWED);
1185 }
1186 }
1187
1188 static void
1189 checkNoCacheDoneWrapper(Acl::Answer answer, void *data)
1190 {
1191 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
1192 calloutContext->checkNoCacheDone(answer);
1193 }
1194
1195 void
1196 ClientRequestContext::checkNoCacheDone(const Acl::Answer &answer)
1197 {
1198 if (answer.denied()) {
1199 http->request->flags.disableCacheUse("a cache deny rule matched");
1200 }
1201 http->doCallouts();
1202 }
1203
1204 #if USE_OPENSSL
1205 bool
1206 ClientRequestContext::sslBumpAccessCheck()
1207 {
1208 if (!http->getConn()) {
1209 http->al->ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
1210 return false;
1211 }
1212
1213 const Ssl::BumpMode bumpMode = http->getConn()->sslBumpMode;
1214 if (http->request->flags.forceTunnel) {
1215 debugs(85, 5, "not needed; already decided to tunnel " << http->getConn());
1216 if (bumpMode != Ssl::bumpEnd)
1217 http->al->ssl.bumpMode = bumpMode; // inherited from bumped connection
1218 return false;
1219 }
1220
1221 // If SSL connection tunneling or bumping decision has been made, obey it.
1222 if (bumpMode != Ssl::bumpEnd) {
1223 debugs(85, 5, "SslBump already decided (" << bumpMode <<
1224 "), " << "ignoring ssl_bump for " << http->getConn());
1225
1226 // We need the following "if" for transparently bumped TLS connection,
1227 // because in this case we are running ssl_bump access list before
1228 // the doCallouts runs. It can be removed after the bug #4340 fixed.
1229 // We do not want to proceed to bumping steps:
1230 // - if the TLS connection with the client is already established
1231 // because we are accepting normal HTTP requests on TLS port,
1232 // or because of the client-first bumping mode
1233 // - When the bumping is already started
1234 if (!http->getConn()->switchedToHttps() &&
1235 !http->getConn()->serverBump())
1236 http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed and not already bumped
1237 http->al->ssl.bumpMode = bumpMode; // inherited from bumped connection
1238 return false;
1239 }
1240
1241 // If we have not decided yet, decide whether to bump now.
1242
1243 // Bumping here can only start with a CONNECT request on a bumping port
1244 // (bumping of intercepted SSL conns is decided before we get 1st request).
1245 // We also do not bump redirected CONNECT requests.
1246 if (http->request->method != Http::METHOD_CONNECT || http->redirect.status ||
1247 !Config.accessList.ssl_bump ||
1248 !http->getConn()->port->flags.tunnelSslBumping) {
1249 http->al->ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
1250 debugs(85, 5, "cannot SslBump this request");
1251 return false;
1252 }
1253
1254 // Do not bump during authentication: clients would not proxy-authenticate
1255 // if we delay a 407 response and respond with 200 OK to CONNECT.
1256 if (error && error->httpStatus == Http::scProxyAuthenticationRequired) {
1257 http->al->ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
1258 debugs(85, 5, "no SslBump during proxy authentication");
1259 return false;
1260 }
1261
1262 if (error) {
1263 debugs(85, 5, "SslBump applies. Force bump action on error " << errorTypeName(error->type));
1264 http->sslBumpNeed(Ssl::bumpBump);
1265 http->al->ssl.bumpMode = Ssl::bumpBump;
1266 return false;
1267 }
1268
1269 debugs(85, 5, "SslBump possible, checking ACL");
1270
1271 auto aclChecklist = clientAclChecklistCreate(Config.accessList.ssl_bump, http);
1272 ACLFilledChecklist::NonBlockingCheck(std::move(aclChecklist), sslBumpAccessCheckDoneWrapper, this);
1273 return true;
1274 }
1275
1276 /**
1277 * A wrapper function to use the ClientRequestContext::sslBumpAccessCheckDone method
1278 * as ACLFilledChecklist callback
1279 */
1280 static void
1281 sslBumpAccessCheckDoneWrapper(Acl::Answer answer, void *data)
1282 {
1283 ClientRequestContext *calloutContext = static_cast<ClientRequestContext *>(data);
1284 calloutContext->sslBumpAccessCheckDone(answer);
1285 }
1286
1287 void
1288 ClientRequestContext::sslBumpAccessCheckDone(const Acl::Answer &answer)
1289 {
1290 const Ssl::BumpMode bumpMode = answer.allowed() ?
1291 static_cast<Ssl::BumpMode>(answer.kind) : Ssl::bumpSplice;
1292 http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed
1293 http->al->ssl.bumpMode = bumpMode; // for logging
1294
1295 if (bumpMode == Ssl::bumpTerminate) {
1296 const Comm::ConnectionPointer clientConn = http->getConn() ? http->getConn()->clientConnection : nullptr;
1297 if (Comm::IsConnOpen(clientConn)) {
1298 debugs(85, 3, "closing after Ssl::bumpTerminate ");
1299 clientConn->close();
1300 }
1301 return;
1302 }
1303
1304 http->doCallouts();
1305 }
1306 #endif
1307
1308 /*
1309 * Identify requests that do not go through the store and client side stream
1310 * and forward them to the appropriate location. All other requests, request
1311 * them.
1312 */
1313 void
1314 ClientHttpRequest::processRequest()
1315 {
1316 debugs(85, 4, request->method << ' ' << uri);
1317
1318 const bool untouchedConnect = request->method == Http::METHOD_CONNECT && !redirect.status;
1319
1320 #if USE_OPENSSL
1321 if (untouchedConnect && sslBumpNeeded()) {
1322 assert(!request->flags.forceTunnel);
1323 sslBumpStart();
1324 return;
1325 }
1326 #endif
1327
1328 if (untouchedConnect || request->flags.forceTunnel) {
1329 getConn()->stopReading(); // tunnels read for themselves
1330 tunnelStart(this);
1331 return;
1332 }
1333
1334 httpStart();
1335 }
1336
1337 void
1338 ClientHttpRequest::httpStart()
1339 {
1340 // XXX: Re-initializes rather than updates. Should not be needed at all.
1341 updateLoggingTags(LOG_TAG_NONE);
1342 debugs(85, 4, loggingTags().c_str() << " for '" << uri << "'");
1343
1344 /* no one should have touched this */
1345 assert(out.offset == 0);
1346 /* Use the Stream Luke */
1347 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
1348 clientStreamRead(node, this, node->readBuffer);
1349 }
1350
1351 #if USE_OPENSSL
1352
1353 void
1354 ClientHttpRequest::sslBumpNeed(Ssl::BumpMode mode)
1355 {
1356 debugs(83, 3, "sslBump required: "<< Ssl::bumpMode(mode));
1357 sslBumpNeed_ = mode;
1358 }
1359
1360 // called when comm_write has completed
1361 static void
1362 SslBumpEstablish(const Comm::ConnectionPointer &, char *, size_t, Comm::Flag errflag, int, void *data)
1363 {
1364 ClientHttpRequest *r = static_cast<ClientHttpRequest*>(data);
1365 debugs(85, 5, "responded to CONNECT: " << r << " ? " << errflag);
1366 r->sslBumpEstablish(errflag);
1367 }
1368
1369 void
1370 ClientHttpRequest::sslBumpEstablish(Comm::Flag errflag)
1371 {
1372 // Bail out quickly on Comm::ERR_CLOSING - close handlers will tidy up
1373 if (errflag == Comm::ERR_CLOSING)
1374 return;
1375
1376 if (errflag) {
1377 debugs(85, 3, "CONNECT response failure in SslBump: " << errflag);
1378 getConn()->clientConnection->close();
1379 return;
1380 }
1381
1382 #if USE_AUTH
1383 // Preserve authentication info for the ssl-bumped request
1384 if (request->auth_user_request != nullptr)
1385 getConn()->setAuth(request->auth_user_request, "SSL-bumped CONNECT");
1386 #endif
1387
1388 assert(sslBumpNeeded());
1389 getConn()->switchToHttps(this, sslBumpNeed_);
1390 }
1391
1392 void
1393 ClientHttpRequest::sslBumpStart()
1394 {
1395 debugs(85, 5, "Confirming " << Ssl::bumpMode(sslBumpNeed_) <<
1396 "-bumped CONNECT tunnel on FD " << getConn()->clientConnection);
1397 getConn()->sslBumpMode = sslBumpNeed_;
1398
1399 AsyncCall::Pointer bumpCall = commCbCall(85, 5, "ClientSocketContext::sslBumpEstablish",
1400 CommIoCbPtrFun(&SslBumpEstablish, this));
1401
1402 if (request->flags.interceptTproxy || request->flags.intercepted) {
1403 CommIoCbParams &params = GetCommParams<CommIoCbParams>(bumpCall);
1404 params.flag = Comm::OK;
1405 params.conn = getConn()->clientConnection;
1406 ScheduleCallHere(bumpCall);
1407 return;
1408 }
1409
1410 al->reply = HttpReply::MakeConnectionEstablished();
1411
1412 const auto mb = al->reply->pack();
1413 // send an HTTP 200 response to kick client SSL negotiation
1414 // TODO: Unify with tunnel.cc and add a Server(?) header
1415 Comm::Write(getConn()->clientConnection, mb, bumpCall);
1416 delete mb;
1417 }
1418
1419 #endif
1420
1421 void
1422 ClientHttpRequest::updateError(const Error &error)
1423 {
1424 if (request)
1425 request->error.update(error);
1426 else
1427 al->updateError(error);
1428 }
1429
1430 bool
1431 ClientHttpRequest::gotEnough() const
1432 {
1433 // TODO: See also (and unify with) clientReplyContext::storeNotOKTransferDone()
1434 int64_t contentLength =
1435 memObject()->baseReply().bodySize(request->method);
1436 assert(contentLength >= 0);
1437
1438 if (out.offset < contentLength)
1439 return false;
1440
1441 return true;
1442 }
1443
1444 void
1445 ClientHttpRequest::storeEntry(StoreEntry *newEntry)
1446 {
1447 entry_ = newEntry;
1448 }
1449
1450 void
1451 ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
1452 {
1453 if (loggingEntry_)
1454 loggingEntry_->unlock("ClientHttpRequest::loggingEntry");
1455
1456 loggingEntry_ = newEntry;
1457
1458 if (loggingEntry_)
1459 loggingEntry_->lock("ClientHttpRequest::loggingEntry");
1460 }
1461
1462 void
1463 ClientHttpRequest::initRequest(HttpRequest *aRequest)
1464 {
1465 assignRequest(aRequest);
1466 if (const auto csd = getConn()) {
1467 if (!csd->notes()->empty())
1468 request->notes()->appendNewOnly(csd->notes().getRaw());
1469 }
1470 // al is created in the constructor
1471 assert(al);
1472 if (!al->request) {
1473 al->request = request;
1474 HTTPMSGLOCK(al->request);
1475 al->syncNotes(request);
1476 }
1477 }
1478
1479 void
1480 ClientHttpRequest::resetRequest(HttpRequest *newRequest)
1481 {
1482 const auto uriChanged = request->effectiveRequestUri() != newRequest->effectiveRequestUri();
1483 resetRequestXXX(newRequest, uriChanged);
1484 }
1485
1486 void
1487 ClientHttpRequest::resetRequestXXX(HttpRequest *newRequest, const bool uriChanged)
1488 {
1489 assert(request != newRequest);
1490 clearRequest();
1491 assignRequest(newRequest);
1492 xfree(uri);
1493 uri = SBufToCstring(request->effectiveRequestUri());
1494
1495 if (uriChanged) {
1496 request->flags.redirected = true;
1497 checkForInternalAccess();
1498 }
1499 }
1500
1501 void
1502 ClientHttpRequest::checkForInternalAccess()
1503 {
1504 if (!internalCheck(request->url.path()))
1505 return;
1506
1507 if (request->url.port() == getMyPort() && internalHostnameIs(SBuf(request->url.host()))) {
1508 debugs(33, 3, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true));
1509 request->flags.internal = true;
1510 } else if (Config.onoff.global_internal_static && internalStaticCheck(request->url.path())) {
1511 debugs(33, 3, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (global_internal_static on)");
1512 request->url.setScheme(AnyP::PROTO_HTTP, "http");
1513 request->url.host(internalHostname());
1514 request->url.port(getMyPort());
1515 request->flags.internal = true;
1516 setLogUriToRequestUri();
1517 } else {
1518 debugs(33, 3, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (not this proxy)");
1519 }
1520
1521 if (ForSomeCacheManager(request->url.path()))
1522 request->flags.disableCacheUse("cache manager URL");
1523 }
1524
1525 void
1526 ClientHttpRequest::assignRequest(HttpRequest *newRequest)
1527 {
1528 assert(newRequest);
1529 assert(!request);
1530 const_cast<HttpRequest *&>(request) = newRequest;
1531 HTTPMSGLOCK(request);
1532 setLogUriToRequestUri();
1533 }
1534
1535 void
1536 ClientHttpRequest::clearRequest()
1537 {
1538 HttpRequest *oldRequest = request;
1539 HTTPMSGUNLOCK(oldRequest);
1540 const_cast<HttpRequest *&>(request) = nullptr;
1541 absorbLogUri(nullptr);
1542 }
1543
1544 /*
1545 * doCallouts() - This function controls the order of "callout"
1546 * executions, including non-blocking access control checks, the
1547 * redirector, and ICAP. Previously, these callouts were chained
1548 * together such that "clientAccessCheckDone()" would call
1549 * "clientRedirectStart()" and so on.
1550 *
1551 * The ClientRequestContext (aka calloutContext) class holds certain
1552 * state data for the callout/callback operations. Previously
1553 * ClientHttpRequest would sort of hand off control to ClientRequestContext
1554 * for a short time. ClientRequestContext would then delete itself
1555 * and pass control back to ClientHttpRequest when all callouts
1556 * were finished.
1557 *
1558 * This caused some problems for ICAP because we want to make the
1559 * ICAP callout after checking ACLs, but before checking the no_cache
1560 * list. We can't stuff the ICAP state into the ClientRequestContext
1561 * class because we still need the ICAP state after ClientRequestContext
1562 * goes away.
1563 *
1564 * Note that ClientRequestContext is created before the first call
1565 * to doCallouts().
1566 *
1567 * Note that we set the _done flags here before actually starting
1568 * the callout. This is strictly for convenience.
1569 */
1570
1571 void
1572 ClientHttpRequest::doCallouts()
1573 {
1574 assert(calloutContext);
1575
1576 if (!calloutContext->error) {
1577 // CVE-2009-0801: verify the Host: header is consistent with other known details.
1578 if (!calloutContext->host_header_verify_done) {
1579 debugs(83, 3, "Doing calloutContext->hostHeaderVerify()");
1580 calloutContext->host_header_verify_done = true;
1581 calloutContext->hostHeaderVerify();
1582 return;
1583 }
1584
1585 if (!calloutContext->http_access_done) {
1586 debugs(83, 3, "Doing calloutContext->clientAccessCheck()");
1587 calloutContext->http_access_done = true;
1588 calloutContext->clientAccessCheck();
1589 return;
1590 }
1591
1592 #if USE_ADAPTATION
1593 if (!calloutContext->adaptation_acl_check_done) {
1594 calloutContext->adaptation_acl_check_done = true;
1595 if (Adaptation::AccessCheck::Start(
1596 Adaptation::methodReqmod, Adaptation::pointPreCache,
1597 request, nullptr, calloutContext->http->al, this))
1598 return; // will call callback
1599 }
1600 #endif
1601
1602 if (!calloutContext->redirect_done) {
1603 calloutContext->redirect_done = true;
1604
1605 if (Config.Program.redirect) {
1606 debugs(83, 3, "Doing calloutContext->clientRedirectStart()");
1607 calloutContext->redirect_state = REDIRECT_PENDING;
1608 calloutContext->clientRedirectStart();
1609 return;
1610 }
1611 }
1612
1613 if (!calloutContext->adapted_http_access_done) {
1614 debugs(83, 3, "Doing calloutContext->clientAccessCheck2()");
1615 calloutContext->adapted_http_access_done = true;
1616 calloutContext->clientAccessCheck2();
1617 return;
1618 }
1619
1620 if (!calloutContext->store_id_done) {
1621 calloutContext->store_id_done = true;
1622
1623 if (Config.Program.store_id) {
1624 debugs(83, 3,"Doing calloutContext->clientStoreIdStart()");
1625 calloutContext->store_id_state = REDIRECT_PENDING;
1626 calloutContext->clientStoreIdStart();
1627 return;
1628 }
1629 }
1630
1631 if (!calloutContext->interpreted_req_hdrs) {
1632 debugs(83, 3, "Doing clientInterpretRequestHeaders()");
1633 calloutContext->interpreted_req_hdrs = 1;
1634 clientInterpretRequestHeaders(this);
1635 }
1636
1637 if (!calloutContext->no_cache_done) {
1638 calloutContext->no_cache_done = true;
1639
1640 if (Config.accessList.noCache && request->flags.cachable) {
1641 debugs(83, 3, "Doing calloutContext->checkNoCache()");
1642 calloutContext->checkNoCache();
1643 return;
1644 }
1645 }
1646 } // if !calloutContext->error
1647
1648 // Set appropriate MARKs and CONNMARKs if needed.
1649 if (getConn() && Comm::IsConnOpen(getConn()->clientConnection)) {
1650 ACLFilledChecklist ch(nullptr, request);
1651 ch.al = calloutContext->http->al;
1652 ch.src_addr = request->client_addr;
1653 ch.my_addr = request->my_addr;
1654 ch.syncAle(request, log_uri);
1655
1656 if (!calloutContext->toClientMarkingDone) {
1657 calloutContext->toClientMarkingDone = true;
1658 tos_t tos = aclMapTOS(Ip::Qos::TheConfig.tosToClient, &ch);
1659 if (tos)
1660 Ip::Qos::setSockTos(getConn()->clientConnection, tos);
1661
1662 const auto packetMark = aclFindNfMarkConfig(Ip::Qos::TheConfig.nfmarkToClient, &ch);
1663 if (!packetMark.isEmpty())
1664 Ip::Qos::setSockNfmark(getConn()->clientConnection, packetMark.mark);
1665
1666 const auto connmark = aclFindNfMarkConfig(Ip::Qos::TheConfig.nfConnmarkToClient, &ch);
1667 if (!connmark.isEmpty())
1668 Ip::Qos::setNfConnmark(getConn()->clientConnection, Ip::Qos::dirAccepted, connmark);
1669 }
1670 }
1671
1672 #if USE_OPENSSL
1673 // Even with calloutContext->error, we call sslBumpAccessCheck() to decide
1674 // whether SslBump applies to this transaction. If it applies, we will
1675 // attempt to bump the client to serve the error.
1676 if (!calloutContext->sslBumpCheckDone) {
1677 calloutContext->sslBumpCheckDone = true;
1678 if (calloutContext->sslBumpAccessCheck())
1679 return;
1680 /* else no ssl bump required*/
1681 }
1682 #endif
1683
1684 if (calloutContext->error) {
1685 // XXX: prformance regression. c_str() reallocates
1686 SBuf storeUriBuf(request->storeId());
1687 const char *storeUri = storeUriBuf.c_str();
1688 StoreEntry *e = storeCreateEntry(storeUri, storeUri, request->flags, request->method);
1689 #if USE_OPENSSL
1690 if (sslBumpNeeded()) {
1691 // We have to serve an error, so bump the client first.
1692 sslBumpNeed(Ssl::bumpClientFirst);
1693 // set final error but delay sending until we bump
1694 Ssl::ServerBump *srvBump = new Ssl::ServerBump(this, e, Ssl::bumpClientFirst);
1695 errorAppendEntry(e, calloutContext->error);
1696 calloutContext->error = nullptr;
1697 getConn()->setServerBump(srvBump);
1698 e->unlock("ClientHttpRequest::doCallouts+sslBumpNeeded");
1699 } else
1700 #endif
1701 {
1702 // send the error to the client now
1703 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1704 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1705 assert (repContext);
1706 repContext->setReplyToStoreEntry(e, "immediate SslBump error");
1707 errorAppendEntry(e, calloutContext->error);
1708 calloutContext->error = nullptr;
1709 if (calloutContext->readNextRequest && getConn())
1710 getConn()->flags.readMore = true; // resume any pipeline reads.
1711 node = (clientStreamNode *)client_stream.tail->data;
1712 clientStreamRead(node, this, node->readBuffer);
1713 e->unlock("ClientHttpRequest::doCallouts-sslBumpNeeded");
1714 return;
1715 }
1716 }
1717
1718 delete calloutContext;
1719 calloutContext = nullptr;
1720
1721 debugs(83, 3, "calling processRequest()");
1722 processRequest();
1723
1724 #if ICAP_CLIENT
1725 Adaptation::Icap::History::Pointer ih = request->icapHistory();
1726 if (ih != nullptr)
1727 ih->logType = loggingTags();
1728 #endif
1729 }
1730
1731 void
1732 ClientHttpRequest::setLogUriToRequestUri()
1733 {
1734 assert(request);
1735 const auto canonicalUri = request->canonicalCleanUrl();
1736 absorbLogUri(xstrndup(canonicalUri, MAX_URL));
1737 }
1738
1739 void
1740 ClientHttpRequest::setLogUriToRawUri(const char *rawUri, const HttpRequestMethod &method)
1741 {
1742 assert(rawUri);
1743 // Should(!request);
1744
1745 // TODO: SBuf() performance regression, fix by converting rawUri to SBuf
1746 char *canonicalUri = urlCanonicalCleanWithoutRequest(SBuf(rawUri), method, AnyP::UriScheme());
1747
1748 absorbLogUri(AnyP::Uri::cleanup(canonicalUri));
1749
1750 char *cleanedRawUri = AnyP::Uri::cleanup(rawUri);
1751 al->setVirginUrlForMissingRequest(SBuf(cleanedRawUri));
1752 xfree(cleanedRawUri);
1753 }
1754
1755 void
1756 ClientHttpRequest::absorbLogUri(char *aUri)
1757 {
1758 xfree(log_uri);
1759 const_cast<char *&>(log_uri) = aUri;
1760 }
1761
1762 void
1763 ClientHttpRequest::setErrorUri(const char *aUri)
1764 {
1765 assert(!uri);
1766 assert(aUri);
1767 // Should(!request);
1768
1769 uri = xstrdup(aUri);
1770 // TODO: SBuf() performance regression, fix by converting setErrorUri() parameter to SBuf
1771 const SBuf errorUri(aUri);
1772 const auto canonicalUri = urlCanonicalCleanWithoutRequest(errorUri, HttpRequestMethod(), AnyP::UriScheme());
1773 absorbLogUri(xstrndup(canonicalUri, MAX_URL));
1774
1775 al->setVirginUrlForMissingRequest(errorUri);
1776 }
1777
1778 // XXX: This should not be a _request_ method. Move range_iter elsewhere.
1779 int64_t
1780 ClientHttpRequest::prepPartialResponseGeneration()
1781 {
1782 assert(request);
1783 assert(request->range);
1784
1785 range_iter.pos = request->range->begin();
1786 range_iter.end = request->range->end();
1787 range_iter.debt_size = 0;
1788 const auto multipart = request->range->specs.size() > 1;
1789 if (multipart)
1790 range_iter.boundary = rangeBoundaryStr();
1791 range_iter.valid = true; // TODO: Remove.
1792 range_iter.updateSpec(); // TODO: Refactor to initialize rather than update.
1793
1794 assert(range_iter.pos != range_iter.end);
1795 const auto &firstRange = *range_iter.pos;
1796 assert(firstRange);
1797 out.offset = firstRange->offset;
1798
1799 return multipart ? mRangeCLen() : firstRange->length;
1800 }
1801
1802 #if USE_ADAPTATION
1803 /// Initiate an asynchronous adaptation transaction which will call us back.
1804 void
1805 ClientHttpRequest::startAdaptation(const Adaptation::ServiceGroupPointer &g)
1806 {
1807 debugs(85, 3, "adaptation needed for " << this);
1808 assert(!virginHeadSource);
1809 assert(!adaptedBodySource);
1810 virginHeadSource = initiateAdaptation(
1811 new Adaptation::Iterator(request, nullptr, al, g));
1812
1813 // we could try to guess whether we can bypass this adaptation
1814 // initiation failure, but it should not really happen
1815 Must(initiated(virginHeadSource));
1816 }
1817
1818 void
1819 ClientHttpRequest::noteAdaptationAnswer(const Adaptation::Answer &answer)
1820 {
1821 clearAdaptation(virginHeadSource);
1822 assert(!adaptedBodySource);
1823
1824 switch (answer.kind) {
1825 case Adaptation::Answer::akForward:
1826 handleAdaptedHeader(const_cast<Http::Message*>(answer.message.getRaw()));
1827 break;
1828
1829 case Adaptation::Answer::akBlock:
1830 handleAdaptationBlock(answer);
1831 break;
1832
1833 case Adaptation::Answer::akError: {
1834 static const auto d = MakeNamedErrorDetail("CLT_REQMOD_ABORT");
1835 handleAdaptationFailure(d, !answer.final);
1836 break;
1837 }
1838 }
1839 }
1840
1841 void
1842 ClientHttpRequest::handleAdaptedHeader(Http::Message *msg)
1843 {
1844 assert(msg);
1845
1846 if (HttpRequest *new_req = dynamic_cast<HttpRequest*>(msg)) {
1847 resetRequest(new_req);
1848 assert(request->method.id());
1849 } else if (HttpReply *new_rep = dynamic_cast<HttpReply*>(msg)) {
1850 debugs(85,3, "REQMOD reply is HTTP reply");
1851
1852 // subscribe to receive reply body
1853 if (new_rep->body_pipe != nullptr) {
1854 adaptedBodySource = new_rep->body_pipe;
1855 int consumer_ok = adaptedBodySource->setConsumerIfNotLate(this);
1856 assert(consumer_ok);
1857 }
1858
1859 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1860 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1861 assert(repContext);
1862 repContext->createStoreEntry(request->method, request->flags);
1863
1864 request_satisfaction_mode = true;
1865 request_satisfaction_offset = 0;
1866 storeEntry()->replaceHttpReply(new_rep);
1867 storeEntry()->timestampsSet();
1868
1869 al->reply = new_rep;
1870
1871 if (request->method == Http::METHOD_CONNECT) {
1872 // Squid has not established a tunnel. We forward the response
1873 // generated by the adaptation service to the HTTP client, but the
1874 // service becomes responsible for the client's reaction to this
1875 // response. A typical use case is a service that responds with a
1876 // "block page" implemented as an HTTP error response.
1877 //
1878 // Since we are not going to tunnel, remove clientProcessRequest()'s
1879 // ban on reading post-CONNECT HTTP requests.
1880 if (const auto c = getConn())
1881 c->flags.readMore = true;
1882 }
1883
1884 if (!adaptedBodySource) // no body
1885 storeEntry()->complete();
1886 clientGetMoreData(node, this);
1887 }
1888
1889 // we are done with getting headers (but may be receiving body)
1890 clearAdaptation(virginHeadSource);
1891
1892 if (!request_satisfaction_mode)
1893 doCallouts();
1894 }
1895
1896 void
1897 ClientHttpRequest::handleAdaptationBlock(const Adaptation::Answer &answer)
1898 {
1899 static const auto d = MakeNamedErrorDetail("REQMOD_BLOCK");
1900 request->detailError(ERR_ACCESS_DENIED, d);
1901 assert(calloutContext);
1902 calloutContext->clientAccessCheckDone(answer.blockedToChecklistAnswer());
1903 }
1904
1905 void
1906 ClientHttpRequest::resumeBodyStorage()
1907 {
1908 if (!adaptedBodySource)
1909 return;
1910
1911 noteMoreBodyDataAvailable(adaptedBodySource);
1912 }
1913
1914 void
1915 ClientHttpRequest::noteMoreBodyDataAvailable(BodyPipe::Pointer)
1916 {
1917 assert(request_satisfaction_mode);
1918 assert(adaptedBodySource != nullptr);
1919
1920 if (size_t contentSize = adaptedBodySource->buf().contentSize()) {
1921 const size_t spaceAvailable = storeEntry()->bytesWanted(Range<size_t>(0,contentSize));
1922
1923 if (spaceAvailable < contentSize ) {
1924 // No or partial body data consuming
1925 typedef NullaryMemFunT<ClientHttpRequest> Dialer;
1926 AsyncCall::Pointer call = asyncCall(93, 5, "ClientHttpRequest::resumeBodyStorage",
1927 Dialer(this, &ClientHttpRequest::resumeBodyStorage));
1928 storeEntry()->deferProducer(call);
1929 }
1930
1931 if (!spaceAvailable)
1932 return;
1933
1934 if (spaceAvailable < contentSize )
1935 contentSize = spaceAvailable;
1936
1937 BodyPipeCheckout bpc(*adaptedBodySource);
1938 const StoreIOBuffer ioBuf(&bpc.buf, request_satisfaction_offset, contentSize);
1939 storeEntry()->write(ioBuf);
1940 // assume StoreEntry::write() writes the entire ioBuf
1941 request_satisfaction_offset += ioBuf.length;
1942 bpc.buf.consume(contentSize);
1943 bpc.checkIn();
1944 }
1945
1946 if (adaptedBodySource->exhausted()) {
1947 // XXX: Setting receivedWholeAdaptedReply here is a workaround for a
1948 // regression, as described in https://bugs.squid-cache.org/show_bug.cgi?id=5187#c6
1949 receivedWholeAdaptedReply = true;
1950 debugs(85, Important(72), "WARNING: Squid bug 5187 workaround triggered");
1951 endRequestSatisfaction();
1952 }
1953 // else wait for more body data
1954 }
1955
1956 void
1957 ClientHttpRequest::noteBodyProductionEnded(BodyPipe::Pointer)
1958 {
1959 assert(!virginHeadSource);
1960
1961 // distinguish this code path from future noteBodyProducerAborted() that
1962 // would continue storing/delivering (truncated) reply if necessary (TODO)
1963 receivedWholeAdaptedReply = true;
1964
1965 // should we end request satisfaction now?
1966 if (adaptedBodySource != nullptr && adaptedBodySource->exhausted())
1967 endRequestSatisfaction();
1968 }
1969
1970 void
1971 ClientHttpRequest::endRequestSatisfaction()
1972 {
1973 debugs(85,4, this << " ends request satisfaction");
1974 assert(request_satisfaction_mode);
1975 stopConsumingFrom(adaptedBodySource);
1976
1977 // TODO: anything else needed to end store entry formation correctly?
1978 if (receivedWholeAdaptedReply) {
1979 // We received the entire reply per receivedWholeAdaptedReply.
1980 // We are called when we consumed everything received (per our callers).
1981 // We consume only what we store per noteMoreBodyDataAvailable().
1982 storeEntry()->completeSuccessfully("received, consumed, and, hence, stored the entire REQMOD reply");
1983 } else {
1984 storeEntry()->completeTruncated("REQMOD request satisfaction default");
1985 }
1986 }
1987
1988 void
1989 ClientHttpRequest::noteBodyProducerAborted(BodyPipe::Pointer)
1990 {
1991 assert(!virginHeadSource);
1992 stopConsumingFrom(adaptedBodySource);
1993
1994 debugs(85,3, "REQMOD body production failed");
1995 if (request_satisfaction_mode) { // too late to recover or serve an error
1996 static const auto d = MakeNamedErrorDetail("CLT_REQMOD_RESP_BODY");
1997 request->detailError(ERR_ICAP_FAILURE, d);
1998 const Comm::ConnectionPointer c = getConn()->clientConnection;
1999 Must(Comm::IsConnOpen(c));
2000 c->close(); // drastic, but we may be writing a response already
2001 } else {
2002 static const auto d = MakeNamedErrorDetail("CLT_REQMOD_REQ_BODY");
2003 handleAdaptationFailure(d);
2004 }
2005 }
2006
2007 void
2008 ClientHttpRequest::handleAdaptationFailure(const ErrorDetail::Pointer &errDetail, bool bypassable)
2009 {
2010 debugs(85,3, "handleAdaptationFailure(" << bypassable << ")");
2011
2012 const bool usedStore = storeEntry() && !storeEntry()->isEmpty();
2013 const bool usedPipe = request->body_pipe != nullptr &&
2014 request->body_pipe->consumedSize() > 0;
2015
2016 if (bypassable && !usedStore && !usedPipe) {
2017 debugs(85,3, "ICAP REQMOD callout failed, bypassing: " << calloutContext);
2018 if (calloutContext)
2019 doCallouts();
2020 return;
2021 }
2022
2023 debugs(85,3, "ICAP REQMOD callout failed, responding with error");
2024
2025 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
2026 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2027 assert(repContext);
2028
2029 calloutsError(ERR_ICAP_FAILURE, errDetail);
2030
2031 if (calloutContext)
2032 doCallouts();
2033 }
2034
2035 void
2036 ClientHttpRequest::callException(const std::exception &ex)
2037 {
2038 if (const auto clientConn = getConn() ? getConn()->clientConnection : nullptr) {
2039 if (Comm::IsConnOpen(clientConn)) {
2040 debugs(85, 3, "closing after exception: " << ex.what());
2041 clientConn->close(); // initiate orderly top-to-bottom cleanup
2042 return;
2043 }
2044 }
2045 debugs(85, DBG_IMPORTANT, "ClientHttpRequest exception without connection. Ignoring " << ex.what());
2046 // XXX: Normally, we mustStop() but we cannot do that here because it is
2047 // likely to leave Http::Stream and ConnStateData with a dangling http
2048 // pointer. See r13480 or XXX in Http::Stream class description.
2049 }
2050 #endif
2051
2052 // XXX: modify and use with ClientRequestContext::clientAccessCheckDone too.
2053 void
2054 ClientHttpRequest::calloutsError(const err_type error, const ErrorDetail::Pointer &errDetail)
2055 {
2056 // The original author of the code also wanted to pass an errno to
2057 // setReplyToError, but it seems unlikely that the errno reflects the
2058 // true cause of the error at this point, so I did not pass it.
2059 if (calloutContext) {
2060 ConnStateData * c = getConn();
2061 calloutContext->error = clientBuildError(error, Http::scInternalServerError,
2062 nullptr, c, request, al);
2063 #if USE_AUTH
2064 calloutContext->error->auth_user_request =
2065 c != nullptr && c->getAuth() != nullptr ? c->getAuth() : request->auth_user_request;
2066 #endif
2067 calloutContext->error->detailError(errDetail);
2068 calloutContext->readNextRequest = true;
2069 if (c != nullptr)
2070 c->expectNoForwarding();
2071 }
2072 //else if(calloutContext == NULL) is it possible?
2073 }
2074