]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpRequest.cc
Merged from trunk
[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 copy->range = range ? new HttpHdrRange(*range) : NULL;
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 range = header.getRange();
368 }
369
370 /* request_flags */
371 bool
372 request_flags::resetTCP() const
373 {
374 return reset_tcp != 0;
375 }
376
377 void
378 request_flags::setResetTCP()
379 {
380 debugs(73, 9, "request_flags::setResetTCP");
381 reset_tcp = 1;
382 }
383
384 void
385 request_flags::clearResetTCP()
386 {
387 debugs(73, 9, "request_flags::clearResetTCP");
388 reset_tcp = 0;
389 }
390
391 #if ICAP_CLIENT
392 Adaptation::Icap::History::Pointer
393 HttpRequest::icapHistory() const
394 {
395 if (!icapHistory_) {
396 if ((LogfileStatus == LOG_ENABLE && alLogformatHasIcapToken) ||
397 IcapLogfileStatus == LOG_ENABLE) {
398 icapHistory_ = new Adaptation::Icap::History();
399 debugs(93,4, HERE << "made " << icapHistory_ << " for " << this);
400 }
401 }
402
403 return icapHistory_;
404 }
405 #endif
406
407 #if USE_ADAPTATION
408 Adaptation::History::Pointer
409 HttpRequest::adaptHistory(bool createIfNone) const
410 {
411 if (!adaptHistory_ && createIfNone) {
412 adaptHistory_ = new Adaptation::History();
413 debugs(93,4, HERE << "made " << adaptHistory_ << " for " << this);
414 }
415
416 return adaptHistory_;
417 }
418
419 Adaptation::History::Pointer
420 HttpRequest::adaptLogHistory() const
421 {
422 const bool loggingNeedsHistory = (LogfileStatus == LOG_ENABLE) &&
423 alLogformatHasAdaptToken; // TODO: make global to remove this method?
424 return HttpRequest::adaptHistory(loggingNeedsHistory);
425 }
426
427 #endif
428
429 bool
430 HttpRequest::multipartRangeRequest() const
431 {
432 return (range && range->specs.count > 1);
433 }
434
435 void
436 request_flags::destinationIPLookupCompleted()
437 {
438 destinationIPLookedUp_ = true;
439 }
440
441 bool
442 request_flags::destinationIPLookedUp() const
443 {
444 return destinationIPLookedUp_;
445 }
446
447 request_flags
448 request_flags::cloneAdaptationImmune() const
449 {
450 // At the time of writing, all flags where either safe to copy after
451 // adaptation or were not set at the time of the adaptation. If there
452 // are flags that are different, they should be cleared in the clone.
453 return *this;
454 }
455
456 bool
457 HttpRequest::bodyNibbled() const
458 {
459 return body_pipe != NULL && body_pipe->consumedSize() > 0;
460 }
461
462 const char *HttpRequest::packableURI(bool full_uri) const
463 {
464 if (full_uri)
465 return urlCanonical((HttpRequest*)this);
466
467 if (urlpath.size())
468 return urlpath.termedBuf();
469
470 return "/";
471 }
472
473 void HttpRequest::packFirstLineInto(Packer * p, bool full_uri) const
474 {
475 // form HTTP request-line
476 packerPrintf(p, "%s %s HTTP/%d.%d\r\n",
477 RequestMethodStr(method),
478 packableURI(full_uri),
479 http_ver.major, http_ver.minor);
480 }
481
482 /*
483 * Indicate whether or not we would usually expect an entity-body
484 * along with this request
485 */
486 bool
487 HttpRequest::expectingBody(const HttpRequestMethod& unused, int64_t& theSize) const
488 {
489 bool expectBody = false;
490
491 /*
492 * GET and HEAD don't usually have bodies, but we should be prepared
493 * to accept one if the request_entities directive is set
494 */
495
496 if (method == METHOD_GET || method == METHOD_HEAD)
497 expectBody = Config.onoff.request_entities ? true : false;
498 else if (method == METHOD_PUT || method == METHOD_POST)
499 expectBody = true;
500 else if (header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ','))
501 expectBody = true;
502 else if (content_length >= 0)
503 expectBody = true;
504 else
505 expectBody = false;
506
507 if (expectBody) {
508 if (header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ','))
509 theSize = -1;
510 else if (content_length >= 0)
511 theSize = content_length;
512 else
513 theSize = -1;
514 }
515
516 return expectBody;
517 }
518
519 /*
520 * Create a Request from a URL and METHOD.
521 *
522 * If the METHOD is CONNECT, then a host:port pair is looked for instead of a URL.
523 * If the request cannot be created cleanly, NULL is returned
524 */
525 HttpRequest *
526 HttpRequest::CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method)
527 {
528 return urlParse(method, url, NULL);
529 }
530
531 /*
532 * Create a Request from a URL.
533 *
534 * If the request cannot be created cleanly, NULL is returned
535 */
536 HttpRequest *
537 HttpRequest::CreateFromUrl(char * url)
538 {
539 return urlParse(METHOD_GET, url, NULL);
540 }
541
542 /*
543 * Are responses to this request possible cacheable ?
544 * If false then no matter what the response must not be cached.
545 */
546 bool
547 HttpRequest::cacheable() const
548 {
549 if (protocol == PROTO_HTTP)
550 return httpCachable(method);
551
552 /*
553 * The below looks questionable: what non HTTP protocols use connect,
554 * trace, put and post? RC
555 */
556
557 if (!method.isCacheble())
558 return false;
559
560 /*
561 * XXX POST may be cached sometimes.. ignored
562 * for now
563 */
564 if (protocol == PROTO_GOPHER)
565 return gopherCachable(this);
566
567 if (protocol == PROTO_CACHEOBJ)
568 return false;
569
570 return true;
571 }
572
573 bool HttpRequest::inheritProperties(const HttpMsg *aMsg)
574 {
575 const HttpRequest* aReq = dynamic_cast<const HttpRequest*>(aMsg);
576 if (!aReq)
577 return false;
578
579 client_addr = aReq->client_addr;
580 #if USE_SQUID_EUI
581 client_eui48 = aReq->client_eui48;
582 client_eui64 = aReq->client_eui64;
583 #endif
584 my_addr = aReq->my_addr;
585
586 dnsWait = aReq->dnsWait;
587
588 #if USE_ADAPTATION
589 adaptHistory_ = aReq->adaptHistory();
590 #endif
591 #if ICAP_CLIENT
592 icapHistory_ = aReq->icapHistory();
593 #endif
594
595 // This may be too conservative for the 204 No Content case
596 // may eventually need cloneNullAdaptationImmune() for that.
597 flags = aReq->flags.cloneAdaptationImmune();
598
599 auth_user_request = aReq->auth_user_request;
600
601 if (aReq->pinned_connection) {
602 pinned_connection = cbdataReference(aReq->pinned_connection);
603 }
604 return true;
605 }
606
607 void HttpRequest::recordLookup(const DnsLookupDetails &dns)
608 {
609 if (dns.wait >= 0) { // known delay
610 if (dnsWait >= 0) // have recorded DNS wait before
611 dnsWait += dns.wait;
612 else
613 dnsWait = dns.wait;
614 }
615 }
616
617 int64_t
618 HttpRequest::getRangeOffsetLimit()
619 {
620 /* -2 is the starting value of rangeOffsetLimit.
621 * If it is -2, that means we haven't checked it yet.
622 * Otherwise, return the current value */
623 if (rangeOffsetLimit != -2)
624 return rangeOffsetLimit;
625
626 rangeOffsetLimit = 0; // default value for rangeOffsetLimit
627
628 ACLFilledChecklist ch(NULL, this, NULL);
629 ch.src_addr = client_addr;
630 ch.my_addr = my_addr;
631
632 for (acl_size_t *l = Config.rangeOffsetLimit; l; l = l -> next) {
633 /* if there is no ACL list or if the ACLs listed match use this limit value */
634 if (!l->aclList || ch.matchAclListFast(l->aclList)) {
635 debugs(58, 4, HERE << "rangeOffsetLimit=" << rangeOffsetLimit);
636 rangeOffsetLimit = l->size; // may be -1
637 break;
638 }
639 }
640
641 return rangeOffsetLimit;
642 }