]>
Commit | Line | Data |
---|---|---|
2ac76861 | 1 | |
cb69b4c7 | 2 | /* |
47f6e231 | 3 | * $Id: HttpReply.cc,v 1.96 2007/08/13 17:20:51 hno Exp $ |
cb69b4c7 | 4 | * |
123abbe1 | 5 | * DEBUG: section 58 HTTP Reply (Response) |
cb69b4c7 | 6 | * AUTHOR: Alex Rousskov |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 9 | * ---------------------------------------------------------- |
cb69b4c7 | 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. | |
cb69b4c7 | 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 | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
cb69b4c7 | 34 | */ |
35 | ||
cb69b4c7 | 36 | #include "squid.h" |
985c86bc | 37 | #include "SquidTime.h" |
528b2c61 | 38 | #include "Store.h" |
8596962e | 39 | #include "HttpReply.h" |
528b2c61 | 40 | #include "HttpHdrContRange.h" |
25b6a907 | 41 | #include "HttpHdrSc.h" |
4fb35c3c | 42 | #include "ACLChecklist.h" |
0eb49b6d | 43 | #include "MemBuf.h" |
cb69b4c7 | 44 | |
45 | /* local constants */ | |
46 | ||
1d7ab0f4 | 47 | /* If we receive a 304 from the origin during a cache revalidation, we must |
48 | * update the headers of the existing entry. Specifically, we need to update all | |
49 | * end-to-end headers and not any hop-by-hop headers (rfc2616 13.5.3). | |
50 | * | |
51 | * This is not the whole story though: since it is possible for a faulty/malicious | |
52 | * origin server to set headers it should not in a 304, we must explicitly ignore | |
53 | * these too. Specifically all entity-headers except those permitted in a 304 | |
54 | * (rfc2616 10.3.5) must be ignored. | |
55 | * | |
56 | * The list of headers we don't update is made up of: | |
57 | * all hop-by-hop headers | |
58 | * all entity-headers except Expires and Content-Location | |
59 | */ | |
2246b732 | 60 | static HttpHeaderMask Denied304HeadersMask; |
61 | static http_hdr_type Denied304HeadersArr[] = | |
62e76326 | 62 | { |
1d7ab0f4 | 63 | // hop-by-hop headers |
64 | HDR_CONNECTION, HDR_KEEP_ALIVE, HDR_PROXY_AUTHENTICATE, HDR_PROXY_AUTHORIZATION, | |
65 | HDR_TE, HDR_TRAILERS, HDR_TRANSFER_ENCODING, HDR_UPGRADE, | |
66 | // entity headers | |
62e76326 | 67 | HDR_ALLOW, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, HDR_CONTENT_LENGTH, |
1d7ab0f4 | 68 | HDR_CONTENT_MD5, HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_LAST_MODIFIED |
62e76326 | 69 | }; |
2246b732 | 70 | |
2246b732 | 71 | /* module initialization */ |
72 | void | |
9bc73deb | 73 | httpReplyInitModule(void) |
2246b732 | 74 | { |
8596962e | 75 | assert(HTTP_STATUS_NONE == 0); // HttpReply::parse() interface assumes that |
97474590 | 76 | httpHeaderMaskInit(&Denied304HeadersMask, 0); |
8abf232c | 77 | httpHeaderCalcMask(&Denied304HeadersMask, Denied304HeadersArr, countof(Denied304HeadersArr)); |
2246b732 | 78 | } |
79 | ||
8596962e | 80 | HttpReply::HttpReply() : HttpMsg(hoReply), date (0), last_modified (0), expires (0), surrogate_control (NULL), content_range (NULL), keep_alive (0), protoPrefix("HTTP/") |
cb69b4c7 | 81 | { |
06a5ae20 | 82 | init(); |
cb69b4c7 | 83 | } |
84 | ||
06a5ae20 | 85 | HttpReply::~HttpReply() |
cb69b4c7 | 86 | { |
06a5ae20 | 87 | if (do_clean) |
88 | clean(); | |
cb69b4c7 | 89 | } |
90 | ||
91 | void | |
06a5ae20 | 92 | HttpReply::init() |
cb69b4c7 | 93 | { |
06a5ae20 | 94 | httpBodyInit(&body); |
95 | hdrCacheInit(); | |
96 | httpStatusLineInit(&sline); | |
c99de607 | 97 | pstate = psReadyToParseStartLine; |
06a5ae20 | 98 | do_clean = true; |
cb69b4c7 | 99 | } |
100 | ||
06a5ae20 | 101 | void HttpReply::reset() |
cb69b4c7 | 102 | { |
06a5ae20 | 103 | |
8596962e | 104 | // reset should not reset the protocol; could have made protoPrefix a |
105 | // virtual function instead, but it is not clear whether virtual methods | |
106 | // are allowed with MEMPROXY_CLASS() and whether some cbdata void* | |
107 | // conversions are not going to kill virtual tables | |
30abd221 | 108 | const String pfx = protoPrefix; |
06a5ae20 | 109 | clean(); |
110 | init(); | |
111 | protoPrefix = pfx; | |
112 | } | |
113 | ||
114 | void | |
115 | HttpReply::clean() | |
116 | { | |
5f8252d2 | 117 | // we used to assert that the pipe is NULL, but now the message only |
118 | // points to a pipe that is owned and initiated by another object. | |
119 | body_pipe = NULL; | |
120 | ||
06a5ae20 | 121 | httpBodyClean(&body); |
122 | hdrCacheClean(); | |
519e0948 | 123 | header.clean(); |
06a5ae20 | 124 | httpStatusLineClean(&sline); |
cb69b4c7 | 125 | } |
126 | ||
cb69b4c7 | 127 | void |
06a5ae20 | 128 | HttpReply::packHeadersInto(Packer * p) const |
cb69b4c7 | 129 | { |
06a5ae20 | 130 | httpStatusLinePackInto(&sline, p); |
a9925b40 | 131 | header.packInto(p); |
cb69b4c7 | 132 | packerAppend(p, "\r\n", 2); |
528b2c61 | 133 | } |
134 | ||
135 | void | |
06a5ae20 | 136 | HttpReply::packInto(Packer * p) |
528b2c61 | 137 | { |
06a5ae20 | 138 | packHeadersInto(p); |
139 | httpBodyPackInto(&body, p); | |
cb69b4c7 | 140 | } |
141 | ||
06a5ae20 | 142 | /* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */ |
032785bf | 143 | MemBuf * |
06a5ae20 | 144 | HttpReply::pack() |
cb69b4c7 | 145 | { |
032785bf | 146 | MemBuf *mb = new MemBuf; |
cb69b4c7 | 147 | Packer p; |
cb69b4c7 | 148 | |
2fe7eff9 | 149 | mb->init(); |
032785bf | 150 | packerToMemInit(&p, mb); |
06a5ae20 | 151 | packInto(&p); |
cb69b4c7 | 152 | packerClean(&p); |
153 | return mb; | |
154 | } | |
155 | ||
032785bf | 156 | MemBuf * |
450e0c10 | 157 | httpPackedReply(HttpVersion ver, http_status status, const char *ctype, |
47f6e231 | 158 | int64_t clen, time_t lmt, time_t expires) |
cb69b4c7 | 159 | { |
06a5ae20 | 160 | HttpReply *rep = new HttpReply; |
161 | rep->setHeaders(ver, status, ctype, NULL, clen, lmt, expires); | |
162 | MemBuf *mb = rep->pack(); | |
163 | delete rep; | |
cb69b4c7 | 164 | return mb; |
165 | } | |
166 | ||
528b2c61 | 167 | HttpReply * |
06a5ae20 | 168 | HttpReply::make304 () const |
cb69b4c7 | 169 | { |
62e76326 | 170 | static const http_hdr_type ImsEntries[] = {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER}; |
171 | ||
06a5ae20 | 172 | HttpReply *rv = new HttpReply; |
728da2ee | 173 | int t; |
de336bbe | 174 | HttpHeaderEntry *e; |
cb69b4c7 | 175 | |
528b2c61 | 176 | /* rv->content_length; */ |
06a5ae20 | 177 | rv->date = date; |
178 | rv->last_modified = last_modified; | |
179 | rv->expires = expires; | |
180 | rv->content_type = content_type; | |
528b2c61 | 181 | /* rv->cache_control */ |
182 | /* rv->content_range */ | |
183 | /* rv->keep_alive */ | |
450e0c10 | 184 | HttpVersion ver(1,0); |
528b2c61 | 185 | httpStatusLineSet(&rv->sline, ver, |
62e76326 | 186 | HTTP_NOT_MODIFIED, ""); |
187 | ||
de336bbe | 188 | for (t = 0; ImsEntries[t] != HDR_OTHER; ++t) |
a9925b40 | 189 | if ((e = header.findEntry(ImsEntries[t]))) |
eede25e7 | 190 | rv->header.addEntry(e->clone()); |
62e76326 | 191 | |
528b2c61 | 192 | /* rv->body */ |
193 | return rv; | |
194 | } | |
195 | ||
032785bf | 196 | MemBuf * |
06a5ae20 | 197 | HttpReply::packed304Reply() |
528b2c61 | 198 | { |
199 | /* Not as efficient as skipping the header duplication, | |
200 | * but easier to maintain | |
201 | */ | |
06a5ae20 | 202 | HttpReply *temp = make304 (); |
203 | MemBuf *rv = temp->pack(); | |
204 | delete temp; | |
528b2c61 | 205 | return rv; |
cb69b4c7 | 206 | } |
207 | ||
208 | void | |
06a5ae20 | 209 | HttpReply::setHeaders(HttpVersion ver, http_status status, const char *reason, |
47f6e231 | 210 | const char *ctype, int64_t clen, time_t lmt, time_t expires) |
cb69b4c7 | 211 | { |
212 | HttpHeader *hdr; | |
06a5ae20 | 213 | httpStatusLineSet(&sline, ver, status, reason); |
214 | hdr = &header; | |
a9925b40 | 215 | hdr->putStr(HDR_SERVER, visible_appname_string); |
216 | hdr->putStr(HDR_MIME_VERSION, "1.0"); | |
217 | hdr->putTime(HDR_DATE, squid_curtime); | |
62e76326 | 218 | |
d8b249ef | 219 | if (ctype) { |
a9925b40 | 220 | hdr->putStr(HDR_CONTENT_TYPE, ctype); |
06a5ae20 | 221 | content_type = ctype; |
d8b249ef | 222 | } else |
30abd221 | 223 | content_type = String(); |
62e76326 | 224 | |
de336bbe | 225 | if (clen >= 0) |
47f6e231 | 226 | hdr->putInt64(HDR_CONTENT_LENGTH, clen); |
62e76326 | 227 | |
cb69b4c7 | 228 | if (expires >= 0) |
a9925b40 | 229 | hdr->putTime(HDR_EXPIRES, expires); |
62e76326 | 230 | |
2ac76861 | 231 | if (lmt > 0) /* this used to be lmt != 0 @?@ */ |
a9925b40 | 232 | hdr->putTime(HDR_LAST_MODIFIED, lmt); |
62e76326 | 233 | |
06a5ae20 | 234 | date = squid_curtime; |
62e76326 | 235 | |
06a5ae20 | 236 | content_length = clen; |
62e76326 | 237 | |
06a5ae20 | 238 | expires = expires; |
62e76326 | 239 | |
06a5ae20 | 240 | last_modified = lmt; |
cb69b4c7 | 241 | } |
242 | ||
6d38ef86 | 243 | void |
06a5ae20 | 244 | HttpReply::redirect(http_status status, const char *loc) |
6d38ef86 | 245 | { |
246 | HttpHeader *hdr; | |
450e0c10 | 247 | HttpVersion ver(1,0); |
06a5ae20 | 248 | httpStatusLineSet(&sline, ver, status, httpStatusString(status)); |
249 | hdr = &header; | |
a9925b40 | 250 | hdr->putStr(HDR_SERVER, full_appname_string); |
251 | hdr->putTime(HDR_DATE, squid_curtime); | |
47f6e231 | 252 | hdr->putInt64(HDR_CONTENT_LENGTH, 0); |
a9925b40 | 253 | hdr->putStr(HDR_LOCATION, loc); |
06a5ae20 | 254 | date = squid_curtime; |
255 | content_length = 0; | |
6d38ef86 | 256 | } |
257 | ||
528b2c61 | 258 | /* compare the validators of two replies. |
259 | * 1 = they match | |
260 | * 0 = they do not match | |
261 | */ | |
262 | int | |
06a5ae20 | 263 | HttpReply::validatorsMatch(HttpReply const * otherRep) const |
62e76326 | 264 | { |
30abd221 | 265 | String one,two; |
06a5ae20 | 266 | assert (otherRep); |
528b2c61 | 267 | /* Numbers first - easiest to check */ |
268 | /* Content-Length */ | |
269 | /* TODO: remove -1 bypass */ | |
62e76326 | 270 | |
06a5ae20 | 271 | if (content_length != otherRep->content_length |
272 | && content_length > -1 && | |
62e76326 | 273 | otherRep->content_length > -1) |
274 | return 0; | |
275 | ||
528b2c61 | 276 | /* ETag */ |
a9925b40 | 277 | one = header.getStrOrList(HDR_ETAG); |
62e76326 | 278 | |
a9925b40 | 279 | two = otherRep->header.getStrOrList(HDR_ETAG); |
62e76326 | 280 | |
30abd221 | 281 | if (!one.buf() || !two.buf() || strcasecmp (one.buf(), two.buf())) { |
282 | one.clean(); | |
283 | two.clean(); | |
62e76326 | 284 | return 0; |
528b2c61 | 285 | } |
62e76326 | 286 | |
06a5ae20 | 287 | if (last_modified != otherRep->last_modified) |
62e76326 | 288 | return 0; |
289 | ||
528b2c61 | 290 | /* MD5 */ |
a9925b40 | 291 | one = header.getStrOrList(HDR_CONTENT_MD5); |
62e76326 | 292 | |
a9925b40 | 293 | two = otherRep->header.getStrOrList(HDR_CONTENT_MD5); |
62e76326 | 294 | |
30abd221 | 295 | if (!one.buf() || !two.buf() || strcasecmp (one.buf(), two.buf())) { |
296 | one.clean(); | |
297 | two.clean(); | |
62e76326 | 298 | return 0; |
528b2c61 | 299 | } |
62e76326 | 300 | |
528b2c61 | 301 | return 1; |
302 | } | |
303 | ||
cb69b4c7 | 304 | void |
06a5ae20 | 305 | HttpReply::updateOnNotModified(HttpReply const * freshRep) |
cb69b4c7 | 306 | { |
07947ad8 | 307 | assert(freshRep); |
1d7ab0f4 | 308 | |
d8b249ef | 309 | /* clean cache */ |
06a5ae20 | 310 | hdrCacheClean(); |
d8b249ef | 311 | /* update raw headers */ |
a9925b40 | 312 | header.update(&freshRep->header, |
313 | (const HttpHeaderMask *) &Denied304HeadersMask); | |
1d7ab0f4 | 314 | |
d8b249ef | 315 | /* init cache */ |
07947ad8 | 316 | hdrCacheInit(); |
cb69b4c7 | 317 | } |
318 | ||
d8b249ef | 319 | /* internal routines */ |
cb69b4c7 | 320 | |
06a5ae20 | 321 | time_t |
322 | HttpReply::hdrExpirationTime() | |
d20b1cd0 | 323 | { |
324 | /* The s-maxage and max-age directive takes priority over Expires */ | |
62e76326 | 325 | |
06a5ae20 | 326 | if (cache_control) { |
327 | if (date >= 0) { | |
328 | if (cache_control->s_maxage >= 0) | |
329 | return date + cache_control->s_maxage; | |
62e76326 | 330 | |
06a5ae20 | 331 | if (cache_control->max_age >= 0) |
332 | return date + cache_control->max_age; | |
62e76326 | 333 | } else { |
334 | /* | |
335 | * Conservatively handle the case when we have a max-age | |
336 | * header, but no Date for reference? | |
337 | */ | |
338 | ||
06a5ae20 | 339 | if (cache_control->s_maxage >= 0) |
62e76326 | 340 | return squid_curtime; |
341 | ||
06a5ae20 | 342 | if (cache_control->max_age >= 0) |
62e76326 | 343 | return squid_curtime; |
344 | } | |
d20b1cd0 | 345 | } |
62e76326 | 346 | |
f66a9ef4 | 347 | if (Config.onoff.vary_ignore_expire && |
a9925b40 | 348 | header.has(HDR_VARY)) { |
349 | const time_t d = header.getTime(HDR_DATE); | |
350 | const time_t e = header.getTime(HDR_EXPIRES); | |
62e76326 | 351 | |
352 | if (d == e) | |
353 | return -1; | |
f66a9ef4 | 354 | } |
62e76326 | 355 | |
a9925b40 | 356 | if (header.has(HDR_EXPIRES)) { |
357 | const time_t e = header.getTime(HDR_EXPIRES); | |
62e76326 | 358 | /* |
359 | * HTTP/1.0 says that robust implementations should consider | |
360 | * bad or malformed Expires header as equivalent to "expires | |
361 | * immediately." | |
362 | */ | |
363 | return e < 0 ? squid_curtime : e; | |
d20b1cd0 | 364 | } |
62e76326 | 365 | |
d20b1cd0 | 366 | return -1; |
367 | } | |
368 | ||
d8b249ef | 369 | /* sync this routine when you update HttpReply struct */ |
8596962e | 370 | void |
07947ad8 | 371 | HttpReply::hdrCacheInit() |
cb69b4c7 | 372 | { |
07947ad8 | 373 | HttpMsg::hdrCacheInit(); |
374 | ||
47f6e231 | 375 | content_length = header.getInt64(HDR_CONTENT_LENGTH); |
a9925b40 | 376 | date = header.getTime(HDR_DATE); |
377 | last_modified = header.getTime(HDR_LAST_MODIFIED); | |
378 | surrogate_control = header.getSc(); | |
379 | content_range = header.getContRange(); | |
07947ad8 | 380 | keep_alive = httpMsgIsPersistent(sline.version, &header); |
a9925b40 | 381 | const char *str = header.getStr(HDR_CONTENT_TYPE); |
62e76326 | 382 | |
d8b249ef | 383 | if (str) |
07947ad8 | 384 | content_type.limitInit(str, strcspn(str, ";\t ")); |
d8b249ef | 385 | else |
30abd221 | 386 | content_type = String(); |
62e76326 | 387 | |
d20b1cd0 | 388 | /* be sure to set expires after date and cache-control */ |
06a5ae20 | 389 | expires = hdrExpirationTime(); |
cb69b4c7 | 390 | } |
391 | ||
d8b249ef | 392 | /* sync this routine when you update HttpReply struct */ |
06a5ae20 | 393 | void |
394 | HttpReply::hdrCacheClean() | |
2ac76861 | 395 | { |
30abd221 | 396 | content_type.clean(); |
62e76326 | 397 | |
06a5ae20 | 398 | if (cache_control) { |
399 | httpHdrCcDestroy(cache_control); | |
400 | cache_control = NULL; | |
07947ad8 | 401 | } |
62e76326 | 402 | |
06a5ae20 | 403 | if (surrogate_control) { |
404 | httpHdrScDestroy(surrogate_control); | |
405 | surrogate_control = NULL; | |
07947ad8 | 406 | } |
43ae1d95 | 407 | |
06a5ae20 | 408 | if (content_range) { |
409 | httpHdrContRangeDestroy(content_range); | |
410 | content_range = NULL; | |
07947ad8 | 411 | } |
63259c34 | 412 | } |
cb69b4c7 | 413 | |
35282fbf | 414 | /* |
415 | * Returns the body size of a HTTP response | |
416 | */ | |
47f6e231 | 417 | int64_t |
06a5ae20 | 418 | HttpReply::bodySize(method_t method) const |
35282fbf | 419 | { |
06a5ae20 | 420 | if (sline.version.major < 1) |
1bda350e | 421 | return -1; |
422 | else if (METHOD_HEAD == method) | |
62e76326 | 423 | return 0; |
06a5ae20 | 424 | else if (sline.status == HTTP_OK) |
62e76326 | 425 | (void) 0; /* common case, continue */ |
06a5ae20 | 426 | else if (sline.status == HTTP_NO_CONTENT) |
62e76326 | 427 | return 0; |
06a5ae20 | 428 | else if (sline.status == HTTP_NOT_MODIFIED) |
62e76326 | 429 | return 0; |
06a5ae20 | 430 | else if (sline.status < HTTP_OK) |
62e76326 | 431 | return 0; |
432 | ||
06a5ae20 | 433 | return content_length; |
35282fbf | 434 | } |
8596962e | 435 | |
436 | bool HttpReply::sanityCheckStartLine(MemBuf *buf, http_status *error) | |
437 | { | |
30abd221 | 438 | if (buf->contentSize() >= protoPrefix.size() && protoPrefix.cmp(buf->content(), protoPrefix.size()) != 0) { |
439 | debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol prefix (" << protoPrefix.buf() << ") in '" << buf->content() << "'"); | |
8596962e | 440 | *error = HTTP_INVALID_HEADER; |
441 | return false; | |
442 | } | |
443 | ||
444 | return true; | |
445 | } | |
446 | ||
447 | void HttpReply::packFirstLineInto(Packer *p, bool unused) const | |
448 | { | |
449 | httpStatusLinePackInto(&sline, p); | |
450 | } | |
429f7150 | 451 | |
452 | bool HttpReply::parseFirstLine(const char *blk_start, const char *blk_end) | |
453 | { | |
454 | return httpStatusLineParse(&sline, protoPrefix, blk_start, blk_end); | |
455 | } | |
5c09dcb8 | 456 | |
fb525683 | 457 | /* handy: resets and returns -1 */ |
458 | int | |
459 | HttpReply::httpMsgParseError() | |
460 | { | |
461 | int result(HttpMsg::httpMsgParseError()); | |
462 | /* indicate an error in the status line */ | |
463 | sline.status = HTTP_INVALID_HEADER; | |
464 | return result; | |
465 | } | |
466 | ||
5c09dcb8 | 467 | /* |
468 | * Indicate whether or not we would usually expect an entity-body | |
469 | * along with this response | |
470 | */ | |
471 | bool | |
47f6e231 | 472 | HttpReply::expectingBody(method_t req_method, int64_t& theSize) const |
5c09dcb8 | 473 | { |
474 | bool expectBody = true; | |
475 | ||
476 | if (req_method == METHOD_HEAD) | |
477 | expectBody = false; | |
478 | else if (sline.status == HTTP_NO_CONTENT) | |
479 | expectBody = false; | |
480 | else if (sline.status == HTTP_NOT_MODIFIED) | |
481 | expectBody = false; | |
482 | else if (sline.status < HTTP_OK) | |
483 | expectBody = false; | |
484 | else | |
485 | expectBody = true; | |
486 | ||
487 | if (expectBody) { | |
a9925b40 | 488 | if (header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ',')) |
5c09dcb8 | 489 | theSize = -1; |
490 | else if (content_length >= 0) | |
491 | theSize = content_length; | |
492 | else | |
493 | theSize = -1; | |
494 | } | |
495 | ||
496 | return expectBody; | |
497 | } |