]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpRequest.cc
Compliance: Forward 1xx control messages to clients that support them.
[thirdparty/squid.git] / src / HttpRequest.cc
1
2 /*
3 * $Id$
4 *
5 * DEBUG: section 73 HTTP Request
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
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.
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.
24 *
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.
29 *
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.
33 *
34 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
35 */
36
37 #include "squid.h"
38 #include "HttpRequest.h"
39 #include "auth/UserRequest.h"
40 #include "HttpHeaderRange.h"
41 #include "MemBuf.h"
42 #include "Store.h"
43 #if ICAP_CLIENT
44 #include "adaptation/icap/icap_log.h"
45 #endif
46 #include "acl/FilledChecklist.h"
47
48 HttpRequest::HttpRequest() : HttpMsg(hoRequest)
49 {
50 init();
51 }
52
53 HttpRequest::HttpRequest(const HttpRequestMethod& aMethod, protocol_t aProtocol, const char *aUrlpath) : HttpMsg(hoRequest)
54 {
55 init();
56 initHTTP(aMethod, aProtocol, aUrlpath);
57 }
58
59 HttpRequest::~HttpRequest()
60 {
61 clean();
62 }
63
64 void
65 HttpRequest::initHTTP(const HttpRequestMethod& aMethod, protocol_t aProtocol, const char *aUrlpath)
66 {
67 method = aMethod;
68 protocol = aProtocol;
69 urlpath = aUrlpath;
70 }
71
72 void
73 HttpRequest::init()
74 {
75 method = METHOD_NONE;
76 protocol = PROTO_NONE;
77 urlpath = NULL;
78 login[0] = '\0';
79 host[0] = '\0';
80 host_is_numeric = -1;
81 auth_user_request = NULL;
82 pinned_connection = NULL;
83 port = 0;
84 canonical = NULL;
85 memset(&flags, '\0', sizeof(flags));
86 range = NULL;
87 ims = -1;
88 imslen = 0;
89 lastmod = -1;
90 max_forwards = -1;
91 client_addr.SetEmpty();
92 #if USE_SQUID_EUI
93 client_eui48.clear();
94 client_eui64.clear();
95 #endif
96 my_addr.SetEmpty();
97 body_pipe = NULL;
98 // hier
99 dnsWait = -1;
100 errType = ERR_NONE;
101 peer_login = NULL; // not allocated/deallocated by this class
102 peer_domain = NULL; // not allocated/deallocated by this class
103 vary_headers = NULL;
104 tag = null_string;
105 extacl_user = null_string;
106 extacl_passwd = null_string;
107 extacl_log = null_string;
108 extacl_message = null_string;
109 pstate = psReadyToParseStartLine;
110 #if FOLLOW_X_FORWARDED_FOR
111 indirect_client_addr.SetEmpty();
112 #endif /* FOLLOW_X_FORWARDED_FOR */
113 #if USE_ADAPTATION
114 adaptHistory_ = NULL;
115 #endif
116 #if ICAP_CLIENT
117 icapHistory_ = NULL;
118 #endif
119 rangeOffsetLimit = -2; //a value of -2 means not checked yet
120 }
121
122 void
123 HttpRequest::clean()
124 {
125 // we used to assert that the pipe is NULL, but now the request only
126 // points to a pipe that is owned and initiated by another object.
127 body_pipe = NULL;
128
129 auth_user_request = NULL;
130
131 safe_free(canonical);
132
133 safe_free(vary_headers);
134
135 urlpath.clean();
136
137 header.clean();
138
139 if (cache_control) {
140 httpHdrCcDestroy(cache_control);
141 cache_control = NULL;
142 }
143
144 if (range) {
145 delete range;
146 range = NULL;
147 }
148
149 if (pinned_connection)
150 cbdataReferenceDone(pinned_connection);
151
152 tag.clean();
153
154 extacl_user.clean();
155
156 extacl_passwd.clean();
157
158 extacl_log.clean();
159
160 extacl_message.clean();
161
162 #if USE_ADAPTATION
163 adaptHistory_ = NULL;
164 #endif
165 #if ICAP_CLIENT
166 icapHistory_ = NULL;
167 #endif
168 }
169
170 void
171 HttpRequest::reset()
172 {
173 clean();
174 init();
175 }
176
177 HttpRequest *
178 HttpRequest::clone() const
179 {
180 HttpRequest *copy = new HttpRequest(method, protocol, urlpath.termedBuf());
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
193 copy->port = port;
194 // urlPath handled in ctor
195 copy->canonical = canonical ? xstrdup(canonical) : NULL;
196
197 // range handled in hdrCacheInit()
198 copy->ims = ims;
199 copy->imslen = imslen;
200 copy->max_forwards = max_forwards;
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
207 copy->lastmod = lastmod;
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;
215 copy->extacl_message = extacl_message;
216
217 assert(copy->inheritProperties(this));
218
219 return copy;
220 }
221
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 */
228 bool
229 HttpRequest::sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, http_status *error)
230 {
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) {
236 debugs(58, 3, HERE << "Too large request header (" << hdr_len << " bytes)");
237 *error = HTTP_INVALID_HEADER;
238 }
239 return false;
240 }
241
242 /* See if the request buffer starts with a known HTTP request method. */
243 if (HttpRequestMethod(buf->content(),NULL) == METHOD_NONE) {
244 debugs(73, 3, "HttpRequest::sanityCheckStartLine: did not find HTTP request method");
245 *error = HTTP_INVALID_HEADER;
246 return false;
247 }
248
249 return true;
250 }
251
252 bool
253 HttpRequest::parseFirstLine(const char *start, const char *end)
254 {
255 const char *t = start + strcspn(start, w_space);
256 method = HttpRequestMethod(start, t);
257
258 if (method == METHOD_NONE)
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)) {
274 debugs(73, 1, "parseRequestLine: Invalid HTTP identifier.");
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;
297 }
298
299 int
300 HttpRequest::parseHeader(const char *parse_start, int len)
301 {
302 const char *blk_start, *blk_end;
303
304 if (!httpMsgIsolateHeaders(&parse_start, len, &blk_start, &blk_end))
305 return 0;
306
307 int result = header.parse(blk_start, blk_end);
308
309 if (result)
310 hdrCacheInit();
311
312 return result;
313 }
314
315 /* swaps out request using httpRequestPack */
316 void
317 HttpRequest::swapOut(StoreEntry * e)
318 {
319 Packer p;
320 assert(e);
321 packerToStoreInit(&p, e);
322 pack(&p);
323 packerClean(&p);
324 }
325
326 /* packs request-line and headers, appends <crlf> terminator */
327 void
328 HttpRequest::pack(Packer * p)
329 {
330 assert(p);
331 /* pack request-line */
332 packerPrintf(p, "%s " SQUIDSTRINGPH " HTTP/%d.%d\r\n",
333 RequestMethodStr(method), SQUIDSTRINGPRINT(urlpath),
334 http_ver.major, http_ver.minor);
335 /* headers */
336 header.packInto(p);
337 /* trailer */
338 packerAppend(p, "\r\n", 2);
339 }
340
341 /*
342 * A wrapper for debugObj()
343 */
344 void
345 httpRequestPack(void *obj, Packer *p)
346 {
347 HttpRequest *request = static_cast<HttpRequest*>(obj);
348 request->pack(p);
349 }
350
351 /* returns the length of request line + headers + crlf */
352 int
353 HttpRequest::prefixLen()
354 {
355 return strlen(RequestMethodStr(method)) + 1 +
356 urlpath.size() + 1 +
357 4 + 1 + 3 + 2 +
358 header.len + 2;
359 }
360
361 /* sync this routine when you update HttpRequest struct */
362 void
363 HttpRequest::hdrCacheInit()
364 {
365 HttpMsg::hdrCacheInit();
366
367 assert(!range);
368 range = header.getRange();
369 }
370
371 /* request_flags */
372 bool
373 request_flags::resetTCP() const
374 {
375 return reset_tcp != 0;
376 }
377
378 void
379 request_flags::setResetTCP()
380 {
381 debugs(73, 9, "request_flags::setResetTCP");
382 reset_tcp = 1;
383 }
384
385 void
386 request_flags::clearResetTCP()
387 {
388 debugs(73, 9, "request_flags::clearResetTCP");
389 reset_tcp = 0;
390 }
391
392 #if ICAP_CLIENT
393 Adaptation::Icap::History::Pointer
394 HttpRequest::icapHistory() const
395 {
396 if (!icapHistory_) {
397 if ((LogfileStatus == LOG_ENABLE && alLogformatHasIcapToken) ||
398 IcapLogfileStatus == LOG_ENABLE) {
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
409 Adaptation::History::Pointer
410 HttpRequest::adaptHistory(bool createIfNone) const
411 {
412 if (!adaptHistory_ && createIfNone) {
413 adaptHistory_ = new Adaptation::History();
414 debugs(93,4, HERE << "made " << adaptHistory_ << " for " << this);
415 }
416
417 return adaptHistory_;
418 }
419
420 Adaptation::History::Pointer
421 HttpRequest::adaptLogHistory() const
422 {
423 const bool loggingNeedsHistory = (LogfileStatus == LOG_ENABLE) &&
424 alLogformatHasAdaptToken; // TODO: make global to remove this method?
425 return HttpRequest::adaptHistory(loggingNeedsHistory);
426 }
427
428 #endif
429
430 bool
431 HttpRequest::multipartRangeRequest() const
432 {
433 return (range && range->specs.count > 1);
434 }
435
436 void
437 request_flags::destinationIPLookupCompleted()
438 {
439 destinationIPLookedUp_ = true;
440 }
441
442 bool
443 request_flags::destinationIPLookedUp() const
444 {
445 return destinationIPLookedUp_;
446 }
447
448 request_flags
449 request_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
457 bool
458 HttpRequest::bodyNibbled() const
459 {
460 return body_pipe != NULL && body_pipe->consumedSize() > 0;
461 }
462
463 const char *HttpRequest::packableURI(bool full_uri) const
464 {
465 if (full_uri)
466 return urlCanonical((HttpRequest*)this);
467
468 if (urlpath.size())
469 return urlpath.termedBuf();
470
471 return "/";
472 }
473
474 void HttpRequest::packFirstLineInto(Packer * p, bool full_uri) const
475 {
476 // form HTTP request-line
477 packerPrintf(p, "%s %s HTTP/%d.%d\r\n",
478 RequestMethodStr(method),
479 packableURI(full_uri),
480 http_ver.major, http_ver.minor);
481 }
482
483 /*
484 * Indicate whether or not we would usually expect an entity-body
485 * along with this request
486 */
487 bool
488 HttpRequest::expectingBody(const HttpRequestMethod& unused, int64_t& theSize) const
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;
501 else if (header.chunked())
502 expectBody = true;
503 else if (content_length >= 0)
504 expectBody = true;
505 else
506 expectBody = false;
507
508 if (expectBody) {
509 if (header.chunked())
510 theSize = -1;
511 else if (content_length >= 0)
512 theSize = content_length;
513 else
514 theSize = -1;
515 }
516
517 return expectBody;
518 }
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 */
526 HttpRequest *
527 HttpRequest::CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method)
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 */
537 HttpRequest *
538 HttpRequest::CreateFromUrl(char * url)
539 {
540 return urlParse(METHOD_GET, url, NULL);
541 }
542
543 /*
544 * Are responses to this request possible cacheable ?
545 * If false then no matter what the response must not be cached.
546 */
547 bool
548 HttpRequest::cacheable() const
549 {
550 if (protocol == PROTO_HTTP)
551 return httpCachable(method);
552
553 /*
554 * The below looks questionable: what non HTTP protocols use connect,
555 * trace, put and post? RC
556 */
557
558 if (!method.isCacheble())
559 return false;
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)
569 return false;
570
571 return true;
572 }
573
574 bool HttpRequest::inheritProperties(const HttpMsg *aMsg)
575 {
576 const HttpRequest* aReq = dynamic_cast<const HttpRequest*>(aMsg);
577 if (!aReq)
578 return false;
579
580 client_addr = aReq->client_addr;
581 #if FOLLOW_X_FORWARDED_FOR
582 indirect_client_addr = aReq->indirect_client_addr;
583 #endif
584 #if USE_SQUID_EUI
585 client_eui48 = aReq->client_eui48;
586 client_eui64 = aReq->client_eui64;
587 #endif
588 my_addr = aReq->my_addr;
589
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
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
603 auth_user_request = aReq->auth_user_request;
604
605 if (aReq->pinned_connection) {
606 pinned_connection = cbdataReference(aReq->pinned_connection);
607 }
608 return true;
609 }
610
611 void 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 }
620
621 int64_t
622 HttpRequest::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 */
627 if (rangeOffsetLimit != -2)
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 }
647
648 bool
649 HttpRequest::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 }