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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications.
20 : * Portions created by the Initial Developer are Copyright (C) 2001
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Darin Fisher <darin@netscape.com> (original author)
25 : * Andreas M. Schneider <clarence@clarence.de>
26 : * Christian Biesinger <cbiesinger@web.de>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either the GNU General Public License Version 2 or later (the "GPL"), or
30 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include <stdlib.h>
43 : #include "nsHttpResponseHead.h"
44 : #include "nsPrintfCString.h"
45 : #include "prprf.h"
46 : #include "prtime.h"
47 : #include "nsCRT.h"
48 :
49 : //-----------------------------------------------------------------------------
50 : // nsHttpResponseHead <public>
51 : //-----------------------------------------------------------------------------
52 :
53 : nsresult
54 28 : nsHttpResponseHead::SetHeader(nsHttpAtom hdr,
55 : const nsACString &val,
56 : bool merge)
57 : {
58 28 : nsresult rv = mHeaders.SetHeader(hdr, val, merge);
59 28 : if (NS_FAILED(rv)) return rv;
60 :
61 : // respond to changes in these headers. we need to reparse the entire
62 : // header since the change may have merged in additional values.
63 28 : if (hdr == nsHttp::Cache_Control)
64 6 : ParseCacheControl(mHeaders.PeekHeader(hdr));
65 22 : else if (hdr == nsHttp::Pragma)
66 0 : ParsePragma(mHeaders.PeekHeader(hdr));
67 :
68 28 : return NS_OK;
69 : }
70 :
71 : void
72 0 : nsHttpResponseHead::SetContentLength(PRInt64 len)
73 : {
74 0 : mContentLength = len;
75 0 : if (!LL_GE_ZERO(len)) // < 0
76 0 : mHeaders.ClearHeader(nsHttp::Content_Length);
77 : else
78 0 : mHeaders.SetHeader(nsHttp::Content_Length, nsPrintfCString(20, "%lld", len));
79 0 : }
80 :
81 : void
82 1284 : nsHttpResponseHead::Flatten(nsACString &buf, bool pruneTransients)
83 : {
84 1284 : if (mVersion == NS_HTTP_VERSION_0_9)
85 1 : return;
86 :
87 1283 : buf.AppendLiteral("HTTP/");
88 1283 : if (mVersion == NS_HTTP_VERSION_1_1)
89 1252 : buf.AppendLiteral("1.1 ");
90 : else
91 31 : buf.AppendLiteral("1.0 ");
92 :
93 1283 : buf.Append(nsPrintfCString("%u", PRUintn(mStatus)) +
94 2566 : NS_LITERAL_CSTRING(" ") +
95 2566 : mStatusText +
96 3849 : NS_LITERAL_CSTRING("\r\n"));
97 :
98 1283 : if (!pruneTransients) {
99 8 : mHeaders.Flatten(buf, false);
100 8 : return;
101 : }
102 :
103 : // otherwise, we need to iterate over the headers and only flatten
104 : // those that are appropriate.
105 1275 : PRUint32 i, count = mHeaders.Count();
106 9230 : for (i=0; i<count; ++i) {
107 : nsHttpAtom header;
108 7955 : const char *value = mHeaders.PeekHeaderAt(i, header);
109 :
110 61587 : if (!value || header == nsHttp::Connection
111 6718 : || header == nsHttp::Proxy_Connection
112 6718 : || header == nsHttp::Keep_Alive
113 6718 : || header == nsHttp::WWW_Authenticate
114 6702 : || header == nsHttp::Proxy_Authenticate
115 6696 : || header == nsHttp::Trailer
116 6696 : || header == nsHttp::Transfer_Encoding
117 6692 : || header == nsHttp::Upgrade
118 : // XXX this will cause problems when we start honoring
119 : // Cache-Control: no-cache="set-cookie", what to do?
120 6692 : || header == nsHttp::Set_Cookie)
121 1265 : continue;
122 :
123 : // otherwise, write out the "header: value\r\n" line
124 6690 : buf.Append(nsDependentCString(header.get()) +
125 13380 : NS_LITERAL_CSTRING(": ") +
126 20070 : nsDependentCString(value) +
127 20070 : NS_LITERAL_CSTRING("\r\n"));
128 : }
129 : }
130 :
131 : nsresult
132 567 : nsHttpResponseHead::Parse(char *block)
133 : {
134 :
135 567 : LOG(("nsHttpResponseHead::Parse [this=%x]\n", this));
136 :
137 : // this command works on a buffer as prepared by Flatten, as such it is
138 : // not very forgiving ;-)
139 :
140 567 : char *p = PL_strstr(block, "\r\n");
141 567 : if (!p)
142 0 : return NS_ERROR_UNEXPECTED;
143 :
144 567 : *p = 0;
145 567 : ParseStatusLine(block);
146 :
147 2591 : do {
148 3158 : block = p + 2;
149 :
150 3158 : if (*block == 0)
151 : break;
152 :
153 2591 : p = PL_strstr(block, "\r\n");
154 2591 : if (!p)
155 0 : return NS_ERROR_UNEXPECTED;
156 :
157 2591 : *p = 0;
158 2591 : ParseHeaderLine(block);
159 :
160 : } while (1);
161 :
162 567 : return NS_OK;
163 : }
164 :
165 : void
166 3397 : nsHttpResponseHead::ParseStatusLine(const char *line)
167 : {
168 : //
169 : // Parse Status-Line:: HTTP-Version SP Status-Code SP Reason-Phrase CRLF
170 : //
171 :
172 : // HTTP-Version
173 3397 : ParseVersion(line);
174 :
175 3397 : if ((mVersion == NS_HTTP_VERSION_0_9) || !(line = PL_strchr(line, ' '))) {
176 1 : mStatus = 200;
177 1 : mStatusText.AssignLiteral("OK");
178 : }
179 : else {
180 : // Status-Code
181 3396 : mStatus = (PRUint16) atoi(++line);
182 3396 : if (mStatus == 0) {
183 0 : LOG(("mal-formed response status; assuming status = 200\n"));
184 0 : mStatus = 200;
185 : }
186 :
187 : // Reason-Phrase is whatever is remaining of the line
188 3396 : if (!(line = PL_strchr(line, ' '))) {
189 0 : LOG(("mal-formed response status line; assuming statusText = 'OK'\n"));
190 0 : mStatusText.AssignLiteral("OK");
191 : }
192 : else
193 3396 : mStatusText = ++line;
194 : }
195 :
196 3397 : LOG(("Have status line [version=%u status=%u statusText=%s]\n",
197 : PRUintn(mVersion), PRUintn(mStatus), mStatusText.get()));
198 3397 : }
199 :
200 : nsresult
201 18781 : nsHttpResponseHead::ParseHeaderLine(const char *line)
202 : {
203 18781 : nsHttpAtom hdr = {0};
204 : char *val;
205 : nsresult rv;
206 :
207 18781 : rv = mHeaders.ParseHeaderLine(line, &hdr, &val);
208 18781 : if (NS_FAILED(rv))
209 11 : return rv;
210 :
211 : // leading and trailing LWS has been removed from |val|
212 :
213 : // handle some special case headers...
214 18770 : if (hdr == nsHttp::Content_Length) {
215 : PRInt64 len;
216 : const char *ignored;
217 : // permit only a single value here.
218 3388 : if (nsHttp::ParseInt64(val, &ignored, &len)) {
219 3382 : mContentLength = len;
220 : }
221 : else {
222 : // If this is a negative content length then just ignore it
223 6 : LOG(("invalid content-length! %s\n", val));
224 : }
225 : }
226 15382 : else if (hdr == nsHttp::Content_Type) {
227 2251 : LOG(("ParseContentType [type=%s]\n", val));
228 : bool dummy;
229 2251 : net_ParseContentType(nsDependentCString(val),
230 2251 : mContentType, mContentCharset, &dummy);
231 : }
232 13131 : else if (hdr == nsHttp::Cache_Control)
233 146 : ParseCacheControl(val);
234 12985 : else if (hdr == nsHttp::Pragma)
235 0 : ParsePragma(val);
236 18770 : return NS_OK;
237 : }
238 :
239 : // From section 13.2.3 of RFC2616, we compute the current age of a cached
240 : // response as follows:
241 : //
242 : // currentAge = max(max(0, responseTime - dateValue), ageValue)
243 : // + now - requestTime
244 : //
245 : // where responseTime == now
246 : //
247 : // This is typically a very small number.
248 : //
249 : nsresult
250 613 : nsHttpResponseHead::ComputeCurrentAge(PRUint32 now,
251 : PRUint32 requestTime,
252 : PRUint32 *result)
253 : {
254 : PRUint32 dateValue;
255 : PRUint32 ageValue;
256 :
257 613 : *result = 0;
258 :
259 613 : if (NS_FAILED(GetDateValue(&dateValue))) {
260 22 : LOG(("nsHttpResponseHead::ComputeCurrentAge [this=%x] "
261 : "Date response header not set!\n", this));
262 : // Assume we have a fast connection and that our clock
263 : // is in sync with the server.
264 22 : dateValue = now;
265 : }
266 :
267 : // Compute apparent age
268 613 : if (now > dateValue)
269 79 : *result = now - dateValue;
270 :
271 : // Compute corrected received age
272 613 : if (NS_SUCCEEDED(GetAgeValue(&ageValue)))
273 0 : *result = NS_MAX(*result, ageValue);
274 :
275 613 : NS_ASSERTION(now >= requestTime, "bogus request time");
276 :
277 : // Compute current age
278 613 : *result += (now - requestTime);
279 613 : return NS_OK;
280 : }
281 :
282 : // From section 13.2.4 of RFC2616, we compute the freshness lifetime of a cached
283 : // response as follows:
284 : //
285 : // freshnessLifetime = max_age_value
286 : // <or>
287 : // freshnessLifetime = expires_value - date_value
288 : // <or>
289 : // freshnessLifetime = (date_value - last_modified_value) * 0.10
290 : // <or>
291 : // freshnessLifetime = 0
292 : //
293 : nsresult
294 851 : nsHttpResponseHead::ComputeFreshnessLifetime(PRUint32 *result)
295 : {
296 851 : *result = 0;
297 :
298 : // Try HTTP/1.1 style max-age directive...
299 851 : if (NS_SUCCEEDED(GetMaxAgeValue(result)))
300 47 : return NS_OK;
301 :
302 804 : *result = 0;
303 :
304 804 : PRUint32 date = 0, date2 = 0;
305 804 : if (NS_FAILED(GetDateValue(&date)))
306 29 : date = NowInSeconds(); // synthesize a date header if none exists
307 :
308 : // Try HTTP/1.0 style expires header...
309 804 : if (NS_SUCCEEDED(GetExpiresValue(&date2))) {
310 5 : if (date2 > date)
311 4 : *result = date2 - date;
312 : // the Expires header can specify a date in the past.
313 5 : return NS_OK;
314 : }
315 :
316 : // Fallback on heuristic using last modified header...
317 799 : if (NS_SUCCEEDED(GetLastModifiedValue(&date2))) {
318 490 : LOG(("using last-modified to determine freshness-lifetime\n"));
319 490 : LOG(("last-modified = %u, date = %u\n", date2, date));
320 490 : if (date2 <= date) {
321 : // this only makes sense if last-modified is actually in the past
322 490 : *result = (date - date2) / 10;
323 490 : return NS_OK;
324 : }
325 : }
326 :
327 : // These responses can be cached indefinitely.
328 309 : if ((mStatus == 300) || (mStatus == 301)) {
329 76 : *result = PRUint32(-1);
330 76 : return NS_OK;
331 : }
332 :
333 233 : LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %x] "
334 : "Insufficient information to compute a non-zero freshness "
335 : "lifetime!\n", this));
336 :
337 233 : return NS_OK;
338 : }
339 :
340 : bool
341 1761 : nsHttpResponseHead::MustValidate()
342 : {
343 1761 : LOG(("nsHttpResponseHead::MustValidate ??\n"));
344 :
345 : // Some response codes are cacheable, but the rest are not. This switch
346 : // should stay in sync with the list in nsHttpChannel::ProcessResponse
347 1761 : switch (mStatus) {
348 : // Success codes
349 : case 200:
350 : case 203:
351 : case 206:
352 : // Cacheable redirects
353 : case 300:
354 : case 301:
355 : case 302:
356 : case 304:
357 : case 307:
358 : break;
359 : // Uncacheable redirects
360 : case 303:
361 : case 305:
362 : // Other known errors
363 : case 401:
364 : case 407:
365 : case 412:
366 : case 416:
367 : default: // revalidate unknown error pages
368 630 : LOG(("Must validate since response is an uncacheable error page\n"));
369 630 : return true;
370 : }
371 :
372 : // The no-cache response header indicates that we must validate this
373 : // cached response before reusing.
374 1131 : if (NoCache()) {
375 21 : LOG(("Must validate since response contains 'no-cache' header\n"));
376 21 : return true;
377 : }
378 :
379 : // Likewise, if the response is no-store, then we must validate this
380 : // cached response before reusing. NOTE: it may seem odd that a no-store
381 : // response may be cached, but indeed all responses are cached in order
382 : // to support File->SaveAs, View->PageSource, and other browser features.
383 1110 : if (NoStore()) {
384 7 : LOG(("Must validate since response contains 'no-store' header\n"));
385 7 : return true;
386 : }
387 :
388 : // Compare the Expires header to the Date header. If the server sent an
389 : // Expires header with a timestamp in the past, then we must validate this
390 : // cached response before reusing.
391 1103 : if (ExpiresInPast()) {
392 2 : LOG(("Must validate since Expires < Date\n"));
393 2 : return true;
394 : }
395 :
396 1101 : LOG(("no mandatory validation requirement\n"));
397 1101 : return false;
398 : }
399 :
400 : bool
401 50 : nsHttpResponseHead::MustValidateIfExpired()
402 : {
403 : // according to RFC2616, section 14.9.4:
404 : //
405 : // When the must-revalidate directive is present in a response received by a
406 : // cache, that cache MUST NOT use the entry after it becomes stale to respond to
407 : // a subsequent request without first revalidating it with the origin server.
408 : //
409 50 : return HasHeaderValue(nsHttp::Cache_Control, "must-revalidate");
410 : }
411 :
412 : bool
413 32 : nsHttpResponseHead::IsResumable()
414 : {
415 : // even though some HTTP/1.0 servers may support byte range requests, we're not
416 : // going to bother with them, since those servers wouldn't understand If-Range.
417 : return mVersion >= NS_HTTP_VERSION_1_1 &&
418 30 : PeekHeader(nsHttp::Content_Length) &&
419 54 : (PeekHeader(nsHttp::ETag) || PeekHeader(nsHttp::Last_Modified)) &&
420 116 : HasHeaderValue(nsHttp::Accept_Ranges, "bytes");
421 : }
422 :
423 : bool
424 1107 : nsHttpResponseHead::ExpiresInPast()
425 : {
426 : PRUint32 maxAgeVal, expiresVal, dateVal;
427 :
428 : // Bug #203271. Ensure max-age directive takes precedence over Expires
429 1107 : if (NS_SUCCEEDED(GetMaxAgeValue(&maxAgeVal))) {
430 77 : return false;
431 : }
432 :
433 1030 : return NS_SUCCEEDED(GetExpiresValue(&expiresVal)) &&
434 12 : NS_SUCCEEDED(GetDateValue(&dateVal)) &&
435 1042 : expiresVal < dateVal;
436 : }
437 :
438 : nsresult
439 8 : nsHttpResponseHead::UpdateHeaders(nsHttpHeaderArray &headers)
440 : {
441 8 : LOG(("nsHttpResponseHead::UpdateHeaders [this=%x]\n", this));
442 :
443 8 : PRUint32 i, count = headers.Count();
444 58 : for (i=0; i<count; ++i) {
445 : nsHttpAtom header;
446 50 : const char *val = headers.PeekHeaderAt(i, header);
447 :
448 50 : if (!val) {
449 0 : continue;
450 : }
451 :
452 : // Ignore any hop-by-hop headers...
453 662 : if (header == nsHttp::Connection ||
454 42 : header == nsHttp::Proxy_Connection ||
455 42 : header == nsHttp::Keep_Alive ||
456 42 : header == nsHttp::Proxy_Authenticate ||
457 42 : header == nsHttp::Proxy_Authorization || // not a response header!
458 42 : header == nsHttp::TE ||
459 42 : header == nsHttp::Trailer ||
460 42 : header == nsHttp::Transfer_Encoding ||
461 42 : header == nsHttp::Upgrade ||
462 : // Ignore any non-modifiable headers...
463 42 : header == nsHttp::Content_Location ||
464 42 : header == nsHttp::Content_MD5 ||
465 42 : header == nsHttp::ETag ||
466 : // Assume Cache-Control: "no-transform"
467 39 : header == nsHttp::Content_Encoding ||
468 39 : header == nsHttp::Content_Range ||
469 37 : header == nsHttp::Content_Type ||
470 : // Ignore wacky headers too...
471 : // this one is for MS servers that send "Content-Length: 0"
472 : // on 304 responses
473 35 : header == nsHttp::Content_Length) {
474 23 : LOG(("ignoring response header [%s: %s]\n", header.get(), val));
475 : }
476 : else {
477 27 : LOG(("new response header [%s: %s]\n", header.get(), val));
478 :
479 : // overwrite the current header value with the new value...
480 27 : SetHeader(header, nsDependentCString(val));
481 : }
482 : }
483 :
484 8 : return NS_OK;
485 : }
486 :
487 : void
488 3397 : nsHttpResponseHead::Reset()
489 : {
490 3397 : LOG(("nsHttpResponseHead::Reset\n"));
491 :
492 3397 : ClearHeaders();
493 :
494 3397 : mVersion = NS_HTTP_VERSION_1_1;
495 3397 : mStatus = 200;
496 3397 : mContentLength = LL_MAXUINT;
497 3397 : mCacheControlNoStore = false;
498 3397 : mCacheControlNoCache = false;
499 3397 : mPragmaNoCache = false;
500 3397 : mStatusText.Truncate();
501 3397 : mContentType.Truncate();
502 3397 : mContentCharset.Truncate();
503 3397 : }
504 :
505 : nsresult
506 2228 : nsHttpResponseHead::ParseDateHeader(nsHttpAtom header, PRUint32 *result)
507 : {
508 2228 : const char *val = PeekHeader(header);
509 2228 : if (!val)
510 360 : return NS_ERROR_NOT_AVAILABLE;
511 :
512 : PRTime time;
513 1868 : PRStatus st = PR_ParseTimeString(val, true, &time);
514 1868 : if (st != PR_SUCCESS)
515 0 : return NS_ERROR_NOT_AVAILABLE;
516 :
517 1868 : *result = PRTimeToSeconds(time);
518 1868 : return NS_OK;
519 : }
520 :
521 : nsresult
522 613 : nsHttpResponseHead::GetAgeValue(PRUint32 *result)
523 : {
524 613 : const char *val = PeekHeader(nsHttp::Age);
525 613 : if (!val)
526 613 : return NS_ERROR_NOT_AVAILABLE;
527 :
528 0 : *result = (PRUint32) atoi(val);
529 0 : return NS_OK;
530 : }
531 :
532 : // Return the value of the (HTTP 1.1) max-age directive, which itself is a
533 : // component of the Cache-Control response header
534 : nsresult
535 1969 : nsHttpResponseHead::GetMaxAgeValue(PRUint32 *result)
536 : {
537 1969 : const char *val = PeekHeader(nsHttp::Cache_Control);
538 1969 : if (!val)
539 1815 : return NS_ERROR_NOT_AVAILABLE;
540 :
541 154 : const char *p = PL_strcasestr(val, "max-age=");
542 154 : if (!p)
543 25 : return NS_ERROR_NOT_AVAILABLE;
544 :
545 129 : int maxAgeValue = atoi(p + 8);
546 129 : if (maxAgeValue < 0)
547 0 : maxAgeValue = 0;
548 129 : *result = PRUint32(maxAgeValue);
549 129 : return NS_OK;
550 : }
551 :
552 : nsresult
553 1850 : nsHttpResponseHead::GetExpiresValue(PRUint32 *result)
554 : {
555 1850 : const char *val = PeekHeader(nsHttp::Expires);
556 1850 : if (!val)
557 1828 : return NS_ERROR_NOT_AVAILABLE;
558 :
559 : PRTime time;
560 22 : PRStatus st = PR_ParseTimeString(val, true, &time);
561 22 : if (st != PR_SUCCESS) {
562 : // parsing failed... RFC 2616 section 14.21 says we should treat this
563 : // as an expiration time in the past.
564 0 : *result = 0;
565 0 : return NS_OK;
566 : }
567 :
568 22 : if (LL_CMP(time, <, LL_Zero()))
569 0 : *result = 0;
570 : else
571 22 : *result = PRTimeToSeconds(time);
572 22 : return NS_OK;
573 : }
574 :
575 : PRInt64
576 33 : nsHttpResponseHead::TotalEntitySize()
577 : {
578 33 : const char* contentRange = PeekHeader(nsHttp::Content_Range);
579 33 : if (!contentRange)
580 23 : return ContentLength();
581 :
582 : // Total length is after a slash
583 10 : const char* slash = strrchr(contentRange, '/');
584 10 : if (!slash)
585 0 : return -1; // No idea what the length is
586 :
587 10 : slash++;
588 10 : if (*slash == '*') // Server doesn't know the length
589 0 : return -1;
590 :
591 : PRInt64 size;
592 10 : if (!nsHttp::ParseInt64(slash, &size))
593 0 : size = LL_MAXUINT;
594 10 : return size;
595 : }
596 :
597 : //-----------------------------------------------------------------------------
598 : // nsHttpResponseHead <private>
599 : //-----------------------------------------------------------------------------
600 :
601 : void
602 3397 : nsHttpResponseHead::ParseVersion(const char *str)
603 : {
604 : // Parse HTTP-Version:: "HTTP" "/" 1*DIGIT "." 1*DIGIT
605 :
606 3397 : LOG(("nsHttpResponseHead::ParseVersion [version=%s]\n", str));
607 :
608 : // make sure we have HTTP at the beginning
609 3397 : if (PL_strncasecmp(str, "HTTP", 4) != 0) {
610 1 : LOG(("looks like a HTTP/0.9 response\n"));
611 1 : mVersion = NS_HTTP_VERSION_0_9;
612 1 : return;
613 : }
614 3396 : str += 4;
615 :
616 3396 : if (*str != '/') {
617 0 : LOG(("server did not send a version number; assuming HTTP/1.0\n"));
618 : // NCSA/1.5.2 has a bug in which it fails to send a version number
619 : // if the request version is HTTP/1.1, so we fall back on HTTP/1.0
620 0 : mVersion = NS_HTTP_VERSION_1_0;
621 0 : return;
622 : }
623 :
624 3396 : char *p = PL_strchr(str, '.');
625 3396 : if (p == nsnull) {
626 0 : LOG(("mal-formed server version; assuming HTTP/1.0\n"));
627 0 : mVersion = NS_HTTP_VERSION_1_0;
628 0 : return;
629 : }
630 :
631 3396 : ++p; // let b point to the minor version
632 :
633 3396 : int major = atoi(str + 1);
634 3396 : int minor = atoi(p);
635 :
636 3396 : if ((major > 1) || ((major == 1) && (minor >= 1)))
637 : // at least HTTP/1.1
638 3353 : mVersion = NS_HTTP_VERSION_1_1;
639 : else
640 : // treat anything else as version 1.0
641 43 : mVersion = NS_HTTP_VERSION_1_0;
642 : }
643 :
644 : void
645 152 : nsHttpResponseHead::ParseCacheControl(const char *val)
646 : {
647 152 : if (!(val && *val)) {
648 : // clear flags
649 0 : mCacheControlNoCache = false;
650 0 : mCacheControlNoStore = false;
651 0 : return;
652 : }
653 :
654 : // search header value for occurrence(s) of "no-cache" but ignore
655 : // occurrence(s) of "no-cache=blah"
656 152 : if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS))
657 25 : mCacheControlNoCache = true;
658 :
659 : // search header value for occurrence of "no-store"
660 152 : if (nsHttp::FindToken(val, "no-store", HTTP_HEADER_VALUE_SEPS))
661 11 : mCacheControlNoStore = true;
662 : }
663 :
664 : void
665 0 : nsHttpResponseHead::ParsePragma(const char *val)
666 : {
667 0 : LOG(("nsHttpResponseHead::ParsePragma [val=%s]\n", val));
668 :
669 0 : if (!(val && *val)) {
670 : // clear no-cache flag
671 0 : mPragmaNoCache = false;
672 0 : return;
673 : }
674 :
675 : // Although 'Pragma: no-cache' is not a standard HTTP response header (it's
676 : // a request header), caching is inhibited when this header is present so
677 : // as to match existing Navigator behavior.
678 0 : if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS))
679 0 : mPragmaNoCache = true;
680 : }
|