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