netwerk/protocol/http/nsHttpResponseHead.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial