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