]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpRequest.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / HttpRequest.cc
CommitLineData
99edd1c3 1/*
bf95c10a 2 * Copyright (C) 1996-2022 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"
d05c079c 22#include "gopher.h"
5c0c642e 23#include "http.h"
4f1c93a7 24#include "http/ContentLengthInterpreter.h"
c99510dd 25#include "http/one/RequestParser.h"
d3dddfb5 26#include "http/Stream.h"
7ebe76de 27#include "HttpHdrCc.h"
528b2c61 28#include "HttpHeaderRange.h"
582c2af2 29#include "HttpRequest.h"
38e16f92 30#include "log/Config.h"
0eb49b6d 31#include "MemBuf.h"
ac38abee 32#include "sbuf/StringConvert.h"
4d5904f7 33#include "SquidConfig.h"
25b6a907 34#include "Store.h"
582c2af2
FC
35
36#if USE_AUTH
37#include "auth/UserRequest.h"
38#endif
3ff65596
AR
39#if ICAP_CLIENT
40#include "adaptation/icap/icap_log.h"
41#endif
528b2c61 42
5ceaee75
CT
43HttpRequest::HttpRequest(const MasterXaction::Pointer &mx) :
44 Http::Message(hoRequest),
45 masterXaction(mx)
75faaa7a 46{
5ceaee75 47 assert(mx);
5cafad19 48 init();
49}
50
5ceaee75
CT
51HttpRequest::HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aSchemeImg, const char *aUrlpath, const MasterXaction::Pointer &mx) :
52 Http::Message(hoRequest),
53 masterXaction(mx)
5cafad19 54{
5ceaee75 55 assert(mx);
5dfc3258 56 static unsigned int id = 1;
bf95c10a 57 debugs(93,7, "constructed, this=" << this << " id=" << ++id);
5cafad19 58 init();
d31d59d8 59 initHTTP(aMethod, aProtocol, aSchemeImg, aUrlpath);
5cafad19 60}
61
62HttpRequest::~HttpRequest()
63{
5cafad19 64 clean();
bf95c10a 65 debugs(93,7, "destructed, this=" << this);
5cafad19 66}
67
68void
d31d59d8 69HttpRequest::initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aSchemeImg, const char *aUrlpath)
5cafad19 70{
71 method = aMethod;
d31d59d8 72 url.setScheme(aProtocol, aSchemeImg);
51b5dcf5 73 url.path(aUrlpath);
5cafad19 74}
75
76void
77HttpRequest::init()
78{
c2a7cefd 79 method = Http::METHOD_NONE;
4e3f4dc7 80 url.clear();
2f1431ea 81#if USE_AUTH
aee3523a 82 auth_user_request = nullptr;
2f1431ea 83#endif
b56b37cf 84 flags = RequestFlags();
aee3523a 85 range = nullptr;
a3f28359 86 ims = -1;
87 imslen = 0;
5cafad19 88 lastmod = -1;
4dd643d5
AJ
89 client_addr.setEmpty();
90 my_addr.setEmpty();
aee3523a 91 body_pipe = nullptr;
a3f28359 92 // hier
3ff65596 93 dnsWait = -1;
83b053a0 94 error.clear();
aee3523a
AR
95 peer_login = nullptr; // not allocated/deallocated by this class
96 peer_domain = nullptr; // not allocated/deallocated by this class
97 peer_host = nullptr;
90ab8f20 98 vary_headers = SBuf();
35fb56c9 99 myportname = null_string;
5cafad19 100 tag = null_string;
2f1431ea 101#if USE_AUTH
5cafad19 102 extacl_user = null_string;
103 extacl_passwd = null_string;
2f1431ea 104#endif
5cafad19 105 extacl_log = null_string;
8c93a598 106 extacl_message = null_string;
fb654382 107 pstate = Http::Message::psReadyToParseStartLine;
3d674977 108#if FOLLOW_X_FORWARDED_FOR
4dd643d5 109 indirect_client_addr.setEmpty();
3d674977 110#endif /* FOLLOW_X_FORWARDED_FOR */
3ff65596 111#if USE_ADAPTATION
aee3523a 112 adaptHistory_ = nullptr;
3ff65596
AR
113#endif
114#if ICAP_CLIENT
aee3523a 115 icapHistory_ = nullptr;
3ff65596 116#endif
11e3fa1c 117 rangeOffsetLimit = -2; //a value of -2 means not checked yet
ec69bdb2 118 forcedBodyContinuation = false;
8596962e 119}
120
99edd1c3 121void
5cafad19 122HttpRequest::clean()
99edd1c3 123{
9e008dda 124 // we used to assert that the pipe is NULL, but now the request only
5f8252d2 125 // points to a pipe that is owned and initiated by another object.
aee3523a 126 body_pipe = nullptr;
2f1431ea 127#if USE_AUTH
aee3523a 128 auth_user_request = nullptr;
2f1431ea 129#endif
90ab8f20 130 vary_headers.clear();
4e3f4dc7 131 url.clear();
62e76326 132
519e0948 133 header.clean();
62e76326 134
07947ad8 135 if (cache_control) {
3d7782c1 136 delete cache_control;
aee3523a 137 cache_control = nullptr;
07947ad8 138 }
62e76326 139
5cafad19 140 if (range) {
8596962e 141 delete range;
aee3523a 142 range = nullptr;
5cafad19 143 }
62e76326 144
35fb56c9
AJ
145 myportname.clean();
146
75d47340 147 theNotes = nullptr;
d06e17ea 148
30abd221 149 tag.clean();
2f1431ea 150#if USE_AUTH
30abd221 151 extacl_user.clean();
30abd221 152 extacl_passwd.clean();
2f1431ea 153#endif
30abd221 154 extacl_log.clean();
3ff65596 155
8c93a598
HN
156 extacl_message.clean();
157
46017fdd
CT
158 etag.clean();
159
3ff65596 160#if USE_ADAPTATION
aee3523a 161 adaptHistory_ = nullptr;
3ff65596
AR
162#endif
163#if ICAP_CLIENT
aee3523a 164 icapHistory_ = nullptr;
3ff65596 165#endif
8596962e 166}
4a972fa2 167
5cafad19 168void
169HttpRequest::reset()
170{
171 clean();
172 init();
173}
174
fa0e6114
AR
175HttpRequest *
176HttpRequest::clone() const
177{
5ceaee75 178 HttpRequest *copy = new HttpRequest(masterXaction);
51b5dcf5 179 copy->method = method;
fa0e6114
AR
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
d31d59d8 188 copy->url = url;
fa0e6114 189
66363092 190 // range handled in hdrCacheInit()
9e008dda
AJ
191 copy->ims = ims;
192 copy->imslen = imslen;
fa0e6114
AR
193 copy->hier = hier; // Is it safe to copy? Should we?
194
83b053a0 195 copy->error = error;
fa0e6114
AR
196
197 // XXX: what to do with copy->peer_login?
198
9e008dda 199 copy->lastmod = lastmod;
46017fdd 200 copy->etag = etag;
90ab8f20 201 copy->vary_headers = vary_headers;
fa0e6114
AR
202 // XXX: what to do with copy->peer_domain?
203
204 copy->tag = tag;
fa0e6114 205 copy->extacl_log = extacl_log;
8c93a598 206 copy->extacl_message = extacl_message;
fa0e6114 207
fbc1c5a6
AJ
208 const bool inheritWorked = copy->inheritProperties(this);
209 assert(inheritWorked);
3ff65596 210
fa0e6114
AR
211 return copy;
212}
213
d5964067 214bool
63df1d28 215HttpRequest::inheritProperties(const Http::Message *aMsg)
d5964067
AJ
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;
d5964067
AJ
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
83b053a0 240 error = aReq->error;
d5964067
AJ
241#if USE_AUTH
242 auth_user_request = aReq->auth_user_request;
f4f6ab06
AJ
243 extacl_user = aReq->extacl_user;
244 extacl_passwd = aReq->extacl_passwd;
d5964067 245#endif
983983ce 246
20fb3571
AJ
247 myportname = aReq->myportname;
248
ec69bdb2
CT
249 forcedBodyContinuation = aReq->forcedBodyContinuation;
250
983983ce 251 // main property is which connection the request was received on (if any)
d5964067 252 clientConnectionManager = aReq->clientConnectionManager;
f4698e0b 253
cda7024f
CT
254 downloader = aReq->downloader;
255
75d47340 256 theNotes = aReq->theNotes;
88df846b
CT
257
258 sources = aReq->sources;
d5964067
AJ
259 return true;
260}
261
96ee497f
AJ
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 */
c21ad0f5 268bool
83b053a0 269HttpRequest::sanityCheckStartLine(const char *buf, const size_t hdr_len, Http::StatusCode *scode)
8596962e 270{
96ee497f
AJ
271 // content is long enough to possibly hold a reply
272 // 2 being magic size of a 1-byte request method plus space delimiter
84ae6223 273 if (hdr_len < 2) {
2f8abb64 274 // this is only a real error if the headers apparently complete.
96ee497f 275 if (hdr_len > 0) {
bf95c10a 276 debugs(58, 3, "Too large request header (" << hdr_len << " bytes)");
83b053a0 277 *scode = Http::scInvalidHeader;
96ee497f
AJ
278 }
279 return false;
280 }
8596962e 281
f9688132
AJ
282 /* See if the request buffer starts with a non-whitespace HTTP request 'method'. */
283 HttpRequestMethod m;
84ae6223 284 m.HttpRequestMethodXXX(buf);
f9688132 285 if (m == Http::METHOD_NONE) {
bf8fe701 286 debugs(73, 3, "HttpRequest::sanityCheckStartLine: did not find HTTP request method");
83b053a0 287 *scode = Http::scInvalidHeader;
8596962e 288 return false;
289 }
290
291 return true;
292}
293
c21ad0f5 294bool
295HttpRequest::parseFirstLine(const char *start, const char *end)
8596962e 296{
f9688132 297 method.HttpRequestMethodXXX(start);
863d85ee 298
c2a7cefd 299 if (method == Http::METHOD_NONE)
863d85ee 300 return false;
301
f9688132
AJ
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
863d85ee 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
5e263176 314 --end;
863d85ee 315
95dc7ff4 316 ++end; // back to space
863d85ee 317
318 if (2 != sscanf(ver + 5, "%d.%d", &http_ver.major, &http_ver.minor)) {
d816f28d 319 debugs(73, DBG_IMPORTANT, "ERROR: parseRequestLine: Invalid HTTP identifier.");
863d85ee 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
6c880a16 330 return url.parse(method, SBuf(start, size_t(end-start)));
99edd1c3 331}
332
a00a7c85 333/* swaps out request using httpRequestPack */
99edd1c3 334void
5cafad19 335HttpRequest::swapOut(StoreEntry * e)
99edd1c3 336{
5cafad19 337 assert(e);
15179984
AJ
338 e->buffer();
339 pack(e);
691ad095 340 e->flush();
a00a7c85 341}
342
343/* packs request-line and headers, appends <crlf> terminator */
344void
1f28a150 345HttpRequest::pack(Packable * p) const
a00a7c85 346{
5cafad19 347 assert(p);
a00a7c85 348 /* pack request-line */
51b5dcf5
AJ
349 p->appendf(SQUIDSBUFPH " " SQUIDSBUFPH " HTTP/%d.%d\r\n",
350 SQUIDSBUFPRINT(method.image()), SQUIDSBUFPRINT(url.path()),
4391cd15 351 http_ver.major, http_ver.minor);
a00a7c85 352 /* headers */
a9925b40 353 header.packInto(p);
2246b732 354 /* trailer */
785b508d 355 p->append("\r\n", 2);
2246b732 356}
357
5cafad19 358/*
359 * A wrapper for debugObj()
360 */
2246b732 361void
17802cf1 362httpRequestPack(void *obj, Packable *p)
2246b732 363{
5cafad19 364 HttpRequest *request = static_cast<HttpRequest*>(obj);
365 request->pack(p);
99edd1c3 366}
eeb423fb 367
2246b732 368/* returns the length of request line + headers + crlf */
369int
51b5dcf5 370HttpRequest::prefixLen() const
2246b732 371{
7f06a3d8 372 return method.image().length() + 1 +
51b5dcf5 373 url.path().length() + 1 +
62e76326 374 4 + 1 + 3 + 2 +
5cafad19 375 header.len + 2;
2246b732 376}
99edd1c3 377
190154cf 378/* sync this routine when you update HttpRequest struct */
8596962e 379void
07947ad8 380HttpRequest::hdrCacheInit()
528b2c61 381{
63df1d28 382 Http::Message::hdrCacheInit();
62e76326 383
66363092 384 assert(!range);
a9925b40 385 range = header.getRange();
528b2c61 386}
387
3ff65596 388#if ICAP_CLIENT
e1381638 389Adaptation::Icap::History::Pointer
3ff65596
AR
390HttpRequest::icapHistory() const
391{
392 if (!icapHistory_) {
38e16f92 393 if (Log::TheConfig.hasIcapToken || IcapLogfileStatus == LOG_ENABLE) {
3ff65596 394 icapHistory_ = new Adaptation::Icap::History();
bf95c10a 395 debugs(93,4, "made " << icapHistory_ << " for " << this);
3ff65596
AR
396 }
397 }
398
399 return icapHistory_;
400}
401#endif
402
403#if USE_ADAPTATION
e1381638 404Adaptation::History::Pointer
a22e6cd3 405HttpRequest::adaptHistory(bool createIfNone) const
3ff65596 406{
a22e6cd3
AR
407 if (!adaptHistory_ && createIfNone) {
408 adaptHistory_ = new Adaptation::History();
bf95c10a 409 debugs(93,4, "made " << adaptHistory_ << " for " << this);
3ff65596
AR
410 }
411
412 return adaptHistory_;
413}
a22e6cd3 414
e1381638 415Adaptation::History::Pointer
a22e6cd3
AR
416HttpRequest::adaptLogHistory() const
417{
38e16f92 418 return HttpRequest::adaptHistory(Log::TheConfig.hasAdaptToken);
a22e6cd3
AR
419}
420
aaf0559d
AR
421void
422HttpRequest::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
3ff65596
AR
432#endif
433
528b2c61 434bool
190154cf 435HttpRequest::multipartRangeRequest() const
528b2c61 436{
9ad511b6 437 return (range && range->specs.size() > 1);
528b2c61 438}
8000a965 439
58217e94 440bool
9e008dda
AJ
441HttpRequest::bodyNibbled() const
442{
aee3523a 443 return body_pipe != nullptr && body_pipe->consumedSize() > 0;
58217e94 444}
5f8252d2 445
f5e17947
CT
446void
447HttpRequest::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
456void
457HttpRequest::prepForDirect()
458{
459 peer_login = nullptr;
460 peer_domain = nullptr;
461 flags.auth_no_keytab = false;
462 debugs(11, 4, this);
463}
464
129fe2a1
CT
465void
466HttpRequest::clearError()
467{
83b053a0
CT
468 debugs(11, 7, "old: " << error);
469 error.clear();
129fe2a1
CT
470}
471
51b5dcf5
AJ
472void
473HttpRequest::packFirstLineInto(Packable * p, bool full_uri) const
8596962e 474{
851feda6 475 const SBuf tmp(full_uri ? effectiveRequestUri() : url.path());
8596962e 476
8596962e 477 // form HTTP request-line
51b5dcf5 478 p->appendf(SQUIDSBUFPH " " SQUIDSBUFPH " HTTP/%d.%d\r\n",
4391cd15 479 SQUIDSBUFPRINT(method.image()),
51b5dcf5 480 SQUIDSBUFPRINT(tmp),
4391cd15 481 http_ver.major, http_ver.minor);
8596962e 482}
8ddea643 483
484/*
684e9c80 485 * Indicate whether or not we would expect an entity-body
8ddea643 486 * along with this request
487 */
488bool
ced8def3 489HttpRequest::expectingBody(const HttpRequestMethod &, int64_t &theSize) const
8ddea643 490{
491 bool expectBody = false;
492
493 /*
684e9c80
HN
494 * Note: Checks for message validity is in clientIsContentLengthValid().
495 * this just checks if a entity-body is expected based on HTTP message syntax
8ddea643 496 */
684e9c80 497 if (header.chunked()) {
8ddea643 498 expectBody = true;
684e9c80
HN
499 theSize = -1;
500 } else if (content_length >= 0) {
8ddea643 501 expectBody = true;
684e9c80
HN
502 theSize = content_length;
503 } else {
8ddea643 504 expectBody = false;
684e9c80 505 // theSize undefined
8ddea643 506 }
507
508 return expectBody;
509}
c21ad0f5 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 */
517HttpRequest *
6c880a16 518HttpRequest::FromUrl(const SBuf &url, const MasterXaction::Pointer &mx, const HttpRequestMethod& method)
c21ad0f5 519{
5ceaee75 520 std::unique_ptr<HttpRequest> req(new HttpRequest(mx));
91489e45
AJ
521 if (req->url.parse(method, url)) {
522 req->method = method;
9157915c 523 return req.release();
91489e45 524 }
9157915c 525 return nullptr;
c21ad0f5 526}
527
6c880a16
AJ
528HttpRequest *
529HttpRequest::FromUrlXXX(const char * url, const MasterXaction::Pointer &mx, const HttpRequestMethod& method)
530{
531 return FromUrl(SBuf(url), mx, method);
532}
533
c2a7cefd 534/**
610ee341 535 * Are responses to this request possible cacheable ?
536 * If false then no matter what the response must not be cached.
537 */
538bool
c2a7cefd 539HttpRequest::maybeCacheable()
610ee341 540{
2962f8b8
AJ
541 // Intercepted request with Host: header which cannot be trusted.
542 // Because it failed verification, or someone bypassed the security tests
2f8abb64 543 // we cannot cache the response for sharing between clients.
2962f8b8 544 // TODO: update cache to store for particular clients only (going to same Host: and destination IP)
0d901ef4 545 if (!flags.hostVerified && (flags.intercepted || flags.interceptTproxy))
2962f8b8
AJ
546 return false;
547
4e3f4dc7 548 switch (url.getScheme()) {
c2a7cefd 549 case AnyP::PROTO_HTTP:
2c32c73e 550 case AnyP::PROTO_HTTPS:
c2a7cefd
AJ
551 if (!method.respMaybeCacheable())
552 return false;
9e008dda 553
38041f53
GD
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.
810d879f 559 if (!flags.ignoreCc && cache_control && cache_control->hasNoStore())
38041f53 560 return false;
c2a7cefd 561 break;
610ee341 562
c2a7cefd
AJ
563 case AnyP::PROTO_GOPHER:
564 if (!gopherCachable(this))
565 return false;
566 break;
610ee341 567
c2a7cefd 568 case AnyP::PROTO_CACHE_OBJECT:
60745f24 569 return false;
610ee341 570
f53969cc 571 //case AnyP::PROTO_FTP:
c2a7cefd
AJ
572 default:
573 break;
574 }
575
60745f24 576 return true;
610ee341 577}
d67acb4e 578
79c8035e
AR
579bool
580HttpRequest::conditional() const
581{
45e5102d 582 return flags.ims ||
789217a2
FC
583 header.has(Http::HdrType::IF_MATCH) ||
584 header.has(Http::HdrType::IF_NONE_MATCH);
79c8035e
AR
585}
586
d5964067 587void
4a3b98d7 588HttpRequest::recordLookup(const Dns::LookupDetails &dns)
3ff65596
AR
589{
590 if (dns.wait >= 0) { // known delay
fd9c47d1
AR
591 if (dnsWait >= 0) { // have recorded DNS wait before
592 debugs(78, 7, this << " " << dnsWait << " += " << dns);
3ff65596 593 dnsWait += dns.wait;
fd9c47d1
AR
594 } else {
595 debugs(78, 7, this << " " << dns);
3ff65596 596 dnsWait = dns.wait;
fd9c47d1 597 }
3ff65596
AR
598 }
599}
11e3fa1c
AJ
600
601int64_t
602HttpRequest::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 */
5e5f247c 607 if (rangeOffsetLimit != -2)
11e3fa1c
AJ
608 return rangeOffsetLimit;
609
610 rangeOffsetLimit = 0; // default value for rangeOffsetLimit
611
aee3523a 612 ACLFilledChecklist ch(nullptr, this, nullptr);
11e3fa1c
AJ
613 ch.src_addr = client_addr;
614 ch.my_addr = my_addr;
615
1328cfb7 616 for (AclSizeLimit *l = Config.rangeOffsetLimit; l; l = l -> next) {
11e3fa1c 617 /* if there is no ACL list or if the ACLs listed match use this limit value */
06bf5384 618 if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
11e3fa1c 619 rangeOffsetLimit = l->size; // may be -1
3755c7a5 620 debugs(58, 4, rangeOffsetLimit);
11e3fa1c
AJ
621 break;
622 }
623 }
624
625 return rangeOffsetLimit;
626}
655daa06 627
f0baf149
AR
628void
629HttpRequest::ignoreRange(const char *reason)
630{
631 if (range) {
632 debugs(73, 3, static_cast<void*>(range) << " for " << reason);
633 delete range;
aee3523a 634 range = nullptr;
f0baf149
AR
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
655daa06
AR
642bool
643HttpRequest::canHandle1xx() const
644{
645 // old clients do not support 1xx unless they sent Expect: 100-continue
789217a2
FC
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))
655daa06
AR
648 return false;
649
650 // others must support 1xx control messages
651 return true;
652}
582c2af2 653
4bd88eb4
AJ
654Http::StatusCode
655HttpRequest::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
4f1c93a7
EB
718bool
719HttpRequest::parseHeader(Http1::Parser &hp)
720{
721 Http::ContentLengthInterpreter clen;
722 return Message::parseHeader(hp, clen);
723}
724
725bool
726HttpRequest::parseHeader(const char *buffer, const size_t size)
727{
728 Http::ContentLengthInterpreter clen;
729 return header.parse(buffer, size, clen);
730}
731
582c2af2 732ConnStateData *
b0fb853f
A
733HttpRequest::pinnedConnection()
734{
582c2af2
FC
735 if (clientConnectionManager.valid() && clientConnectionManager->pinning.pinned)
736 return clientConnectionManager.get();
aee3523a 737 return nullptr;
582c2af2 738}
a8a0b1c2 739
851feda6 740const SBuf
a8a0b1c2
EC
741HttpRequest::storeId()
742{
743 if (store_id.size() != 0) {
851feda6 744 debugs(73, 3, "sent back store_id: " << store_id);
d7ef451f 745 return StringToSBuf(store_id);
a8a0b1c2 746 }
851feda6
AJ
747 debugs(73, 3, "sent back effectiveRequestUrl: " << effectiveRequestUri());
748 return effectiveRequestUri();
749}
a8a0b1c2 750
851feda6
AJ
751const SBuf &
752HttpRequest::effectiveRequestUri() const
753{
6b2b6cfe 754 if (method.id() == Http::METHOD_CONNECT || url.getScheme() == AnyP::PROTO_AUTHORITY_FORM)
851feda6
AJ
755 return url.authority(true); // host:port
756 return url.absolute();
a8a0b1c2 757}
f53969cc 758
75d47340
CT
759NotePairs::Pointer
760HttpRequest::notes()
761{
762 if (!theNotes)
763 theNotes = new NotePairs;
764 return theNotes;
765}
766
767void
768UpdateRequestNotes(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
cd4a5c60
CT
781void
782HttpRequest::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;
b6307671 803
cd4a5c60
CT
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;
cb365059 811 checklist->syncAle(this, nullptr);
06bf5384 812 flags.spoofClientIp = checklist->fastCheck().allowed();
cd4a5c60
CT
813 delete checklist;
814 } else
815 flags.spoofClientIp = true;
816 } else
817 flags.spoofClientIp = false;
818 }
819}
b6307671 820
bec110e4
EB
821char *
822HttpRequest::canonicalCleanUrl() const
823{
824 return urlCanonicalCleanWithoutRequest(effectiveRequestUri(), method, url.getScheme());
825}
826
ea35939b 827/// a helper for handling PortCfg cases of FindListeningPortAddress()
b6c0f0dc 828template <typename Filter>
ea35939b 829static const Ip::Address *
b6c0f0dc 830FindGoodListeningPortAddressInPort(const AnyP::PortCfgPointer &port, const Filter isGood)
ea35939b 831{
b6c0f0dc 832 return (port && isGood(port->s)) ? &port->s : nullptr;
ea35939b
A
833}
834
835/// a helper for handling Connection cases of FindListeningPortAddress()
b6c0f0dc 836template <typename Filter>
ea35939b 837static const Ip::Address *
b6c0f0dc 838FindGoodListeningPortAddressInConn(const Comm::ConnectionPointer &conn, const Filter isGood)
ea35939b 839{
b6c0f0dc 840 return (conn && isGood(conn->local)) ? &conn->local : nullptr;
ea35939b
A
841}
842
b6c0f0dc 843template <typename Filter>
ea35939b 844const Ip::Address *
b6c0f0dc 845FindGoodListeningPortAddress(const HttpRequest *callerRequest, const AccessLogEntry *ale, const Filter filter)
ea35939b
A
846{
847 // Check all sources of usable listening port information, giving
848 // HttpRequest and masterXaction a preference over ALE.
849
850 const HttpRequest *request = callerRequest;
851 if (!request && ale)
852 request = ale->request;
853 if (!request)
854 return nullptr; // not enough information
855
b6c0f0dc 856 auto ip = FindGoodListeningPortAddressInPort(request->masterXaction->squidPort, filter);
ea35939b 857 if (!ip && ale)
b6c0f0dc 858 ip = FindGoodListeningPortAddressInPort(ale->cache.port, filter);
ea35939b
A
859
860 // XXX: also handle PROXY protocol here when we have a flag to identify such request
861 if (ip || request->flags.interceptTproxy || request->flags.intercepted)
862 return ip;
863
864 /* handle non-intercepted cases that were not handled above */
b6c0f0dc 865 ip = FindGoodListeningPortAddressInConn(request->masterXaction->tcpClient, filter);
ea35939b 866 if (!ip && ale)
b6c0f0dc 867 ip = FindGoodListeningPortAddressInConn(ale->tcpClient, filter);
ea35939b
A
868 return ip; // may still be nil
869}
870
b6c0f0dc
AR
871const Ip::Address *
872FindListeningPortAddress(const HttpRequest *callerRequest, const AccessLogEntry *ale)
873{
874 return FindGoodListeningPortAddress(callerRequest, ale, [](const Ip::Address &address) {
875 // FindListeningPortAddress() callers do not want INADDR_ANY addresses
876 return !address.isAnyAddr();
877 });
878}
879
880unsigned short
881FindListeningPortNumber(const HttpRequest *callerRequest, const AccessLogEntry *ale)
882{
883 const auto ip = FindGoodListeningPortAddress(callerRequest, ale, [](const Ip::Address &address) {
884 return address.port() > 0;
885 });
886 return ip ? ip->port() : 0;
887}