]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpRequest.cc
Shuffle packerPrintf() to be Packer::Printf method
[thirdparty/squid.git] / src / HttpRequest.cc
1 /*
2 * Copyright (C) 1996-2015 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 "client_side.h"
16 #include "dns/LookupDetails.h"
17 #include "err_detail_type.h"
18 #include "globals.h"
19 #include "gopher.h"
20 #include "http.h"
21 #include "http/one/RequestParser.h"
22 #include "HttpHdrCc.h"
23 #include "HttpHeaderRange.h"
24 #include "HttpRequest.h"
25 #include "log/Config.h"
26 #include "MemBuf.h"
27 #include "SquidConfig.h"
28 #include "Store.h"
29 #include "URL.h"
30
31 #if USE_AUTH
32 #include "auth/UserRequest.h"
33 #endif
34 #if ICAP_CLIENT
35 #include "adaptation/icap/icap_log.h"
36 #endif
37
38 HttpRequest::HttpRequest() :
39 HttpMsg(hoRequest)
40 {
41 init();
42 }
43
44 HttpRequest::HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath) :
45 HttpMsg(hoRequest)
46 {
47 static unsigned int id = 1;
48 debugs(93,7, HERE << "constructed, this=" << this << " id=" << ++id);
49 init();
50 initHTTP(aMethod, aProtocol, aUrlpath);
51 }
52
53 HttpRequest::~HttpRequest()
54 {
55 clean();
56 debugs(93,7, HERE << "destructed, this=" << this);
57 }
58
59 void
60 HttpRequest::initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath)
61 {
62 method = aMethod;
63 url.setScheme(aProtocol);
64 urlpath = aUrlpath;
65 }
66
67 void
68 HttpRequest::init()
69 {
70 method = Http::METHOD_NONE;
71 url.clear();
72 urlpath = NULL;
73 host[0] = '\0';
74 host_is_numeric = -1;
75 #if USE_AUTH
76 auth_user_request = NULL;
77 #endif
78 port = 0;
79 canonical = NULL;
80 memset(&flags, '\0', sizeof(flags));
81 range = NULL;
82 ims = -1;
83 imslen = 0;
84 lastmod = -1;
85 client_addr.setEmpty();
86 my_addr.setEmpty();
87 body_pipe = NULL;
88 // hier
89 dnsWait = -1;
90 errType = ERR_NONE;
91 errDetail = ERR_DETAIL_NONE;
92 peer_login = NULL; // not allocated/deallocated by this class
93 peer_domain = NULL; // not allocated/deallocated by this class
94 peer_host = NULL;
95 vary_headers = NULL;
96 myportname = null_string;
97 tag = null_string;
98 #if USE_AUTH
99 extacl_user = null_string;
100 extacl_passwd = null_string;
101 #endif
102 extacl_log = null_string;
103 extacl_message = null_string;
104 pstate = psReadyToParseStartLine;
105 #if FOLLOW_X_FORWARDED_FOR
106 indirect_client_addr.setEmpty();
107 #endif /* FOLLOW_X_FORWARDED_FOR */
108 #if USE_ADAPTATION
109 adaptHistory_ = NULL;
110 #endif
111 #if ICAP_CLIENT
112 icapHistory_ = NULL;
113 #endif
114 rangeOffsetLimit = -2; //a value of -2 means not checked yet
115 forcedBodyContinuation = false;
116 }
117
118 void
119 HttpRequest::clean()
120 {
121 // we used to assert that the pipe is NULL, but now the request only
122 // points to a pipe that is owned and initiated by another object.
123 body_pipe = NULL;
124 #if USE_AUTH
125 auth_user_request = NULL;
126 #endif
127 safe_free(canonical);
128
129 safe_free(vary_headers);
130
131 url.clear();
132 urlpath.clean();
133
134 header.clean();
135
136 if (cache_control) {
137 delete cache_control;
138 cache_control = NULL;
139 }
140
141 if (range) {
142 delete range;
143 range = NULL;
144 }
145
146 myportname.clean();
147
148 notes = NULL;
149
150 tag.clean();
151 #if USE_AUTH
152 extacl_user.clean();
153 extacl_passwd.clean();
154 #endif
155 extacl_log.clean();
156
157 extacl_message.clean();
158
159 etag.clean();
160
161 #if USE_ADAPTATION
162 adaptHistory_ = NULL;
163 #endif
164 #if ICAP_CLIENT
165 icapHistory_ = NULL;
166 #endif
167 }
168
169 void
170 HttpRequest::reset()
171 {
172 clean();
173 init();
174 }
175
176 HttpRequest *
177 HttpRequest::clone() const
178 {
179 HttpRequest *copy = new HttpRequest(method, url.getScheme(), urlpath.termedBuf());
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.userInfo(url.userInfo());
189 strncpy(copy->host, host, sizeof(host)); // SQUIDHOSTNAMELEN
190 copy->host_addr = host_addr;
191
192 copy->port = port;
193 // urlPath handled in ctor
194 copy->canonical = canonical ? xstrdup(canonical) : NULL;
195
196 // range handled in hdrCacheInit()
197 copy->ims = ims;
198 copy->imslen = imslen;
199 copy->hier = hier; // Is it safe to copy? Should we?
200
201 copy->errType = errType;
202
203 // XXX: what to do with copy->peer_login?
204
205 copy->lastmod = lastmod;
206 copy->etag = etag;
207 copy->vary_headers = vary_headers ? xstrdup(vary_headers) : NULL;
208 // XXX: what to do with copy->peer_domain?
209
210 copy->tag = tag;
211 copy->extacl_log = extacl_log;
212 copy->extacl_message = extacl_message;
213
214 const bool inheritWorked = copy->inheritProperties(this);
215 assert(inheritWorked);
216
217 return copy;
218 }
219
220 bool
221 HttpRequest::inheritProperties(const HttpMsg *aMsg)
222 {
223 const HttpRequest* aReq = dynamic_cast<const HttpRequest*>(aMsg);
224 if (!aReq)
225 return false;
226
227 client_addr = aReq->client_addr;
228 #if FOLLOW_X_FORWARDED_FOR
229 indirect_client_addr = aReq->indirect_client_addr;
230 #endif
231 my_addr = aReq->my_addr;
232
233 dnsWait = aReq->dnsWait;
234
235 #if USE_ADAPTATION
236 adaptHistory_ = aReq->adaptHistory();
237 #endif
238 #if ICAP_CLIENT
239 icapHistory_ = aReq->icapHistory();
240 #endif
241
242 // This may be too conservative for the 204 No Content case
243 // may eventually need cloneNullAdaptationImmune() for that.
244 flags = aReq->flags.cloneAdaptationImmune();
245
246 errType = aReq->errType;
247 errDetail = aReq->errDetail;
248 #if USE_AUTH
249 auth_user_request = aReq->auth_user_request;
250 extacl_user = aReq->extacl_user;
251 extacl_passwd = aReq->extacl_passwd;
252 #endif
253
254 myportname = aReq->myportname;
255
256 forcedBodyContinuation = aReq->forcedBodyContinuation;
257
258 // main property is which connection the request was received on (if any)
259 clientConnectionManager = aReq->clientConnectionManager;
260
261 notes = aReq->notes;
262 return true;
263 }
264
265 /**
266 * Checks the first line of an HTTP request is valid
267 * currently just checks the request method is present.
268 *
269 * NP: Other errors are left for detection later in the parse.
270 */
271 bool
272 HttpRequest::sanityCheckStartLine(const char *buf, const size_t hdr_len, Http::StatusCode *error)
273 {
274 // content is long enough to possibly hold a reply
275 // 2 being magic size of a 1-byte request method plus space delimiter
276 if (hdr_len < 2) {
277 // this is ony a real error if the headers apparently complete.
278 if (hdr_len > 0) {
279 debugs(58, 3, HERE << "Too large request header (" << hdr_len << " bytes)");
280 *error = Http::scInvalidHeader;
281 }
282 return false;
283 }
284
285 /* See if the request buffer starts with a non-whitespace HTTP request 'method'. */
286 HttpRequestMethod m;
287 m.HttpRequestMethodXXX(buf);
288 if (m == Http::METHOD_NONE) {
289 debugs(73, 3, "HttpRequest::sanityCheckStartLine: did not find HTTP request method");
290 *error = Http::scInvalidHeader;
291 return false;
292 }
293
294 return true;
295 }
296
297 bool
298 HttpRequest::parseFirstLine(const char *start, const char *end)
299 {
300 method.HttpRequestMethodXXX(start);
301
302 if (method == Http::METHOD_NONE)
303 return false;
304
305 // XXX: performance regression, strcspn() over the method bytes a second time.
306 // cheaper than allocate+copy+deallocate cycle to SBuf convert a piece of start.
307 const char *t = start + strcspn(start, w_space);
308
309 start = t + strspn(t, w_space); // skip w_space after method
310
311 const char *ver = findTrailingHTTPVersion(start, end);
312
313 if (ver) {
314 end = ver - 1;
315
316 while (xisspace(*end)) // find prev non-space
317 --end;
318
319 ++end; // back to space
320
321 if (2 != sscanf(ver + 5, "%d.%d", &http_ver.major, &http_ver.minor)) {
322 debugs(73, DBG_IMPORTANT, "parseRequestLine: Invalid HTTP identifier.");
323 return false;
324 }
325 } else {
326 http_ver.major = 0;
327 http_ver.minor = 9;
328 }
329
330 if (end < start) // missing URI
331 return false;
332
333 char save = *end;
334
335 * (char *) end = '\0'; // temp terminate URI, XXX dangerous?
336
337 HttpRequest *tmp = urlParse(method, (char *) start, this);
338
339 * (char *) end = save;
340
341 if (NULL == tmp)
342 return false;
343
344 return true;
345 }
346
347 bool
348 HttpRequest::parseHeader(Http1::RequestParser &hp)
349 {
350 // HTTP/1 message contains "zero or more header fields"
351 // zero does not need parsing
352 if (!hp.headerBlockSize())
353 return true;
354
355 // XXX: c_str() reallocates. performance regression.
356 const bool result = header.parse(hp.mimeHeader().c_str(), hp.headerBlockSize());
357
358 if (result)
359 hdrCacheInit();
360
361 return result;
362 }
363
364 /* swaps out request using httpRequestPack */
365 void
366 HttpRequest::swapOut(StoreEntry * e)
367 {
368 Packer p;
369 assert(e);
370 packerToStoreInit(&p, e);
371 pack(&p);
372 }
373
374 /* packs request-line and headers, appends <crlf> terminator */
375 void
376 HttpRequest::pack(Packer * p)
377 {
378 assert(p);
379 /* pack request-line */
380 p->Printf(SQUIDSBUFPH " " SQUIDSTRINGPH " HTTP/%d.%d\r\n",
381 SQUIDSBUFPRINT(method.image()), SQUIDSTRINGPRINT(urlpath),
382 http_ver.major, http_ver.minor);
383 /* headers */
384 header.packInto(p);
385 /* trailer */
386 p->append("\r\n", 2);
387 }
388
389 /*
390 * A wrapper for debugObj()
391 */
392 void
393 httpRequestPack(void *obj, Packer *p)
394 {
395 HttpRequest *request = static_cast<HttpRequest*>(obj);
396 request->pack(p);
397 }
398
399 /* returns the length of request line + headers + crlf */
400 int
401 HttpRequest::prefixLen()
402 {
403 return method.image().length() + 1 +
404 urlpath.size() + 1 +
405 4 + 1 + 3 + 2 +
406 header.len + 2;
407 }
408
409 /* sync this routine when you update HttpRequest struct */
410 void
411 HttpRequest::hdrCacheInit()
412 {
413 HttpMsg::hdrCacheInit();
414
415 assert(!range);
416 range = header.getRange();
417 }
418
419 #if ICAP_CLIENT
420 Adaptation::Icap::History::Pointer
421 HttpRequest::icapHistory() const
422 {
423 if (!icapHistory_) {
424 if (Log::TheConfig.hasIcapToken || IcapLogfileStatus == LOG_ENABLE) {
425 icapHistory_ = new Adaptation::Icap::History();
426 debugs(93,4, HERE << "made " << icapHistory_ << " for " << this);
427 }
428 }
429
430 return icapHistory_;
431 }
432 #endif
433
434 #if USE_ADAPTATION
435 Adaptation::History::Pointer
436 HttpRequest::adaptHistory(bool createIfNone) const
437 {
438 if (!adaptHistory_ && createIfNone) {
439 adaptHistory_ = new Adaptation::History();
440 debugs(93,4, HERE << "made " << adaptHistory_ << " for " << this);
441 }
442
443 return adaptHistory_;
444 }
445
446 Adaptation::History::Pointer
447 HttpRequest::adaptLogHistory() const
448 {
449 return HttpRequest::adaptHistory(Log::TheConfig.hasAdaptToken);
450 }
451
452 void
453 HttpRequest::adaptHistoryImport(const HttpRequest &them)
454 {
455 if (!adaptHistory_) {
456 adaptHistory_ = them.adaptHistory_; // may be nil
457 } else {
458 // check that histories did not diverge
459 Must(!them.adaptHistory_ || them.adaptHistory_ == adaptHistory_);
460 }
461 }
462
463 #endif
464
465 bool
466 HttpRequest::multipartRangeRequest() const
467 {
468 return (range && range->specs.size() > 1);
469 }
470
471 bool
472 HttpRequest::bodyNibbled() const
473 {
474 return body_pipe != NULL && body_pipe->consumedSize() > 0;
475 }
476
477 void
478 HttpRequest::detailError(err_type aType, int aDetail)
479 {
480 if (errType || errDetail)
481 debugs(11, 5, HERE << "old error details: " << errType << '/' << errDetail);
482 debugs(11, 5, HERE << "current error details: " << aType << '/' << aDetail);
483 // checking type and detail separately may cause inconsistency, but
484 // may result in more details available if they only become available later
485 if (!errType)
486 errType = aType;
487 if (!errDetail)
488 errDetail = aDetail;
489 }
490
491 void
492 HttpRequest::clearError()
493 {
494 debugs(11, 7, HERE << "old error details: " << errType << '/' << errDetail);
495 errType = ERR_NONE;
496 errDetail = ERR_DETAIL_NONE;
497 }
498
499 const char *HttpRequest::packableURI(bool full_uri) const
500 {
501 if (full_uri)
502 return urlCanonical((HttpRequest*)this);
503
504 if (urlpath.size())
505 return urlpath.termedBuf();
506
507 return "/";
508 }
509
510 void HttpRequest::packFirstLineInto(Packer * p, bool full_uri) const
511 {
512 // form HTTP request-line
513 p->Printf(SQUIDSBUFPH " %s HTTP/%d.%d\r\n",
514 SQUIDSBUFPRINT(method.image()),
515 packableURI(full_uri),
516 http_ver.major, http_ver.minor);
517 }
518
519 /*
520 * Indicate whether or not we would expect an entity-body
521 * along with this request
522 */
523 bool
524 HttpRequest::expectingBody(const HttpRequestMethod &, int64_t &theSize) const
525 {
526 bool expectBody = false;
527
528 /*
529 * Note: Checks for message validity is in clientIsContentLengthValid().
530 * this just checks if a entity-body is expected based on HTTP message syntax
531 */
532 if (header.chunked()) {
533 expectBody = true;
534 theSize = -1;
535 } else if (content_length >= 0) {
536 expectBody = true;
537 theSize = content_length;
538 } else {
539 expectBody = false;
540 // theSize undefined
541 }
542
543 return expectBody;
544 }
545
546 /*
547 * Create a Request from a URL and METHOD.
548 *
549 * If the METHOD is CONNECT, then a host:port pair is looked for instead of a URL.
550 * If the request cannot be created cleanly, NULL is returned
551 */
552 HttpRequest *
553 HttpRequest::CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method)
554 {
555 return urlParse(method, url, NULL);
556 }
557
558 /*
559 * Create a Request from a URL.
560 *
561 * If the request cannot be created cleanly, NULL is returned
562 */
563 HttpRequest *
564 HttpRequest::CreateFromUrl(char * url)
565 {
566 return urlParse(Http::METHOD_GET, url, NULL);
567 }
568
569 /**
570 * Are responses to this request possible cacheable ?
571 * If false then no matter what the response must not be cached.
572 */
573 bool
574 HttpRequest::maybeCacheable()
575 {
576 // Intercepted request with Host: header which cannot be trusted.
577 // Because it failed verification, or someone bypassed the security tests
578 // we cannot cache the reponse for sharing between clients.
579 // TODO: update cache to store for particular clients only (going to same Host: and destination IP)
580 if (!flags.hostVerified && (flags.intercepted || flags.interceptTproxy))
581 return false;
582
583 switch (url.getScheme()) {
584 case AnyP::PROTO_HTTP:
585 case AnyP::PROTO_HTTPS:
586 if (!method.respMaybeCacheable())
587 return false;
588
589 // XXX: this would seem the correct place to detect request cache-controls
590 // no-store, private and related which block cacheability
591 break;
592
593 case AnyP::PROTO_GOPHER:
594 if (!gopherCachable(this))
595 return false;
596 break;
597
598 case AnyP::PROTO_CACHE_OBJECT:
599 return false;
600
601 //case AnyP::PROTO_FTP:
602 default:
603 break;
604 }
605
606 return true;
607 }
608
609 bool
610 HttpRequest::conditional() const
611 {
612 return flags.ims ||
613 header.has(HDR_IF_MATCH) ||
614 header.has(HDR_IF_NONE_MATCH);
615 }
616
617 void
618 HttpRequest::recordLookup(const Dns::LookupDetails &dns)
619 {
620 if (dns.wait >= 0) { // known delay
621 if (dnsWait >= 0) // have recorded DNS wait before
622 dnsWait += dns.wait;
623 else
624 dnsWait = dns.wait;
625 }
626 }
627
628 int64_t
629 HttpRequest::getRangeOffsetLimit()
630 {
631 /* -2 is the starting value of rangeOffsetLimit.
632 * If it is -2, that means we haven't checked it yet.
633 * Otherwise, return the current value */
634 if (rangeOffsetLimit != -2)
635 return rangeOffsetLimit;
636
637 rangeOffsetLimit = 0; // default value for rangeOffsetLimit
638
639 ACLFilledChecklist ch(NULL, this, NULL);
640 ch.src_addr = client_addr;
641 ch.my_addr = my_addr;
642
643 for (AclSizeLimit *l = Config.rangeOffsetLimit; l; l = l -> next) {
644 /* if there is no ACL list or if the ACLs listed match use this limit value */
645 if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
646 debugs(58, 4, HERE << "rangeOffsetLimit=" << rangeOffsetLimit);
647 rangeOffsetLimit = l->size; // may be -1
648 break;
649 }
650 }
651
652 return rangeOffsetLimit;
653 }
654
655 void
656 HttpRequest::ignoreRange(const char *reason)
657 {
658 if (range) {
659 debugs(73, 3, static_cast<void*>(range) << " for " << reason);
660 delete range;
661 range = NULL;
662 }
663 // Some callers also reset isRanged but it may not be safe for all callers:
664 // isRanged is used to determine whether a weak ETag comparison is allowed,
665 // and that check should not ignore the Range header if it was present.
666 // TODO: Some callers also delete HDR_RANGE, HDR_REQUEST_RANGE. Should we?
667 }
668
669 bool
670 HttpRequest::canHandle1xx() const
671 {
672 // old clients do not support 1xx unless they sent Expect: 100-continue
673 // (we reject all other HDR_EXPECT values so just check for HDR_EXPECT)
674 if (http_ver <= Http::ProtocolVersion(1,0) && !header.has(HDR_EXPECT))
675 return false;
676
677 // others must support 1xx control messages
678 return true;
679 }
680
681 ConnStateData *
682 HttpRequest::pinnedConnection()
683 {
684 if (clientConnectionManager.valid() && clientConnectionManager->pinning.pinned)
685 return clientConnectionManager.get();
686 return NULL;
687 }
688
689 const char *
690 HttpRequest::storeId()
691 {
692 if (store_id.size() != 0) {
693 debugs(73, 3, "sent back store_id:" << store_id);
694
695 return store_id.termedBuf();
696 }
697 debugs(73, 3, "sent back canonicalUrl:" << urlCanonical(this) );
698
699 return urlCanonical(this);
700 }
701