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 "MIMETypeRegistry.h" |
34 | #include "ParsedContentRange.h" |
35 | #include "ResourceResponse.h" |
36 | #include <wtf/MathExtras.h> |
37 | #include <wtf/StdLibExtras.h> |
38 | #include <wtf/text/StringView.h> |
39 | |
40 | namespace WebCore { |
41 | |
42 | bool isScriptAllowedByNosniff(const ResourceResponse& response) |
43 | { |
44 | if (parseContentTypeOptionsHeader(response.httpHeaderField(HTTPHeaderName::XContentTypeOptions)) != ContentTypeOptionsNosniff) |
45 | return true; |
46 | String mimeType = extractMIMETypeFromMediaType(response.httpHeaderField(HTTPHeaderName::ContentType)); |
47 | return MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType); |
48 | } |
49 | |
50 | ResourceResponseBase::ResourceResponseBase() |
51 | : m_haveParsedCacheControlHeader(false) |
52 | , m_haveParsedAgeHeader(false) |
53 | , m_haveParsedDateHeader(false) |
54 | , m_haveParsedExpiresHeader(false) |
55 | , m_haveParsedLastModifiedHeader(false) |
56 | , m_haveParsedContentRangeHeader(false) |
57 | , m_isRedirected(false) |
58 | , m_isNull(true) |
59 | { |
60 | } |
61 | |
62 | ResourceResponseBase::ResourceResponseBase(const URL& url, const String& mimeType, long long expectedLength, const String& textEncodingName) |
63 | : m_url(url) |
64 | , m_mimeType(mimeType) |
65 | , m_expectedContentLength(expectedLength) |
66 | , m_textEncodingName(textEncodingName) |
67 | , m_certificateInfo(CertificateInfo()) // Empty but valid for synthetic responses. |
68 | , m_haveParsedCacheControlHeader(false) |
69 | , m_haveParsedAgeHeader(false) |
70 | , m_haveParsedDateHeader(false) |
71 | , m_haveParsedExpiresHeader(false) |
72 | , m_haveParsedLastModifiedHeader(false) |
73 | , m_haveParsedContentRangeHeader(false) |
74 | , m_isRedirected(false) |
75 | , m_isNull(false) |
76 | { |
77 | } |
78 | |
79 | ResourceResponseBase::CrossThreadData ResourceResponseBase::crossThreadData() const |
80 | { |
81 | CrossThreadData data; |
82 | |
83 | data.url = url().isolatedCopy(); |
84 | data.mimeType = mimeType().isolatedCopy(); |
85 | data.expectedContentLength = expectedContentLength(); |
86 | data.textEncodingName = textEncodingName().isolatedCopy(); |
87 | |
88 | data.httpStatusCode = httpStatusCode(); |
89 | data.httpStatusText = httpStatusText().isolatedCopy(); |
90 | data.httpVersion = httpVersion().isolatedCopy(); |
91 | |
92 | data.httpHeaderFields = httpHeaderFields().isolatedCopy(); |
93 | data.networkLoadMetrics = m_networkLoadMetrics.isolatedCopy(); |
94 | data.type = m_type; |
95 | data.tainting = m_tainting; |
96 | data.isRedirected = m_isRedirected; |
97 | |
98 | return data; |
99 | } |
100 | |
101 | ResourceResponse ResourceResponseBase::fromCrossThreadData(CrossThreadData&& data) |
102 | { |
103 | ResourceResponse response; |
104 | |
105 | response.setURL(data.url); |
106 | response.setMimeType(data.mimeType); |
107 | response.setExpectedContentLength(data.expectedContentLength); |
108 | response.setTextEncodingName(data.textEncodingName); |
109 | |
110 | response.setHTTPStatusCode(data.httpStatusCode); |
111 | response.setHTTPStatusText(data.httpStatusText); |
112 | response.setHTTPVersion(data.httpVersion); |
113 | |
114 | response.m_httpHeaderFields = WTFMove(data.httpHeaderFields); |
115 | response.m_networkLoadMetrics = data.networkLoadMetrics; |
116 | response.m_type = data.type; |
117 | response.m_tainting = data.tainting; |
118 | response.m_isRedirected = data.isRedirected; |
119 | |
120 | return response; |
121 | } |
122 | |
123 | ResourceResponse ResourceResponseBase::syntheticRedirectResponse(const URL& fromURL, const URL& toURL) |
124 | { |
125 | ResourceResponse redirectResponse; |
126 | redirectResponse.setURL(fromURL); |
127 | redirectResponse.setHTTPStatusCode(302); |
128 | redirectResponse.setHTTPVersion("HTTP/1.1"_s ); |
129 | redirectResponse.setHTTPHeaderField(HTTPHeaderName::Location, toURL.string()); |
130 | redirectResponse.setHTTPHeaderField(HTTPHeaderName::CacheControl, "no-store"_s ); |
131 | |
132 | return redirectResponse; |
133 | } |
134 | |
135 | ResourceResponse ResourceResponseBase::filter(const ResourceResponse& response) |
136 | { |
137 | if (response.tainting() == Tainting::Opaque) { |
138 | ResourceResponse opaqueResponse; |
139 | opaqueResponse.setTainting(Tainting::Opaque); |
140 | opaqueResponse.setType(Type::Opaque); |
141 | return opaqueResponse; |
142 | } |
143 | |
144 | if (response.tainting() == Tainting::Opaqueredirect) { |
145 | ResourceResponse opaqueResponse; |
146 | opaqueResponse.setTainting(Tainting::Opaqueredirect); |
147 | opaqueResponse.setType(Type::Opaqueredirect); |
148 | opaqueResponse.setURL(response.url()); |
149 | return opaqueResponse; |
150 | } |
151 | |
152 | ResourceResponse filteredResponse = response; |
153 | // Let's initialize filteredResponse to remove some header fields. |
154 | filteredResponse.lazyInit(AllFields); |
155 | |
156 | if (response.tainting() == Tainting::Basic) { |
157 | filteredResponse.setType(Type::Basic); |
158 | filteredResponse.m_httpHeaderFields.remove(HTTPHeaderName::SetCookie); |
159 | filteredResponse.m_httpHeaderFields.remove(HTTPHeaderName::SetCookie2); |
160 | return filteredResponse; |
161 | } |
162 | |
163 | ASSERT(response.tainting() == Tainting::Cors); |
164 | filteredResponse.setType(Type::Cors); |
165 | |
166 | auto = parseAccessControlAllowList<ASCIICaseInsensitiveHash>(response.httpHeaderField(HTTPHeaderName::AccessControlExposeHeaders)); |
167 | filteredResponse.m_httpHeaderFields.uncommonHeaders().removeAllMatching([&](auto& entry) { |
168 | return !isCrossOriginSafeHeader(entry.key, accessControlExposeHeaderSet); |
169 | }); |
170 | filteredResponse.m_httpHeaderFields.commonHeaders().removeAllMatching([&](auto& entry) { |
171 | return !isCrossOriginSafeHeader(entry.key, accessControlExposeHeaderSet); |
172 | }); |
173 | |
174 | return filteredResponse; |
175 | } |
176 | |
177 | // FIXME: Name does not make it clear this is true for HTTPS! |
178 | bool ResourceResponseBase::isHTTP() const |
179 | { |
180 | lazyInit(CommonFieldsOnly); |
181 | |
182 | return m_url.protocolIsInHTTPFamily(); |
183 | } |
184 | |
185 | const URL& ResourceResponseBase::url() const |
186 | { |
187 | lazyInit(CommonFieldsOnly); |
188 | |
189 | return m_url; |
190 | } |
191 | |
192 | void ResourceResponseBase::setURL(const URL& url) |
193 | { |
194 | lazyInit(CommonFieldsOnly); |
195 | m_isNull = false; |
196 | |
197 | m_url = url; |
198 | |
199 | // FIXME: Should invalidate or update platform response if present. |
200 | } |
201 | |
202 | const String& ResourceResponseBase::mimeType() const |
203 | { |
204 | lazyInit(CommonFieldsOnly); |
205 | |
206 | return m_mimeType; |
207 | } |
208 | |
209 | void ResourceResponseBase::setMimeType(const String& mimeType) |
210 | { |
211 | lazyInit(CommonFieldsOnly); |
212 | m_isNull = false; |
213 | |
214 | // FIXME: MIME type is determined by HTTP Content-Type header. We should update the header, so that it doesn't disagree with m_mimeType. |
215 | m_mimeType = mimeType; |
216 | |
217 | // FIXME: Should invalidate or update platform response if present. |
218 | } |
219 | |
220 | long long ResourceResponseBase::expectedContentLength() const |
221 | { |
222 | lazyInit(CommonFieldsOnly); |
223 | |
224 | return m_expectedContentLength; |
225 | } |
226 | |
227 | void ResourceResponseBase::setExpectedContentLength(long long expectedContentLength) |
228 | { |
229 | lazyInit(CommonFieldsOnly); |
230 | m_isNull = false; |
231 | |
232 | // FIXME: Content length is determined by HTTP Content-Length header. We should update the header, so that it doesn't disagree with m_expectedContentLength. |
233 | m_expectedContentLength = expectedContentLength; |
234 | |
235 | // FIXME: Should invalidate or update platform response if present. |
236 | } |
237 | |
238 | const String& ResourceResponseBase::textEncodingName() const |
239 | { |
240 | lazyInit(CommonFieldsOnly); |
241 | |
242 | return m_textEncodingName; |
243 | } |
244 | |
245 | void ResourceResponseBase::setTextEncodingName(const String& encodingName) |
246 | { |
247 | lazyInit(CommonFieldsOnly); |
248 | m_isNull = false; |
249 | |
250 | // FIXME: Text encoding is determined by HTTP Content-Type header. We should update the header, so that it doesn't disagree with m_textEncodingName. |
251 | m_textEncodingName = encodingName; |
252 | |
253 | // FIXME: Should invalidate or update platform response if present. |
254 | } |
255 | |
256 | void ResourceResponseBase::setType(Type type) |
257 | { |
258 | m_isNull = false; |
259 | m_type = type; |
260 | } |
261 | |
262 | void ResourceResponseBase::includeCertificateInfo() const |
263 | { |
264 | if (m_certificateInfo) |
265 | return; |
266 | m_certificateInfo = static_cast<const ResourceResponse*>(this)->platformCertificateInfo(); |
267 | } |
268 | |
269 | String ResourceResponseBase::suggestedFilename() const |
270 | { |
271 | return static_cast<const ResourceResponse*>(this)->platformSuggestedFilename(); |
272 | } |
273 | |
274 | String ResourceResponseBase::sanitizeSuggestedFilename(const String& suggestedFilename) |
275 | { |
276 | if (suggestedFilename.isEmpty()) |
277 | return suggestedFilename; |
278 | |
279 | ResourceResponse response(URL({ }, "http://example.com/" ), String(), -1, String()); |
280 | response.setHTTPStatusCode(200); |
281 | String escapedSuggestedFilename = String(suggestedFilename).replace('\\', "\\\\" ).replace('"', "\\\"" ); |
282 | String value = makeString("attachment; filename=\"" , escapedSuggestedFilename, '"'); |
283 | response.setHTTPHeaderField(HTTPHeaderName::ContentDisposition, value); |
284 | return response.suggestedFilename(); |
285 | } |
286 | |
287 | bool ResourceResponseBase::isSuccessful() const |
288 | { |
289 | int code = httpStatusCode(); |
290 | return code >= 200 && code < 300; |
291 | } |
292 | |
293 | int ResourceResponseBase::httpStatusCode() const |
294 | { |
295 | lazyInit(CommonFieldsOnly); |
296 | |
297 | return m_httpStatusCode; |
298 | } |
299 | |
300 | void ResourceResponseBase::setHTTPStatusCode(int statusCode) |
301 | { |
302 | lazyInit(CommonFieldsOnly); |
303 | |
304 | m_httpStatusCode = statusCode; |
305 | m_isNull = false; |
306 | |
307 | // FIXME: Should invalidate or update platform response if present. |
308 | } |
309 | |
310 | bool ResourceResponseBase::isRedirection() const |
311 | { |
312 | return isRedirectionStatusCode(m_httpStatusCode); |
313 | } |
314 | |
315 | const String& ResourceResponseBase::httpStatusText() const |
316 | { |
317 | lazyInit(AllFields); |
318 | |
319 | return m_httpStatusText; |
320 | } |
321 | |
322 | void ResourceResponseBase::setHTTPStatusText(const String& statusText) |
323 | { |
324 | lazyInit(AllFields); |
325 | |
326 | m_httpStatusText = statusText; |
327 | |
328 | // FIXME: Should invalidate or update platform response if present. |
329 | } |
330 | |
331 | const String& ResourceResponseBase::httpVersion() const |
332 | { |
333 | lazyInit(AllFields); |
334 | |
335 | return m_httpVersion; |
336 | } |
337 | |
338 | void ResourceResponseBase::setHTTPVersion(const String& versionText) |
339 | { |
340 | lazyInit(AllFields); |
341 | |
342 | m_httpVersion = versionText; |
343 | |
344 | // FIXME: Should invalidate or update platform response if present. |
345 | } |
346 | |
347 | static bool (HTTPHeaderName name) |
348 | { |
349 | // WebCore needs to keep location and cache related headers as it does caching. |
350 | // We also keep CORS/ReferrerPolicy headers until CORS checks/Referrer computation are done in NetworkProcess. |
351 | return name == HTTPHeaderName::Location |
352 | || name == HTTPHeaderName::ReferrerPolicy |
353 | || name == HTTPHeaderName::CacheControl |
354 | || name == HTTPHeaderName::Date |
355 | || name == HTTPHeaderName::Expires |
356 | || name == HTTPHeaderName::ETag |
357 | || name == HTTPHeaderName::LastModified |
358 | || name == HTTPHeaderName::Age |
359 | || name == HTTPHeaderName::Pragma |
360 | || name == HTTPHeaderName::ReferrerPolicy |
361 | || name == HTTPHeaderName::Refresh |
362 | || name == HTTPHeaderName::Vary |
363 | || name == HTTPHeaderName::AccessControlAllowCredentials |
364 | || name == HTTPHeaderName::AccessControlAllowHeaders |
365 | || name == HTTPHeaderName::AccessControlAllowMethods |
366 | || name == HTTPHeaderName::AccessControlAllowOrigin |
367 | || name == HTTPHeaderName::AccessControlExposeHeaders |
368 | || name == HTTPHeaderName::AccessControlMaxAge |
369 | || name == HTTPHeaderName::CrossOriginResourcePolicy |
370 | || name == HTTPHeaderName::TimingAllowOrigin; |
371 | } |
372 | |
373 | static bool (HTTPHeaderName name) |
374 | { |
375 | // All known response headers used in WebProcesses. |
376 | return name == HTTPHeaderName::AcceptRanges |
377 | || name == HTTPHeaderName::AccessControlAllowCredentials |
378 | || name == HTTPHeaderName::AccessControlAllowHeaders |
379 | || name == HTTPHeaderName::AccessControlAllowMethods |
380 | || name == HTTPHeaderName::AccessControlAllowOrigin |
381 | || name == HTTPHeaderName::AccessControlExposeHeaders |
382 | || name == HTTPHeaderName::AccessControlMaxAge |
383 | || name == HTTPHeaderName::AccessControlRequestHeaders |
384 | || name == HTTPHeaderName::AccessControlRequestMethod |
385 | || name == HTTPHeaderName::Age |
386 | || name == HTTPHeaderName::CacheControl |
387 | || name == HTTPHeaderName::ContentDisposition |
388 | || name == HTTPHeaderName::ContentEncoding |
389 | || name == HTTPHeaderName::ContentLanguage |
390 | || name == HTTPHeaderName::ContentLength |
391 | || name == HTTPHeaderName::ContentRange |
392 | || name == HTTPHeaderName::ContentSecurityPolicy |
393 | || name == HTTPHeaderName::ContentSecurityPolicyReportOnly |
394 | || name == HTTPHeaderName::ContentType |
395 | || name == HTTPHeaderName::CrossOriginResourcePolicy |
396 | || name == HTTPHeaderName::Date |
397 | || name == HTTPHeaderName::ETag |
398 | || name == HTTPHeaderName::Expires |
399 | || name == HTTPHeaderName::IcyMetaInt |
400 | || name == HTTPHeaderName::IcyMetadata |
401 | || name == HTTPHeaderName::LastEventID |
402 | || name == HTTPHeaderName::LastModified |
403 | || name == HTTPHeaderName::Link |
404 | || name == HTTPHeaderName::Location |
405 | || name == HTTPHeaderName::Pragma |
406 | || name == HTTPHeaderName::Range |
407 | || name == HTTPHeaderName::ReferrerPolicy |
408 | || name == HTTPHeaderName::Refresh |
409 | || name == HTTPHeaderName::ServerTiming |
410 | || name == HTTPHeaderName::SourceMap |
411 | || name == HTTPHeaderName::XSourceMap |
412 | || name == HTTPHeaderName::TimingAllowOrigin |
413 | || name == HTTPHeaderName::Trailer |
414 | || name == HTTPHeaderName::Vary |
415 | || name == HTTPHeaderName::XContentTypeOptions |
416 | || name == HTTPHeaderName::XDNSPrefetchControl |
417 | || name == HTTPHeaderName::XFrameOptions |
418 | || name == HTTPHeaderName::XWebKitCSP |
419 | || name == HTTPHeaderName::XWebKitCSPReportOnly |
420 | || name == HTTPHeaderName::XXSSProtection; |
421 | } |
422 | |
423 | void ResourceResponseBase::() |
424 | { |
425 | switch (m_tainting) { |
426 | case ResourceResponse::Tainting::Basic: |
427 | return; |
428 | case ResourceResponse::Tainting::Cors: { |
429 | HTTPHeaderMap ; |
430 | for (auto& : m_httpHeaderFields.commonHeaders()) { |
431 | if (isSafeCrossOriginResponseHeader(header.key)) |
432 | filteredHeaders.add(header.key, WTFMove(header.value)); |
433 | } |
434 | auto = parseAccessControlAllowList<ASCIICaseInsensitiveHash>(httpHeaderField(HTTPHeaderName::AccessControlExposeHeaders)); |
435 | for (auto& : corsSafeHeaderSet) { |
436 | if (!filteredHeaders.contains(headerName)) { |
437 | auto value = m_httpHeaderFields.get(headerName); |
438 | if (!value.isNull()) |
439 | filteredHeaders.add(headerName, value); |
440 | } |
441 | } |
442 | m_httpHeaderFields = WTFMove(filteredHeaders); |
443 | return; |
444 | } |
445 | case ResourceResponse::Tainting::Opaque: |
446 | case ResourceResponse::Tainting::Opaqueredirect: { |
447 | HTTPHeaderMap ; |
448 | for (auto& : m_httpHeaderFields.commonHeaders()) { |
449 | if (isSafeCrossOriginResponseHeader(header.key)) |
450 | filteredHeaders.add(header.key, WTFMove(header.value)); |
451 | } |
452 | m_httpHeaderFields = WTFMove(filteredHeaders); |
453 | return; |
454 | } |
455 | } |
456 | } |
457 | |
458 | void ResourceResponseBase::(SanitizationType type) |
459 | { |
460 | lazyInit(AllFields); |
461 | |
462 | m_httpHeaderFields.remove(HTTPHeaderName::SetCookie); |
463 | m_httpHeaderFields.remove(HTTPHeaderName::SetCookie2); |
464 | |
465 | switch (type) { |
466 | case SanitizationType::RemoveCookies: |
467 | return; |
468 | case SanitizationType::Redirection: { |
469 | auto = WTFMove(m_httpHeaderFields.commonHeaders()); |
470 | for (auto& : commonHeaders) { |
471 | if (isSafeRedirectionResponseHeader(header.key)) |
472 | m_httpHeaderFields.add(header.key, WTFMove(header.value)); |
473 | } |
474 | m_httpHeaderFields.uncommonHeaders().clear(); |
475 | return; |
476 | } |
477 | case SanitizationType::CrossOriginSafe: |
478 | sanitizeHTTPHeaderFieldsAccordingToTainting(); |
479 | } |
480 | } |
481 | |
482 | bool ResourceResponseBase::isHTTP09() const |
483 | { |
484 | lazyInit(AllFields); |
485 | |
486 | return m_httpVersion.startsWith("HTTP/0.9" ); |
487 | } |
488 | |
489 | String ResourceResponseBase::(const String& name) const |
490 | { |
491 | lazyInit(CommonFieldsOnly); |
492 | |
493 | // If we already have the header, just return it instead of consuming memory by grabing all headers. |
494 | String value = m_httpHeaderFields.get(name); |
495 | if (!value.isEmpty()) |
496 | return value; |
497 | |
498 | lazyInit(AllFields); |
499 | |
500 | return m_httpHeaderFields.get(name); |
501 | } |
502 | |
503 | String ResourceResponseBase::(HTTPHeaderName name) const |
504 | { |
505 | lazyInit(CommonFieldsOnly); |
506 | |
507 | // If we already have the header, just return it instead of consuming memory by grabing all headers. |
508 | String value = m_httpHeaderFields.get(name); |
509 | if (!value.isEmpty()) |
510 | return value; |
511 | |
512 | lazyInit(AllFields); |
513 | |
514 | return m_httpHeaderFields.get(name); |
515 | } |
516 | |
517 | void ResourceResponseBase::(HTTPHeaderName name) |
518 | { |
519 | switch (name) { |
520 | case HTTPHeaderName::Age: |
521 | m_haveParsedAgeHeader = false; |
522 | break; |
523 | |
524 | case HTTPHeaderName::CacheControl: |
525 | case HTTPHeaderName::Pragma: |
526 | m_haveParsedCacheControlHeader = false; |
527 | break; |
528 | |
529 | case HTTPHeaderName::Date: |
530 | m_haveParsedDateHeader = false; |
531 | break; |
532 | |
533 | case HTTPHeaderName::Expires: |
534 | m_haveParsedExpiresHeader = false; |
535 | break; |
536 | |
537 | case HTTPHeaderName::LastModified: |
538 | m_haveParsedLastModifiedHeader = false; |
539 | break; |
540 | |
541 | case HTTPHeaderName::ContentRange: |
542 | m_haveParsedContentRangeHeader = false; |
543 | break; |
544 | |
545 | default: |
546 | break; |
547 | } |
548 | } |
549 | |
550 | void ResourceResponseBase::(const String& name, const String& value) |
551 | { |
552 | lazyInit(AllFields); |
553 | |
554 | HTTPHeaderName ; |
555 | if (findHTTPHeaderName(name, headerName)) |
556 | updateHeaderParsedState(headerName); |
557 | |
558 | m_httpHeaderFields.set(name, value); |
559 | |
560 | // FIXME: Should invalidate or update platform response if present. |
561 | } |
562 | |
563 | void ResourceResponseBase::(HTTPHeaderMap&& ) |
564 | { |
565 | lazyInit(AllFields); |
566 | |
567 | m_httpHeaderFields = WTFMove(headerFields); |
568 | } |
569 | |
570 | void ResourceResponseBase::(HTTPHeaderName name, const String& value) |
571 | { |
572 | lazyInit(AllFields); |
573 | |
574 | updateHeaderParsedState(name); |
575 | |
576 | m_httpHeaderFields.set(name, value); |
577 | |
578 | // FIXME: Should invalidate or update platform response if present. |
579 | } |
580 | |
581 | void ResourceResponseBase::(HTTPHeaderName name, const String& value) |
582 | { |
583 | lazyInit(AllFields); |
584 | updateHeaderParsedState(name); |
585 | m_httpHeaderFields.add(name, value); |
586 | } |
587 | |
588 | void ResourceResponseBase::(const String& name, const String& value) |
589 | { |
590 | HTTPHeaderName ; |
591 | if (findHTTPHeaderName(name, headerName)) |
592 | addHTTPHeaderField(headerName, value); |
593 | else { |
594 | lazyInit(AllFields); |
595 | m_httpHeaderFields.add(name, value); |
596 | } |
597 | } |
598 | |
599 | const HTTPHeaderMap& ResourceResponseBase::() const |
600 | { |
601 | lazyInit(AllFields); |
602 | |
603 | return m_httpHeaderFields; |
604 | } |
605 | |
606 | void ResourceResponseBase::parseCacheControlDirectives() const |
607 | { |
608 | ASSERT(!m_haveParsedCacheControlHeader); |
609 | |
610 | lazyInit(CommonFieldsOnly); |
611 | |
612 | m_cacheControlDirectives = WebCore::parseCacheControlDirectives(m_httpHeaderFields); |
613 | m_haveParsedCacheControlHeader = true; |
614 | } |
615 | |
616 | bool ResourceResponseBase::cacheControlContainsNoCache() const |
617 | { |
618 | if (!m_haveParsedCacheControlHeader) |
619 | parseCacheControlDirectives(); |
620 | return m_cacheControlDirectives.noCache; |
621 | } |
622 | |
623 | bool ResourceResponseBase::cacheControlContainsNoStore() const |
624 | { |
625 | if (!m_haveParsedCacheControlHeader) |
626 | parseCacheControlDirectives(); |
627 | return m_cacheControlDirectives.noStore; |
628 | } |
629 | |
630 | bool ResourceResponseBase::cacheControlContainsMustRevalidate() const |
631 | { |
632 | if (!m_haveParsedCacheControlHeader) |
633 | parseCacheControlDirectives(); |
634 | return m_cacheControlDirectives.mustRevalidate; |
635 | } |
636 | |
637 | bool ResourceResponseBase::cacheControlContainsImmutable() const |
638 | { |
639 | if (!m_haveParsedCacheControlHeader) |
640 | parseCacheControlDirectives(); |
641 | return m_cacheControlDirectives.immutable; |
642 | } |
643 | |
644 | bool ResourceResponseBase::hasCacheValidatorFields() const |
645 | { |
646 | lazyInit(CommonFieldsOnly); |
647 | |
648 | return !m_httpHeaderFields.get(HTTPHeaderName::LastModified).isEmpty() || !m_httpHeaderFields.get(HTTPHeaderName::ETag).isEmpty(); |
649 | } |
650 | |
651 | Optional<Seconds> ResourceResponseBase::cacheControlMaxAge() const |
652 | { |
653 | if (!m_haveParsedCacheControlHeader) |
654 | parseCacheControlDirectives(); |
655 | return m_cacheControlDirectives.maxAge; |
656 | } |
657 | |
658 | static Optional<WallTime> (const HTTPHeaderMap& , HTTPHeaderName ) |
659 | { |
660 | String = headers.get(headerName); |
661 | if (headerValue.isEmpty()) |
662 | return WTF::nullopt; |
663 | // This handles all date formats required by RFC2616: |
664 | // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 |
665 | // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 |
666 | // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format |
667 | return parseHTTPDate(headerValue); |
668 | } |
669 | |
670 | Optional<WallTime> ResourceResponseBase::date() const |
671 | { |
672 | lazyInit(CommonFieldsOnly); |
673 | |
674 | if (!m_haveParsedDateHeader) { |
675 | m_date = parseDateValueInHeader(m_httpHeaderFields, HTTPHeaderName::Date); |
676 | m_haveParsedDateHeader = true; |
677 | } |
678 | return m_date; |
679 | } |
680 | |
681 | Optional<Seconds> ResourceResponseBase::age() const |
682 | { |
683 | lazyInit(CommonFieldsOnly); |
684 | |
685 | if (!m_haveParsedAgeHeader) { |
686 | String = m_httpHeaderFields.get(HTTPHeaderName::Age); |
687 | bool ok; |
688 | double ageDouble = headerValue.toDouble(&ok); |
689 | if (ok) |
690 | m_age = Seconds { ageDouble }; |
691 | m_haveParsedAgeHeader = true; |
692 | } |
693 | return m_age; |
694 | } |
695 | |
696 | Optional<WallTime> ResourceResponseBase::expires() const |
697 | { |
698 | lazyInit(CommonFieldsOnly); |
699 | |
700 | if (!m_haveParsedExpiresHeader) { |
701 | m_expires = parseDateValueInHeader(m_httpHeaderFields, HTTPHeaderName::Expires); |
702 | m_haveParsedExpiresHeader = true; |
703 | } |
704 | return m_expires; |
705 | } |
706 | |
707 | Optional<WallTime> ResourceResponseBase::lastModified() const |
708 | { |
709 | lazyInit(CommonFieldsOnly); |
710 | |
711 | if (!m_haveParsedLastModifiedHeader) { |
712 | m_lastModified = parseDateValueInHeader(m_httpHeaderFields, HTTPHeaderName::LastModified); |
713 | #if PLATFORM(COCOA) |
714 | // CFNetwork converts malformed dates into Epoch so we need to treat Epoch as |
715 | // an invalid value (rdar://problem/22352838). |
716 | const WallTime epoch = WallTime::fromRawSeconds(0); |
717 | if (m_lastModified && m_lastModified.value() == epoch) |
718 | m_lastModified = WTF::nullopt; |
719 | #endif |
720 | m_haveParsedLastModifiedHeader = true; |
721 | } |
722 | return m_lastModified; |
723 | } |
724 | |
725 | static ParsedContentRange (const HTTPHeaderMap& ) |
726 | { |
727 | String contentRangeValue = headers.get(HTTPHeaderName::ContentRange); |
728 | if (contentRangeValue.isEmpty()) |
729 | return ParsedContentRange(); |
730 | |
731 | return ParsedContentRange(contentRangeValue); |
732 | } |
733 | |
734 | const ParsedContentRange& ResourceResponseBase::contentRange() const |
735 | { |
736 | lazyInit(CommonFieldsOnly); |
737 | |
738 | if (!m_haveParsedContentRangeHeader) { |
739 | m_contentRange = parseContentRangeInHeader(m_httpHeaderFields); |
740 | m_haveParsedContentRangeHeader = true; |
741 | } |
742 | |
743 | return m_contentRange; |
744 | } |
745 | |
746 | bool ResourceResponseBase::isAttachment() const |
747 | { |
748 | lazyInit(AllFields); |
749 | |
750 | auto value = m_httpHeaderFields.get(HTTPHeaderName::ContentDisposition); |
751 | return equalLettersIgnoringASCIICase(value.left(value.find(';')).stripWhiteSpace(), "attachment" ); |
752 | } |
753 | |
754 | bool ResourceResponseBase::isAttachmentWithFilename() const |
755 | { |
756 | lazyInit(AllFields); |
757 | |
758 | String contentDisposition = m_httpHeaderFields.get(HTTPHeaderName::ContentDisposition); |
759 | if (contentDisposition.isNull()) |
760 | return false; |
761 | |
762 | if (!equalLettersIgnoringASCIICase(contentDisposition.left(contentDisposition.find(';')).stripWhiteSpace(), "attachment" )) |
763 | return false; |
764 | |
765 | String filename = filenameFromHTTPContentDisposition(contentDisposition); |
766 | return !filename.isNull(); |
767 | } |
768 | |
769 | ResourceResponseBase::Source ResourceResponseBase::source() const |
770 | { |
771 | lazyInit(AllFields); |
772 | |
773 | return m_source; |
774 | } |
775 | |
776 | void ResourceResponseBase::lazyInit(InitLevel initLevel) const |
777 | { |
778 | const_cast<ResourceResponse*>(static_cast<const ResourceResponse*>(this))->platformLazyInit(initLevel); |
779 | } |
780 | |
781 | bool ResourceResponseBase::compare(const ResourceResponse& a, const ResourceResponse& b) |
782 | { |
783 | if (a.isNull() != b.isNull()) |
784 | return false; |
785 | if (a.url() != b.url()) |
786 | return false; |
787 | if (a.mimeType() != b.mimeType()) |
788 | return false; |
789 | if (a.expectedContentLength() != b.expectedContentLength()) |
790 | return false; |
791 | if (a.textEncodingName() != b.textEncodingName()) |
792 | return false; |
793 | if (a.suggestedFilename() != b.suggestedFilename()) |
794 | return false; |
795 | if (a.httpStatusCode() != b.httpStatusCode()) |
796 | return false; |
797 | if (a.httpStatusText() != b.httpStatusText()) |
798 | return false; |
799 | if (a.httpHeaderFields() != b.httpHeaderFields()) |
800 | return false; |
801 | if (a.deprecatedNetworkLoadMetrics() != b.deprecatedNetworkLoadMetrics()) |
802 | return false; |
803 | return ResourceResponse::platformCompare(a, b); |
804 | } |
805 | |
806 | } |
807 | |