92a014c4b374d8624dccc49f194dbe280191e4d2
[WebKit-https.git] / Source / WebCore / platform / network / ResourceResponseBase.cpp
1 /*
2  * Copyright (C) 2006, 2008, 2016 Apple Inc. All rights reserved.
3  * Copyright (C) 2009 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "ResourceResponseBase.h"
29
30 #include "CacheValidation.h"
31 #include "HTTPHeaderNames.h"
32 #include "HTTPParsers.h"
33 #include "ParsedContentRange.h"
34 #include "ResourceResponse.h"
35 #include <wtf/CurrentTime.h>
36 #include <wtf/MathExtras.h>
37 #include <wtf/StdLibExtras.h>
38 #include <wtf/text/StringView.h>
39
40 namespace WebCore {
41
42 ResourceResponseBase::ResourceResponseBase()
43     : m_isNull(true)
44     , m_expectedContentLength(0)
45     , m_httpStatusCode(0)
46 {
47 }
48
49 ResourceResponseBase::ResourceResponseBase(const URL& url, const String& mimeType, long long expectedLength, const String& textEncodingName)
50     : m_isNull(false)
51     , m_url(url)
52     , m_mimeType(mimeType)
53     , m_expectedContentLength(expectedLength)
54     , m_textEncodingName(textEncodingName)
55     , m_certificateInfo(CertificateInfo()) // Empty but valid for synthetic responses.
56     , m_httpStatusCode(0)
57 {
58 }
59
60 ResourceResponseBase::CrossThreadData ResourceResponseBase::crossThreadData() const
61 {
62     CrossThreadData data;
63
64     data.url = url().isolatedCopy();
65     data.mimeType = mimeType().isolatedCopy();
66     data.expectedContentLength = expectedContentLength();
67     data.textEncodingName = textEncodingName().isolatedCopy();
68
69     data.httpStatusCode = httpStatusCode();
70     data.httpStatusText = httpStatusText().isolatedCopy();
71     data.httpVersion = httpVersion().isolatedCopy();
72
73     data.httpHeaderFields = httpHeaderFields().isolatedCopy();
74     data.networkLoadMetrics = m_networkLoadMetrics.isolatedCopy();
75     data.type = m_type;
76     data.isRedirected = m_isRedirected;
77
78     return data;
79 }
80
81 ResourceResponse ResourceResponseBase::fromCrossThreadData(CrossThreadData&& data)
82 {
83     ResourceResponse response;
84
85     response.setURL(data.url);
86     response.setMimeType(data.mimeType);
87     response.setExpectedContentLength(data.expectedContentLength);
88     response.setTextEncodingName(data.textEncodingName);
89
90     response.setHTTPStatusCode(data.httpStatusCode);
91     response.setHTTPStatusText(data.httpStatusText);
92     response.setHTTPVersion(data.httpVersion);
93
94     response.m_httpHeaderFields = WTFMove(data.httpHeaderFields);
95     response.m_networkLoadMetrics = data.networkLoadMetrics;
96     response.m_type = data.type;
97     response.m_isRedirected = data.isRedirected;
98
99     return response;
100 }
101
102 ResourceResponse ResourceResponseBase::filterResponse(const ResourceResponse& response, ResourceResponse::Tainting tainting)
103 {
104     if (tainting == ResourceResponse::Tainting::Opaque) {
105         ResourceResponse opaqueResponse;
106         opaqueResponse.setType(ResourceResponse::Type::Opaque);
107         return opaqueResponse;
108     }
109
110     ResourceResponse filteredResponse = response;
111     // Let's initialize filteredResponse to remove some header fields.
112     filteredResponse.lazyInit(AllFields);
113
114     if (tainting == ResourceResponse::Tainting::Basic) {
115         filteredResponse.setType(ResourceResponse::Type::Basic);
116         filteredResponse.m_httpHeaderFields.remove(HTTPHeaderName::SetCookie);
117         filteredResponse.m_httpHeaderFields.remove(HTTPHeaderName::SetCookie2);
118         return filteredResponse;
119     }
120
121     ASSERT(tainting == ResourceResponse::Tainting::Cors);
122     filteredResponse.setType(ResourceResponse::Type::Cors);
123
124     HTTPHeaderSet accessControlExposeHeaderSet;
125     parseAccessControlExposeHeadersAllowList(response.httpHeaderField(HTTPHeaderName::AccessControlExposeHeaders), accessControlExposeHeaderSet);
126     filteredResponse.m_httpHeaderFields.uncommonHeaders().removeIf([&](auto& entry) {
127         return !isCrossOriginSafeHeader(entry.key, accessControlExposeHeaderSet);
128     });
129     filteredResponse.m_httpHeaderFields.commonHeaders().removeIf([&](auto& entry) {
130         return !isCrossOriginSafeHeader(entry.key, accessControlExposeHeaderSet);
131     });
132
133     return filteredResponse;
134 }
135
136 // FIXME: Name does not make it clear this is true for HTTPS!
137 bool ResourceResponseBase::isHTTP() const
138 {
139     lazyInit(CommonFieldsOnly);
140
141     return m_url.protocolIsInHTTPFamily();
142 }
143
144 const URL& ResourceResponseBase::url() const
145 {
146     lazyInit(CommonFieldsOnly);
147
148     return m_url;
149 }
150
151 void ResourceResponseBase::setURL(const URL& url)
152 {
153     lazyInit(CommonFieldsOnly);
154     m_isNull = false;
155
156     m_url = url;
157
158     // FIXME: Should invalidate or update platform response if present.
159 }
160
161 const String& ResourceResponseBase::mimeType() const
162 {
163     lazyInit(CommonFieldsOnly);
164
165     return m_mimeType; 
166 }
167
168 void ResourceResponseBase::setMimeType(const String& mimeType)
169 {
170     lazyInit(CommonFieldsOnly);
171     m_isNull = false;
172
173     // FIXME: MIME type is determined by HTTP Content-Type header. We should update the header, so that it doesn't disagree with m_mimeType.
174     m_mimeType = mimeType;
175
176     // FIXME: Should invalidate or update platform response if present.
177 }
178
179 long long ResourceResponseBase::expectedContentLength() const 
180 {
181     lazyInit(CommonFieldsOnly);
182
183     return m_expectedContentLength;
184 }
185
186 void ResourceResponseBase::setExpectedContentLength(long long expectedContentLength)
187 {
188     lazyInit(CommonFieldsOnly);
189     m_isNull = false;
190
191     // FIXME: Content length is determined by HTTP Content-Length header. We should update the header, so that it doesn't disagree with m_expectedContentLength.
192     m_expectedContentLength = expectedContentLength; 
193
194     // FIXME: Should invalidate or update platform response if present.
195 }
196
197 const String& ResourceResponseBase::textEncodingName() const
198 {
199     lazyInit(CommonFieldsOnly);
200
201     return m_textEncodingName;
202 }
203
204 void ResourceResponseBase::setTextEncodingName(const String& encodingName)
205 {
206     lazyInit(CommonFieldsOnly);
207     m_isNull = false;
208
209     // FIXME: Text encoding is determined by HTTP Content-Type header. We should update the header, so that it doesn't disagree with m_textEncodingName.
210     m_textEncodingName = encodingName;
211
212     // FIXME: Should invalidate or update platform response if present.
213 }
214
215 void ResourceResponseBase::includeCertificateInfo() const
216 {
217     if (m_certificateInfo)
218         return;
219     m_certificateInfo = static_cast<const ResourceResponse*>(this)->platformCertificateInfo();
220 }
221
222 String ResourceResponseBase::suggestedFilename() const
223 {
224     return static_cast<const ResourceResponse*>(this)->platformSuggestedFilename();
225 }
226
227 String ResourceResponseBase::sanitizeSuggestedFilename(const String& suggestedFilename)
228 {
229     if (suggestedFilename.isEmpty())
230         return suggestedFilename;
231
232     ResourceResponse response(URL(ParsedURLString, "http://example.com/"), String(), -1, String());
233     response.setHTTPStatusCode(200);
234     String escapedSuggestedFilename = String(suggestedFilename).replace('\"', "\\\"");
235     String value = makeString("attachment; filename=\"", escapedSuggestedFilename, '"');
236     response.setHTTPHeaderField(HTTPHeaderName::ContentDisposition, value);
237     return response.suggestedFilename();
238 }
239
240 bool ResourceResponseBase::isSuccessful() const
241 {
242     int code = httpStatusCode();
243     return code >= 200 && code < 300;
244 }
245
246 int ResourceResponseBase::httpStatusCode() const
247 {
248     lazyInit(CommonFieldsOnly);
249
250     return m_httpStatusCode;
251 }
252
253 void ResourceResponseBase::setHTTPStatusCode(int statusCode)
254 {
255     lazyInit(CommonFieldsOnly);
256
257     m_httpStatusCode = statusCode;
258
259     // FIXME: Should invalidate or update platform response if present.
260 }
261
262 const String& ResourceResponseBase::httpStatusText() const 
263 {
264     lazyInit(AllFields);
265
266     return m_httpStatusText; 
267 }
268
269 void ResourceResponseBase::setHTTPStatusText(const String& statusText) 
270 {
271     lazyInit(AllFields);
272
273     m_httpStatusText = statusText; 
274
275     // FIXME: Should invalidate or update platform response if present.
276 }
277
278 const String& ResourceResponseBase::httpVersion() const
279 {
280     lazyInit(AllFields);
281     
282     return m_httpVersion;
283 }
284
285 void ResourceResponseBase::setHTTPVersion(const String& versionText)
286 {
287     lazyInit(AllFields);
288     
289     m_httpVersion = versionText;
290     
291     // FIXME: Should invalidate or update platform response if present.
292 }
293
294 bool ResourceResponseBase::isHTTP09() const
295 {
296     lazyInit(AllFields);
297
298     return m_httpVersion.startsWith("HTTP/0.9");
299 }
300
301 String ResourceResponseBase::httpHeaderField(const String& name) const
302 {
303     lazyInit(CommonFieldsOnly);
304
305     // If we already have the header, just return it instead of consuming memory by grabing all headers.
306     String value = m_httpHeaderFields.get(name);
307     if (!value.isEmpty())        
308         return value;
309
310     lazyInit(AllFields);
311
312     return m_httpHeaderFields.get(name); 
313 }
314
315 String ResourceResponseBase::httpHeaderField(HTTPHeaderName name) const
316 {
317     lazyInit(CommonFieldsOnly);
318
319     // If we already have the header, just return it instead of consuming memory by grabing all headers.
320     String value = m_httpHeaderFields.get(name);
321     if (!value.isEmpty())
322         return value;
323
324     lazyInit(AllFields);
325
326     return m_httpHeaderFields.get(name); 
327 }
328
329 void ResourceResponseBase::updateHeaderParsedState(HTTPHeaderName name)
330 {
331     switch (name) {
332     case HTTPHeaderName::Age:
333         m_haveParsedAgeHeader = false;
334         break;
335
336     case HTTPHeaderName::CacheControl:
337     case HTTPHeaderName::Pragma:
338         m_haveParsedCacheControlHeader = false;
339         break;
340
341     case HTTPHeaderName::Date:
342         m_haveParsedDateHeader = false;
343         break;
344
345     case HTTPHeaderName::Expires:
346         m_haveParsedExpiresHeader = false;
347         break;
348
349     case HTTPHeaderName::LastModified:
350         m_haveParsedLastModifiedHeader = false;
351         break;
352
353     case HTTPHeaderName::ContentRange:
354         m_haveParsedContentRangeHeader = false;
355         break;
356
357     default:
358         break;
359     }
360 }
361
362 void ResourceResponseBase::setHTTPHeaderField(const String& name, const String& value)
363 {
364     lazyInit(AllFields);
365
366     HTTPHeaderName headerName;
367     if (findHTTPHeaderName(name, headerName))
368         updateHeaderParsedState(headerName);
369
370     m_httpHeaderFields.set(name, value);
371
372     // FIXME: Should invalidate or update platform response if present.
373 }
374
375 void ResourceResponseBase::setHTTPHeaderField(HTTPHeaderName name, const String& value)
376 {
377     lazyInit(AllFields);
378
379     updateHeaderParsedState(name);
380
381     m_httpHeaderFields.set(name, value);
382
383     // FIXME: Should invalidate or update platform response if present.
384 }
385
386 void ResourceResponseBase::addHTTPHeaderField(HTTPHeaderName name, const String& value)
387 {
388     lazyInit(AllFields);
389     updateHeaderParsedState(name);
390     m_httpHeaderFields.add(name, value);
391 }
392
393 void ResourceResponseBase::addHTTPHeaderField(const String& name, const String& value)
394 {
395     HTTPHeaderName headerName;
396     if (findHTTPHeaderName(name, headerName))
397         addHTTPHeaderField(headerName, value);
398     else {
399         lazyInit(AllFields);
400         m_httpHeaderFields.add(name, value);
401     }
402 }
403
404 const HTTPHeaderMap& ResourceResponseBase::httpHeaderFields() const
405 {
406     lazyInit(AllFields);
407
408     return m_httpHeaderFields;
409 }
410
411 void ResourceResponseBase::parseCacheControlDirectives() const
412 {
413     ASSERT(!m_haveParsedCacheControlHeader);
414
415     lazyInit(CommonFieldsOnly);
416
417     m_cacheControlDirectives = WebCore::parseCacheControlDirectives(m_httpHeaderFields);
418     m_haveParsedCacheControlHeader = true;
419 }
420     
421 bool ResourceResponseBase::cacheControlContainsNoCache() const
422 {
423     if (!m_haveParsedCacheControlHeader)
424         parseCacheControlDirectives();
425     return m_cacheControlDirectives.noCache;
426 }
427
428 bool ResourceResponseBase::cacheControlContainsNoStore() const
429 {
430     if (!m_haveParsedCacheControlHeader)
431         parseCacheControlDirectives();
432     return m_cacheControlDirectives.noStore;
433 }
434
435 bool ResourceResponseBase::cacheControlContainsMustRevalidate() const
436 {
437     if (!m_haveParsedCacheControlHeader)
438         parseCacheControlDirectives();
439     return m_cacheControlDirectives.mustRevalidate;
440 }
441     
442 bool ResourceResponseBase::cacheControlContainsImmutable() const
443 {
444     if (!m_haveParsedCacheControlHeader)
445         parseCacheControlDirectives();
446     return m_cacheControlDirectives.immutable;
447 }
448
449 bool ResourceResponseBase::hasCacheValidatorFields() const
450 {
451     lazyInit(CommonFieldsOnly);
452
453     return !m_httpHeaderFields.get(HTTPHeaderName::LastModified).isEmpty() || !m_httpHeaderFields.get(HTTPHeaderName::ETag).isEmpty();
454 }
455
456 std::optional<std::chrono::microseconds> ResourceResponseBase::cacheControlMaxAge() const
457 {
458     if (!m_haveParsedCacheControlHeader)
459         parseCacheControlDirectives();
460     return m_cacheControlDirectives.maxAge;
461 }
462
463 static std::optional<std::chrono::system_clock::time_point> parseDateValueInHeader(const HTTPHeaderMap& headers, HTTPHeaderName headerName)
464 {
465     String headerValue = headers.get(headerName);
466     if (headerValue.isEmpty())
467         return { };
468     // This handles all date formats required by RFC2616:
469     // Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
470     // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
471     // Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
472     return parseHTTPDate(headerValue);
473 }
474
475 std::optional<std::chrono::system_clock::time_point> ResourceResponseBase::date() const
476 {
477     lazyInit(CommonFieldsOnly);
478
479     if (!m_haveParsedDateHeader) {
480         m_date = parseDateValueInHeader(m_httpHeaderFields, HTTPHeaderName::Date);
481         m_haveParsedDateHeader = true;
482     }
483     return m_date;
484 }
485
486 std::optional<std::chrono::microseconds> ResourceResponseBase::age() const
487 {
488     using namespace std::chrono;
489
490     lazyInit(CommonFieldsOnly);
491
492     if (!m_haveParsedAgeHeader) {
493         String headerValue = m_httpHeaderFields.get(HTTPHeaderName::Age);
494         bool ok;
495         double ageDouble = headerValue.toDouble(&ok);
496         if (ok)
497             m_age = duration_cast<microseconds>(duration<double>(ageDouble));
498         m_haveParsedAgeHeader = true;
499     }
500     return m_age;
501 }
502
503 std::optional<std::chrono::system_clock::time_point> ResourceResponseBase::expires() const
504 {
505     lazyInit(CommonFieldsOnly);
506
507     if (!m_haveParsedExpiresHeader) {
508         m_expires = parseDateValueInHeader(m_httpHeaderFields, HTTPHeaderName::Expires);
509         m_haveParsedExpiresHeader = true;
510     }
511     return m_expires;
512 }
513
514 std::optional<std::chrono::system_clock::time_point> ResourceResponseBase::lastModified() const
515 {
516     lazyInit(CommonFieldsOnly);
517
518     if (!m_haveParsedLastModifiedHeader) {
519         m_lastModified = parseDateValueInHeader(m_httpHeaderFields, HTTPHeaderName::LastModified);
520 #if PLATFORM(COCOA)
521         // CFNetwork converts malformed dates into Epoch so we need to treat Epoch as
522         // an invalid value (rdar://problem/22352838).
523         const std::chrono::system_clock::time_point epoch;
524         if (m_lastModified && m_lastModified.value() == epoch)
525             m_lastModified = std::nullopt;
526 #endif
527         m_haveParsedLastModifiedHeader = true;
528     }
529     return m_lastModified;
530 }
531
532 static ParsedContentRange parseContentRangeInHeader(const HTTPHeaderMap& headers)
533 {
534     String contentRangeValue = headers.get(HTTPHeaderName::ContentRange);
535     if (contentRangeValue.isEmpty())
536         return ParsedContentRange();
537
538     return ParsedContentRange(contentRangeValue);
539 }
540
541 ParsedContentRange& ResourceResponseBase::contentRange() const
542 {
543     lazyInit(CommonFieldsOnly);
544
545     if (!m_haveParsedContentRangeHeader) {
546         m_contentRange = parseContentRangeInHeader(m_httpHeaderFields);
547         m_haveParsedContentRangeHeader = true;
548     }
549
550     return m_contentRange;
551 }
552
553 bool ResourceResponseBase::isAttachment() const
554 {
555     lazyInit(AllFields);
556
557     auto value = m_httpHeaderFields.get(HTTPHeaderName::ContentDisposition);
558     return equalLettersIgnoringASCIICase(value.left(value.find(';')).stripWhiteSpace(), "attachment");
559 }
560
561 ResourceResponseBase::Source ResourceResponseBase::source() const
562 {
563     lazyInit(AllFields);
564
565     return m_source;
566 }
567
568 void ResourceResponseBase::setSource(Source source)
569 {
570     m_source = source;
571 }
572
573 void ResourceResponseBase::lazyInit(InitLevel initLevel) const
574 {
575     const_cast<ResourceResponse*>(static_cast<const ResourceResponse*>(this))->platformLazyInit(initLevel);
576 }
577
578 bool ResourceResponseBase::compare(const ResourceResponse& a, const ResourceResponse& b)
579 {
580     if (a.isNull() != b.isNull())
581         return false;  
582     if (a.url() != b.url())
583         return false;
584     if (a.mimeType() != b.mimeType())
585         return false;
586     if (a.expectedContentLength() != b.expectedContentLength())
587         return false;
588     if (a.textEncodingName() != b.textEncodingName())
589         return false;
590     if (a.suggestedFilename() != b.suggestedFilename())
591         return false;
592     if (a.httpStatusCode() != b.httpStatusCode())
593         return false;
594     if (a.httpStatusText() != b.httpStatusText())
595         return false;
596     if (a.httpHeaderFields() != b.httpHeaderFields())
597         return false;
598     if (a.deprecatedNetworkLoadMetrics() != b.deprecatedNetworkLoadMetrics())
599         return false;
600     return ResourceResponse::platformCompare(a, b);
601 }
602
603 }