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