]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpRequest.cc
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 73 HTTP Request */
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"
21 #include "HttpHdrCc.h"
22 #include "HttpHeaderRange.h"
23 #include "HttpRequest.h"
24 #include "log/Config.h"
26 #include "SquidConfig.h"
31 #include "auth/UserRequest.h"
34 #include "adaptation/icap/icap_log.h"
37 HttpRequest::HttpRequest() :
43 HttpRequest::HttpRequest(const HttpRequestMethod
& aMethod
, AnyP::ProtocolType aProtocol
, const char *aUrlpath
) :
46 static unsigned int id
= 1;
47 debugs(93,7, HERE
<< "constructed, this=" << this << " id=" << ++id
);
49 initHTTP(aMethod
, aProtocol
, aUrlpath
);
52 HttpRequest::~HttpRequest()
55 debugs(93,7, HERE
<< "destructed, this=" << this);
59 HttpRequest::initHTTP(const HttpRequestMethod
& aMethod
, AnyP::ProtocolType aProtocol
, const char *aUrlpath
)
62 url
.setScheme(aProtocol
);
69 method
= Http::METHOD_NONE
;
76 auth_user_request
= NULL
;
80 memset(&flags
, '\0', sizeof(flags
));
85 client_addr
.setEmpty();
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
96 myportname
= null_string
;
99 extacl_user
= null_string
;
100 extacl_passwd
= null_string
;
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 */
109 adaptHistory_
= NULL
;
114 rangeOffsetLimit
= -2; //a value of -2 means not checked yet
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.
124 auth_user_request
= NULL
;
126 safe_free(canonical
);
128 safe_free(vary_headers
);
136 delete cache_control
;
137 cache_control
= NULL
;
152 extacl_passwd
.clean();
156 extacl_message
.clean();
161 adaptHistory_
= NULL
;
176 HttpRequest::clone() const
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
;
187 strncpy(copy
->login
, login
, sizeof(login
)); // MAX_LOGIN_SZ
188 strncpy(copy
->host
, host
, sizeof(host
)); // SQUIDHOSTNAMELEN
189 copy
->host_addr
= host_addr
;
192 // urlPath handled in ctor
193 copy
->canonical
= canonical
? xstrdup(canonical
) : NULL
;
195 // range handled in hdrCacheInit()
197 copy
->imslen
= imslen
;
198 copy
->hier
= hier
; // Is it safe to copy? Should we?
200 copy
->errType
= errType
;
202 // XXX: what to do with copy->peer_login?
204 copy
->lastmod
= lastmod
;
206 copy
->vary_headers
= vary_headers
? xstrdup(vary_headers
) : NULL
;
207 // XXX: what to do with copy->peer_domain?
210 copy
->extacl_log
= extacl_log
;
211 copy
->extacl_message
= extacl_message
;
213 const bool inheritWorked
= copy
->inheritProperties(this);
214 assert(inheritWorked
);
220 HttpRequest::inheritProperties(const HttpMsg
*aMsg
)
222 const HttpRequest
* aReq
= dynamic_cast<const HttpRequest
*>(aMsg
);
226 client_addr
= aReq
->client_addr
;
227 #if FOLLOW_X_FORWARDED_FOR
228 indirect_client_addr
= aReq
->indirect_client_addr
;
230 my_addr
= aReq
->my_addr
;
232 dnsWait
= aReq
->dnsWait
;
235 adaptHistory_
= aReq
->adaptHistory();
238 icapHistory_
= aReq
->icapHistory();
241 // This may be too conservative for the 204 No Content case
242 // may eventually need cloneNullAdaptationImmune() for that.
243 flags
= aReq
->flags
.cloneAdaptationImmune();
245 errType
= aReq
->errType
;
246 errDetail
= aReq
->errDetail
;
248 auth_user_request
= aReq
->auth_user_request
;
249 extacl_user
= aReq
->extacl_user
;
250 extacl_passwd
= aReq
->extacl_passwd
;
253 myportname
= aReq
->myportname
;
255 // main property is which connection the request was received on (if any)
256 clientConnectionManager
= aReq
->clientConnectionManager
;
263 * Checks the first line of an HTTP request is valid
264 * currently just checks the request method is present.
266 * NP: Other errors are left for detection later in the parse.
269 HttpRequest::sanityCheckStartLine(MemBuf
*buf
, const size_t hdr_len
, Http::StatusCode
*error
)
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.
276 debugs(58, 3, HERE
<< "Too large request header (" << hdr_len
<< " bytes)");
277 *error
= Http::scInvalidHeader
;
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
;
293 HttpRequest::parseFirstLine(const char *start
, const char *end
)
295 const char *t
= start
+ strcspn(start
, w_space
);
296 method
= HttpRequestMethod(start
, t
);
298 if (method
== Http::METHOD_NONE
)
301 start
= t
+ strspn(t
, w_space
);
303 const char *ver
= findTrailingHTTPVersion(start
, end
);
308 while (xisspace(*end
)) // find prev non-space
311 ++end
; // back to space
313 if (2 != sscanf(ver
+ 5, "%d.%d", &http_ver
.major
, &http_ver
.minor
)) {
314 debugs(73, DBG_IMPORTANT
, "parseRequestLine: Invalid HTTP identifier.");
322 if (end
< start
) // missing URI
327 * (char *) end
= '\0'; // temp terminate URI, XXX dangerous?
329 HttpRequest
*tmp
= urlParse(method
, (char *) start
, this);
331 * (char *) end
= save
;
340 HttpRequest::parseHeader(const char *parse_start
, int len
)
342 const char *blk_start
, *blk_end
;
344 if (!httpMsgIsolateHeaders(&parse_start
, len
, &blk_start
, &blk_end
))
347 int result
= header
.parse(blk_start
, blk_end
);
355 /* swaps out request using httpRequestPack */
357 HttpRequest::swapOut(StoreEntry
* e
)
361 packerToStoreInit(&p
, e
);
366 /* packs request-line and headers, appends <crlf> terminator */
368 HttpRequest::pack(Packer
* 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
);
378 packerAppend(p
, "\r\n", 2);
382 * A wrapper for debugObj()
385 httpRequestPack(void *obj
, Packer
*p
)
387 HttpRequest
*request
= static_cast<HttpRequest
*>(obj
);
391 /* returns the length of request line + headers + crlf */
393 HttpRequest::prefixLen()
395 return method
.image().length() + 1 +
401 /* sync this routine when you update HttpRequest struct */
403 HttpRequest::hdrCacheInit()
405 HttpMsg::hdrCacheInit();
408 range
= header
.getRange();
412 Adaptation::Icap::History::Pointer
413 HttpRequest::icapHistory() const
416 if (Log::TheConfig
.hasIcapToken
|| IcapLogfileStatus
== LOG_ENABLE
) {
417 icapHistory_
= new Adaptation::Icap::History();
418 debugs(93,4, HERE
<< "made " << icapHistory_
<< " for " << this);
427 Adaptation::History::Pointer
428 HttpRequest::adaptHistory(bool createIfNone
) const
430 if (!adaptHistory_
&& createIfNone
) {
431 adaptHistory_
= new Adaptation::History();
432 debugs(93,4, HERE
<< "made " << adaptHistory_
<< " for " << this);
435 return adaptHistory_
;
438 Adaptation::History::Pointer
439 HttpRequest::adaptLogHistory() const
441 return HttpRequest::adaptHistory(Log::TheConfig
.hasAdaptToken
);
445 HttpRequest::adaptHistoryImport(const HttpRequest
&them
)
447 if (!adaptHistory_
) {
448 adaptHistory_
= them
.adaptHistory_
; // may be nil
450 // check that histories did not diverge
451 Must(!them
.adaptHistory_
|| them
.adaptHistory_
== adaptHistory_
);
458 HttpRequest::multipartRangeRequest() const
460 return (range
&& range
->specs
.size() > 1);
464 HttpRequest::bodyNibbled() const
466 return body_pipe
!= NULL
&& body_pipe
->consumedSize() > 0;
470 HttpRequest::detailError(err_type aType
, int aDetail
)
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
484 HttpRequest::clearError()
486 debugs(11, 7, HERE
<< "old error details: " << errType
<< '/' << errDetail
);
488 errDetail
= ERR_DETAIL_NONE
;
491 const char *HttpRequest::packableURI(bool full_uri
) const
494 return urlCanonical((HttpRequest
*)this);
497 return urlpath
.termedBuf();
502 void HttpRequest::packFirstLineInto(Packer
* p
, bool full_uri
) const
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
);
512 * Indicate whether or not we would expect an entity-body
513 * along with this request
516 HttpRequest::expectingBody(const HttpRequestMethod
& unused
, int64_t& theSize
) const
518 bool expectBody
= false;
521 * Note: Checks for message validity is in clientIsContentLengthValid().
522 * this just checks if a entity-body is expected based on HTTP message syntax
524 if (header
.chunked()) {
527 } else if (content_length
>= 0) {
529 theSize
= content_length
;
539 * Create a Request from a URL and METHOD.
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
545 HttpRequest::CreateFromUrlAndMethod(char * url
, const HttpRequestMethod
& method
)
547 return urlParse(method
, url
, NULL
);
551 * Create a Request from a URL.
553 * If the request cannot be created cleanly, NULL is returned
556 HttpRequest::CreateFromUrl(char * url
)
558 return urlParse(Http::METHOD_GET
, url
, NULL
);
562 * Are responses to this request possible cacheable ?
563 * If false then no matter what the response must not be cached.
566 HttpRequest::maybeCacheable()
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
))
575 switch (url
.getScheme()) {
576 case AnyP::PROTO_HTTP
:
577 case AnyP::PROTO_HTTPS
:
578 if (!method
.respMaybeCacheable())
581 // XXX: this would seem the correct place to detect request cache-controls
582 // no-store, private and related which block cacheability
585 case AnyP::PROTO_GOPHER
:
586 if (!gopherCachable(this))
590 case AnyP::PROTO_CACHE_OBJECT
:
593 //case AnyP::PROTO_FTP:
602 HttpRequest::conditional() const
605 header
.has(HDR_IF_MATCH
) ||
606 header
.has(HDR_IF_NONE_MATCH
);
610 HttpRequest::recordLookup(const DnsLookupDetails
&dns
)
612 if (dns
.wait
>= 0) { // known delay
613 if (dnsWait
>= 0) // have recorded DNS wait before
621 HttpRequest::getRangeOffsetLimit()
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
;
629 rangeOffsetLimit
= 0; // default value for rangeOffsetLimit
631 ACLFilledChecklist
ch(NULL
, this, NULL
);
632 ch
.src_addr
= client_addr
;
633 ch
.my_addr
= my_addr
;
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
644 return rangeOffsetLimit
;
648 HttpRequest::ignoreRange(const char *reason
)
651 debugs(73, 3, static_cast<void*>(range
) << " for " << reason
);
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?
662 HttpRequest::canHandle1xx() const
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
))
669 // others must support 1xx control messages
674 HttpRequest::pinnedConnection()
676 if (clientConnectionManager
.valid() && clientConnectionManager
->pinning
.pinned
)
677 return clientConnectionManager
.get();
682 HttpRequest::storeId()
684 if (store_id
.size() != 0) {
685 debugs(73, 3, "sent back store_id:" << store_id
);
687 return store_id
.termedBuf();
689 debugs(73, 3, "sent back canonicalUrl:" << urlCanonical(this) );
691 return urlCanonical(this);