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