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