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