]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpRequest.cc
11ed80211857e6ec3b2e7fd4f8fe0d2f5fb1dec7
5 * DEBUG: section 73 HTTP Request
6 * AUTHOR: Duane Wessels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
34 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
38 #include "HttpRequest.h"
39 #include "auth/UserRequest.h"
40 #include "HttpHeaderRange.h"
44 #include "adaptation/icap/icap_log.h"
47 HttpRequest::HttpRequest() : HttpMsg(hoRequest
)
52 HttpRequest::HttpRequest(const HttpRequestMethod
& aMethod
, protocol_t aProtocol
, const char *aUrlpath
) : HttpMsg(hoRequest
)
55 initHTTP(aMethod
, aProtocol
, aUrlpath
);
58 HttpRequest::~HttpRequest()
64 HttpRequest::initHTTP(const HttpRequestMethod
& aMethod
, protocol_t aProtocol
, const char *aUrlpath
)
75 protocol
= PROTO_NONE
;
79 auth_user_request
= NULL
;
80 pinned_connection
= NULL
;
83 memset(&flags
, '\0', sizeof(flags
));
89 client_addr
.SetEmpty();
95 peer_login
= NULL
; // not allocated/deallocated by this class
96 peer_domain
= NULL
; // not allocated/deallocated by this class
99 extacl_user
= null_string
;
100 extacl_passwd
= null_string
;
101 extacl_log
= null_string
;
102 pstate
= psReadyToParseStartLine
;
103 #if FOLLOW_X_FORWARDED_FOR
104 indirect_client_addr
.SetEmpty();
105 #endif /* FOLLOW_X_FORWARDED_FOR */
107 adaptHistory_
= NULL
;
117 // we used to assert that the pipe is NULL, but now the request only
118 // points to a pipe that is owned and initiated by another object.
121 AUTHUSERREQUESTUNLOCK(auth_user_request
, "request");
123 safe_free(canonical
);
125 safe_free(vary_headers
);
132 httpHdrCcDestroy(cache_control
);
133 cache_control
= NULL
;
141 if (pinned_connection
)
142 cbdataReferenceDone(pinned_connection
);
148 extacl_passwd
.clean();
153 adaptHistory_
= NULL
;
168 HttpRequest::clone() const
170 HttpRequest
*copy
= new HttpRequest(method
, protocol
, urlpath
.termedBuf());
171 // TODO: move common cloning clone to Msg::copyTo() or copy ctor
172 copy
->header
.append(&header
);
173 copy
->hdrCacheInit();
174 copy
->hdr_sz
= hdr_sz
;
175 copy
->http_ver
= http_ver
;
176 copy
->pstate
= pstate
; // TODO: should we assert a specific state here?
177 copy
->body_pipe
= body_pipe
;
179 strncpy(copy
->login
, login
, sizeof(login
)); // MAX_LOGIN_SZ
180 strncpy(copy
->host
, host
, sizeof(host
)); // SQUIDHOSTNAMELEN
181 copy
->host_addr
= host_addr
;
184 // urlPath handled in ctor
185 copy
->canonical
= canonical
? xstrdup(canonical
) : NULL
;
187 copy
->range
= range
? new HttpHdrRange(*range
) : NULL
;
189 copy
->imslen
= imslen
;
190 copy
->max_forwards
= max_forwards
;
191 copy
->hier
= hier
; // Is it safe to copy? Should we?
193 copy
->errType
= errType
;
195 // XXX: what to do with copy->peer_login?
197 copy
->lastmod
= lastmod
;
198 copy
->vary_headers
= vary_headers
? xstrdup(vary_headers
) : NULL
;
199 // XXX: what to do with copy->peer_domain?
202 copy
->extacl_user
= extacl_user
;
203 copy
->extacl_passwd
= extacl_passwd
;
204 copy
->extacl_log
= extacl_log
;
206 assert(copy
->inheritProperties(this));
212 * Checks the first line of an HTTP request is valid
213 * currently just checks the request method is present.
215 * NP: Other errors are left for detection later in the parse.
218 HttpRequest::sanityCheckStartLine(MemBuf
*buf
, const size_t hdr_len
, http_status
*error
)
220 // content is long enough to possibly hold a reply
221 // 2 being magic size of a 1-byte request method plus space delimiter
222 if ( buf
->contentSize() < 2 ) {
223 // this is ony a real error if the headers apparently complete.
225 debugs(58, 3, HERE
<< "Too large request header (" << hdr_len
<< " bytes)");
226 *error
= HTTP_INVALID_HEADER
;
231 /* See if the request buffer starts with a known HTTP request method. */
232 if (HttpRequestMethod(buf
->content(),NULL
) == METHOD_NONE
) {
233 debugs(73, 3, "HttpRequest::sanityCheckStartLine: did not find HTTP request method");
234 *error
= HTTP_INVALID_HEADER
;
242 HttpRequest::parseFirstLine(const char *start
, const char *end
)
244 const char *t
= start
+ strcspn(start
, w_space
);
245 method
= HttpRequestMethod(start
, t
);
247 if (method
== METHOD_NONE
)
250 start
= t
+ strspn(t
, w_space
);
252 const char *ver
= findTrailingHTTPVersion(start
, end
);
257 while (xisspace(*end
)) // find prev non-space
260 end
++; // back to space
262 if (2 != sscanf(ver
+ 5, "%d.%d", &http_ver
.major
, &http_ver
.minor
)) {
263 debugs(73, 1, "parseRequestLine: Invalid HTTP identifier.");
271 if (end
< start
) // missing URI
276 * (char *) end
= '\0'; // temp terminate URI, XXX dangerous?
278 HttpRequest
*tmp
= urlParse(method
, (char *) start
, this);
280 * (char *) end
= save
;
289 HttpRequest::parseHeader(const char *parse_start
, int len
)
291 const char *blk_start
, *blk_end
;
293 if (!httpMsgIsolateHeaders(&parse_start
, len
, &blk_start
, &blk_end
))
296 int result
= header
.parse(blk_start
, blk_end
);
304 /* swaps out request using httpRequestPack */
306 HttpRequest::swapOut(StoreEntry
* e
)
310 packerToStoreInit(&p
, e
);
315 /* packs request-line and headers, appends <crlf> terminator */
317 HttpRequest::pack(Packer
* p
)
320 /* pack request-line */
321 packerPrintf(p
, "%s " SQUIDSTRINGPH
" HTTP/1.0\r\n",
322 RequestMethodStr(method
), SQUIDSTRINGPRINT(urlpath
));
326 packerAppend(p
, "\r\n", 2);
330 * A wrapper for debugObj()
333 httpRequestPack(void *obj
, Packer
*p
)
335 HttpRequest
*request
= static_cast<HttpRequest
*>(obj
);
339 /* returns the length of request line + headers + crlf */
341 HttpRequest::prefixLen()
343 return strlen(RequestMethodStr(method
)) + 1 +
349 /* sync this routine when you update HttpRequest struct */
351 HttpRequest::hdrCacheInit()
353 HttpMsg::hdrCacheInit();
355 range
= header
.getRange();
360 request_flags::resetTCP() const
362 return reset_tcp
!= 0;
366 request_flags::setResetTCP()
368 debugs(73, 9, "request_flags::setResetTCP");
373 request_flags::clearResetTCP()
375 debugs(73, 9, "request_flags::clearResetTCP");
380 Adaptation::Icap::History::Pointer
381 HttpRequest::icapHistory() const
384 if ((LogfileStatus
== LOG_ENABLE
&& alLogformatHasIcapToken
) ||
385 IcapLogfileStatus
== LOG_ENABLE
) {
386 icapHistory_
= new Adaptation::Icap::History();
387 debugs(93,4, HERE
<< "made " << icapHistory_
<< " for " << this);
396 Adaptation::History::Pointer
397 HttpRequest::adaptHistory(bool createIfNone
) const
399 if (!adaptHistory_
&& createIfNone
) {
400 adaptHistory_
= new Adaptation::History();
401 debugs(93,4, HERE
<< "made " << adaptHistory_
<< " for " << this);
404 return adaptHistory_
;
407 Adaptation::History::Pointer
408 HttpRequest::adaptLogHistory() const
410 const bool loggingNeedsHistory
= (LogfileStatus
== LOG_ENABLE
) &&
411 alLogformatHasAdaptToken
; // TODO: make global to remove this method?
412 return HttpRequest::adaptHistory(loggingNeedsHistory
);
418 HttpRequest::multipartRangeRequest() const
420 return (range
&& range
->specs
.count
> 1);
424 request_flags::destinationIPLookupCompleted()
426 destinationIPLookedUp_
= true;
430 request_flags::destinationIPLookedUp() const
432 return destinationIPLookedUp_
;
436 request_flags::cloneAdaptationImmune() const
438 // At the time of writing, all flags where either safe to copy after
439 // adaptation or were not set at the time of the adaptation. If there
440 // are flags that are different, they should be cleared in the clone.
445 HttpRequest::bodyNibbled() const
447 return body_pipe
!= NULL
&& body_pipe
->consumedSize() > 0;
450 const char *HttpRequest::packableURI(bool full_uri
) const
453 return urlCanonical((HttpRequest
*)this);
456 return urlpath
.termedBuf();
461 void HttpRequest::packFirstLineInto(Packer
* p
, bool full_uri
) const
463 // form HTTP request-line
464 packerPrintf(p
, "%s %s HTTP/%d.%d\r\n",
465 RequestMethodStr(method
),
466 packableURI(full_uri
),
467 http_ver
.major
, http_ver
.minor
);
471 * Indicate whether or not we would usually expect an entity-body
472 * along with this request
475 HttpRequest::expectingBody(const HttpRequestMethod
& unused
, int64_t& theSize
) const
477 bool expectBody
= false;
480 * GET and HEAD don't usually have bodies, but we should be prepared
481 * to accept one if the request_entities directive is set
484 if (method
== METHOD_GET
|| method
== METHOD_HEAD
)
485 expectBody
= Config
.onoff
.request_entities
? true : false;
486 else if (method
== METHOD_PUT
|| method
== METHOD_POST
)
488 else if (header
.hasListMember(HDR_TRANSFER_ENCODING
, "chunked", ','))
490 else if (content_length
>= 0)
496 if (header
.hasListMember(HDR_TRANSFER_ENCODING
, "chunked", ','))
498 else if (content_length
>= 0)
499 theSize
= content_length
;
508 * Create a Request from a URL and METHOD.
510 * If the METHOD is CONNECT, then a host:port pair is looked for instead of a URL.
511 * If the request cannot be created cleanly, NULL is returned
514 HttpRequest::CreateFromUrlAndMethod(char * url
, const HttpRequestMethod
& method
)
516 return urlParse(method
, url
, NULL
);
520 * Create a Request from a URL.
522 * If the request cannot be created cleanly, NULL is returned
525 HttpRequest::CreateFromUrl(char * url
)
527 return urlParse(METHOD_GET
, url
, NULL
);
531 * Are responses to this request possible cacheable ?
532 * If false then no matter what the response must not be cached.
535 HttpRequest::cacheable() const
537 if (protocol
== PROTO_HTTP
)
538 return httpCachable(method
);
541 * The below looks questionable: what non HTTP protocols use connect,
542 * trace, put and post? RC
545 if (!method
.isCacheble())
549 * XXX POST may be cached sometimes.. ignored
552 if (protocol
== PROTO_GOPHER
)
553 return gopherCachable(this);
555 if (protocol
== PROTO_CACHEOBJ
)
561 bool HttpRequest::inheritProperties(const HttpMsg
*aMsg
)
563 const HttpRequest
* aReq
= dynamic_cast<const HttpRequest
*>(aMsg
);
567 client_addr
= aReq
->client_addr
;
568 my_addr
= aReq
->my_addr
;
570 dnsWait
= aReq
->dnsWait
;
573 adaptHistory_
= aReq
->adaptHistory();
576 icapHistory_
= aReq
->icapHistory();
579 // This may be too conservative for the 204 No Content case
580 // may eventually need cloneNullAdaptationImmune() for that.
581 flags
= aReq
->flags
.cloneAdaptationImmune();
583 if (aReq
->auth_user_request
) {
584 auth_user_request
= aReq
->auth_user_request
;
585 AUTHUSERREQUESTLOCK(auth_user_request
, "inheritProperties");
588 if (aReq
->pinned_connection
) {
589 pinned_connection
= cbdataReference(aReq
->pinned_connection
);
594 void HttpRequest::recordLookup(const DnsLookupDetails
&dns
)
596 if (dns
.wait
>= 0) { // known delay
597 if (dnsWait
>= 0) // have recorded DNS wait before