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