]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpRequest.cc
e38e69f9f01a32b905f69991cee66351baab6b3c
2 * DEBUG: section 73 HTTP Request
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
31 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
35 #include "AccessLogEntry.h"
36 #include "acl/AclSizeLimit.h"
37 #include "acl/FilledChecklist.h"
38 #include "client_side.h"
39 #include "DnsLookupDetails.h"
40 #include "err_detail_type.h"
44 #include "http/Http1Parser.h"
45 #include "HttpHdrCc.h"
46 #include "HttpHeaderRange.h"
47 #include "HttpRequest.h"
48 #include "log/Config.h"
50 #include "SquidConfig.h"
55 #include "auth/UserRequest.h"
58 #include "adaptation/icap/icap_log.h"
61 HttpRequest::HttpRequest() :
67 HttpRequest::HttpRequest(const HttpRequestMethod
& aMethod
, AnyP::ProtocolType aProtocol
, const char *aUrlpath
) :
70 static unsigned int id
= 1;
71 debugs(93,7, HERE
<< "constructed, this=" << this << " id=" << ++id
);
73 initHTTP(aMethod
, aProtocol
, aUrlpath
);
76 HttpRequest::~HttpRequest()
79 debugs(93,7, HERE
<< "destructed, this=" << this);
83 HttpRequest::initHTTP(const HttpRequestMethod
& aMethod
, AnyP::ProtocolType aProtocol
, const char *aUrlpath
)
93 method
= Http::METHOD_NONE
;
94 protocol
= AnyP::PROTO_NONE
;
100 auth_user_request
= NULL
;
104 memset(&flags
, '\0', sizeof(flags
));
109 client_addr
.setEmpty();
115 errDetail
= ERR_DETAIL_NONE
;
116 peer_login
= NULL
; // not allocated/deallocated by this class
117 peer_domain
= NULL
; // not allocated/deallocated by this class
120 myportname
= null_string
;
123 extacl_user
= null_string
;
124 extacl_passwd
= null_string
;
126 extacl_log
= null_string
;
127 extacl_message
= null_string
;
128 pstate
= psReadyToParseStartLine
;
129 #if FOLLOW_X_FORWARDED_FOR
130 indirect_client_addr
.setEmpty();
131 #endif /* FOLLOW_X_FORWARDED_FOR */
133 adaptHistory_
= NULL
;
138 rangeOffsetLimit
= -2; //a value of -2 means not checked yet
144 // we used to assert that the pipe is NULL, but now the request only
145 // points to a pipe that is owned and initiated by another object.
148 auth_user_request
= NULL
;
150 safe_free(canonical
);
152 safe_free(vary_headers
);
159 delete cache_control
;
160 cache_control
= NULL
;
175 extacl_passwd
.clean();
179 extacl_message
.clean();
184 adaptHistory_
= NULL
;
199 HttpRequest::clone() const
201 HttpRequest
*copy
= new HttpRequest(method
, protocol
, urlpath
.termedBuf());
202 // TODO: move common cloning clone to Msg::copyTo() or copy ctor
203 copy
->header
.append(&header
);
204 copy
->hdrCacheInit();
205 copy
->hdr_sz
= hdr_sz
;
206 copy
->http_ver
= http_ver
;
207 copy
->pstate
= pstate
; // TODO: should we assert a specific state here?
208 copy
->body_pipe
= body_pipe
;
210 strncpy(copy
->login
, login
, sizeof(login
)); // MAX_LOGIN_SZ
211 strncpy(copy
->host
, host
, sizeof(host
)); // SQUIDHOSTNAMELEN
212 copy
->host_addr
= host_addr
;
215 // urlPath handled in ctor
216 copy
->canonical
= canonical
? xstrdup(canonical
) : NULL
;
218 // range handled in hdrCacheInit()
220 copy
->imslen
= imslen
;
221 copy
->hier
= hier
; // Is it safe to copy? Should we?
223 copy
->errType
= errType
;
225 // XXX: what to do with copy->peer_login?
227 copy
->lastmod
= lastmod
;
229 copy
->vary_headers
= vary_headers
? xstrdup(vary_headers
) : NULL
;
230 // XXX: what to do with copy->peer_domain?
233 copy
->extacl_log
= extacl_log
;
234 copy
->extacl_message
= extacl_message
;
236 const bool inheritWorked
= copy
->inheritProperties(this);
237 assert(inheritWorked
);
243 HttpRequest::inheritProperties(const HttpMsg
*aMsg
)
245 const HttpRequest
* aReq
= dynamic_cast<const HttpRequest
*>(aMsg
);
249 client_addr
= aReq
->client_addr
;
250 #if FOLLOW_X_FORWARDED_FOR
251 indirect_client_addr
= aReq
->indirect_client_addr
;
253 my_addr
= aReq
->my_addr
;
255 dnsWait
= aReq
->dnsWait
;
258 adaptHistory_
= aReq
->adaptHistory();
261 icapHistory_
= aReq
->icapHistory();
264 // This may be too conservative for the 204 No Content case
265 // may eventually need cloneNullAdaptationImmune() for that.
266 flags
= aReq
->flags
.cloneAdaptationImmune();
268 errType
= aReq
->errType
;
269 errDetail
= aReq
->errDetail
;
271 auth_user_request
= aReq
->auth_user_request
;
272 extacl_user
= aReq
->extacl_user
;
273 extacl_passwd
= aReq
->extacl_passwd
;
276 myportname
= aReq
->myportname
;
278 // main property is which connection the request was received on (if any)
279 clientConnectionManager
= aReq
->clientConnectionManager
;
286 * Checks the first line of an HTTP request is valid
287 * currently just checks the request method is present.
289 * NP: Other errors are left for detection later in the parse.
292 HttpRequest::sanityCheckStartLine(MemBuf
*buf
, const size_t hdr_len
, Http::StatusCode
*error
)
294 // content is long enough to possibly hold a reply
295 // 2 being magic size of a 1-byte request method plus space delimiter
296 if ( buf
->contentSize() < 2 ) {
297 // this is ony a real error if the headers apparently complete.
299 debugs(58, 3, HERE
<< "Too large request header (" << hdr_len
<< " bytes)");
300 *error
= Http::scInvalidHeader
;
305 /* See if the request buffer starts with a known HTTP request method. */
306 if (HttpRequestMethod(buf
->content(),NULL
) == Http::METHOD_NONE
) {
307 debugs(73, 3, "HttpRequest::sanityCheckStartLine: did not find HTTP request method");
308 *error
= Http::scInvalidHeader
;
316 HttpRequest::parseFirstLine(const char *start
, const char *end
)
318 const char *t
= start
+ strcspn(start
, w_space
);
319 method
= HttpRequestMethod(start
, t
);
321 if (method
== Http::METHOD_NONE
)
324 start
= t
+ strspn(t
, w_space
);
326 const char *ver
= findTrailingHTTPVersion(start
, end
);
331 while (xisspace(*end
)) // find prev non-space
334 ++end
; // back to space
336 if (2 != sscanf(ver
+ 5, "%d.%d", &http_ver
.major
, &http_ver
.minor
)) {
337 debugs(73, DBG_IMPORTANT
, "parseRequestLine: Invalid HTTP identifier.");
345 if (end
< start
) // missing URI
350 * (char *) end
= '\0'; // temp terminate URI, XXX dangerous?
352 HttpRequest
*tmp
= urlParse(method
, (char *) start
, this);
354 * (char *) end
= save
;
363 HttpRequest::parseHeader(Http1::RequestParser
&hp
)
365 if (!hp
.headerBlockSize())
368 bool result
= header
.parse(hp
.rawHeaderBuf(), hp
.headerBlockSize());
376 /* swaps out request using httpRequestPack */
378 HttpRequest::swapOut(StoreEntry
* e
)
382 packerToStoreInit(&p
, e
);
387 /* packs request-line and headers, appends <crlf> terminator */
389 HttpRequest::pack(Packer
* p
)
392 /* pack request-line */
393 packerPrintf(p
, "%s " SQUIDSTRINGPH
" HTTP/%d.%d\r\n",
394 RequestMethodStr(method
), SQUIDSTRINGPRINT(urlpath
),
395 http_ver
.major
, http_ver
.minor
);
399 packerAppend(p
, "\r\n", 2);
403 * A wrapper for debugObj()
406 httpRequestPack(void *obj
, Packer
*p
)
408 HttpRequest
*request
= static_cast<HttpRequest
*>(obj
);
412 /* returns the length of request line + headers + crlf */
414 HttpRequest::prefixLen()
416 return strlen(RequestMethodStr(method
)) + 1 +
422 /* sync this routine when you update HttpRequest struct */
424 HttpRequest::hdrCacheInit()
426 HttpMsg::hdrCacheInit();
429 range
= header
.getRange();
433 Adaptation::Icap::History::Pointer
434 HttpRequest::icapHistory() const
437 if (Log::TheConfig
.hasIcapToken
|| IcapLogfileStatus
== LOG_ENABLE
) {
438 icapHistory_
= new Adaptation::Icap::History();
439 debugs(93,4, HERE
<< "made " << icapHistory_
<< " for " << this);
448 Adaptation::History::Pointer
449 HttpRequest::adaptHistory(bool createIfNone
) const
451 if (!adaptHistory_
&& createIfNone
) {
452 adaptHistory_
= new Adaptation::History();
453 debugs(93,4, HERE
<< "made " << adaptHistory_
<< " for " << this);
456 return adaptHistory_
;
459 Adaptation::History::Pointer
460 HttpRequest::adaptLogHistory() const
462 return HttpRequest::adaptHistory(Log::TheConfig
.hasAdaptToken
);
466 HttpRequest::adaptHistoryImport(const HttpRequest
&them
)
468 if (!adaptHistory_
) {
469 adaptHistory_
= them
.adaptHistory_
; // may be nil
471 // check that histories did not diverge
472 Must(!them
.adaptHistory_
|| them
.adaptHistory_
== adaptHistory_
);
479 HttpRequest::multipartRangeRequest() const
481 return (range
&& range
->specs
.count
> 1);
485 HttpRequest::bodyNibbled() const
487 return body_pipe
!= NULL
&& body_pipe
->consumedSize() > 0;
491 HttpRequest::detailError(err_type aType
, int aDetail
)
493 if (errType
|| errDetail
)
494 debugs(11, 5, HERE
<< "old error details: " << errType
<< '/' << errDetail
);
495 debugs(11, 5, HERE
<< "current error details: " << aType
<< '/' << aDetail
);
496 // checking type and detail separately may cause inconsistency, but
497 // may result in more details available if they only become available later
505 HttpRequest::clearError()
507 debugs(11, 7, HERE
<< "old error details: " << errType
<< '/' << errDetail
);
509 errDetail
= ERR_DETAIL_NONE
;
512 const char *HttpRequest::packableURI(bool full_uri
) const
515 return urlCanonical((HttpRequest
*)this);
518 return urlpath
.termedBuf();
523 void HttpRequest::packFirstLineInto(Packer
* p
, bool full_uri
) const
525 // form HTTP request-line
526 packerPrintf(p
, "%s %s HTTP/%d.%d\r\n",
527 RequestMethodStr(method
),
528 packableURI(full_uri
),
529 http_ver
.major
, http_ver
.minor
);
533 * Indicate whether or not we would expect an entity-body
534 * along with this request
537 HttpRequest::expectingBody(const HttpRequestMethod
& unused
, int64_t& theSize
) const
539 bool expectBody
= false;
542 * Note: Checks for message validity is in clientIsContentLengthValid().
543 * this just checks if a entity-body is expected based on HTTP message syntax
545 if (header
.chunked()) {
548 } else if (content_length
>= 0) {
550 theSize
= content_length
;
560 * Create a Request from a URL and METHOD.
562 * If the METHOD is CONNECT, then a host:port pair is looked for instead of a URL.
563 * If the request cannot be created cleanly, NULL is returned
566 HttpRequest::CreateFromUrlAndMethod(char * url
, const HttpRequestMethod
& method
)
568 return urlParse(method
, url
, NULL
);
572 * Create a Request from a URL.
574 * If the request cannot be created cleanly, NULL is returned
577 HttpRequest::CreateFromUrl(char * url
)
579 return urlParse(Http::METHOD_GET
, url
, NULL
);
583 * Are responses to this request possible cacheable ?
584 * If false then no matter what the response must not be cached.
587 HttpRequest::maybeCacheable()
589 // Intercepted request with Host: header which cannot be trusted.
590 // Because it failed verification, or someone bypassed the security tests
591 // we cannot cache the reponse for sharing between clients.
592 // TODO: update cache to store for particular clients only (going to same Host: and destination IP)
593 if (!flags
.hostVerified
&& (flags
.intercepted
|| flags
.interceptTproxy
))
597 case AnyP::PROTO_HTTP
:
598 if (!method
.respMaybeCacheable())
601 // XXX: this would seem the correct place to detect request cache-controls
602 // no-store, private and related which block cacheability
605 case AnyP::PROTO_GOPHER
:
606 if (!gopherCachable(this))
610 case AnyP::PROTO_CACHE_OBJECT
:
613 //case AnyP::PROTO_FTP:
622 HttpRequest::conditional() const
625 header
.has(HDR_IF_MATCH
) ||
626 header
.has(HDR_IF_NONE_MATCH
);
630 HttpRequest::recordLookup(const DnsLookupDetails
&dns
)
632 if (dns
.wait
>= 0) { // known delay
633 if (dnsWait
>= 0) // have recorded DNS wait before
641 HttpRequest::getRangeOffsetLimit()
643 /* -2 is the starting value of rangeOffsetLimit.
644 * If it is -2, that means we haven't checked it yet.
645 * Otherwise, return the current value */
646 if (rangeOffsetLimit
!= -2)
647 return rangeOffsetLimit
;
649 rangeOffsetLimit
= 0; // default value for rangeOffsetLimit
651 ACLFilledChecklist
ch(NULL
, this, NULL
);
652 ch
.src_addr
= client_addr
;
653 ch
.my_addr
= my_addr
;
655 for (AclSizeLimit
*l
= Config
.rangeOffsetLimit
; l
; l
= l
-> next
) {
656 /* if there is no ACL list or if the ACLs listed match use this limit value */
657 if (!l
->aclList
|| ch
.fastCheck(l
->aclList
) == ACCESS_ALLOWED
) {
658 debugs(58, 4, HERE
<< "rangeOffsetLimit=" << rangeOffsetLimit
);
659 rangeOffsetLimit
= l
->size
; // may be -1
664 return rangeOffsetLimit
;
668 HttpRequest::canHandle1xx() const
670 // old clients do not support 1xx unless they sent Expect: 100-continue
671 // (we reject all other HDR_EXPECT values so just check for HDR_EXPECT)
672 if (http_ver
<= Http::ProtocolVersion(1,0) && !header
.has(HDR_EXPECT
))
675 // others must support 1xx control messages
680 HttpRequest::pinnedConnection()
682 if (clientConnectionManager
.valid() && clientConnectionManager
->pinning
.pinned
)
683 return clientConnectionManager
.get();
688 HttpRequest::storeId()
690 if (store_id
.size() != 0) {
691 debugs(73, 3, "sent back store_id:" << store_id
);
693 return store_id
.termedBuf();
695 debugs(73, 3, "sent back canonicalUrl:" << urlCanonical(this) );
697 return urlCanonical(this);