netwerk/protocol/http/nsHttpResponseHead.cpp

changeset 0
6474c204b198
     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

mercurial