2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, 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 COMPUTER, 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.
28 #include "platform/network/ResourceResponse.h"
30 #include "wtf/CurrentTime.h"
31 #include "wtf/StdLibExtras.h"
35 ResourceResponse::ResourceResponse()
36 : m_expectedContentLength(0)
38 , m_lastModifiedDate(0)
41 , m_connectionReused(false)
43 , m_haveParsedAgeHeader(false)
44 , m_haveParsedDateHeader(false)
45 , m_haveParsedExpiresHeader(false)
46 , m_haveParsedLastModifiedHeader(false)
51 , m_securityStyle(SecurityStyleUnknown
)
52 , m_httpVersion(Unknown
)
54 , m_isMultipartPayload(false)
55 , m_wasFetchedViaSPDY(false)
56 , m_wasNpnNegotiated(false)
57 , m_wasAlternateProtocolAvailable(false)
58 , m_wasFetchedViaProxy(false)
59 , m_wasFetchedViaServiceWorker(false)
60 , m_wasFallbackRequiredByServiceWorker(false)
61 , m_serviceWorkerResponseType(WebServiceWorkerResponseTypeDefault
)
67 ResourceResponse::ResourceResponse(const KURL
& url
, const AtomicString
& mimeType
, long long expectedLength
, const AtomicString
& textEncodingName
, const String
& filename
)
69 , m_mimeType(mimeType
)
70 , m_expectedContentLength(expectedLength
)
71 , m_textEncodingName(textEncodingName
)
72 , m_suggestedFilename(filename
)
74 , m_lastModifiedDate(0)
77 , m_connectionReused(false)
79 , m_haveParsedAgeHeader(false)
80 , m_haveParsedDateHeader(false)
81 , m_haveParsedExpiresHeader(false)
82 , m_haveParsedLastModifiedHeader(false)
87 , m_securityStyle(SecurityStyleUnknown
)
88 , m_httpVersion(Unknown
)
90 , m_isMultipartPayload(false)
91 , m_wasFetchedViaSPDY(false)
92 , m_wasNpnNegotiated(false)
93 , m_wasAlternateProtocolAvailable(false)
94 , m_wasFetchedViaProxy(false)
95 , m_wasFetchedViaServiceWorker(false)
96 , m_wasFallbackRequiredByServiceWorker(false)
97 , m_serviceWorkerResponseType(WebServiceWorkerResponseTypeDefault
)
103 PassOwnPtr
<ResourceResponse
> ResourceResponse::adopt(PassOwnPtr
<CrossThreadResourceResponseData
> data
)
105 OwnPtr
<ResourceResponse
> response
= adoptPtr(new ResourceResponse
);
106 response
->setURL(data
->m_url
);
107 response
->setMimeType(AtomicString(data
->m_mimeType
));
108 response
->setExpectedContentLength(data
->m_expectedContentLength
);
109 response
->setTextEncodingName(AtomicString(data
->m_textEncodingName
));
110 response
->setSuggestedFilename(data
->m_suggestedFilename
);
112 response
->setHTTPStatusCode(data
->m_httpStatusCode
);
113 response
->setHTTPStatusText(AtomicString(data
->m_httpStatusText
));
115 response
->m_httpHeaderFields
.adopt(data
->m_httpHeaders
.release());
116 response
->setLastModifiedDate(data
->m_lastModifiedDate
);
117 response
->setResourceLoadTiming(data
->m_resourceLoadTiming
.release());
118 response
->m_securityInfo
= data
->m_securityInfo
;
119 response
->m_securityStyle
= data
->m_securityStyle
;
120 response
->m_securityDetails
.protocol
= data
->m_securityDetails
.protocol
;
121 response
->m_securityDetails
.cipher
= data
->m_securityDetails
.cipher
;
122 response
->m_securityDetails
.keyExchange
= data
->m_securityDetails
.keyExchange
;
123 response
->m_securityDetails
.mac
= data
->m_securityDetails
.mac
;
124 response
->m_securityDetails
.certID
= data
->m_securityDetails
.certID
;
125 response
->m_httpVersion
= data
->m_httpVersion
;
126 response
->m_appCacheID
= data
->m_appCacheID
;
127 response
->m_appCacheManifestURL
= data
->m_appCacheManifestURL
.copy();
128 response
->m_isMultipartPayload
= data
->m_isMultipartPayload
;
129 response
->m_wasFetchedViaSPDY
= data
->m_wasFetchedViaSPDY
;
130 response
->m_wasNpnNegotiated
= data
->m_wasNpnNegotiated
;
131 response
->m_wasAlternateProtocolAvailable
= data
->m_wasAlternateProtocolAvailable
;
132 response
->m_wasFetchedViaProxy
= data
->m_wasFetchedViaProxy
;
133 response
->m_wasFetchedViaServiceWorker
= data
->m_wasFetchedViaServiceWorker
;
134 response
->m_wasFallbackRequiredByServiceWorker
= data
->m_wasFallbackRequiredByServiceWorker
;
135 response
->m_serviceWorkerResponseType
= data
->m_serviceWorkerResponseType
;
136 response
->m_originalURLViaServiceWorker
= data
->m_originalURLViaServiceWorker
;
137 response
->m_responseTime
= data
->m_responseTime
;
138 response
->m_remoteIPAddress
= AtomicString(data
->m_remoteIPAddress
);
139 response
->m_remotePort
= data
->m_remotePort
;
140 response
->m_downloadedFilePath
= data
->m_downloadedFilePath
;
141 response
->m_downloadedFileHandle
= data
->m_downloadedFileHandle
;
143 // Bug https://bugs.webkit.org/show_bug.cgi?id=60397 this doesn't support
144 // whatever values may be present in the opaque m_extraData structure.
146 return response
.release();
149 PassOwnPtr
<CrossThreadResourceResponseData
> ResourceResponse::copyData() const
151 OwnPtr
<CrossThreadResourceResponseData
> data
= adoptPtr(new CrossThreadResourceResponseData
);
152 data
->m_url
= url().copy();
153 data
->m_mimeType
= mimeType().string().isolatedCopy();
154 data
->m_expectedContentLength
= expectedContentLength();
155 data
->m_textEncodingName
= textEncodingName().string().isolatedCopy();
156 data
->m_suggestedFilename
= suggestedFilename().isolatedCopy();
157 data
->m_httpStatusCode
= httpStatusCode();
158 data
->m_httpStatusText
= httpStatusText().string().isolatedCopy();
159 data
->m_httpHeaders
= httpHeaderFields().copyData();
160 data
->m_lastModifiedDate
= lastModifiedDate();
161 if (m_resourceLoadTiming
)
162 data
->m_resourceLoadTiming
= m_resourceLoadTiming
->deepCopy();
163 data
->m_securityInfo
= CString(m_securityInfo
.data(), m_securityInfo
.length());
164 data
->m_securityStyle
= m_securityStyle
;
165 data
->m_securityDetails
.protocol
= m_securityDetails
.protocol
.isolatedCopy();
166 data
->m_securityDetails
.cipher
= m_securityDetails
.cipher
.isolatedCopy();
167 data
->m_securityDetails
.keyExchange
= m_securityDetails
.keyExchange
.isolatedCopy();
168 data
->m_securityDetails
.mac
= m_securityDetails
.mac
.isolatedCopy();
169 data
->m_securityDetails
.certID
= m_securityDetails
.certID
;
170 data
->m_httpVersion
= m_httpVersion
;
171 data
->m_appCacheID
= m_appCacheID
;
172 data
->m_appCacheManifestURL
= m_appCacheManifestURL
.copy();
173 data
->m_isMultipartPayload
= m_isMultipartPayload
;
174 data
->m_wasFetchedViaSPDY
= m_wasFetchedViaSPDY
;
175 data
->m_wasNpnNegotiated
= m_wasNpnNegotiated
;
176 data
->m_wasAlternateProtocolAvailable
= m_wasAlternateProtocolAvailable
;
177 data
->m_wasFetchedViaProxy
= m_wasFetchedViaProxy
;
178 data
->m_wasFetchedViaServiceWorker
= m_wasFetchedViaServiceWorker
;
179 data
->m_wasFallbackRequiredByServiceWorker
= m_wasFallbackRequiredByServiceWorker
;
180 data
->m_serviceWorkerResponseType
= m_serviceWorkerResponseType
;
181 data
->m_originalURLViaServiceWorker
= m_originalURLViaServiceWorker
.copy();
182 data
->m_responseTime
= m_responseTime
;
183 data
->m_remoteIPAddress
= m_remoteIPAddress
.string().isolatedCopy();
184 data
->m_remotePort
= m_remotePort
;
185 data
->m_downloadedFilePath
= m_downloadedFilePath
.isolatedCopy();
186 data
->m_downloadedFileHandle
= m_downloadedFileHandle
;
188 // Bug https://bugs.webkit.org/show_bug.cgi?id=60397 this doesn't support
189 // whatever values may be present in the opaque m_extraData structure.
191 return data
.release();
194 bool ResourceResponse::isHTTP() const
196 return m_url
.protocolIsInHTTPFamily();
199 const KURL
& ResourceResponse::url() const
204 void ResourceResponse::setURL(const KURL
& url
)
211 const AtomicString
& ResourceResponse::mimeType() const
216 void ResourceResponse::setMimeType(const AtomicString
& mimeType
)
220 // FIXME: MIME type is determined by HTTP Content-Type header. We should update the header, so that it doesn't disagree with m_mimeType.
221 m_mimeType
= mimeType
;
224 long long ResourceResponse::expectedContentLength() const
226 return m_expectedContentLength
;
229 void ResourceResponse::setExpectedContentLength(long long expectedContentLength
)
233 // FIXME: Content length is determined by HTTP Content-Length header. We should update the header, so that it doesn't disagree with m_expectedContentLength.
234 m_expectedContentLength
= expectedContentLength
;
237 const AtomicString
& ResourceResponse::textEncodingName() const
239 return m_textEncodingName
;
242 void ResourceResponse::setTextEncodingName(const AtomicString
& encodingName
)
246 // FIXME: Text encoding is determined by HTTP Content-Type header. We should update the header, so that it doesn't disagree with m_textEncodingName.
247 m_textEncodingName
= encodingName
;
250 // FIXME should compute this on the fly
251 const String
& ResourceResponse::suggestedFilename() const
253 return m_suggestedFilename
;
256 void ResourceResponse::setSuggestedFilename(const String
& suggestedName
)
260 // FIXME: Suggested file name is calculated based on other headers. There should not be a setter for it.
261 m_suggestedFilename
= suggestedName
;
264 int ResourceResponse::httpStatusCode() const
266 return m_httpStatusCode
;
269 void ResourceResponse::setHTTPStatusCode(int statusCode
)
271 m_httpStatusCode
= statusCode
;
274 const AtomicString
& ResourceResponse::httpStatusText() const
276 return m_httpStatusText
;
279 void ResourceResponse::setHTTPStatusText(const AtomicString
& statusText
)
281 m_httpStatusText
= statusText
;
284 const AtomicString
& ResourceResponse::httpHeaderField(const AtomicString
& name
) const
286 return m_httpHeaderFields
.get(name
);
289 const AtomicString
& ResourceResponse::httpHeaderField(const char* name
) const
291 return m_httpHeaderFields
.get(name
);
294 static const AtomicString
& cacheControlHeaderString()
296 DEFINE_STATIC_LOCAL(const AtomicString
, cacheControlHeader
, ("cache-control", AtomicString::ConstructFromLiteral
));
297 return cacheControlHeader
;
300 static const AtomicString
& pragmaHeaderString()
302 DEFINE_STATIC_LOCAL(const AtomicString
, pragmaHeader
, ("pragma", AtomicString::ConstructFromLiteral
));
306 void ResourceResponse::updateHeaderParsedState(const AtomicString
& name
)
308 DEFINE_STATIC_LOCAL(const AtomicString
, ageHeader
, ("age", AtomicString::ConstructFromLiteral
));
309 DEFINE_STATIC_LOCAL(const AtomicString
, dateHeader
, ("date", AtomicString::ConstructFromLiteral
));
310 DEFINE_STATIC_LOCAL(const AtomicString
, expiresHeader
, ("expires", AtomicString::ConstructFromLiteral
));
311 DEFINE_STATIC_LOCAL(const AtomicString
, lastModifiedHeader
, ("last-modified", AtomicString::ConstructFromLiteral
));
313 if (equalIgnoringCase(name
, ageHeader
))
314 m_haveParsedAgeHeader
= false;
315 else if (equalIgnoringCase(name
, cacheControlHeaderString()) || equalIgnoringCase(name
, pragmaHeaderString()))
316 m_cacheControlHeader
= CacheControlHeader();
317 else if (equalIgnoringCase(name
, dateHeader
))
318 m_haveParsedDateHeader
= false;
319 else if (equalIgnoringCase(name
, expiresHeader
))
320 m_haveParsedExpiresHeader
= false;
321 else if (equalIgnoringCase(name
, lastModifiedHeader
))
322 m_haveParsedLastModifiedHeader
= false;
325 void ResourceResponse::setSecurityDetails(const String
& protocol
, const String
& keyExchange
, const String
& cipher
, const String
& mac
, int certId
)
327 m_securityDetails
.protocol
= protocol
;
328 m_securityDetails
.keyExchange
= keyExchange
;
329 m_securityDetails
.cipher
= cipher
;
330 m_securityDetails
.mac
= mac
;
331 m_securityDetails
.certID
= certId
;
334 void ResourceResponse::setHTTPHeaderField(const AtomicString
& name
, const AtomicString
& value
)
336 updateHeaderParsedState(name
);
338 m_httpHeaderFields
.set(name
, value
);
341 void ResourceResponse::addHTTPHeaderField(const AtomicString
& name
, const AtomicString
& value
)
343 updateHeaderParsedState(name
);
345 HTTPHeaderMap::AddResult result
= m_httpHeaderFields
.add(name
, value
);
346 if (!result
.isNewEntry
)
347 result
.storedValue
->value
= result
.storedValue
->value
+ ", " + value
;
350 void ResourceResponse::clearHTTPHeaderField(const AtomicString
& name
)
352 m_httpHeaderFields
.remove(name
);
355 const HTTPHeaderMap
& ResourceResponse::httpHeaderFields() const
357 return m_httpHeaderFields
;
360 bool ResourceResponse::cacheControlContainsNoCache()
362 if (!m_cacheControlHeader
.parsed
)
363 m_cacheControlHeader
= parseCacheControlDirectives(m_httpHeaderFields
.get(cacheControlHeaderString()), m_httpHeaderFields
.get(pragmaHeaderString()));
364 return m_cacheControlHeader
.containsNoCache
;
367 bool ResourceResponse::cacheControlContainsNoStore()
369 if (!m_cacheControlHeader
.parsed
)
370 m_cacheControlHeader
= parseCacheControlDirectives(m_httpHeaderFields
.get(cacheControlHeaderString()), m_httpHeaderFields
.get(pragmaHeaderString()));
371 return m_cacheControlHeader
.containsNoStore
;
374 bool ResourceResponse::cacheControlContainsMustRevalidate()
376 if (!m_cacheControlHeader
.parsed
)
377 m_cacheControlHeader
= parseCacheControlDirectives(m_httpHeaderFields
.get(cacheControlHeaderString()), m_httpHeaderFields
.get(pragmaHeaderString()));
378 return m_cacheControlHeader
.containsMustRevalidate
;
381 bool ResourceResponse::hasCacheValidatorFields() const
383 DEFINE_STATIC_LOCAL(const AtomicString
, lastModifiedHeader
, ("last-modified", AtomicString::ConstructFromLiteral
));
384 DEFINE_STATIC_LOCAL(const AtomicString
, eTagHeader
, ("etag", AtomicString::ConstructFromLiteral
));
385 return !m_httpHeaderFields
.get(lastModifiedHeader
).isEmpty() || !m_httpHeaderFields
.get(eTagHeader
).isEmpty();
388 double ResourceResponse::cacheControlMaxAge()
390 if (!m_cacheControlHeader
.parsed
)
391 m_cacheControlHeader
= parseCacheControlDirectives(m_httpHeaderFields
.get(cacheControlHeaderString()), m_httpHeaderFields
.get(pragmaHeaderString()));
392 return m_cacheControlHeader
.maxAge
;
395 double ResourceResponse::cacheControlStaleWhileRevalidate()
397 if (!m_cacheControlHeader
.parsed
)
398 m_cacheControlHeader
= parseCacheControlDirectives(m_httpHeaderFields
.get(cacheControlHeaderString()), m_httpHeaderFields
.get(pragmaHeaderString()));
399 return m_cacheControlHeader
.staleWhileRevalidate
;
402 static double parseDateValueInHeader(const HTTPHeaderMap
& headers
, const AtomicString
& headerName
)
404 const AtomicString
& headerValue
= headers
.get(headerName
);
405 if (headerValue
.isEmpty())
406 return std::numeric_limits
<double>::quiet_NaN();
407 // This handles all date formats required by RFC2616:
408 // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
409 // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
410 // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
411 double dateInMilliseconds
= parseDate(headerValue
);
412 if (!std::isfinite(dateInMilliseconds
))
413 return std::numeric_limits
<double>::quiet_NaN();
414 return dateInMilliseconds
/ 1000;
417 double ResourceResponse::date() const
419 if (!m_haveParsedDateHeader
) {
420 DEFINE_STATIC_LOCAL(const AtomicString
, headerName
, ("date", AtomicString::ConstructFromLiteral
));
421 m_date
= parseDateValueInHeader(m_httpHeaderFields
, headerName
);
422 m_haveParsedDateHeader
= true;
427 double ResourceResponse::age() const
429 if (!m_haveParsedAgeHeader
) {
430 DEFINE_STATIC_LOCAL(const AtomicString
, headerName
, ("age", AtomicString::ConstructFromLiteral
));
431 const AtomicString
& headerValue
= m_httpHeaderFields
.get(headerName
);
433 m_age
= headerValue
.toDouble(&ok
);
435 m_age
= std::numeric_limits
<double>::quiet_NaN();
436 m_haveParsedAgeHeader
= true;
441 double ResourceResponse::expires() const
443 if (!m_haveParsedExpiresHeader
) {
444 DEFINE_STATIC_LOCAL(const AtomicString
, headerName
, ("expires", AtomicString::ConstructFromLiteral
));
445 m_expires
= parseDateValueInHeader(m_httpHeaderFields
, headerName
);
446 m_haveParsedExpiresHeader
= true;
451 double ResourceResponse::lastModified() const
453 if (!m_haveParsedLastModifiedHeader
) {
454 DEFINE_STATIC_LOCAL(const AtomicString
, headerName
, ("last-modified", AtomicString::ConstructFromLiteral
));
455 m_lastModified
= parseDateValueInHeader(m_httpHeaderFields
, headerName
);
456 m_haveParsedLastModifiedHeader
= true;
458 return m_lastModified
;
461 bool ResourceResponse::isAttachment() const
463 DEFINE_STATIC_LOCAL(const AtomicString
, headerName
, ("content-disposition", AtomicString::ConstructFromLiteral
));
464 String value
= m_httpHeaderFields
.get(headerName
);
465 size_t loc
= value
.find(';');
466 if (loc
!= kNotFound
)
467 value
= value
.left(loc
);
468 value
= value
.stripWhiteSpace();
469 DEFINE_STATIC_LOCAL(const AtomicString
, attachmentString
, ("attachment", AtomicString::ConstructFromLiteral
));
470 return equalIgnoringCase(value
, attachmentString
);
473 void ResourceResponse::setLastModifiedDate(time_t lastModifiedDate
)
475 m_lastModifiedDate
= lastModifiedDate
;
478 time_t ResourceResponse::lastModifiedDate() const
480 return m_lastModifiedDate
;
483 bool ResourceResponse::wasCached() const
488 void ResourceResponse::setWasCached(bool value
)
493 bool ResourceResponse::connectionReused() const
495 return m_connectionReused
;
498 void ResourceResponse::setConnectionReused(bool connectionReused
)
500 m_connectionReused
= connectionReused
;
503 unsigned ResourceResponse::connectionID() const
505 return m_connectionID
;
508 void ResourceResponse::setConnectionID(unsigned connectionID
)
510 m_connectionID
= connectionID
;
513 ResourceLoadTiming
* ResourceResponse::resourceLoadTiming() const
515 return m_resourceLoadTiming
.get();
518 void ResourceResponse::setResourceLoadTiming(PassRefPtr
<ResourceLoadTiming
> resourceLoadTiming
)
520 m_resourceLoadTiming
= resourceLoadTiming
;
523 PassRefPtr
<ResourceLoadInfo
> ResourceResponse::resourceLoadInfo() const
525 return m_resourceLoadInfo
.get();
528 void ResourceResponse::setResourceLoadInfo(PassRefPtr
<ResourceLoadInfo
> loadInfo
)
530 m_resourceLoadInfo
= loadInfo
;
533 void ResourceResponse::setDownloadedFilePath(const String
& downloadedFilePath
)
535 m_downloadedFilePath
= downloadedFilePath
;
536 if (m_downloadedFilePath
.isEmpty()) {
537 m_downloadedFileHandle
.clear();
540 OwnPtr
<BlobData
> blobData
= BlobData::create();
541 blobData
->appendFile(m_downloadedFilePath
);
542 blobData
->detachFromCurrentThread();
543 m_downloadedFileHandle
= BlobDataHandle::create(blobData
.release(), -1);
546 bool ResourceResponse::compare(const ResourceResponse
& a
, const ResourceResponse
& b
)
548 if (a
.isNull() != b
.isNull())
550 if (a
.url() != b
.url())
552 if (a
.mimeType() != b
.mimeType())
554 if (a
.expectedContentLength() != b
.expectedContentLength())
556 if (a
.textEncodingName() != b
.textEncodingName())
558 if (a
.suggestedFilename() != b
.suggestedFilename())
560 if (a
.httpStatusCode() != b
.httpStatusCode())
562 if (a
.httpStatusText() != b
.httpStatusText())
564 if (a
.httpHeaderFields() != b
.httpHeaderFields())
566 if (a
.resourceLoadTiming() && b
.resourceLoadTiming() && *a
.resourceLoadTiming() == *b
.resourceLoadTiming())
568 if (a
.resourceLoadTiming() != b
.resourceLoadTiming())