]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpRequest.cc
Partial bug #2964 fix: Avoid stuck ICAP REQMOD transactions when HTTP fails.
[thirdparty/squid.git] / src / HttpRequest.cc
CommitLineData
99edd1c3 1
2/*
262a0e14 3 * $Id$
99edd1c3 4 *
5 * DEBUG: section 73 HTTP Request
6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
99edd1c3 10 *
2b6662ba 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.
99edd1c3 19 *
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.
9e008dda 24 *
99edd1c3 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.
9e008dda 29 *
99edd1c3 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
51ee7c82 34 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
99edd1c3 35 */
36
37#include "squid.h"
8596962e 38#include "HttpRequest.h"
2d2b0bb7 39#include "auth/UserRequest.h"
528b2c61 40#include "HttpHeaderRange.h"
0eb49b6d 41#include "MemBuf.h"
25b6a907 42#include "Store.h"
3ff65596
AR
43#if ICAP_CLIENT
44#include "adaptation/icap/icap_log.h"
45#endif
11e3fa1c 46#include "acl/FilledChecklist.h"
528b2c61 47
4a56ee8d 48HttpRequest::HttpRequest() : HttpMsg(hoRequest)
75faaa7a 49{
5cafad19 50 init();
51}
52
60745f24 53HttpRequest::HttpRequest(const HttpRequestMethod& aMethod, protocol_t aProtocol, const char *aUrlpath) : HttpMsg(hoRequest)
5cafad19 54{
55 init();
0e8aad88 56 initHTTP(aMethod, aProtocol, aUrlpath);
5cafad19 57}
58
59HttpRequest::~HttpRequest()
60{
5cafad19 61 clean();
62}
63
64void
60745f24 65HttpRequest::initHTTP(const HttpRequestMethod& aMethod, protocol_t aProtocol, const char *aUrlpath)
5cafad19 66{
67 method = aMethod;
68 protocol = aProtocol;
69 urlpath = aUrlpath;
70}
71
72void
73HttpRequest::init()
74{
a3f28359 75 method = METHOD_NONE;
5cafad19 76 protocol = PROTO_NONE;
30abd221 77 urlpath = NULL;
a3f28359 78 login[0] = '\0';
79 host[0] = '\0';
12ef783b 80 host_is_numeric = -1;
a3f28359 81 auth_user_request = NULL;
d67acb4e 82 pinned_connection = NULL;
a3f28359 83 port = 0;
84 canonical = NULL;
a3f28359 85 memset(&flags, '\0', sizeof(flags));
86 range = NULL;
87 ims = -1;
88 imslen = 0;
5cafad19 89 lastmod = -1;
90 max_forwards = -1;
cc192b50 91 client_addr.SetEmpty();
a98c2da5
AJ
92#if USE_SQUID_EUI
93 client_eui48.clear();
94 client_eui64.clear();
95#endif
cc192b50 96 my_addr.SetEmpty();
5f8252d2 97 body_pipe = NULL;
a3f28359 98 // hier
3ff65596 99 dnsWait = -1;
a3f28359 100 errType = ERR_NONE;
5cafad19 101 peer_login = NULL; // not allocated/deallocated by this class
102 peer_domain = NULL; // not allocated/deallocated by this class
a3f28359 103 vary_headers = NULL;
5cafad19 104 tag = null_string;
105 extacl_user = null_string;
106 extacl_passwd = null_string;
107 extacl_log = null_string;
8c93a598 108 extacl_message = null_string;
c99de607 109 pstate = psReadyToParseStartLine;
3d674977
AJ
110#if FOLLOW_X_FORWARDED_FOR
111 indirect_client_addr.SetEmpty();
112#endif /* FOLLOW_X_FORWARDED_FOR */
3ff65596
AR
113#if USE_ADAPTATION
114 adaptHistory_ = NULL;
115#endif
116#if ICAP_CLIENT
117 icapHistory_ = NULL;
118#endif
11e3fa1c 119 rangeOffsetLimit = -2; //a value of -2 means not checked yet
8596962e 120}
121
99edd1c3 122void
5cafad19 123HttpRequest::clean()
99edd1c3 124{
9e008dda 125 // we used to assert that the pipe is NULL, but now the request only
5f8252d2 126 // points to a pipe that is owned and initiated by another object.
9e008dda 127 body_pipe = NULL;
62e76326 128
a33a428a 129 auth_user_request = NULL;
62e76326 130
8596962e 131 safe_free(canonical);
62e76326 132
8596962e 133 safe_free(vary_headers);
62e76326 134
30abd221 135 urlpath.clean();
62e76326 136
519e0948 137 header.clean();
62e76326 138
07947ad8 139 if (cache_control) {
8596962e 140 httpHdrCcDestroy(cache_control);
07947ad8 141 cache_control = NULL;
142 }
62e76326 143
5cafad19 144 if (range) {
8596962e 145 delete range;
5cafad19 146 range = NULL;
147 }
62e76326 148
9e008dda
AJ
149 if (pinned_connection)
150 cbdataReferenceDone(pinned_connection);
d67acb4e 151
30abd221 152 tag.clean();
abb929f0 153
30abd221 154 extacl_user.clean();
abb929f0 155
30abd221 156 extacl_passwd.clean();
abb929f0 157
30abd221 158 extacl_log.clean();
3ff65596 159
8c93a598
HN
160 extacl_message.clean();
161
3ff65596
AR
162#if USE_ADAPTATION
163 adaptHistory_ = NULL;
164#endif
165#if ICAP_CLIENT
166 icapHistory_ = NULL;
167#endif
8596962e 168}
4a972fa2 169
5cafad19 170void
171HttpRequest::reset()
172{
173 clean();
174 init();
175}
176
fa0e6114
AR
177HttpRequest *
178HttpRequest::clone() const
179{
b4f2886c 180 HttpRequest *copy = new HttpRequest(method, protocol, urlpath.termedBuf());
fa0e6114
AR
181 // TODO: move common cloning clone to Msg::copyTo() or copy ctor
182 copy->header.append(&header);
183 copy->hdrCacheInit();
184 copy->hdr_sz = hdr_sz;
185 copy->http_ver = http_ver;
186 copy->pstate = pstate; // TODO: should we assert a specific state here?
187 copy->body_pipe = body_pipe;
188
189 strncpy(copy->login, login, sizeof(login)); // MAX_LOGIN_SZ
190 strncpy(copy->host, host, sizeof(host)); // SQUIDHOSTNAMELEN
191 copy->host_addr = host_addr;
192
fa0e6114
AR
193 copy->port = port;
194 // urlPath handled in ctor
9e008dda
AJ
195 copy->canonical = canonical ? xstrdup(canonical) : NULL;
196
66363092 197 // range handled in hdrCacheInit()
9e008dda
AJ
198 copy->ims = ims;
199 copy->imslen = imslen;
200 copy->max_forwards = max_forwards;
fa0e6114
AR
201 copy->hier = hier; // Is it safe to copy? Should we?
202
203 copy->errType = errType;
204
205 // XXX: what to do with copy->peer_login?
206
9e008dda 207 copy->lastmod = lastmod;
fa0e6114
AR
208 copy->vary_headers = vary_headers ? xstrdup(vary_headers) : NULL;
209 // XXX: what to do with copy->peer_domain?
210
211 copy->tag = tag;
212 copy->extacl_user = extacl_user;
213 copy->extacl_passwd = extacl_passwd;
214 copy->extacl_log = extacl_log;
8c93a598 215 copy->extacl_message = extacl_message;
fa0e6114 216
3ff65596
AR
217 assert(copy->inheritProperties(this));
218
fa0e6114
AR
219 return copy;
220}
221
96ee497f
AJ
222/**
223 * Checks the first line of an HTTP request is valid
224 * currently just checks the request method is present.
225 *
226 * NP: Other errors are left for detection later in the parse.
227 */
c21ad0f5 228bool
96ee497f 229HttpRequest::sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, http_status *error)
8596962e 230{
96ee497f
AJ
231 // content is long enough to possibly hold a reply
232 // 2 being magic size of a 1-byte request method plus space delimiter
233 if ( buf->contentSize() < 2 ) {
234 // this is ony a real error if the headers apparently complete.
235 if (hdr_len > 0) {
0246f6b8 236 debugs(58, 3, HERE << "Too large request header (" << hdr_len << " bytes)");
96ee497f
AJ
237 *error = HTTP_INVALID_HEADER;
238 }
239 return false;
240 }
8596962e 241
96ee497f 242 /* See if the request buffer starts with a known HTTP request method. */
914b89a2 243 if (HttpRequestMethod(buf->content(),NULL) == METHOD_NONE) {
bf8fe701 244 debugs(73, 3, "HttpRequest::sanityCheckStartLine: did not find HTTP request method");
96ee497f 245 *error = HTTP_INVALID_HEADER;
8596962e 246 return false;
247 }
248
249 return true;
250}
251
c21ad0f5 252bool
253HttpRequest::parseFirstLine(const char *start, const char *end)
8596962e 254{
863d85ee 255 const char *t = start + strcspn(start, w_space);
985c86bc 256 method = HttpRequestMethod(start, t);
863d85ee 257
914b89a2 258 if (method == METHOD_NONE)
863d85ee 259 return false;
260
261 start = t + strspn(t, w_space);
262
263 const char *ver = findTrailingHTTPVersion(start, end);
264
265 if (ver) {
266 end = ver - 1;
267
268 while (xisspace(*end)) // find prev non-space
269 end--;
270
271 end++; // back to space
272
273 if (2 != sscanf(ver + 5, "%d.%d", &http_ver.major, &http_ver.minor)) {
bf8fe701 274 debugs(73, 1, "parseRequestLine: Invalid HTTP identifier.");
863d85ee 275 return false;
276 }
277 } else {
278 http_ver.major = 0;
279 http_ver.minor = 9;
280 }
281
282 if (end < start) // missing URI
283 return false;
284
285 char save = *end;
286
287 * (char *) end = '\0'; // temp terminate URI, XXX dangerous?
288
289 HttpRequest *tmp = urlParse(method, (char *) start, this);
290
291 * (char *) end = save;
292
293 if (NULL == tmp)
294 return false;
295
296 return true;
99edd1c3 297}
298
99edd1c3 299int
666f514b 300HttpRequest::parseHeader(const char *parse_start, int len)
99edd1c3 301{
302 const char *blk_start, *blk_end;
62e76326 303
666f514b 304 if (!httpMsgIsolateHeaders(&parse_start, len, &blk_start, &blk_end))
62e76326 305 return 0;
306
a9925b40 307 int result = header.parse(blk_start, blk_end);
62e76326 308
528b2c61 309 if (result)
07947ad8 310 hdrCacheInit();
62e76326 311
528b2c61 312 return result;
99edd1c3 313}
314
a00a7c85 315/* swaps out request using httpRequestPack */
99edd1c3 316void
5cafad19 317HttpRequest::swapOut(StoreEntry * e)
99edd1c3 318{
a00a7c85 319 Packer p;
5cafad19 320 assert(e);
a00a7c85 321 packerToStoreInit(&p, e);
5cafad19 322 pack(&p);
a00a7c85 323 packerClean(&p);
324}
325
326/* packs request-line and headers, appends <crlf> terminator */
327void
5cafad19 328HttpRequest::pack(Packer * p)
a00a7c85 329{
5cafad19 330 assert(p);
a00a7c85 331 /* pack request-line */
3872be7c
AJ
332 packerPrintf(p, "%s " SQUIDSTRINGPH " HTTP/%d.%d\r\n",
333 RequestMethodStr(method), SQUIDSTRINGPRINT(urlpath),
334 http_ver.major, http_ver.minor);
a00a7c85 335 /* headers */
a9925b40 336 header.packInto(p);
2246b732 337 /* trailer */
a00a7c85 338 packerAppend(p, "\r\n", 2);
2246b732 339}
340
5cafad19 341/*
342 * A wrapper for debugObj()
343 */
2246b732 344void
5cafad19 345httpRequestPack(void *obj, Packer *p)
2246b732 346{
5cafad19 347 HttpRequest *request = static_cast<HttpRequest*>(obj);
348 request->pack(p);
99edd1c3 349}
eeb423fb 350
2246b732 351/* returns the length of request line + headers + crlf */
352int
5cafad19 353HttpRequest::prefixLen()
2246b732 354{
60745f24 355 return strlen(RequestMethodStr(method)) + 1 +
5cafad19 356 urlpath.size() + 1 +
62e76326 357 4 + 1 + 3 + 2 +
5cafad19 358 header.len + 2;
2246b732 359}
99edd1c3 360
190154cf 361/* sync this routine when you update HttpRequest struct */
8596962e 362void
07947ad8 363HttpRequest::hdrCacheInit()
528b2c61 364{
07947ad8 365 HttpMsg::hdrCacheInit();
62e76326 366
66363092 367 assert(!range);
a9925b40 368 range = header.getRange();
528b2c61 369}
370
e429f975 371/* request_flags */
372bool
373request_flags::resetTCP() const
374{
375 return reset_tcp != 0;
376}
377
378void
379request_flags::setResetTCP()
380{
bf8fe701 381 debugs(73, 9, "request_flags::setResetTCP");
e429f975 382 reset_tcp = 1;
383}
384
385void
386request_flags::clearResetTCP()
387{
bf8fe701 388 debugs(73, 9, "request_flags::clearResetTCP");
e429f975 389 reset_tcp = 0;
390}
528b2c61 391
3ff65596 392#if ICAP_CLIENT
e1381638 393Adaptation::Icap::History::Pointer
3ff65596
AR
394HttpRequest::icapHistory() const
395{
396 if (!icapHistory_) {
397 if ((LogfileStatus == LOG_ENABLE && alLogformatHasIcapToken) ||
e1381638 398 IcapLogfileStatus == LOG_ENABLE) {
3ff65596
AR
399 icapHistory_ = new Adaptation::Icap::History();
400 debugs(93,4, HERE << "made " << icapHistory_ << " for " << this);
401 }
402 }
403
404 return icapHistory_;
405}
406#endif
407
408#if USE_ADAPTATION
e1381638 409Adaptation::History::Pointer
a22e6cd3 410HttpRequest::adaptHistory(bool createIfNone) const
3ff65596 411{
a22e6cd3
AR
412 if (!adaptHistory_ && createIfNone) {
413 adaptHistory_ = new Adaptation::History();
414 debugs(93,4, HERE << "made " << adaptHistory_ << " for " << this);
3ff65596
AR
415 }
416
417 return adaptHistory_;
418}
a22e6cd3 419
e1381638 420Adaptation::History::Pointer
a22e6cd3
AR
421HttpRequest::adaptLogHistory() const
422{
423 const bool loggingNeedsHistory = (LogfileStatus == LOG_ENABLE) &&
e1381638 424 alLogformatHasAdaptToken; // TODO: make global to remove this method?
a22e6cd3
AR
425 return HttpRequest::adaptHistory(loggingNeedsHistory);
426}
427
3ff65596
AR
428#endif
429
528b2c61 430bool
190154cf 431HttpRequest::multipartRangeRequest() const
528b2c61 432{
433 return (range && range->specs.count > 1);
434}
8000a965 435
436void
437request_flags::destinationIPLookupCompleted()
438{
439 destinationIPLookedUp_ = true;
440}
441
442bool
443request_flags::destinationIPLookedUp() const
444{
445 return destinationIPLookedUp_;
446}
8596962e 447
5f8252d2 448request_flags
449request_flags::cloneAdaptationImmune() const
450{
451 // At the time of writing, all flags where either safe to copy after
452 // adaptation or were not set at the time of the adaptation. If there
453 // are flags that are different, they should be cleared in the clone.
454 return *this;
455}
456
58217e94 457bool
9e008dda
AJ
458HttpRequest::bodyNibbled() const
459{
58217e94 460 return body_pipe != NULL && body_pipe->consumedSize() > 0;
461}
5f8252d2 462
8596962e 463const char *HttpRequest::packableURI(bool full_uri) const
464{
465 if (full_uri)
466 return urlCanonical((HttpRequest*)this);
467
468 if (urlpath.size())
b4f2886c 469 return urlpath.termedBuf();
8596962e 470
471 return "/";
472}
473
474void HttpRequest::packFirstLineInto(Packer * p, bool full_uri) const
475{
476 // form HTTP request-line
477 packerPrintf(p, "%s %s HTTP/%d.%d\r\n",
60745f24 478 RequestMethodStr(method),
8596962e 479 packableURI(full_uri),
480 http_ver.major, http_ver.minor);
481}
8ddea643 482
483/*
484 * Indicate whether or not we would usually expect an entity-body
485 * along with this request
486 */
487bool
60745f24 488HttpRequest::expectingBody(const HttpRequestMethod& unused, int64_t& theSize) const
8ddea643 489{
490 bool expectBody = false;
491
492 /*
493 * GET and HEAD don't usually have bodies, but we should be prepared
494 * to accept one if the request_entities directive is set
495 */
496
497 if (method == METHOD_GET || method == METHOD_HEAD)
498 expectBody = Config.onoff.request_entities ? true : false;
499 else if (method == METHOD_PUT || method == METHOD_POST)
500 expectBody = true;
c3d0ba0c 501 else if (header.chunked())
8ddea643 502 expectBody = true;
503 else if (content_length >= 0)
504 expectBody = true;
505 else
506 expectBody = false;
507
508 if (expectBody) {
c3d0ba0c 509 if (header.chunked())
8ddea643 510 theSize = -1;
511 else if (content_length >= 0)
512 theSize = content_length;
513 else
514 theSize = -1;
515 }
516
517 return expectBody;
518}
c21ad0f5 519
520/*
521 * Create a Request from a URL and METHOD.
522 *
523 * If the METHOD is CONNECT, then a host:port pair is looked for instead of a URL.
524 * If the request cannot be created cleanly, NULL is returned
525 */
526HttpRequest *
60745f24 527HttpRequest::CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method)
c21ad0f5 528{
529 return urlParse(method, url, NULL);
530}
531
532/*
533 * Create a Request from a URL.
534 *
535 * If the request cannot be created cleanly, NULL is returned
536 */
537HttpRequest *
538HttpRequest::CreateFromUrl(char * url)
539{
540 return urlParse(METHOD_GET, url, NULL);
541}
610ee341 542
543/*
544 * Are responses to this request possible cacheable ?
545 * If false then no matter what the response must not be cached.
546 */
547bool
548HttpRequest::cacheable() const
549{
550 if (protocol == PROTO_HTTP)
551 return httpCachable(method);
552
610ee341 553 /*
554 * The below looks questionable: what non HTTP protocols use connect,
555 * trace, put and post? RC
556 */
9e008dda 557
60745f24 558 if (!method.isCacheble())
9e008dda 559 return false;
610ee341 560
561 /*
562 * XXX POST may be cached sometimes.. ignored
563 * for now
564 */
565 if (protocol == PROTO_GOPHER)
566 return gopherCachable(this);
567
568 if (protocol == PROTO_CACHEOBJ)
60745f24 569 return false;
610ee341 570
60745f24 571 return true;
610ee341 572}
d67acb4e
AJ
573
574bool HttpRequest::inheritProperties(const HttpMsg *aMsg)
575{
576 const HttpRequest* aReq = dynamic_cast<const HttpRequest*>(aMsg);
9e008dda
AJ
577 if (!aReq)
578 return false;
579
d67acb4e 580 client_addr = aReq->client_addr;
396ea4a0
AJ
581#if FOLLOW_X_FORWARDED_FOR
582 indirect_client_addr = aReq->indirect_client_addr;
583#endif
a98c2da5
AJ
584#if USE_SQUID_EUI
585 client_eui48 = aReq->client_eui48;
586 client_eui64 = aReq->client_eui64;
587#endif
d67acb4e
AJ
588 my_addr = aReq->my_addr;
589
3ff65596
AR
590 dnsWait = aReq->dnsWait;
591
592#if USE_ADAPTATION
593 adaptHistory_ = aReq->adaptHistory();
594#endif
595#if ICAP_CLIENT
596 icapHistory_ = aReq->icapHistory();
597#endif
598
d67acb4e
AJ
599 // This may be too conservative for the 204 No Content case
600 // may eventually need cloneNullAdaptationImmune() for that.
601 flags = aReq->flags.cloneAdaptationImmune();
602
a33a428a 603 auth_user_request = aReq->auth_user_request;
d67acb4e 604
9e008dda
AJ
605 if (aReq->pinned_connection) {
606 pinned_connection = cbdataReference(aReq->pinned_connection);
d67acb4e
AJ
607 }
608 return true;
609}
3ff65596
AR
610
611void HttpRequest::recordLookup(const DnsLookupDetails &dns)
612{
613 if (dns.wait >= 0) { // known delay
614 if (dnsWait >= 0) // have recorded DNS wait before
615 dnsWait += dns.wait;
616 else
617 dnsWait = dns.wait;
618 }
619}
11e3fa1c
AJ
620
621int64_t
622HttpRequest::getRangeOffsetLimit()
623{
624 /* -2 is the starting value of rangeOffsetLimit.
625 * If it is -2, that means we haven't checked it yet.
626 * Otherwise, return the current value */
5e5f247c 627 if (rangeOffsetLimit != -2)
11e3fa1c
AJ
628 return rangeOffsetLimit;
629
630 rangeOffsetLimit = 0; // default value for rangeOffsetLimit
631
632 ACLFilledChecklist ch(NULL, this, NULL);
633 ch.src_addr = client_addr;
634 ch.my_addr = my_addr;
635
636 for (acl_size_t *l = Config.rangeOffsetLimit; l; l = l -> next) {
637 /* if there is no ACL list or if the ACLs listed match use this limit value */
638 if (!l->aclList || ch.matchAclListFast(l->aclList)) {
639 debugs(58, 4, HERE << "rangeOffsetLimit=" << rangeOffsetLimit);
640 rangeOffsetLimit = l->size; // may be -1
641 break;
642 }
643 }
644
645 return rangeOffsetLimit;
646}
655daa06
AR
647
648bool
649HttpRequest::canHandle1xx() const
650{
651 // old clients do not support 1xx unless they sent Expect: 100-continue
652 // (we reject all other HDR_EXPECT values so just check for HDR_EXPECT)
653 if (http_ver <= HttpVersion(1,0) && !header.has(HDR_EXPECT))
654 return false;
655
656 // others must support 1xx control messages
657 return true;
658}