netwerk/protocol/http/nsHttpResponseHead.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim:set ts=4 sw=4 sts=4 et cin: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 // HttpLog.h should generally be included first
     8 #include "HttpLog.h"
    10 #include "nsHttpResponseHead.h"
    11 #include "nsPrintfCString.h"
    12 #include "prtime.h"
    13 #include "nsURLHelper.h"
    14 #include <algorithm>
    16 namespace mozilla {
    17 namespace net {
    19 //-----------------------------------------------------------------------------
    20 // nsHttpResponseHead <public>
    21 //-----------------------------------------------------------------------------
    23 nsresult
    24 nsHttpResponseHead::SetHeader(nsHttpAtom hdr,
    25                               const nsACString &val,
    26                               bool merge)
    27 {
    28     nsresult rv = mHeaders.SetHeader(hdr, val, merge);
    29     if (NS_FAILED(rv)) return rv;
    31     // respond to changes in these headers.  we need to reparse the entire
    32     // header since the change may have merged in additional values.
    33     if (hdr == nsHttp::Cache_Control)
    34         ParseCacheControl(mHeaders.PeekHeader(hdr));
    35     else if (hdr == nsHttp::Pragma)
    36         ParsePragma(mHeaders.PeekHeader(hdr));
    38     return NS_OK;
    39 }
    41 void
    42 nsHttpResponseHead::SetContentLength(int64_t len)
    43 {
    44     mContentLength = len;
    45     if (len < 0)
    46         mHeaders.ClearHeader(nsHttp::Content_Length);
    47     else
    48         mHeaders.SetHeader(nsHttp::Content_Length, nsPrintfCString("%lld", len));
    49 }
    51 void
    52 nsHttpResponseHead::Flatten(nsACString &buf, bool pruneTransients)
    53 {
    54     if (mVersion == NS_HTTP_VERSION_0_9)
    55         return;
    57     buf.AppendLiteral("HTTP/");
    58     if (mVersion == NS_HTTP_VERSION_2_0)
    59         buf.AppendLiteral("2.0 ");
    60     else if (mVersion == NS_HTTP_VERSION_1_1)
    61         buf.AppendLiteral("1.1 ");
    62     else
    63         buf.AppendLiteral("1.0 ");
    65     buf.Append(nsPrintfCString("%u", unsigned(mStatus)) +
    66                NS_LITERAL_CSTRING(" ") +
    67                mStatusText +
    68                NS_LITERAL_CSTRING("\r\n"));
    70     if (!pruneTransients) {
    71         mHeaders.Flatten(buf, false);
    72         return;
    73     }
    75     // otherwise, we need to iterate over the headers and only flatten
    76     // those that are appropriate.
    77     uint32_t i, count = mHeaders.Count();
    78     for (i=0; i<count; ++i) {
    79         nsHttpAtom header;
    80         const char *value = mHeaders.PeekHeaderAt(i, header);
    82         if (!value || header == nsHttp::Connection
    83                    || header == nsHttp::Proxy_Connection
    84                    || header == nsHttp::Keep_Alive
    85                    || header == nsHttp::WWW_Authenticate
    86                    || header == nsHttp::Proxy_Authenticate
    87                    || header == nsHttp::Trailer
    88                    || header == nsHttp::Transfer_Encoding
    89                    || header == nsHttp::Upgrade
    90                    // XXX this will cause problems when we start honoring
    91                    // Cache-Control: no-cache="set-cookie", what to do?
    92                    || header == nsHttp::Set_Cookie)
    93             continue;
    95         // otherwise, write out the "header: value\r\n" line
    96         buf.Append(nsDependentCString(header.get()) +
    97                    NS_LITERAL_CSTRING(": ") +
    98                    nsDependentCString(value) +
    99                    NS_LITERAL_CSTRING("\r\n"));
   100     }
   101 }
   103 nsresult
   104 nsHttpResponseHead::Parse(char *block)
   105 {
   107     LOG(("nsHttpResponseHead::Parse [this=%p]\n", this));
   109     // this command works on a buffer as prepared by Flatten, as such it is
   110     // not very forgiving ;-)
   112     char *p = PL_strstr(block, "\r\n");
   113     if (!p)
   114         return NS_ERROR_UNEXPECTED;
   116     *p = 0;
   117     ParseStatusLine(block);
   119     do {
   120         block = p + 2;
   122 		if (*block == 0)
   123 			break;
   125         p = PL_strstr(block, "\r\n");
   126         if (!p)
   127             return NS_ERROR_UNEXPECTED;
   129         *p = 0;
   130         ParseHeaderLine(block);
   132     } while (1);
   134     return NS_OK;
   135 }
   137 void
   138 nsHttpResponseHead::AssignDefaultStatusText()
   139 {
   140     LOG(("response status line needs default reason phrase\n"));
   142     // if a http response doesn't contain a reason phrase, put one in based
   143     // on the status code. The reason phrase is totally meaningless so its
   144     // ok to have a default catch all here - but this makes debuggers and addons
   145     // a little saner to use if we don't map things to "404 OK" or other nonsense.
   146     // In particular, HTTP/2 does not use reason phrases at all so they need to
   147     // always be injected.
   149     switch (mStatus) {
   150         // start with the most common
   151     case 200:
   152         mStatusText.AssignLiteral("OK");
   153         break;
   154     case 404:
   155         mStatusText.AssignLiteral("Not Found");
   156         break;
   157     case 301:
   158         mStatusText.AssignLiteral("Moved Permanently");
   159         break;
   160     case 304:
   161         mStatusText.AssignLiteral("Not Modified");
   162         break;
   163     case 307:
   164         mStatusText.AssignLiteral("Temporary Redirect");
   165         break;
   166     case 500:
   167         mStatusText.AssignLiteral("Internal Server Error");
   168         break;
   170         // also well known
   171     case 100:
   172         mStatusText.AssignLiteral("Continue");
   173         break;
   174     case 101:
   175         mStatusText.AssignLiteral("Switching Protocols");
   176         break;
   177     case 201:
   178         mStatusText.AssignLiteral("Created");
   179         break;
   180     case 202:
   181         mStatusText.AssignLiteral("Accepted");
   182         break;
   183     case 203:
   184         mStatusText.AssignLiteral("Non Authoritative");
   185         break;
   186     case 204:
   187         mStatusText.AssignLiteral("No Content");
   188         break;
   189     case 205:
   190         mStatusText.AssignLiteral("Reset Content");
   191         break;
   192     case 206:
   193         mStatusText.AssignLiteral("Partial Content");
   194         break;
   195     case 300:
   196         mStatusText.AssignLiteral("Multiple Choices");
   197         break;
   198     case 302:
   199         mStatusText.AssignLiteral("Found");
   200         break;
   201     case 303:
   202         mStatusText.AssignLiteral("See Other");
   203         break;
   204     case 305:
   205         mStatusText.AssignLiteral("Use Proxy");
   206         break;
   207     case 308:
   208         mStatusText.AssignLiteral("Permanent Redirect");
   209         break;
   210     case 400:
   211         mStatusText.AssignLiteral("Bad Request");
   212         break;
   213     case 401:
   214         mStatusText.AssignLiteral("Unauthorized");
   215         break;
   216     case 402:
   217         mStatusText.AssignLiteral("Payment Required");
   218         break;
   219     case 403:
   220         mStatusText.AssignLiteral("Forbidden");
   221         break;
   222     case 405:
   223         mStatusText.AssignLiteral("Method Not Allowed");
   224         break;
   225     case 406:
   226         mStatusText.AssignLiteral("Not Acceptable");
   227         break;
   228     case 407:
   229         mStatusText.AssignLiteral("Proxy Authentication Required");
   230         break;
   231     case 408:
   232         mStatusText.AssignLiteral("Request Timeout");
   233         break;
   234     case 409:
   235         mStatusText.AssignLiteral("Conflict");
   236         break;
   237     case 410:
   238         mStatusText.AssignLiteral("Gone");
   239         break;
   240     case 411:
   241         mStatusText.AssignLiteral("Length Required");
   242         break;
   243     case 412:
   244         mStatusText.AssignLiteral("Precondition Failed");
   245         break;
   246     case 413:
   247         mStatusText.AssignLiteral("Request Entity Too Large");
   248         break;
   249     case 414:
   250         mStatusText.AssignLiteral("Request URI Too Long");
   251         break;
   252     case 415:
   253         mStatusText.AssignLiteral("Unsupported Media Type");
   254         break;
   255     case 416:
   256         mStatusText.AssignLiteral("Requested Range Not Satisfiable");
   257         break;
   258     case 417:
   259         mStatusText.AssignLiteral("Expectation Failed");
   260         break;
   261     case 501:
   262         mStatusText.AssignLiteral("Not Implemented");
   263         break;
   264     case 502:
   265         mStatusText.AssignLiteral("Bad Gateway");
   266         break;
   267     case 503:
   268         mStatusText.AssignLiteral("Service Unavailable");
   269         break;
   270     case 504:
   271         mStatusText.AssignLiteral("Gateway Timeout");
   272         break;
   273     case 505:
   274         mStatusText.AssignLiteral("HTTP Version Unsupported");
   275         break;
   276     default:
   277         mStatusText.AssignLiteral("No Reason Phrase");
   278         break;
   279     }
   280 }
   282 void
   283 nsHttpResponseHead::ParseStatusLine(const char *line)
   284 {
   285     //
   286     // Parse Status-Line:: HTTP-Version SP Status-Code SP Reason-Phrase CRLF
   287     //
   289     // HTTP-Version
   290     ParseVersion(line);
   292     if ((mVersion == NS_HTTP_VERSION_0_9) || !(line = PL_strchr(line, ' '))) {
   293         mStatus = 200;
   294         AssignDefaultStatusText();
   295     }
   296     else {
   297         // Status-Code
   298         mStatus = (uint16_t) atoi(++line);
   299         if (mStatus == 0) {
   300             LOG(("mal-formed response status; assuming status = 200\n"));
   301             mStatus = 200;
   302         }
   304         // Reason-Phrase is whatever is remaining of the line
   305         if (!(line = PL_strchr(line, ' '))) {
   306             AssignDefaultStatusText();
   307         }
   308         else
   309             mStatusText = nsDependentCString(++line);
   310     }
   312     LOG(("Have status line [version=%u status=%u statusText=%s]\n",
   313         unsigned(mVersion), unsigned(mStatus), mStatusText.get()));
   314 }
   316 nsresult
   317 nsHttpResponseHead::ParseHeaderLine(const char *line)
   318 {
   319     nsHttpAtom hdr = {0};
   320     char *val;
   321     nsresult rv;
   323     rv = mHeaders.ParseHeaderLine(line, &hdr, &val);
   324     if (NS_FAILED(rv))
   325         return rv;
   327     // leading and trailing LWS has been removed from |val|
   329     // handle some special case headers...
   330     if (hdr == nsHttp::Content_Length) {
   331         int64_t len;
   332         const char *ignored;
   333         // permit only a single value here.
   334         if (nsHttp::ParseInt64(val, &ignored, &len)) {
   335             mContentLength = len;
   336         }
   337         else {
   338             // If this is a negative content length then just ignore it
   339             LOG(("invalid content-length! %s\n", val));
   340         }
   341     }
   342     else if (hdr == nsHttp::Content_Type) {
   343         LOG(("ParseContentType [type=%s]\n", val));
   344         bool dummy;
   345         net_ParseContentType(nsDependentCString(val),
   346                              mContentType, mContentCharset, &dummy);
   347     }
   348     else if (hdr == nsHttp::Cache_Control)
   349         ParseCacheControl(val);
   350     else if (hdr == nsHttp::Pragma)
   351         ParsePragma(val);
   352     return NS_OK;
   353 }
   355 // From section 13.2.3 of RFC2616, we compute the current age of a cached
   356 // response as follows:
   357 //
   358 //    currentAge = max(max(0, responseTime - dateValue), ageValue)
   359 //               + now - requestTime
   360 //
   361 //    where responseTime == now
   362 //
   363 // This is typically a very small number.
   364 //
   365 nsresult
   366 nsHttpResponseHead::ComputeCurrentAge(uint32_t now,
   367                                       uint32_t requestTime,
   368                                       uint32_t *result) const
   369 {
   370     uint32_t dateValue;
   371     uint32_t ageValue;
   373     *result = 0;
   375     if (NS_FAILED(GetDateValue(&dateValue))) {
   376         LOG(("nsHttpResponseHead::ComputeCurrentAge [this=%p] "
   377              "Date response header not set!\n", this));
   378         // Assume we have a fast connection and that our clock
   379         // is in sync with the server.
   380         dateValue = now;
   381     }
   383     // Compute apparent age
   384     if (now > dateValue)
   385         *result = now - dateValue;
   387     // Compute corrected received age
   388     if (NS_SUCCEEDED(GetAgeValue(&ageValue)))
   389         *result = std::max(*result, ageValue);
   391     MOZ_ASSERT(now >= requestTime, "bogus request time");
   393     // Compute current age
   394     *result += (now - requestTime);
   395     return NS_OK;
   396 }
   398 // From section 13.2.4 of RFC2616, we compute the freshness lifetime of a cached
   399 // response as follows:
   400 //
   401 //     freshnessLifetime = max_age_value
   402 // <or>
   403 //     freshnessLifetime = expires_value - date_value
   404 // <or>
   405 //     freshnessLifetime = (date_value - last_modified_value) * 0.10
   406 // <or>
   407 //     freshnessLifetime = 0
   408 //
   409 nsresult
   410 nsHttpResponseHead::ComputeFreshnessLifetime(uint32_t *result) const
   411 {
   412     *result = 0;
   414     // Try HTTP/1.1 style max-age directive...
   415     if (NS_SUCCEEDED(GetMaxAgeValue(result)))
   416         return NS_OK;
   418     *result = 0;
   420     uint32_t date = 0, date2 = 0;
   421     if (NS_FAILED(GetDateValue(&date)))
   422         date = NowInSeconds(); // synthesize a date header if none exists
   424     // Try HTTP/1.0 style expires header...
   425     if (NS_SUCCEEDED(GetExpiresValue(&date2))) {
   426         if (date2 > date)
   427             *result = date2 - date;
   428         // the Expires header can specify a date in the past.
   429         return NS_OK;
   430     }
   432     // Fallback on heuristic using last modified header...
   433     if (NS_SUCCEEDED(GetLastModifiedValue(&date2))) {
   434         LOG(("using last-modified to determine freshness-lifetime\n"));
   435         LOG(("last-modified = %u, date = %u\n", date2, date));
   436         if (date2 <= date) {
   437             // this only makes sense if last-modified is actually in the past
   438             *result = (date - date2) / 10;
   439             return NS_OK;
   440         }
   441     }
   443     // These responses can be cached indefinitely.
   444     if ((mStatus == 300) || nsHttp::IsPermanentRedirect(mStatus)) {
   445         *result = uint32_t(-1);
   446         return NS_OK;
   447     }
   449     LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %x] "
   450          "Insufficient information to compute a non-zero freshness "
   451          "lifetime!\n", this));
   453     return NS_OK;
   454 }
   456 bool
   457 nsHttpResponseHead::MustValidate() const
   458 {
   459     LOG(("nsHttpResponseHead::MustValidate ??\n"));
   461     // Some response codes are cacheable, but the rest are not.  This switch
   462     // should stay in sync with the list in nsHttpChannel::ProcessResponse
   463     switch (mStatus) {
   464         // Success codes
   465     case 200:
   466     case 203:
   467     case 206:
   468         // Cacheable redirects
   469     case 300:
   470     case 301:
   471     case 302:
   472     case 304:
   473     case 307:
   474     case 308:
   475         break;
   476         // Uncacheable redirects
   477     case 303:
   478     case 305:
   479         // Other known errors
   480     case 401:
   481     case 407:
   482     case 412:
   483     case 416:
   484     default:  // revalidate unknown error pages
   485         LOG(("Must validate since response is an uncacheable error page\n"));
   486         return true;
   487     }
   489     // The no-cache response header indicates that we must validate this
   490     // cached response before reusing.
   491     if (NoCache()) {
   492         LOG(("Must validate since response contains 'no-cache' header\n"));
   493         return true;
   494     }
   496     // Likewise, if the response is no-store, then we must validate this
   497     // cached response before reusing.  NOTE: it may seem odd that a no-store
   498     // response may be cached, but indeed all responses are cached in order
   499     // to support File->SaveAs, View->PageSource, and other browser features.
   500     if (NoStore()) {
   501         LOG(("Must validate since response contains 'no-store' header\n"));
   502         return true;
   503     }
   505     // Compare the Expires header to the Date header.  If the server sent an
   506     // Expires header with a timestamp in the past, then we must validate this
   507     // cached response before reusing.
   508     if (ExpiresInPast()) {
   509         LOG(("Must validate since Expires < Date\n"));
   510         return true;
   511     }
   513     LOG(("no mandatory validation requirement\n"));
   514     return false;
   515 }
   517 bool
   518 nsHttpResponseHead::MustValidateIfExpired() const
   519 {
   520     // according to RFC2616, section 14.9.4:
   521     //
   522     //  When the must-revalidate directive is present in a response received by a
   523     //  cache, that cache MUST NOT use the entry after it becomes stale to respond to
   524     //  a subsequent request without first revalidating it with the origin server.
   525     //
   526     return HasHeaderValue(nsHttp::Cache_Control, "must-revalidate");
   527 }
   529 bool
   530 nsHttpResponseHead::IsResumable() const
   531 {
   532     // even though some HTTP/1.0 servers may support byte range requests, we're not
   533     // going to bother with them, since those servers wouldn't understand If-Range.
   534     // Also, while in theory it may be possible to resume when the status code
   535     // is not 200, it is unlikely to be worth the trouble, especially for
   536     // non-2xx responses.
   537     return mStatus == 200 &&
   538            mVersion >= NS_HTTP_VERSION_1_1 &&
   539            PeekHeader(nsHttp::Content_Length) &&
   540           (PeekHeader(nsHttp::ETag) || PeekHeader(nsHttp::Last_Modified)) &&
   541            HasHeaderValue(nsHttp::Accept_Ranges, "bytes");
   542 }
   544 bool
   545 nsHttpResponseHead::ExpiresInPast() const
   546 {
   547     uint32_t maxAgeVal, expiresVal, dateVal;
   549     // Bug #203271. Ensure max-age directive takes precedence over Expires
   550     if (NS_SUCCEEDED(GetMaxAgeValue(&maxAgeVal))) {
   551         return false;
   552     }
   554     return NS_SUCCEEDED(GetExpiresValue(&expiresVal)) &&
   555            NS_SUCCEEDED(GetDateValue(&dateVal)) &&
   556            expiresVal < dateVal;
   557 }
   559 nsresult
   560 nsHttpResponseHead::UpdateHeaders(const nsHttpHeaderArray &headers)
   561 {
   562     LOG(("nsHttpResponseHead::UpdateHeaders [this=%p]\n", this));
   564     uint32_t i, count = headers.Count();
   565     for (i=0; i<count; ++i) {
   566         nsHttpAtom header;
   567         const char *val = headers.PeekHeaderAt(i, header);
   569         if (!val) {
   570             continue;
   571         }
   573         // Ignore any hop-by-hop headers...
   574         if (header == nsHttp::Connection          ||
   575             header == nsHttp::Proxy_Connection    ||
   576             header == nsHttp::Keep_Alive          ||
   577             header == nsHttp::Proxy_Authenticate  ||
   578             header == nsHttp::Proxy_Authorization || // not a response header!
   579             header == nsHttp::TE                  ||
   580             header == nsHttp::Trailer             ||
   581             header == nsHttp::Transfer_Encoding   ||
   582             header == nsHttp::Upgrade             ||
   583         // Ignore any non-modifiable headers...
   584             header == nsHttp::Content_Location    ||
   585             header == nsHttp::Content_MD5         ||
   586             header == nsHttp::ETag                ||
   587         // Assume Cache-Control: "no-transform"
   588             header == nsHttp::Content_Encoding    ||
   589             header == nsHttp::Content_Range       ||
   590             header == nsHttp::Content_Type        ||
   591         // Ignore wacky headers too...
   592             // this one is for MS servers that send "Content-Length: 0"
   593             // on 304 responses
   594             header == nsHttp::Content_Length) {
   595             LOG(("ignoring response header [%s: %s]\n", header.get(), val));
   596         }
   597         else {
   598             LOG(("new response header [%s: %s]\n", header.get(), val));
   600             // overwrite the current header value with the new value...
   601             SetHeader(header, nsDependentCString(val));
   602         }
   603     }
   605     return NS_OK;
   606 }
   608 void
   609 nsHttpResponseHead::Reset()
   610 {
   611     LOG(("nsHttpResponseHead::Reset\n"));
   613     ClearHeaders();
   615     mVersion = NS_HTTP_VERSION_1_1;
   616     mStatus = 200;
   617     mContentLength = UINT64_MAX;
   618     mCacheControlNoStore = false;
   619     mCacheControlNoCache = false;
   620     mPragmaNoCache = false;
   621     mStatusText.Truncate();
   622     mContentType.Truncate();
   623     mContentCharset.Truncate();
   624 }
   626 nsresult
   627 nsHttpResponseHead::ParseDateHeader(nsHttpAtom header, uint32_t *result) const
   628 {
   629     const char *val = PeekHeader(header);
   630     if (!val)
   631         return NS_ERROR_NOT_AVAILABLE;
   633     PRTime time;
   634     PRStatus st = PR_ParseTimeString(val, true, &time);
   635     if (st != PR_SUCCESS)
   636         return NS_ERROR_NOT_AVAILABLE;
   638     *result = PRTimeToSeconds(time);
   639     return NS_OK;
   640 }
   642 nsresult
   643 nsHttpResponseHead::GetAgeValue(uint32_t *result) const
   644 {
   645     const char *val = PeekHeader(nsHttp::Age);
   646     if (!val)
   647         return NS_ERROR_NOT_AVAILABLE;
   649     *result = (uint32_t) atoi(val);
   650     return NS_OK;
   651 }
   653 // Return the value of the (HTTP 1.1) max-age directive, which itself is a
   654 // component of the Cache-Control response header
   655 nsresult
   656 nsHttpResponseHead::GetMaxAgeValue(uint32_t *result) const
   657 {
   658     const char *val = PeekHeader(nsHttp::Cache_Control);
   659     if (!val)
   660         return NS_ERROR_NOT_AVAILABLE;
   662     const char *p = nsHttp::FindToken(val, "max-age", HTTP_HEADER_VALUE_SEPS "=");
   663     if (!p)
   664         return NS_ERROR_NOT_AVAILABLE;
   665     p += 7;
   666     while (*p == ' ' || *p == '\t')
   667         ++p;
   668     if (*p != '=')
   669         return NS_ERROR_NOT_AVAILABLE;
   670     ++p;
   671     while (*p == ' ' || *p == '\t')
   672         ++p;
   674     int maxAgeValue = atoi(p);
   675     if (maxAgeValue < 0)
   676         maxAgeValue = 0;
   677     *result = static_cast<uint32_t>(maxAgeValue);
   678     return NS_OK;
   679 }
   681 nsresult
   682 nsHttpResponseHead::GetExpiresValue(uint32_t *result) const
   683 {
   684     const char *val = PeekHeader(nsHttp::Expires);
   685     if (!val)
   686         return NS_ERROR_NOT_AVAILABLE;
   688     PRTime time;
   689     PRStatus st = PR_ParseTimeString(val, true, &time);
   690     if (st != PR_SUCCESS) {
   691         // parsing failed... RFC 2616 section 14.21 says we should treat this
   692         // as an expiration time in the past.
   693         *result = 0;
   694         return NS_OK;
   695     }
   697     if (time < 0)
   698         *result = 0;
   699     else
   700         *result = PRTimeToSeconds(time);
   701     return NS_OK;
   702 }
   704 int64_t
   705 nsHttpResponseHead::TotalEntitySize() const
   706 {
   707     const char* contentRange = PeekHeader(nsHttp::Content_Range);
   708     if (!contentRange)
   709         return ContentLength();
   711     // Total length is after a slash
   712     const char* slash = strrchr(contentRange, '/');
   713     if (!slash)
   714         return -1; // No idea what the length is
   716     slash++;
   717     if (*slash == '*') // Server doesn't know the length
   718         return -1;
   720     int64_t size;
   721     if (!nsHttp::ParseInt64(slash, &size))
   722         size = UINT64_MAX;
   723     return size;
   724 }
   726 //-----------------------------------------------------------------------------
   727 // nsHttpResponseHead <private>
   728 //-----------------------------------------------------------------------------
   730 void
   731 nsHttpResponseHead::ParseVersion(const char *str)
   732 {
   733     // Parse HTTP-Version:: "HTTP" "/" 1*DIGIT "." 1*DIGIT
   735     LOG(("nsHttpResponseHead::ParseVersion [version=%s]\n", str));
   737     // make sure we have HTTP at the beginning
   738     if (PL_strncasecmp(str, "HTTP", 4) != 0) {
   739         if (PL_strncasecmp(str, "ICY ", 4) == 0) {
   740             // ShoutCast ICY is HTTP/1.0-like. Assume it is HTTP/1.0.
   741             LOG(("Treating ICY as HTTP 1.0\n"));
   742             mVersion = NS_HTTP_VERSION_1_0;
   743             return;
   744         }
   745         LOG(("looks like a HTTP/0.9 response\n"));
   746         mVersion = NS_HTTP_VERSION_0_9;
   747         return;
   748     }
   749     str += 4;
   751     if (*str != '/') {
   752         LOG(("server did not send a version number; assuming HTTP/1.0\n"));
   753         // NCSA/1.5.2 has a bug in which it fails to send a version number
   754         // if the request version is HTTP/1.1, so we fall back on HTTP/1.0
   755         mVersion = NS_HTTP_VERSION_1_0;
   756         return;
   757     }
   759     char *p = PL_strchr(str, '.');
   760     if (p == nullptr) {
   761         LOG(("mal-formed server version; assuming HTTP/1.0\n"));
   762         mVersion = NS_HTTP_VERSION_1_0;
   763         return;
   764     }
   766     ++p; // let b point to the minor version
   768     int major = atoi(str + 1);
   769     int minor = atoi(p);
   771     if ((major > 2) || ((major == 2) && (minor >= 0)))
   772         mVersion = NS_HTTP_VERSION_2_0;
   773     else if ((major == 1) && (minor >= 1))
   774         // at least HTTP/1.1
   775         mVersion = NS_HTTP_VERSION_1_1;
   776     else
   777         // treat anything else as version 1.0
   778         mVersion = NS_HTTP_VERSION_1_0;
   779 }
   781 void
   782 nsHttpResponseHead::ParseCacheControl(const char *val)
   783 {
   784     if (!(val && *val)) {
   785         // clear flags
   786         mCacheControlNoCache = false;
   787         mCacheControlNoStore = false;
   788         return;
   789     }
   791     // search header value for occurrence(s) of "no-cache" but ignore
   792     // occurrence(s) of "no-cache=blah"
   793     if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS))
   794         mCacheControlNoCache = true;
   796     // search header value for occurrence of "no-store"
   797     if (nsHttp::FindToken(val, "no-store", HTTP_HEADER_VALUE_SEPS))
   798         mCacheControlNoStore = true;
   799 }
   801 void
   802 nsHttpResponseHead::ParsePragma(const char *val)
   803 {
   804     LOG(("nsHttpResponseHead::ParsePragma [val=%s]\n", val));
   806     if (!(val && *val)) {
   807         // clear no-cache flag
   808         mPragmaNoCache = false;
   809         return;
   810     }
   812     // Although 'Pragma: no-cache' is not a standard HTTP response header (it's
   813     // a request header), caching is inhibited when this header is present so
   814     // as to match existing Navigator behavior.
   815     if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS))
   816         mPragmaNoCache = true;
   817 }
   819 } // namespace mozilla::net
   820 } // namespace mozilla

mercurial