1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/nsHttpResponseHead.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,820 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim:set ts=4 sw=4 sts=4 et cin: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// HttpLog.h should generally be included first 1.11 +#include "HttpLog.h" 1.12 + 1.13 +#include "nsHttpResponseHead.h" 1.14 +#include "nsPrintfCString.h" 1.15 +#include "prtime.h" 1.16 +#include "nsURLHelper.h" 1.17 +#include <algorithm> 1.18 + 1.19 +namespace mozilla { 1.20 +namespace net { 1.21 + 1.22 +//----------------------------------------------------------------------------- 1.23 +// nsHttpResponseHead <public> 1.24 +//----------------------------------------------------------------------------- 1.25 + 1.26 +nsresult 1.27 +nsHttpResponseHead::SetHeader(nsHttpAtom hdr, 1.28 + const nsACString &val, 1.29 + bool merge) 1.30 +{ 1.31 + nsresult rv = mHeaders.SetHeader(hdr, val, merge); 1.32 + if (NS_FAILED(rv)) return rv; 1.33 + 1.34 + // respond to changes in these headers. we need to reparse the entire 1.35 + // header since the change may have merged in additional values. 1.36 + if (hdr == nsHttp::Cache_Control) 1.37 + ParseCacheControl(mHeaders.PeekHeader(hdr)); 1.38 + else if (hdr == nsHttp::Pragma) 1.39 + ParsePragma(mHeaders.PeekHeader(hdr)); 1.40 + 1.41 + return NS_OK; 1.42 +} 1.43 + 1.44 +void 1.45 +nsHttpResponseHead::SetContentLength(int64_t len) 1.46 +{ 1.47 + mContentLength = len; 1.48 + if (len < 0) 1.49 + mHeaders.ClearHeader(nsHttp::Content_Length); 1.50 + else 1.51 + mHeaders.SetHeader(nsHttp::Content_Length, nsPrintfCString("%lld", len)); 1.52 +} 1.53 + 1.54 +void 1.55 +nsHttpResponseHead::Flatten(nsACString &buf, bool pruneTransients) 1.56 +{ 1.57 + if (mVersion == NS_HTTP_VERSION_0_9) 1.58 + return; 1.59 + 1.60 + buf.AppendLiteral("HTTP/"); 1.61 + if (mVersion == NS_HTTP_VERSION_2_0) 1.62 + buf.AppendLiteral("2.0 "); 1.63 + else if (mVersion == NS_HTTP_VERSION_1_1) 1.64 + buf.AppendLiteral("1.1 "); 1.65 + else 1.66 + buf.AppendLiteral("1.0 "); 1.67 + 1.68 + buf.Append(nsPrintfCString("%u", unsigned(mStatus)) + 1.69 + NS_LITERAL_CSTRING(" ") + 1.70 + mStatusText + 1.71 + NS_LITERAL_CSTRING("\r\n")); 1.72 + 1.73 + if (!pruneTransients) { 1.74 + mHeaders.Flatten(buf, false); 1.75 + return; 1.76 + } 1.77 + 1.78 + // otherwise, we need to iterate over the headers and only flatten 1.79 + // those that are appropriate. 1.80 + uint32_t i, count = mHeaders.Count(); 1.81 + for (i=0; i<count; ++i) { 1.82 + nsHttpAtom header; 1.83 + const char *value = mHeaders.PeekHeaderAt(i, header); 1.84 + 1.85 + if (!value || header == nsHttp::Connection 1.86 + || header == nsHttp::Proxy_Connection 1.87 + || header == nsHttp::Keep_Alive 1.88 + || header == nsHttp::WWW_Authenticate 1.89 + || header == nsHttp::Proxy_Authenticate 1.90 + || header == nsHttp::Trailer 1.91 + || header == nsHttp::Transfer_Encoding 1.92 + || header == nsHttp::Upgrade 1.93 + // XXX this will cause problems when we start honoring 1.94 + // Cache-Control: no-cache="set-cookie", what to do? 1.95 + || header == nsHttp::Set_Cookie) 1.96 + continue; 1.97 + 1.98 + // otherwise, write out the "header: value\r\n" line 1.99 + buf.Append(nsDependentCString(header.get()) + 1.100 + NS_LITERAL_CSTRING(": ") + 1.101 + nsDependentCString(value) + 1.102 + NS_LITERAL_CSTRING("\r\n")); 1.103 + } 1.104 +} 1.105 + 1.106 +nsresult 1.107 +nsHttpResponseHead::Parse(char *block) 1.108 +{ 1.109 + 1.110 + LOG(("nsHttpResponseHead::Parse [this=%p]\n", this)); 1.111 + 1.112 + // this command works on a buffer as prepared by Flatten, as such it is 1.113 + // not very forgiving ;-) 1.114 + 1.115 + char *p = PL_strstr(block, "\r\n"); 1.116 + if (!p) 1.117 + return NS_ERROR_UNEXPECTED; 1.118 + 1.119 + *p = 0; 1.120 + ParseStatusLine(block); 1.121 + 1.122 + do { 1.123 + block = p + 2; 1.124 + 1.125 + if (*block == 0) 1.126 + break; 1.127 + 1.128 + p = PL_strstr(block, "\r\n"); 1.129 + if (!p) 1.130 + return NS_ERROR_UNEXPECTED; 1.131 + 1.132 + *p = 0; 1.133 + ParseHeaderLine(block); 1.134 + 1.135 + } while (1); 1.136 + 1.137 + return NS_OK; 1.138 +} 1.139 + 1.140 +void 1.141 +nsHttpResponseHead::AssignDefaultStatusText() 1.142 +{ 1.143 + LOG(("response status line needs default reason phrase\n")); 1.144 + 1.145 + // if a http response doesn't contain a reason phrase, put one in based 1.146 + // on the status code. The reason phrase is totally meaningless so its 1.147 + // ok to have a default catch all here - but this makes debuggers and addons 1.148 + // a little saner to use if we don't map things to "404 OK" or other nonsense. 1.149 + // In particular, HTTP/2 does not use reason phrases at all so they need to 1.150 + // always be injected. 1.151 + 1.152 + switch (mStatus) { 1.153 + // start with the most common 1.154 + case 200: 1.155 + mStatusText.AssignLiteral("OK"); 1.156 + break; 1.157 + case 404: 1.158 + mStatusText.AssignLiteral("Not Found"); 1.159 + break; 1.160 + case 301: 1.161 + mStatusText.AssignLiteral("Moved Permanently"); 1.162 + break; 1.163 + case 304: 1.164 + mStatusText.AssignLiteral("Not Modified"); 1.165 + break; 1.166 + case 307: 1.167 + mStatusText.AssignLiteral("Temporary Redirect"); 1.168 + break; 1.169 + case 500: 1.170 + mStatusText.AssignLiteral("Internal Server Error"); 1.171 + break; 1.172 + 1.173 + // also well known 1.174 + case 100: 1.175 + mStatusText.AssignLiteral("Continue"); 1.176 + break; 1.177 + case 101: 1.178 + mStatusText.AssignLiteral("Switching Protocols"); 1.179 + break; 1.180 + case 201: 1.181 + mStatusText.AssignLiteral("Created"); 1.182 + break; 1.183 + case 202: 1.184 + mStatusText.AssignLiteral("Accepted"); 1.185 + break; 1.186 + case 203: 1.187 + mStatusText.AssignLiteral("Non Authoritative"); 1.188 + break; 1.189 + case 204: 1.190 + mStatusText.AssignLiteral("No Content"); 1.191 + break; 1.192 + case 205: 1.193 + mStatusText.AssignLiteral("Reset Content"); 1.194 + break; 1.195 + case 206: 1.196 + mStatusText.AssignLiteral("Partial Content"); 1.197 + break; 1.198 + case 300: 1.199 + mStatusText.AssignLiteral("Multiple Choices"); 1.200 + break; 1.201 + case 302: 1.202 + mStatusText.AssignLiteral("Found"); 1.203 + break; 1.204 + case 303: 1.205 + mStatusText.AssignLiteral("See Other"); 1.206 + break; 1.207 + case 305: 1.208 + mStatusText.AssignLiteral("Use Proxy"); 1.209 + break; 1.210 + case 308: 1.211 + mStatusText.AssignLiteral("Permanent Redirect"); 1.212 + break; 1.213 + case 400: 1.214 + mStatusText.AssignLiteral("Bad Request"); 1.215 + break; 1.216 + case 401: 1.217 + mStatusText.AssignLiteral("Unauthorized"); 1.218 + break; 1.219 + case 402: 1.220 + mStatusText.AssignLiteral("Payment Required"); 1.221 + break; 1.222 + case 403: 1.223 + mStatusText.AssignLiteral("Forbidden"); 1.224 + break; 1.225 + case 405: 1.226 + mStatusText.AssignLiteral("Method Not Allowed"); 1.227 + break; 1.228 + case 406: 1.229 + mStatusText.AssignLiteral("Not Acceptable"); 1.230 + break; 1.231 + case 407: 1.232 + mStatusText.AssignLiteral("Proxy Authentication Required"); 1.233 + break; 1.234 + case 408: 1.235 + mStatusText.AssignLiteral("Request Timeout"); 1.236 + break; 1.237 + case 409: 1.238 + mStatusText.AssignLiteral("Conflict"); 1.239 + break; 1.240 + case 410: 1.241 + mStatusText.AssignLiteral("Gone"); 1.242 + break; 1.243 + case 411: 1.244 + mStatusText.AssignLiteral("Length Required"); 1.245 + break; 1.246 + case 412: 1.247 + mStatusText.AssignLiteral("Precondition Failed"); 1.248 + break; 1.249 + case 413: 1.250 + mStatusText.AssignLiteral("Request Entity Too Large"); 1.251 + break; 1.252 + case 414: 1.253 + mStatusText.AssignLiteral("Request URI Too Long"); 1.254 + break; 1.255 + case 415: 1.256 + mStatusText.AssignLiteral("Unsupported Media Type"); 1.257 + break; 1.258 + case 416: 1.259 + mStatusText.AssignLiteral("Requested Range Not Satisfiable"); 1.260 + break; 1.261 + case 417: 1.262 + mStatusText.AssignLiteral("Expectation Failed"); 1.263 + break; 1.264 + case 501: 1.265 + mStatusText.AssignLiteral("Not Implemented"); 1.266 + break; 1.267 + case 502: 1.268 + mStatusText.AssignLiteral("Bad Gateway"); 1.269 + break; 1.270 + case 503: 1.271 + mStatusText.AssignLiteral("Service Unavailable"); 1.272 + break; 1.273 + case 504: 1.274 + mStatusText.AssignLiteral("Gateway Timeout"); 1.275 + break; 1.276 + case 505: 1.277 + mStatusText.AssignLiteral("HTTP Version Unsupported"); 1.278 + break; 1.279 + default: 1.280 + mStatusText.AssignLiteral("No Reason Phrase"); 1.281 + break; 1.282 + } 1.283 +} 1.284 + 1.285 +void 1.286 +nsHttpResponseHead::ParseStatusLine(const char *line) 1.287 +{ 1.288 + // 1.289 + // Parse Status-Line:: HTTP-Version SP Status-Code SP Reason-Phrase CRLF 1.290 + // 1.291 + 1.292 + // HTTP-Version 1.293 + ParseVersion(line); 1.294 + 1.295 + if ((mVersion == NS_HTTP_VERSION_0_9) || !(line = PL_strchr(line, ' '))) { 1.296 + mStatus = 200; 1.297 + AssignDefaultStatusText(); 1.298 + } 1.299 + else { 1.300 + // Status-Code 1.301 + mStatus = (uint16_t) atoi(++line); 1.302 + if (mStatus == 0) { 1.303 + LOG(("mal-formed response status; assuming status = 200\n")); 1.304 + mStatus = 200; 1.305 + } 1.306 + 1.307 + // Reason-Phrase is whatever is remaining of the line 1.308 + if (!(line = PL_strchr(line, ' '))) { 1.309 + AssignDefaultStatusText(); 1.310 + } 1.311 + else 1.312 + mStatusText = nsDependentCString(++line); 1.313 + } 1.314 + 1.315 + LOG(("Have status line [version=%u status=%u statusText=%s]\n", 1.316 + unsigned(mVersion), unsigned(mStatus), mStatusText.get())); 1.317 +} 1.318 + 1.319 +nsresult 1.320 +nsHttpResponseHead::ParseHeaderLine(const char *line) 1.321 +{ 1.322 + nsHttpAtom hdr = {0}; 1.323 + char *val; 1.324 + nsresult rv; 1.325 + 1.326 + rv = mHeaders.ParseHeaderLine(line, &hdr, &val); 1.327 + if (NS_FAILED(rv)) 1.328 + return rv; 1.329 + 1.330 + // leading and trailing LWS has been removed from |val| 1.331 + 1.332 + // handle some special case headers... 1.333 + if (hdr == nsHttp::Content_Length) { 1.334 + int64_t len; 1.335 + const char *ignored; 1.336 + // permit only a single value here. 1.337 + if (nsHttp::ParseInt64(val, &ignored, &len)) { 1.338 + mContentLength = len; 1.339 + } 1.340 + else { 1.341 + // If this is a negative content length then just ignore it 1.342 + LOG(("invalid content-length! %s\n", val)); 1.343 + } 1.344 + } 1.345 + else if (hdr == nsHttp::Content_Type) { 1.346 + LOG(("ParseContentType [type=%s]\n", val)); 1.347 + bool dummy; 1.348 + net_ParseContentType(nsDependentCString(val), 1.349 + mContentType, mContentCharset, &dummy); 1.350 + } 1.351 + else if (hdr == nsHttp::Cache_Control) 1.352 + ParseCacheControl(val); 1.353 + else if (hdr == nsHttp::Pragma) 1.354 + ParsePragma(val); 1.355 + return NS_OK; 1.356 +} 1.357 + 1.358 +// From section 13.2.3 of RFC2616, we compute the current age of a cached 1.359 +// response as follows: 1.360 +// 1.361 +// currentAge = max(max(0, responseTime - dateValue), ageValue) 1.362 +// + now - requestTime 1.363 +// 1.364 +// where responseTime == now 1.365 +// 1.366 +// This is typically a very small number. 1.367 +// 1.368 +nsresult 1.369 +nsHttpResponseHead::ComputeCurrentAge(uint32_t now, 1.370 + uint32_t requestTime, 1.371 + uint32_t *result) const 1.372 +{ 1.373 + uint32_t dateValue; 1.374 + uint32_t ageValue; 1.375 + 1.376 + *result = 0; 1.377 + 1.378 + if (NS_FAILED(GetDateValue(&dateValue))) { 1.379 + LOG(("nsHttpResponseHead::ComputeCurrentAge [this=%p] " 1.380 + "Date response header not set!\n", this)); 1.381 + // Assume we have a fast connection and that our clock 1.382 + // is in sync with the server. 1.383 + dateValue = now; 1.384 + } 1.385 + 1.386 + // Compute apparent age 1.387 + if (now > dateValue) 1.388 + *result = now - dateValue; 1.389 + 1.390 + // Compute corrected received age 1.391 + if (NS_SUCCEEDED(GetAgeValue(&ageValue))) 1.392 + *result = std::max(*result, ageValue); 1.393 + 1.394 + MOZ_ASSERT(now >= requestTime, "bogus request time"); 1.395 + 1.396 + // Compute current age 1.397 + *result += (now - requestTime); 1.398 + return NS_OK; 1.399 +} 1.400 + 1.401 +// From section 13.2.4 of RFC2616, we compute the freshness lifetime of a cached 1.402 +// response as follows: 1.403 +// 1.404 +// freshnessLifetime = max_age_value 1.405 +// <or> 1.406 +// freshnessLifetime = expires_value - date_value 1.407 +// <or> 1.408 +// freshnessLifetime = (date_value - last_modified_value) * 0.10 1.409 +// <or> 1.410 +// freshnessLifetime = 0 1.411 +// 1.412 +nsresult 1.413 +nsHttpResponseHead::ComputeFreshnessLifetime(uint32_t *result) const 1.414 +{ 1.415 + *result = 0; 1.416 + 1.417 + // Try HTTP/1.1 style max-age directive... 1.418 + if (NS_SUCCEEDED(GetMaxAgeValue(result))) 1.419 + return NS_OK; 1.420 + 1.421 + *result = 0; 1.422 + 1.423 + uint32_t date = 0, date2 = 0; 1.424 + if (NS_FAILED(GetDateValue(&date))) 1.425 + date = NowInSeconds(); // synthesize a date header if none exists 1.426 + 1.427 + // Try HTTP/1.0 style expires header... 1.428 + if (NS_SUCCEEDED(GetExpiresValue(&date2))) { 1.429 + if (date2 > date) 1.430 + *result = date2 - date; 1.431 + // the Expires header can specify a date in the past. 1.432 + return NS_OK; 1.433 + } 1.434 + 1.435 + // Fallback on heuristic using last modified header... 1.436 + if (NS_SUCCEEDED(GetLastModifiedValue(&date2))) { 1.437 + LOG(("using last-modified to determine freshness-lifetime\n")); 1.438 + LOG(("last-modified = %u, date = %u\n", date2, date)); 1.439 + if (date2 <= date) { 1.440 + // this only makes sense if last-modified is actually in the past 1.441 + *result = (date - date2) / 10; 1.442 + return NS_OK; 1.443 + } 1.444 + } 1.445 + 1.446 + // These responses can be cached indefinitely. 1.447 + if ((mStatus == 300) || nsHttp::IsPermanentRedirect(mStatus)) { 1.448 + *result = uint32_t(-1); 1.449 + return NS_OK; 1.450 + } 1.451 + 1.452 + LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %x] " 1.453 + "Insufficient information to compute a non-zero freshness " 1.454 + "lifetime!\n", this)); 1.455 + 1.456 + return NS_OK; 1.457 +} 1.458 + 1.459 +bool 1.460 +nsHttpResponseHead::MustValidate() const 1.461 +{ 1.462 + LOG(("nsHttpResponseHead::MustValidate ??\n")); 1.463 + 1.464 + // Some response codes are cacheable, but the rest are not. This switch 1.465 + // should stay in sync with the list in nsHttpChannel::ProcessResponse 1.466 + switch (mStatus) { 1.467 + // Success codes 1.468 + case 200: 1.469 + case 203: 1.470 + case 206: 1.471 + // Cacheable redirects 1.472 + case 300: 1.473 + case 301: 1.474 + case 302: 1.475 + case 304: 1.476 + case 307: 1.477 + case 308: 1.478 + break; 1.479 + // Uncacheable redirects 1.480 + case 303: 1.481 + case 305: 1.482 + // Other known errors 1.483 + case 401: 1.484 + case 407: 1.485 + case 412: 1.486 + case 416: 1.487 + default: // revalidate unknown error pages 1.488 + LOG(("Must validate since response is an uncacheable error page\n")); 1.489 + return true; 1.490 + } 1.491 + 1.492 + // The no-cache response header indicates that we must validate this 1.493 + // cached response before reusing. 1.494 + if (NoCache()) { 1.495 + LOG(("Must validate since response contains 'no-cache' header\n")); 1.496 + return true; 1.497 + } 1.498 + 1.499 + // Likewise, if the response is no-store, then we must validate this 1.500 + // cached response before reusing. NOTE: it may seem odd that a no-store 1.501 + // response may be cached, but indeed all responses are cached in order 1.502 + // to support File->SaveAs, View->PageSource, and other browser features. 1.503 + if (NoStore()) { 1.504 + LOG(("Must validate since response contains 'no-store' header\n")); 1.505 + return true; 1.506 + } 1.507 + 1.508 + // Compare the Expires header to the Date header. If the server sent an 1.509 + // Expires header with a timestamp in the past, then we must validate this 1.510 + // cached response before reusing. 1.511 + if (ExpiresInPast()) { 1.512 + LOG(("Must validate since Expires < Date\n")); 1.513 + return true; 1.514 + } 1.515 + 1.516 + LOG(("no mandatory validation requirement\n")); 1.517 + return false; 1.518 +} 1.519 + 1.520 +bool 1.521 +nsHttpResponseHead::MustValidateIfExpired() const 1.522 +{ 1.523 + // according to RFC2616, section 14.9.4: 1.524 + // 1.525 + // When the must-revalidate directive is present in a response received by a 1.526 + // cache, that cache MUST NOT use the entry after it becomes stale to respond to 1.527 + // a subsequent request without first revalidating it with the origin server. 1.528 + // 1.529 + return HasHeaderValue(nsHttp::Cache_Control, "must-revalidate"); 1.530 +} 1.531 + 1.532 +bool 1.533 +nsHttpResponseHead::IsResumable() const 1.534 +{ 1.535 + // even though some HTTP/1.0 servers may support byte range requests, we're not 1.536 + // going to bother with them, since those servers wouldn't understand If-Range. 1.537 + // Also, while in theory it may be possible to resume when the status code 1.538 + // is not 200, it is unlikely to be worth the trouble, especially for 1.539 + // non-2xx responses. 1.540 + return mStatus == 200 && 1.541 + mVersion >= NS_HTTP_VERSION_1_1 && 1.542 + PeekHeader(nsHttp::Content_Length) && 1.543 + (PeekHeader(nsHttp::ETag) || PeekHeader(nsHttp::Last_Modified)) && 1.544 + HasHeaderValue(nsHttp::Accept_Ranges, "bytes"); 1.545 +} 1.546 + 1.547 +bool 1.548 +nsHttpResponseHead::ExpiresInPast() const 1.549 +{ 1.550 + uint32_t maxAgeVal, expiresVal, dateVal; 1.551 + 1.552 + // Bug #203271. Ensure max-age directive takes precedence over Expires 1.553 + if (NS_SUCCEEDED(GetMaxAgeValue(&maxAgeVal))) { 1.554 + return false; 1.555 + } 1.556 + 1.557 + return NS_SUCCEEDED(GetExpiresValue(&expiresVal)) && 1.558 + NS_SUCCEEDED(GetDateValue(&dateVal)) && 1.559 + expiresVal < dateVal; 1.560 +} 1.561 + 1.562 +nsresult 1.563 +nsHttpResponseHead::UpdateHeaders(const nsHttpHeaderArray &headers) 1.564 +{ 1.565 + LOG(("nsHttpResponseHead::UpdateHeaders [this=%p]\n", this)); 1.566 + 1.567 + uint32_t i, count = headers.Count(); 1.568 + for (i=0; i<count; ++i) { 1.569 + nsHttpAtom header; 1.570 + const char *val = headers.PeekHeaderAt(i, header); 1.571 + 1.572 + if (!val) { 1.573 + continue; 1.574 + } 1.575 + 1.576 + // Ignore any hop-by-hop headers... 1.577 + if (header == nsHttp::Connection || 1.578 + header == nsHttp::Proxy_Connection || 1.579 + header == nsHttp::Keep_Alive || 1.580 + header == nsHttp::Proxy_Authenticate || 1.581 + header == nsHttp::Proxy_Authorization || // not a response header! 1.582 + header == nsHttp::TE || 1.583 + header == nsHttp::Trailer || 1.584 + header == nsHttp::Transfer_Encoding || 1.585 + header == nsHttp::Upgrade || 1.586 + // Ignore any non-modifiable headers... 1.587 + header == nsHttp::Content_Location || 1.588 + header == nsHttp::Content_MD5 || 1.589 + header == nsHttp::ETag || 1.590 + // Assume Cache-Control: "no-transform" 1.591 + header == nsHttp::Content_Encoding || 1.592 + header == nsHttp::Content_Range || 1.593 + header == nsHttp::Content_Type || 1.594 + // Ignore wacky headers too... 1.595 + // this one is for MS servers that send "Content-Length: 0" 1.596 + // on 304 responses 1.597 + header == nsHttp::Content_Length) { 1.598 + LOG(("ignoring response header [%s: %s]\n", header.get(), val)); 1.599 + } 1.600 + else { 1.601 + LOG(("new response header [%s: %s]\n", header.get(), val)); 1.602 + 1.603 + // overwrite the current header value with the new value... 1.604 + SetHeader(header, nsDependentCString(val)); 1.605 + } 1.606 + } 1.607 + 1.608 + return NS_OK; 1.609 +} 1.610 + 1.611 +void 1.612 +nsHttpResponseHead::Reset() 1.613 +{ 1.614 + LOG(("nsHttpResponseHead::Reset\n")); 1.615 + 1.616 + ClearHeaders(); 1.617 + 1.618 + mVersion = NS_HTTP_VERSION_1_1; 1.619 + mStatus = 200; 1.620 + mContentLength = UINT64_MAX; 1.621 + mCacheControlNoStore = false; 1.622 + mCacheControlNoCache = false; 1.623 + mPragmaNoCache = false; 1.624 + mStatusText.Truncate(); 1.625 + mContentType.Truncate(); 1.626 + mContentCharset.Truncate(); 1.627 +} 1.628 + 1.629 +nsresult 1.630 +nsHttpResponseHead::ParseDateHeader(nsHttpAtom header, uint32_t *result) const 1.631 +{ 1.632 + const char *val = PeekHeader(header); 1.633 + if (!val) 1.634 + return NS_ERROR_NOT_AVAILABLE; 1.635 + 1.636 + PRTime time; 1.637 + PRStatus st = PR_ParseTimeString(val, true, &time); 1.638 + if (st != PR_SUCCESS) 1.639 + return NS_ERROR_NOT_AVAILABLE; 1.640 + 1.641 + *result = PRTimeToSeconds(time); 1.642 + return NS_OK; 1.643 +} 1.644 + 1.645 +nsresult 1.646 +nsHttpResponseHead::GetAgeValue(uint32_t *result) const 1.647 +{ 1.648 + const char *val = PeekHeader(nsHttp::Age); 1.649 + if (!val) 1.650 + return NS_ERROR_NOT_AVAILABLE; 1.651 + 1.652 + *result = (uint32_t) atoi(val); 1.653 + return NS_OK; 1.654 +} 1.655 + 1.656 +// Return the value of the (HTTP 1.1) max-age directive, which itself is a 1.657 +// component of the Cache-Control response header 1.658 +nsresult 1.659 +nsHttpResponseHead::GetMaxAgeValue(uint32_t *result) const 1.660 +{ 1.661 + const char *val = PeekHeader(nsHttp::Cache_Control); 1.662 + if (!val) 1.663 + return NS_ERROR_NOT_AVAILABLE; 1.664 + 1.665 + const char *p = nsHttp::FindToken(val, "max-age", HTTP_HEADER_VALUE_SEPS "="); 1.666 + if (!p) 1.667 + return NS_ERROR_NOT_AVAILABLE; 1.668 + p += 7; 1.669 + while (*p == ' ' || *p == '\t') 1.670 + ++p; 1.671 + if (*p != '=') 1.672 + return NS_ERROR_NOT_AVAILABLE; 1.673 + ++p; 1.674 + while (*p == ' ' || *p == '\t') 1.675 + ++p; 1.676 + 1.677 + int maxAgeValue = atoi(p); 1.678 + if (maxAgeValue < 0) 1.679 + maxAgeValue = 0; 1.680 + *result = static_cast<uint32_t>(maxAgeValue); 1.681 + return NS_OK; 1.682 +} 1.683 + 1.684 +nsresult 1.685 +nsHttpResponseHead::GetExpiresValue(uint32_t *result) const 1.686 +{ 1.687 + const char *val = PeekHeader(nsHttp::Expires); 1.688 + if (!val) 1.689 + return NS_ERROR_NOT_AVAILABLE; 1.690 + 1.691 + PRTime time; 1.692 + PRStatus st = PR_ParseTimeString(val, true, &time); 1.693 + if (st != PR_SUCCESS) { 1.694 + // parsing failed... RFC 2616 section 14.21 says we should treat this 1.695 + // as an expiration time in the past. 1.696 + *result = 0; 1.697 + return NS_OK; 1.698 + } 1.699 + 1.700 + if (time < 0) 1.701 + *result = 0; 1.702 + else 1.703 + *result = PRTimeToSeconds(time); 1.704 + return NS_OK; 1.705 +} 1.706 + 1.707 +int64_t 1.708 +nsHttpResponseHead::TotalEntitySize() const 1.709 +{ 1.710 + const char* contentRange = PeekHeader(nsHttp::Content_Range); 1.711 + if (!contentRange) 1.712 + return ContentLength(); 1.713 + 1.714 + // Total length is after a slash 1.715 + const char* slash = strrchr(contentRange, '/'); 1.716 + if (!slash) 1.717 + return -1; // No idea what the length is 1.718 + 1.719 + slash++; 1.720 + if (*slash == '*') // Server doesn't know the length 1.721 + return -1; 1.722 + 1.723 + int64_t size; 1.724 + if (!nsHttp::ParseInt64(slash, &size)) 1.725 + size = UINT64_MAX; 1.726 + return size; 1.727 +} 1.728 + 1.729 +//----------------------------------------------------------------------------- 1.730 +// nsHttpResponseHead <private> 1.731 +//----------------------------------------------------------------------------- 1.732 + 1.733 +void 1.734 +nsHttpResponseHead::ParseVersion(const char *str) 1.735 +{ 1.736 + // Parse HTTP-Version:: "HTTP" "/" 1*DIGIT "." 1*DIGIT 1.737 + 1.738 + LOG(("nsHttpResponseHead::ParseVersion [version=%s]\n", str)); 1.739 + 1.740 + // make sure we have HTTP at the beginning 1.741 + if (PL_strncasecmp(str, "HTTP", 4) != 0) { 1.742 + if (PL_strncasecmp(str, "ICY ", 4) == 0) { 1.743 + // ShoutCast ICY is HTTP/1.0-like. Assume it is HTTP/1.0. 1.744 + LOG(("Treating ICY as HTTP 1.0\n")); 1.745 + mVersion = NS_HTTP_VERSION_1_0; 1.746 + return; 1.747 + } 1.748 + LOG(("looks like a HTTP/0.9 response\n")); 1.749 + mVersion = NS_HTTP_VERSION_0_9; 1.750 + return; 1.751 + } 1.752 + str += 4; 1.753 + 1.754 + if (*str != '/') { 1.755 + LOG(("server did not send a version number; assuming HTTP/1.0\n")); 1.756 + // NCSA/1.5.2 has a bug in which it fails to send a version number 1.757 + // if the request version is HTTP/1.1, so we fall back on HTTP/1.0 1.758 + mVersion = NS_HTTP_VERSION_1_0; 1.759 + return; 1.760 + } 1.761 + 1.762 + char *p = PL_strchr(str, '.'); 1.763 + if (p == nullptr) { 1.764 + LOG(("mal-formed server version; assuming HTTP/1.0\n")); 1.765 + mVersion = NS_HTTP_VERSION_1_0; 1.766 + return; 1.767 + } 1.768 + 1.769 + ++p; // let b point to the minor version 1.770 + 1.771 + int major = atoi(str + 1); 1.772 + int minor = atoi(p); 1.773 + 1.774 + if ((major > 2) || ((major == 2) && (minor >= 0))) 1.775 + mVersion = NS_HTTP_VERSION_2_0; 1.776 + else if ((major == 1) && (minor >= 1)) 1.777 + // at least HTTP/1.1 1.778 + mVersion = NS_HTTP_VERSION_1_1; 1.779 + else 1.780 + // treat anything else as version 1.0 1.781 + mVersion = NS_HTTP_VERSION_1_0; 1.782 +} 1.783 + 1.784 +void 1.785 +nsHttpResponseHead::ParseCacheControl(const char *val) 1.786 +{ 1.787 + if (!(val && *val)) { 1.788 + // clear flags 1.789 + mCacheControlNoCache = false; 1.790 + mCacheControlNoStore = false; 1.791 + return; 1.792 + } 1.793 + 1.794 + // search header value for occurrence(s) of "no-cache" but ignore 1.795 + // occurrence(s) of "no-cache=blah" 1.796 + if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS)) 1.797 + mCacheControlNoCache = true; 1.798 + 1.799 + // search header value for occurrence of "no-store" 1.800 + if (nsHttp::FindToken(val, "no-store", HTTP_HEADER_VALUE_SEPS)) 1.801 + mCacheControlNoStore = true; 1.802 +} 1.803 + 1.804 +void 1.805 +nsHttpResponseHead::ParsePragma(const char *val) 1.806 +{ 1.807 + LOG(("nsHttpResponseHead::ParsePragma [val=%s]\n", val)); 1.808 + 1.809 + if (!(val && *val)) { 1.810 + // clear no-cache flag 1.811 + mPragmaNoCache = false; 1.812 + return; 1.813 + } 1.814 + 1.815 + // Although 'Pragma: no-cache' is not a standard HTTP response header (it's 1.816 + // a request header), caching is inhibited when this header is present so 1.817 + // as to match existing Navigator behavior. 1.818 + if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS)) 1.819 + mPragmaNoCache = true; 1.820 +} 1.821 + 1.822 +} // namespace mozilla::net 1.823 +} // namespace mozilla