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