Thu, 15 Jan 2015 21:03:48 +0100
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