Home | History | Annotate | Download | only in network
      1 /*
      2  * Copyright (C) 2006, 2008 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 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.
     25  */
     26 
     27 #include "config.h"
     28 #include "platform/network/ResourceResponse.h"
     29 
     30 #include "wtf/CurrentTime.h"
     31 #include "wtf/StdLibExtras.h"
     32 
     33 namespace WebCore {
     34 
     35 ResourceResponse::ResourceResponse()
     36     : m_expectedContentLength(0)
     37     , m_httpStatusCode(0)
     38     , m_lastModifiedDate(0)
     39     , m_wasCached(false)
     40     , m_connectionID(0)
     41     , m_connectionReused(false)
     42     , m_isNull(true)
     43     , m_haveParsedAgeHeader(false)
     44     , m_haveParsedDateHeader(false)
     45     , m_haveParsedExpiresHeader(false)
     46     , m_haveParsedLastModifiedHeader(false)
     47     , m_age(0.0)
     48     , m_date(0.0)
     49     , m_expires(0.0)
     50     , m_lastModified(0.0)
     51     , m_httpVersion(Unknown)
     52     , m_appCacheID(0)
     53     , m_isMultipartPayload(false)
     54     , m_wasFetchedViaSPDY(false)
     55     , m_wasNpnNegotiated(false)
     56     , m_wasAlternateProtocolAvailable(false)
     57     , m_wasFetchedViaProxy(false)
     58     , m_responseTime(0)
     59     , m_remotePort(0)
     60 {
     61 }
     62 
     63 ResourceResponse::ResourceResponse(const KURL& url, const AtomicString& mimeType, long long expectedLength, const AtomicString& textEncodingName, const String& filename)
     64     : m_url(url)
     65     , m_mimeType(mimeType)
     66     , m_expectedContentLength(expectedLength)
     67     , m_textEncodingName(textEncodingName)
     68     , m_suggestedFilename(filename)
     69     , m_httpStatusCode(0)
     70     , m_lastModifiedDate(0)
     71     , m_wasCached(false)
     72     , m_connectionID(0)
     73     , m_connectionReused(false)
     74     , m_isNull(false)
     75     , m_haveParsedAgeHeader(false)
     76     , m_haveParsedDateHeader(false)
     77     , m_haveParsedExpiresHeader(false)
     78     , m_haveParsedLastModifiedHeader(false)
     79     , m_age(0.0)
     80     , m_date(0.0)
     81     , m_expires(0.0)
     82     , m_lastModified(0.0)
     83     , m_httpVersion(Unknown)
     84     , m_appCacheID(0)
     85     , m_isMultipartPayload(false)
     86     , m_wasFetchedViaSPDY(false)
     87     , m_wasNpnNegotiated(false)
     88     , m_wasAlternateProtocolAvailable(false)
     89     , m_wasFetchedViaProxy(false)
     90     , m_responseTime(0)
     91     , m_remotePort(0)
     92 {
     93 }
     94 
     95 PassOwnPtr<ResourceResponse> ResourceResponse::adopt(PassOwnPtr<CrossThreadResourceResponseData> data)
     96 {
     97     OwnPtr<ResourceResponse> response = adoptPtr(new ResourceResponse);
     98     response->setURL(data->m_url);
     99     response->setMimeType(AtomicString(data->m_mimeType));
    100     response->setExpectedContentLength(data->m_expectedContentLength);
    101     response->setTextEncodingName(AtomicString(data->m_textEncodingName));
    102     response->setSuggestedFilename(data->m_suggestedFilename);
    103 
    104     response->setHTTPStatusCode(data->m_httpStatusCode);
    105     response->setHTTPStatusText(AtomicString(data->m_httpStatusText));
    106 
    107     response->m_httpHeaderFields.adopt(data->m_httpHeaders.release());
    108     response->setLastModifiedDate(data->m_lastModifiedDate);
    109     response->setResourceLoadTiming(data->m_resourceLoadTiming.release());
    110     response->m_securityInfo = data->m_securityInfo;
    111     response->m_httpVersion = data->m_httpVersion;
    112     response->m_appCacheID = data->m_appCacheID;
    113     response->m_appCacheManifestURL = data->m_appCacheManifestURL.copy();
    114     response->m_isMultipartPayload = data->m_isMultipartPayload;
    115     response->m_wasFetchedViaSPDY = data->m_wasFetchedViaSPDY;
    116     response->m_wasNpnNegotiated = data->m_wasNpnNegotiated;
    117     response->m_wasAlternateProtocolAvailable = data->m_wasAlternateProtocolAvailable;
    118     response->m_wasFetchedViaProxy = data->m_wasFetchedViaProxy;
    119     response->m_responseTime = data->m_responseTime;
    120     response->m_remoteIPAddress = AtomicString(data->m_remoteIPAddress);
    121     response->m_remotePort = data->m_remotePort;
    122     response->m_downloadedFilePath = data->m_downloadedFilePath;
    123     response->m_downloadedFileHandle = data->m_downloadedFileHandle;
    124 
    125     // Bug https://bugs.webkit.org/show_bug.cgi?id=60397 this doesn't support
    126     // whatever values may be present in the opaque m_extraData structure.
    127 
    128     return response.release();
    129 }
    130 
    131 PassOwnPtr<CrossThreadResourceResponseData> ResourceResponse::copyData() const
    132 {
    133     OwnPtr<CrossThreadResourceResponseData> data = adoptPtr(new CrossThreadResourceResponseData);
    134     data->m_url = url().copy();
    135     data->m_mimeType = mimeType().string().isolatedCopy();
    136     data->m_expectedContentLength = expectedContentLength();
    137     data->m_textEncodingName = textEncodingName().string().isolatedCopy();
    138     data->m_suggestedFilename = suggestedFilename().isolatedCopy();
    139     data->m_httpStatusCode = httpStatusCode();
    140     data->m_httpStatusText = httpStatusText().string().isolatedCopy();
    141     data->m_httpHeaders = httpHeaderFields().copyData();
    142     data->m_lastModifiedDate = lastModifiedDate();
    143     if (m_resourceLoadTiming)
    144         data->m_resourceLoadTiming = m_resourceLoadTiming->deepCopy();
    145     data->m_securityInfo = CString(m_securityInfo.data(), m_securityInfo.length());
    146     data->m_httpVersion = m_httpVersion;
    147     data->m_appCacheID = m_appCacheID;
    148     data->m_appCacheManifestURL = m_appCacheManifestURL.copy();
    149     data->m_isMultipartPayload = m_isMultipartPayload;
    150     data->m_wasFetchedViaSPDY = m_wasFetchedViaSPDY;
    151     data->m_wasNpnNegotiated = m_wasNpnNegotiated;
    152     data->m_wasAlternateProtocolAvailable = m_wasAlternateProtocolAvailable;
    153     data->m_wasFetchedViaProxy = m_wasFetchedViaProxy;
    154     data->m_responseTime = m_responseTime;
    155     data->m_remoteIPAddress = m_remoteIPAddress.string().isolatedCopy();
    156     data->m_remotePort = m_remotePort;
    157     data->m_downloadedFilePath = m_downloadedFilePath.isolatedCopy();
    158     data->m_downloadedFileHandle = m_downloadedFileHandle;
    159 
    160     // Bug https://bugs.webkit.org/show_bug.cgi?id=60397 this doesn't support
    161     // whatever values may be present in the opaque m_extraData structure.
    162 
    163     return data.release();
    164 }
    165 
    166 bool ResourceResponse::isHTTP() const
    167 {
    168     return m_url.protocolIsInHTTPFamily();
    169 }
    170 
    171 const KURL& ResourceResponse::url() const
    172 {
    173     return m_url;
    174 }
    175 
    176 void ResourceResponse::setURL(const KURL& url)
    177 {
    178     m_isNull = false;
    179 
    180     m_url = url;
    181 }
    182 
    183 const AtomicString& ResourceResponse::mimeType() const
    184 {
    185     return m_mimeType;
    186 }
    187 
    188 void ResourceResponse::setMimeType(const AtomicString& mimeType)
    189 {
    190     m_isNull = false;
    191 
    192     // FIXME: MIME type is determined by HTTP Content-Type header. We should update the header, so that it doesn't disagree with m_mimeType.
    193     m_mimeType = mimeType;
    194 }
    195 
    196 long long ResourceResponse::expectedContentLength() const
    197 {
    198     return m_expectedContentLength;
    199 }
    200 
    201 void ResourceResponse::setExpectedContentLength(long long expectedContentLength)
    202 {
    203     m_isNull = false;
    204 
    205     // FIXME: Content length is determined by HTTP Content-Length header. We should update the header, so that it doesn't disagree with m_expectedContentLength.
    206     m_expectedContentLength = expectedContentLength;
    207 }
    208 
    209 const AtomicString& ResourceResponse::textEncodingName() const
    210 {
    211     return m_textEncodingName;
    212 }
    213 
    214 void ResourceResponse::setTextEncodingName(const AtomicString& encodingName)
    215 {
    216     m_isNull = false;
    217 
    218     // FIXME: Text encoding is determined by HTTP Content-Type header. We should update the header, so that it doesn't disagree with m_textEncodingName.
    219     m_textEncodingName = encodingName;
    220 }
    221 
    222 // FIXME should compute this on the fly
    223 const String& ResourceResponse::suggestedFilename() const
    224 {
    225     return m_suggestedFilename;
    226 }
    227 
    228 void ResourceResponse::setSuggestedFilename(const String& suggestedName)
    229 {
    230     m_isNull = false;
    231 
    232     // FIXME: Suggested file name is calculated based on other headers. There should not be a setter for it.
    233     m_suggestedFilename = suggestedName;
    234 }
    235 
    236 int ResourceResponse::httpStatusCode() const
    237 {
    238     return m_httpStatusCode;
    239 }
    240 
    241 void ResourceResponse::setHTTPStatusCode(int statusCode)
    242 {
    243     m_httpStatusCode = statusCode;
    244 }
    245 
    246 const AtomicString& ResourceResponse::httpStatusText() const
    247 {
    248     return m_httpStatusText;
    249 }
    250 
    251 void ResourceResponse::setHTTPStatusText(const AtomicString& statusText)
    252 {
    253     m_httpStatusText = statusText;
    254 }
    255 
    256 const AtomicString& ResourceResponse::httpHeaderField(const AtomicString& name) const
    257 {
    258     return m_httpHeaderFields.get(name);
    259 }
    260 
    261 const AtomicString& ResourceResponse::httpHeaderField(const char* name) const
    262 {
    263     return m_httpHeaderFields.get(name);
    264 }
    265 
    266 static const AtomicString& cacheControlHeaderString()
    267 {
    268     DEFINE_STATIC_LOCAL(const AtomicString, cacheControlHeader, ("cache-control", AtomicString::ConstructFromLiteral));
    269     return cacheControlHeader;
    270 }
    271 
    272 static const AtomicString& pragmaHeaderString()
    273 {
    274     DEFINE_STATIC_LOCAL(const AtomicString, pragmaHeader, ("pragma", AtomicString::ConstructFromLiteral));
    275     return pragmaHeader;
    276 }
    277 
    278 void ResourceResponse::updateHeaderParsedState(const AtomicString& name)
    279 {
    280     DEFINE_STATIC_LOCAL(const AtomicString, ageHeader, ("age", AtomicString::ConstructFromLiteral));
    281     DEFINE_STATIC_LOCAL(const AtomicString, dateHeader, ("date", AtomicString::ConstructFromLiteral));
    282     DEFINE_STATIC_LOCAL(const AtomicString, expiresHeader, ("expires", AtomicString::ConstructFromLiteral));
    283     DEFINE_STATIC_LOCAL(const AtomicString, lastModifiedHeader, ("last-modified", AtomicString::ConstructFromLiteral));
    284 
    285     if (equalIgnoringCase(name, ageHeader))
    286         m_haveParsedAgeHeader = false;
    287     else if (equalIgnoringCase(name, cacheControlHeaderString()) || equalIgnoringCase(name, pragmaHeaderString()))
    288         m_cacheControlHeader = CacheControlHeader();
    289     else if (equalIgnoringCase(name, dateHeader))
    290         m_haveParsedDateHeader = false;
    291     else if (equalIgnoringCase(name, expiresHeader))
    292         m_haveParsedExpiresHeader = false;
    293     else if (equalIgnoringCase(name, lastModifiedHeader))
    294         m_haveParsedLastModifiedHeader = false;
    295 }
    296 
    297 void ResourceResponse::setHTTPHeaderField(const AtomicString& name, const AtomicString& value)
    298 {
    299     updateHeaderParsedState(name);
    300 
    301     m_httpHeaderFields.set(name, value);
    302 }
    303 
    304 void ResourceResponse::addHTTPHeaderField(const AtomicString& name, const AtomicString& value)
    305 {
    306     updateHeaderParsedState(name);
    307 
    308     HTTPHeaderMap::AddResult result = m_httpHeaderFields.add(name, value);
    309     if (!result.isNewEntry)
    310         result.storedValue->value = result.storedValue->value + ", " + value;
    311 }
    312 
    313 void ResourceResponse::clearHTTPHeaderField(const AtomicString& name)
    314 {
    315     m_httpHeaderFields.remove(name);
    316 }
    317 
    318 const HTTPHeaderMap& ResourceResponse::httpHeaderFields() const
    319 {
    320     return m_httpHeaderFields;
    321 }
    322 
    323 bool ResourceResponse::cacheControlContainsNoCache()
    324 {
    325     if (!m_cacheControlHeader.parsed)
    326         m_cacheControlHeader = parseCacheControlDirectives(m_httpHeaderFields.get(cacheControlHeaderString()), m_httpHeaderFields.get(pragmaHeaderString()));
    327     return m_cacheControlHeader.containsNoCache;
    328 }
    329 
    330 bool ResourceResponse::cacheControlContainsNoStore()
    331 {
    332     if (!m_cacheControlHeader.parsed)
    333         m_cacheControlHeader = parseCacheControlDirectives(m_httpHeaderFields.get(cacheControlHeaderString()), m_httpHeaderFields.get(pragmaHeaderString()));
    334     return m_cacheControlHeader.containsNoStore;
    335 }
    336 
    337 bool ResourceResponse::cacheControlContainsMustRevalidate()
    338 {
    339     if (!m_cacheControlHeader.parsed)
    340         m_cacheControlHeader = parseCacheControlDirectives(m_httpHeaderFields.get(cacheControlHeaderString()), m_httpHeaderFields.get(pragmaHeaderString()));
    341     return m_cacheControlHeader.containsMustRevalidate;
    342 }
    343 
    344 bool ResourceResponse::hasCacheValidatorFields() const
    345 {
    346     DEFINE_STATIC_LOCAL(const AtomicString, lastModifiedHeader, ("last-modified", AtomicString::ConstructFromLiteral));
    347     DEFINE_STATIC_LOCAL(const AtomicString, eTagHeader, ("etag", AtomicString::ConstructFromLiteral));
    348     return !m_httpHeaderFields.get(lastModifiedHeader).isEmpty() || !m_httpHeaderFields.get(eTagHeader).isEmpty();
    349 }
    350 
    351 double ResourceResponse::cacheControlMaxAge()
    352 {
    353     if (!m_cacheControlHeader.parsed)
    354         m_cacheControlHeader = parseCacheControlDirectives(m_httpHeaderFields.get(cacheControlHeaderString()), m_httpHeaderFields.get(pragmaHeaderString()));
    355     return m_cacheControlHeader.maxAge;
    356 }
    357 
    358 static double parseDateValueInHeader(const HTTPHeaderMap& headers, const AtomicString& headerName)
    359 {
    360     const AtomicString& headerValue = headers.get(headerName);
    361     if (headerValue.isEmpty())
    362         return std::numeric_limits<double>::quiet_NaN();
    363     // This handles all date formats required by RFC2616:
    364     // Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
    365     // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
    366     // Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
    367     double dateInMilliseconds = parseDate(headerValue);
    368     if (!std::isfinite(dateInMilliseconds))
    369         return std::numeric_limits<double>::quiet_NaN();
    370     return dateInMilliseconds / 1000;
    371 }
    372 
    373 double ResourceResponse::date() const
    374 {
    375     if (!m_haveParsedDateHeader) {
    376         DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("date", AtomicString::ConstructFromLiteral));
    377         m_date = parseDateValueInHeader(m_httpHeaderFields, headerName);
    378         m_haveParsedDateHeader = true;
    379     }
    380     return m_date;
    381 }
    382 
    383 double ResourceResponse::age() const
    384 {
    385     if (!m_haveParsedAgeHeader) {
    386         DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("age", AtomicString::ConstructFromLiteral));
    387         const AtomicString& headerValue = m_httpHeaderFields.get(headerName);
    388         bool ok;
    389         m_age = headerValue.toDouble(&ok);
    390         if (!ok)
    391             m_age = std::numeric_limits<double>::quiet_NaN();
    392         m_haveParsedAgeHeader = true;
    393     }
    394     return m_age;
    395 }
    396 
    397 double ResourceResponse::expires() const
    398 {
    399     if (!m_haveParsedExpiresHeader) {
    400         DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("expires", AtomicString::ConstructFromLiteral));
    401         m_expires = parseDateValueInHeader(m_httpHeaderFields, headerName);
    402         m_haveParsedExpiresHeader = true;
    403     }
    404     return m_expires;
    405 }
    406 
    407 double ResourceResponse::lastModified() const
    408 {
    409     if (!m_haveParsedLastModifiedHeader) {
    410         DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("last-modified", AtomicString::ConstructFromLiteral));
    411         m_lastModified = parseDateValueInHeader(m_httpHeaderFields, headerName);
    412         m_haveParsedLastModifiedHeader = true;
    413     }
    414     return m_lastModified;
    415 }
    416 
    417 bool ResourceResponse::isAttachment() const
    418 {
    419     DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("content-disposition", AtomicString::ConstructFromLiteral));
    420     String value = m_httpHeaderFields.get(headerName);
    421     size_t loc = value.find(';');
    422     if (loc != kNotFound)
    423         value = value.left(loc);
    424     value = value.stripWhiteSpace();
    425     DEFINE_STATIC_LOCAL(const AtomicString, attachmentString, ("attachment", AtomicString::ConstructFromLiteral));
    426     return equalIgnoringCase(value, attachmentString);
    427 }
    428 
    429 void ResourceResponse::setLastModifiedDate(time_t lastModifiedDate)
    430 {
    431     m_lastModifiedDate = lastModifiedDate;
    432 }
    433 
    434 time_t ResourceResponse::lastModifiedDate() const
    435 {
    436     return m_lastModifiedDate;
    437 }
    438 
    439 bool ResourceResponse::wasCached() const
    440 {
    441     return m_wasCached;
    442 }
    443 
    444 void ResourceResponse::setWasCached(bool value)
    445 {
    446     m_wasCached = value;
    447 }
    448 
    449 bool ResourceResponse::connectionReused() const
    450 {
    451     return m_connectionReused;
    452 }
    453 
    454 void ResourceResponse::setConnectionReused(bool connectionReused)
    455 {
    456     m_connectionReused = connectionReused;
    457 }
    458 
    459 unsigned ResourceResponse::connectionID() const
    460 {
    461     return m_connectionID;
    462 }
    463 
    464 void ResourceResponse::setConnectionID(unsigned connectionID)
    465 {
    466     m_connectionID = connectionID;
    467 }
    468 
    469 ResourceLoadTiming* ResourceResponse::resourceLoadTiming() const
    470 {
    471     return m_resourceLoadTiming.get();
    472 }
    473 
    474 void ResourceResponse::setResourceLoadTiming(PassRefPtr<ResourceLoadTiming> resourceLoadTiming)
    475 {
    476     m_resourceLoadTiming = resourceLoadTiming;
    477 }
    478 
    479 PassRefPtr<ResourceLoadInfo> ResourceResponse::resourceLoadInfo() const
    480 {
    481     return m_resourceLoadInfo.get();
    482 }
    483 
    484 void ResourceResponse::setResourceLoadInfo(PassRefPtr<ResourceLoadInfo> loadInfo)
    485 {
    486     m_resourceLoadInfo = loadInfo;
    487 }
    488 
    489 void ResourceResponse::setDownloadedFilePath(const String& downloadedFilePath)
    490 {
    491     m_downloadedFilePath = downloadedFilePath;
    492     if (m_downloadedFilePath.isEmpty()) {
    493         m_downloadedFileHandle.clear();
    494         return;
    495     }
    496     OwnPtr<BlobData> blobData = BlobData::create();
    497     blobData->appendFile(m_downloadedFilePath);
    498     blobData->detachFromCurrentThread();
    499     m_downloadedFileHandle = BlobDataHandle::create(blobData.release(), -1);
    500 }
    501 
    502 bool ResourceResponse::compare(const ResourceResponse& a, const ResourceResponse& b)
    503 {
    504     if (a.isNull() != b.isNull())
    505         return false;
    506     if (a.url() != b.url())
    507         return false;
    508     if (a.mimeType() != b.mimeType())
    509         return false;
    510     if (a.expectedContentLength() != b.expectedContentLength())
    511         return false;
    512     if (a.textEncodingName() != b.textEncodingName())
    513         return false;
    514     if (a.suggestedFilename() != b.suggestedFilename())
    515         return false;
    516     if (a.httpStatusCode() != b.httpStatusCode())
    517         return false;
    518     if (a.httpStatusText() != b.httpStatusText())
    519         return false;
    520     if (a.httpHeaderFields() != b.httpHeaderFields())
    521         return false;
    522     if (a.resourceLoadTiming() && b.resourceLoadTiming() && *a.resourceLoadTiming() == *b.resourceLoadTiming())
    523         return true;
    524     if (a.resourceLoadTiming() != b.resourceLoadTiming())
    525         return false;
    526     return true;
    527 }
    528 
    529 }
    530