]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpRequest.cc
e5f48f6dbd9c660baf7b4b301664b71f5f437ebb
[thirdparty/squid.git] / src / HttpRequest.cc
1 /*
2 * Copyright (C) 1996-2025 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 73 HTTP Request */
10
11 #include "squid.h"
12 #include "AccessLogEntry.h"
13 #include "acl/AclSizeLimit.h"
14 #include "acl/FilledChecklist.h"
15 #include "CachePeer.h"
16 #include "client_side.h"
17 #include "client_side_request.h"
18 #include "dns/LookupDetails.h"
19 #include "Downloader.h"
20 #include "error/Detail.h"
21 #include "globals.h"
22 #include "http.h"
23 #include "http/ContentLengthInterpreter.h"
24 #include "http/one/RequestParser.h"
25 #include "http/Stream.h"
26 #include "HttpHdrCc.h"
27 #include "HttpHeaderRange.h"
28 #include "HttpRequest.h"
29 #include "log/Config.h"
30 #include "MemBuf.h"
31 #include "sbuf/StringConvert.h"
32 #include "SquidConfig.h"
33 #include "Store.h"
34
35 #if USE_AUTH
36 #include "auth/UserRequest.h"
37 #endif
38 #if ICAP_CLIENT
39 #include "adaptation/icap/icap_log.h"
40 #endif
41
42 HttpRequest::HttpRequest(const MasterXaction::Pointer &mx) :
43 Http::Message(hoRequest),
44 masterXaction(mx)
45 {
46 assert(mx);
47 init();
48 }
49
50 HttpRequest::HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aSchemeImg, const char *aUrlpath, const MasterXaction::Pointer &mx) :
51 Http::Message(hoRequest),
52 masterXaction(mx)
53 {
54 assert(mx);
55 static unsigned int id = 1;
56 debugs(93,7, "constructed, this=" << this << " id=" << ++id);
57 init();
58 initHTTP(aMethod, aProtocol, aSchemeImg, aUrlpath);
59 }
60
61 HttpRequest::~HttpRequest()
62 {
63 clean();
64 debugs(93,7, "destructed, this=" << this);
65 }
66
67 void
68 HttpRequest::initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aSchemeImg, const char *aUrlpath)
69 {
70 method = aMethod;
71 url.setScheme(aProtocol, aSchemeImg);
72 url.path(aUrlpath);
73 }
74
75 void
76 HttpRequest::init()
77 {
78 method = Http::METHOD_NONE;
79 url.clear();
80 #if USE_AUTH
81 auth_user_request = nullptr;
82 #endif
83 flags = RequestFlags();
84 range = nullptr;
85 ims = -1;
86 imslen = 0;
87 lastmod = -1;
88 client_addr.setEmpty();
89 my_addr.setEmpty();
90 body_pipe = nullptr;
91 // hier
92 dnsWait = -1;
93 error.clear();
94 peer_login = nullptr; // not allocated/deallocated by this class
95 peer_domain = nullptr; // not allocated/deallocated by this class
96 vary_headers = SBuf();
97 myportname = null_string;
98 tag = null_string;
99 #if USE_AUTH
100 extacl_user = null_string;
101 extacl_passwd = null_string;
102 #endif
103 extacl_log = null_string;
104 extacl_message = null_string;
105 pstate = Http::Message::psReadyToParseStartLine;
106 #if FOLLOW_X_FORWARDED_FOR
107 indirect_client_addr.setEmpty();
108 #endif /* FOLLOW_X_FORWARDED_FOR */
109 #if USE_ADAPTATION
110 adaptHistory_ = nullptr;
111 #endif
112 #if ICAP_CLIENT
113 icapHistory_ = nullptr;
114 #endif
115 rangeOffsetLimit = -2; //a value of -2 means not checked yet
116 forcedBodyContinuation = false;
117 }
118
119 void
120 HttpRequest::clean()
121 {
122 // we used to assert that the pipe is NULL, but now the request only
123 // points to a pipe that is owned and initiated by another object.
124 body_pipe = nullptr;
125 #if USE_AUTH
126 auth_user_request = nullptr;
127 #endif
128 vary_headers.clear();
129 url.clear();
130
131 header.clean();
132
133 if (cache_control) {
134 delete cache_control;
135 cache_control = nullptr;
136 }
137
138 if (range) {
139 delete range;
140 range = nullptr;
141 }
142
143 myportname.clean();
144
145 theNotes = nullptr;
146
147 tag.clean();
148 #if USE_AUTH
149 extacl_user.clean();
150 extacl_passwd.clean();
151 #endif
152 extacl_log.clean();
153
154 extacl_message.clean();
155
156 etag.clean();
157
158 #if USE_ADAPTATION
159 adaptHistory_ = nullptr;
160 #endif
161 #if ICAP_CLIENT
162 icapHistory_ = nullptr;
163 #endif
164 }
165
166 void
167 HttpRequest::reset()
168 {
169 clean();
170 init();
171 }
172
173 HttpRequest *
174 HttpRequest::clone() const
175 {
176 HttpRequest *copy = new HttpRequest(masterXaction);
177 copy->method = method;
178 // TODO: move common cloning clone to Msg::copyTo() or copy ctor
179 copy->header.append(&header);
180 copy->hdrCacheInit();
181 copy->hdr_sz = hdr_sz;
182 copy->http_ver = http_ver;
183 copy->pstate = pstate; // TODO: should we assert a specific state here?
184 copy->body_pipe = body_pipe;
185
186 copy->url = url;
187
188 // range handled in hdrCacheInit()
189 copy->ims = ims;
190 copy->imslen = imslen;
191 copy->hier = hier; // Is it safe to copy? Should we?
192
193 copy->error = error;
194
195 // XXX: what to do with copy->peer_login?
196
197 copy->lastmod = lastmod;
198 copy->etag = etag;
199 copy->vary_headers = vary_headers;
200 // XXX: what to do with copy->peer_domain?
201
202 copy->tag = tag;
203 copy->extacl_log = extacl_log;
204 copy->extacl_message = extacl_message;
205
206 const bool inheritWorked = copy->inheritProperties(this);
207 assert(inheritWorked);
208
209 return copy;
210 }
211
212 bool
213 HttpRequest::inheritProperties(const Http::Message *aMsg)
214 {
215 const HttpRequest* aReq = dynamic_cast<const HttpRequest*>(aMsg);
216 if (!aReq)
217 return false;
218
219 client_addr = aReq->client_addr;
220 #if FOLLOW_X_FORWARDED_FOR
221 indirect_client_addr = aReq->indirect_client_addr;
222 #endif
223 my_addr = aReq->my_addr;
224
225 dnsWait = aReq->dnsWait;
226
227 #if USE_ADAPTATION
228 adaptHistory_ = aReq->adaptHistory();
229 #endif
230 #if ICAP_CLIENT
231 icapHistory_ = aReq->icapHistory();
232 #endif
233
234 // This may be too conservative for the 204 No Content case
235 // may eventually need cloneNullAdaptationImmune() for that.
236 flags = aReq->flags.cloneAdaptationImmune();
237
238 error = aReq->error;
239 #if USE_AUTH
240 auth_user_request = aReq->auth_user_request;
241 extacl_user = aReq->extacl_user;
242 extacl_passwd = aReq->extacl_passwd;
243 #endif
244
245 myportname = aReq->myportname;
246
247 forcedBodyContinuation = aReq->forcedBodyContinuation;
248
249 // main property is which connection the request was received on (if any)
250 clientConnectionManager = aReq->clientConnectionManager;
251
252 downloader = aReq->downloader;
253
254 theNotes = aReq->theNotes;
255
256 sources = aReq->sources;
257 return true;
258 }
259
260 /**
261 * Checks the first line of an HTTP request is valid
262 * currently just checks the request method is present.
263 *
264 * NP: Other errors are left for detection later in the parse.
265 */
266 bool
267 HttpRequest::sanityCheckStartLine(const char *buf, const size_t hdr_len, Http::StatusCode *scode)
268 {
269 // content is long enough to possibly hold a reply
270 // 2 being magic size of a 1-byte request method plus space delimiter
271 if (hdr_len < 2) {
272 // this is only a real error if the headers apparently complete.
273 if (hdr_len > 0) {
274 debugs(58, 3, "Too large request header (" << hdr_len << " bytes)");
275 *scode = Http::scInvalidHeader;
276 }
277 return false;
278 }
279
280 /* See if the request buffer starts with a non-whitespace HTTP request 'method'. */
281 HttpRequestMethod m;
282 m.HttpRequestMethodXXX(buf);
283 if (m == Http::METHOD_NONE) {
284 debugs(73, 3, "HttpRequest::sanityCheckStartLine: did not find HTTP request method");
285 *scode = Http::scInvalidHeader;
286 return false;
287 }
288
289 return true;
290 }
291
292 bool
293 HttpRequest::parseFirstLine(const char *start, const char *end)
294 {
295 method.HttpRequestMethodXXX(start);
296
297 if (method == Http::METHOD_NONE)
298 return false;
299
300 // XXX: performance regression, strcspn() over the method bytes a second time.
301 // cheaper than allocate+copy+deallocate cycle to SBuf convert a piece of start.
302 const char *t = start + strcspn(start, w_space);
303
304 start = t + strspn(t, w_space); // skip w_space after method
305
306 const char *ver = findTrailingHTTPVersion(start, end);
307
308 if (ver) {
309 end = ver - 1;
310
311 while (xisspace(*end)) // find prev non-space
312 --end;
313
314 ++end; // back to space
315
316 if (2 != sscanf(ver + 5, "%d.%d", &http_ver.major, &http_ver.minor)) {
317 debugs(73, DBG_IMPORTANT, "ERROR: parseRequestLine: Invalid HTTP identifier.");
318 return false;
319 }
320 } else {
321 http_ver.major = 0;
322 http_ver.minor = 9;
323 }
324
325 if (end < start) // missing URI
326 return false;
327
328 return url.parse(method, SBuf(start, size_t(end-start)));
329 }
330
331 /* swaps out request using httpRequestPack */
332 void
333 HttpRequest::swapOut(StoreEntry * e)
334 {
335 assert(e);
336 e->buffer();
337 pack(e);
338 e->flush();
339 }
340
341 /* packs request-line and headers, appends <crlf> terminator */
342 void
343 HttpRequest::pack(Packable * p) const
344 {
345 assert(p);
346 /* pack request-line */
347 p->appendf(SQUIDSBUFPH " " SQUIDSBUFPH " HTTP/%d.%d\r\n",
348 SQUIDSBUFPRINT(method.image()), SQUIDSBUFPRINT(url.path()),
349 http_ver.major, http_ver.minor);
350 /* headers */
351 header.packInto(p);
352 /* trailer */
353 p->append("\r\n", 2);
354 }
355
356 /*
357 * A wrapper for debugObj()
358 */
359 void
360 httpRequestPack(void *obj, Packable *p)
361 {
362 HttpRequest *request = static_cast<HttpRequest*>(obj);
363 request->pack(p);
364 }
365
366 /* returns the length of request line + headers + crlf */
367 int
368 HttpRequest::prefixLen() const
369 {
370 return method.image().length() + 1 +
371 url.path().length() + 1 +
372 4 + 1 + 3 + 2 +
373 header.len + 2;
374 }
375
376 /* sync this routine when you update HttpRequest struct */
377 void
378 HttpRequest::hdrCacheInit()
379 {
380 Http::Message::hdrCacheInit();
381
382 assert(!range);
383 range = header.getRange();
384 }
385
386 #if ICAP_CLIENT
387 Adaptation::Icap::History::Pointer
388 HttpRequest::icapHistory() const
389 {
390 if (!icapHistory_) {
391 if (Log::TheConfig.hasIcapToken || IcapLogfileStatus == LOG_ENABLE) {
392 icapHistory_ = new Adaptation::Icap::History();
393 debugs(93,4, "made " << icapHistory_ << " for " << this);
394 }
395 }
396
397 return icapHistory_;
398 }
399 #endif
400
401 #if USE_ADAPTATION
402 Adaptation::History::Pointer
403 HttpRequest::adaptHistory(bool createIfNone) const
404 {
405 if (!adaptHistory_ && createIfNone) {
406 adaptHistory_ = new Adaptation::History();
407 debugs(93,4, "made " << adaptHistory_ << " for " << this);
408 }
409
410 return adaptHistory_;
411 }
412
413 Adaptation::History::Pointer
414 HttpRequest::adaptLogHistory() const
415 {
416 return HttpRequest::adaptHistory(Log::TheConfig.hasAdaptToken);
417 }
418
419 void
420 HttpRequest::adaptHistoryImport(const HttpRequest &them)
421 {
422 if (!adaptHistory_) {
423 adaptHistory_ = them.adaptHistory_; // may be nil
424 } else {
425 // check that histories did not diverge
426 Must(!them.adaptHistory_ || them.adaptHistory_ == adaptHistory_);
427 }
428 }
429
430 #endif
431
432 bool
433 HttpRequest::multipartRangeRequest() const
434 {
435 return (range && range->specs.size() > 1);
436 }
437
438 bool
439 HttpRequest::bodyNibbled() const
440 {
441 return body_pipe != nullptr && body_pipe->consumedSize() > 0;
442 }
443
444 void
445 HttpRequest::prepForPeering(const CachePeer &peer)
446 {
447 // XXX: Saving two pointers to memory controlled by an independent object.
448 peer_login = peer.login;
449 peer_domain = peer.domain;
450 flags.auth_no_keytab = peer.options.auth_no_keytab;
451 debugs(11, 4, this << " to " << peer);
452 }
453
454 void
455 HttpRequest::prepForDirect()
456 {
457 peer_login = nullptr;
458 peer_domain = nullptr;
459 flags.auth_no_keytab = false;
460 debugs(11, 4, this);
461 }
462
463 void
464 HttpRequest::clearError()
465 {
466 debugs(11, 7, "old: " << error);
467 error.clear();
468 }
469
470 void
471 HttpRequest::packFirstLineInto(Packable * p, bool full_uri) const
472 {
473 const SBuf tmp(full_uri ? effectiveRequestUri() : url.path());
474
475 // form HTTP request-line
476 p->appendf(SQUIDSBUFPH " " SQUIDSBUFPH " HTTP/%d.%d\r\n",
477 SQUIDSBUFPRINT(method.image()),
478 SQUIDSBUFPRINT(tmp),
479 http_ver.major, http_ver.minor);
480 }
481
482 /*
483 * Indicate whether or not we would expect an entity-body
484 * along with this request
485 */
486 bool
487 HttpRequest::expectingBody(const HttpRequestMethod &, int64_t &theSize) const
488 {
489 bool expectBody = false;
490
491 /*
492 * Note: Checks for message validity is in clientIsContentLengthValid().
493 * this just checks if a entity-body is expected based on HTTP message syntax
494 */
495 if (header.chunked()) {
496 expectBody = true;
497 theSize = -1;
498 } else if (content_length >= 0) {
499 expectBody = true;
500 theSize = content_length;
501 } else {
502 expectBody = false;
503 // theSize undefined
504 }
505
506 return expectBody;
507 }
508
509 /*
510 * Create a Request from a URL and METHOD.
511 *
512 * If the METHOD is CONNECT, then a host:port pair is looked for instead of a URL.
513 * If the request cannot be created cleanly, NULL is returned
514 */
515 HttpRequest *
516 HttpRequest::FromUrl(const SBuf &url, const MasterXaction::Pointer &mx, const HttpRequestMethod& method)
517 {
518 std::unique_ptr<HttpRequest> req(new HttpRequest(mx));
519 if (req->url.parse(method, url)) {
520 req->method = method;
521 return req.release();
522 }
523 return nullptr;
524 }
525
526 HttpRequest *
527 HttpRequest::FromUrlXXX(const char * url, const MasterXaction::Pointer &mx, const HttpRequestMethod& method)
528 {
529 return FromUrl(SBuf(url), mx, method);
530 }
531
532 /**
533 * Are responses to this request possible cacheable ?
534 * If false then no matter what the response must not be cached.
535 */
536 bool
537 HttpRequest::maybeCacheable()
538 {
539 // Intercepted request with Host: header which cannot be trusted.
540 // Because it failed verification, or someone bypassed the security tests
541 // we cannot cache the response for sharing between clients.
542 // TODO: update cache to store for particular clients only (going to same Host: and destination IP)
543 if (!flags.hostVerified && (flags.intercepted || flags.interceptTproxy))
544 return false;
545
546 switch (url.getScheme()) {
547 case AnyP::PROTO_HTTP:
548 case AnyP::PROTO_HTTPS:
549 if (!method.respMaybeCacheable())
550 return false;
551
552 // RFC 9111 section 5.2.1.5:
553 // "The no-store request directive indicates that a cache MUST NOT
554 // store any part of either this request or any response to it."
555 //
556 // NP: refresh_pattern ignore-no-store only applies to response messages
557 // this test is handling request message CC header.
558 if (!flags.ignoreCc && cache_control && cache_control->hasNoStore())
559 return false;
560 break;
561
562 //case AnyP::PROTO_FTP:
563 default:
564 break;
565 }
566
567 return true;
568 }
569
570 bool
571 HttpRequest::conditional() const
572 {
573 return flags.ims ||
574 header.has(Http::HdrType::IF_MATCH) ||
575 header.has(Http::HdrType::IF_NONE_MATCH);
576 }
577
578 void
579 HttpRequest::recordLookup(const Dns::LookupDetails &dns)
580 {
581 if (dns.wait >= 0) { // known delay
582 if (dnsWait >= 0) { // have recorded DNS wait before
583 debugs(78, 7, this << " " << dnsWait << " += " << dns);
584 dnsWait += dns.wait;
585 } else {
586 debugs(78, 7, this << " " << dns);
587 dnsWait = dns.wait;
588 }
589 }
590 }
591
592 int64_t
593 HttpRequest::getRangeOffsetLimit()
594 {
595 /* -2 is the starting value of rangeOffsetLimit.
596 * If it is -2, that means we haven't checked it yet.
597 * Otherwise, return the current value */
598 if (rangeOffsetLimit != -2)
599 return rangeOffsetLimit;
600
601 rangeOffsetLimit = 0; // default value for rangeOffsetLimit
602
603 ACLFilledChecklist ch(nullptr, this);
604 ch.src_addr = client_addr;
605 ch.my_addr = my_addr;
606
607 for (AclSizeLimit *l = Config.rangeOffsetLimit; l; l = l -> next) {
608 /* if there is no ACL list or if the ACLs listed match use this limit value */
609 if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
610 rangeOffsetLimit = l->size; // may be -1
611 debugs(58, 4, rangeOffsetLimit);
612 break;
613 }
614 }
615
616 return rangeOffsetLimit;
617 }
618
619 void
620 HttpRequest::ignoreRange(const char *reason)
621 {
622 if (range) {
623 debugs(73, 3, static_cast<void*>(range) << " for " << reason);
624 delete range;
625 range = nullptr;
626 }
627 // Some callers also reset isRanged but it may not be safe for all callers:
628 // isRanged is used to determine whether a weak ETag comparison is allowed,
629 // and that check should not ignore the Range header if it was present.
630 // TODO: Some callers also delete HDR_RANGE, HDR_REQUEST_RANGE. Should we?
631 }
632
633 bool
634 HttpRequest::canHandle1xx() const
635 {
636 // old clients do not support 1xx unless they sent Expect: 100-continue
637 // (we reject all other Http::HdrType::EXPECT values so just check for Http::HdrType::EXPECT)
638 if (http_ver <= Http::ProtocolVersion(1,0) && !header.has(Http::HdrType::EXPECT))
639 return false;
640
641 // others must support 1xx control messages
642 return true;
643 }
644
645 Http::StatusCode
646 HttpRequest::checkEntityFraming() const
647 {
648 // RFC 7230 section 3.3.1:
649 // "
650 // A server that receives a request message with a transfer coding it
651 // does not understand SHOULD respond with 501 (Not Implemented).
652 // "
653 if (header.unsupportedTe())
654 return Http::scNotImplemented;
655
656 // RFC 7230 section 3.3.3 #3 paragraph 3:
657 // Transfer-Encoding overrides Content-Length
658 if (header.chunked())
659 return Http::scNone;
660
661 // RFC 7230 Section 3.3.3 #4:
662 // conflicting Content-Length(s) mean a message framing error
663 if (header.conflictingContentLength())
664 return Http::scBadRequest;
665
666 // HTTP/1.0 requirements differ from HTTP/1.1
667 if (http_ver <= Http::ProtocolVersion(1,0)) {
668 const auto m = method.id();
669
670 // RFC 1945 section 8.3:
671 // "
672 // A valid Content-Length is required on all HTTP/1.0 POST requests.
673 // "
674 // RFC 1945 Appendix D.1.1:
675 // "
676 // The fundamental difference between the POST and PUT requests is
677 // reflected in the different meaning of the Request-URI.
678 // "
679 if (m == Http::METHOD_POST || m == Http::METHOD_PUT)
680 return (content_length >= 0 ? Http::scNone : Http::scLengthRequired);
681
682 // RFC 1945 section 7.2:
683 // "
684 // An entity body is included with a request message only when the
685 // request method calls for one.
686 // "
687 // section 8.1-2: GET and HEAD do not define ('call for') an entity
688 if (m == Http::METHOD_GET || m == Http::METHOD_HEAD)
689 return (content_length < 0 ? Http::scNone : Http::scBadRequest);
690 // appendix D1.1.2-4: DELETE, LINK, UNLINK do not define ('call for') an entity
691 if (m == Http::METHOD_DELETE || m == Http::METHOD_LINK || m == Http::METHOD_UNLINK)
692 return (content_length < 0 ? Http::scNone : Http::scBadRequest);
693
694 // other methods are not defined in RFC 1945
695 // assume they support an (optional) entity
696 return Http::scNone;
697 }
698
699 // RFC 7230 section 3.3
700 // "
701 // The presence of a message body in a request is signaled by a
702 // Content-Length or Transfer-Encoding header field. Request message
703 // framing is independent of method semantics, even if the method does
704 // not define any use for a message body.
705 // "
706 return Http::scNone;
707 }
708
709 bool
710 HttpRequest::parseHeader(Http1::Parser &hp)
711 {
712 Http::ContentLengthInterpreter clen;
713 return Message::parseHeader(hp, clen);
714 }
715
716 bool
717 HttpRequest::parseHeader(const char *buffer, const size_t size)
718 {
719 Http::ContentLengthInterpreter clen;
720 return header.parse(buffer, size, clen);
721 }
722
723 ConnStateData *
724 HttpRequest::pinnedConnection()
725 {
726 if (clientConnectionManager.valid() && clientConnectionManager->pinning.pinned)
727 return clientConnectionManager.get();
728 return nullptr;
729 }
730
731 const SBuf
732 HttpRequest::storeId()
733 {
734 if (store_id.size() != 0) {
735 debugs(73, 3, "sent back store_id: " << store_id);
736 return StringToSBuf(store_id);
737 }
738 debugs(73, 3, "sent back effectiveRequestUrl: " << effectiveRequestUri());
739 return effectiveRequestUri();
740 }
741
742 const SBuf &
743 HttpRequest::effectiveRequestUri() const
744 {
745 if (method.id() == Http::METHOD_CONNECT || url.getScheme() == AnyP::PROTO_AUTHORITY_FORM)
746 return url.authority(true); // host:port
747 return url.absolute();
748 }
749
750 NotePairs::Pointer
751 HttpRequest::notes()
752 {
753 if (!theNotes)
754 theNotes = new NotePairs;
755 return theNotes;
756 }
757
758 void
759 UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const &helperNotes)
760 {
761 // Tag client connection if the helper responded with clt_conn_tag=tag.
762 const char *cltTag = "clt_conn_tag";
763 if (const char *connTag = helperNotes.findFirst(cltTag)) {
764 if (csd) {
765 csd->notes()->remove(cltTag);
766 csd->notes()->add(cltTag, connTag);
767 }
768 }
769 request.notes()->replaceOrAdd(&helperNotes);
770 }
771
772 void
773 HttpRequest::manager(const CbcPointer<ConnStateData> &aMgr, const AccessLogEntryPointer &al)
774 {
775 clientConnectionManager = aMgr;
776
777 if (!clientConnectionManager.valid())
778 return;
779
780 AnyP::PortCfgPointer port = clientConnectionManager->port;
781 if (port) {
782 myportname = port->name;
783 flags.ignoreCc = port->ignore_cc;
784 }
785
786 if (auto clientConnection = clientConnectionManager->clientConnection) {
787 client_addr = clientConnection->remote; // XXX: remove request->client_addr member.
788 #if FOLLOW_X_FORWARDED_FOR
789 // indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:)
790 // not details about the TCP connection itself
791 indirect_client_addr = clientConnection->remote;
792 #endif /* FOLLOW_X_FORWARDED_FOR */
793 my_addr = clientConnection->local;
794
795 flags.intercepted = ((clientConnection->flags & COMM_INTERCEPTION) != 0);
796 flags.interceptTproxy = ((clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
797 const bool proxyProtocolPort = port ? port->flags.proxySurrogate : false;
798 if (flags.interceptTproxy && !proxyProtocolPort) {
799 if (Config.accessList.spoof_client_ip) {
800 ACLFilledChecklist checklist(Config.accessList.spoof_client_ip, this);
801 checklist.al = al;
802 checklist.syncAle(this, nullptr);
803 flags.spoofClientIp = checklist.fastCheck().allowed();
804 } else
805 flags.spoofClientIp = true;
806 } else
807 flags.spoofClientIp = false;
808 }
809 }
810
811 char *
812 HttpRequest::canonicalCleanUrl() const
813 {
814 return urlCanonicalCleanWithoutRequest(effectiveRequestUri(), method, url.getScheme());
815 }
816
817 /// a helper for handling PortCfg cases of FindListeningPortAddress()
818 template <typename Filter>
819 static const Ip::Address *
820 FindGoodListeningPortAddressInPort(const AnyP::PortCfgPointer &port, const Filter isGood)
821 {
822 return (port && isGood(port->s)) ? &port->s : nullptr;
823 }
824
825 /// a helper for handling Connection cases of FindListeningPortAddress()
826 template <typename Filter>
827 static const Ip::Address *
828 FindGoodListeningPortAddressInConn(const Comm::ConnectionPointer &conn, const Filter isGood)
829 {
830 return (conn && isGood(conn->local)) ? &conn->local : nullptr;
831 }
832
833 template <typename Filter>
834 const Ip::Address *
835 FindGoodListeningPortAddress(const HttpRequest *callerRequest, const AccessLogEntry *ale, const Filter filter)
836 {
837 // Check all sources of usable listening port information, giving
838 // HttpRequest and masterXaction a preference over ALE.
839
840 const HttpRequest *request = callerRequest;
841 if (!request && ale)
842 request = ale->request;
843 if (!request)
844 return nullptr; // not enough information
845
846 auto ip = FindGoodListeningPortAddressInPort(request->masterXaction->squidPort, filter);
847 if (!ip && ale)
848 ip = FindGoodListeningPortAddressInPort(ale->cache.port, filter);
849
850 // XXX: also handle PROXY protocol here when we have a flag to identify such request
851 if (ip || request->flags.interceptTproxy || request->flags.intercepted)
852 return ip;
853
854 /* handle non-intercepted cases that were not handled above */
855 ip = FindGoodListeningPortAddressInConn(request->masterXaction->tcpClient, filter);
856 if (!ip && ale)
857 ip = FindGoodListeningPortAddressInConn(ale->tcpClient, filter);
858 return ip; // may still be nil
859 }
860
861 const Ip::Address *
862 FindListeningPortAddress(const HttpRequest *callerRequest, const AccessLogEntry *ale)
863 {
864 return FindGoodListeningPortAddress(callerRequest, ale, [](const Ip::Address &address) {
865 // FindListeningPortAddress() callers do not want INADDR_ANY addresses
866 return !address.isAnyAddr();
867 });
868 }
869
870 AnyP::Port
871 FindListeningPortNumber(const HttpRequest *callerRequest, const AccessLogEntry *ale)
872 {
873 const auto ip = FindGoodListeningPortAddress(callerRequest, ale, [](const Ip::Address &address) {
874 return address.port() > 0;
875 });
876
877 if (!ip)
878 return std::nullopt;
879
880 Assure(ip->port() > 0);
881 return ip->port();
882 }