blob: 567b3e5cd981722773a15aec18fb91140bd5a73f [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/fetch/FetchResponseData.h"
#include "core/dom/DOMArrayBuffer.h"
#include "core/fetch/CrossOriginAccessControl.h"
#include "core/fetch/FetchUtils.h"
#include "modules/fetch/BodyStreamBuffer.h"
#include "modules/fetch/DataConsumerHandleUtil.h"
#include "modules/fetch/DataConsumerTee.h"
#include "modules/fetch/FetchHeaderList.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerResponse.h"
namespace blink {
namespace {
WebServiceWorkerResponseType fetchTypeToWebType(FetchResponseData::Type fetchType)
{
WebServiceWorkerResponseType webType = WebServiceWorkerResponseTypeDefault;
switch (fetchType) {
case FetchResponseData::BasicType:
webType = WebServiceWorkerResponseTypeBasic;
break;
case FetchResponseData::CORSType:
webType = WebServiceWorkerResponseTypeCORS;
break;
case FetchResponseData::DefaultType:
webType = WebServiceWorkerResponseTypeDefault;
break;
case FetchResponseData::ErrorType:
webType = WebServiceWorkerResponseTypeError;
break;
case FetchResponseData::OpaqueType:
webType = WebServiceWorkerResponseTypeOpaque;
break;
case FetchResponseData::OpaqueRedirectType:
webType = WebServiceWorkerResponseTypeOpaqueRedirect;
break;
}
return webType;
}
} // namespace
FetchResponseData* FetchResponseData::create()
{
// "Unless stated otherwise, a response's url is null, status is 200, status
// message is `OK`, header list is an empty header list, and body is null."
return new FetchResponseData(DefaultType, 200, "OK");
}
FetchResponseData* FetchResponseData::createNetworkErrorResponse()
{
// "A network error is a response whose status is always 0, status message
// is always the empty byte sequence, header list is aways an empty list,
// and body is always null."
return new FetchResponseData(ErrorType, 0, "");
}
FetchResponseData* FetchResponseData::createWithBuffer(BodyStreamBuffer* buffer)
{
FetchResponseData* response = FetchResponseData::create();
response->m_buffer = buffer;
return response;
}
FetchResponseData* FetchResponseData::createBasicFilteredResponse()
{
// "A basic filtered response is a filtered response whose type is |basic|,
// header list excludes any headers in internal response's header list whose
// name is `Set-Cookie` or `Set-Cookie2`."
FetchResponseData* response = new FetchResponseData(BasicType, m_status, m_statusMessage);
response->m_url = m_url;
for (size_t i = 0; i < m_headerList->size(); ++i) {
const FetchHeaderList::Header* header = m_headerList->list()[i].get();
if (FetchUtils::isForbiddenResponseHeaderName(header->first))
continue;
response->m_headerList->append(header->first, header->second);
}
response->m_buffer = m_buffer;
response->m_mimeType = m_mimeType;
response->m_internalResponse = this;
return response;
}
FetchResponseData* FetchResponseData::createCORSFilteredResponse()
{
// "A CORS filtered response is a filtered response whose type is |CORS|,
// header list excludes all headers in internal response's header list,
// except those whose name is either one of `Cache-Control`,
// `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and
// `Pragma`, and except those whose name is one of the values resulting from
// parsing `Access-Control-Expose-Headers` in internal response's header
// list."
FetchResponseData* response = new FetchResponseData(CORSType, m_status, m_statusMessage);
response->m_url = m_url;
HTTPHeaderSet accessControlExposeHeaderSet;
String accessControlExposeHeaders;
if (m_headerList->get("access-control-expose-headers", accessControlExposeHeaders))
parseAccessControlExposeHeadersAllowList(accessControlExposeHeaders, accessControlExposeHeaderSet);
for (size_t i = 0; i < m_headerList->size(); ++i) {
const FetchHeaderList::Header* header = m_headerList->list()[i].get();
const String& name = header->first;
if (isOnAccessControlResponseHeaderWhitelist(name) || (accessControlExposeHeaderSet.contains(name) && !FetchUtils::isForbiddenResponseHeaderName(name)))
response->m_headerList->append(name, header->second);
}
response->m_buffer = m_buffer;
response->m_mimeType = m_mimeType;
response->m_internalResponse = this;
return response;
}
FetchResponseData* FetchResponseData::createOpaqueFilteredResponse()
{
// "An opaque filtered response is a filtered response whose type is
// 'opaque', url list is the empty list, status is 0, status message is the
// empty byte sequence, header list is the empty list, body is null, and
// cache state is 'none'."
//
// https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
FetchResponseData* response = new FetchResponseData(OpaqueType, 0, "");
response->m_internalResponse = this;
return response;
}
FetchResponseData* FetchResponseData::createOpaqueRedirectFilteredResponse()
{
// "An opaque filtered response is a filtered response whose type is
// 'opaqueredirect', status is 0, status message is the empty byte sequence,
// header list is the empty list, body is null, and cache state is 'none'."
//
// https://fetch.spec.whatwg.org/#concept-filtered-response-opaque-redirect
FetchResponseData* response = new FetchResponseData(OpaqueRedirectType, 0, "");
response->m_url = m_url;
response->m_internalResponse = this;
return response;
}
String FetchResponseData::mimeType() const
{
return m_mimeType;
}
BodyStreamBuffer* FetchResponseData::internalBuffer() const
{
if (m_internalResponse) {
return m_internalResponse->m_buffer;
}
return m_buffer;
}
String FetchResponseData::internalMIMEType() const
{
if (m_internalResponse) {
return m_internalResponse->mimeType();
}
return m_mimeType;
}
FetchResponseData* FetchResponseData::clone(ExecutionContext* executionContext)
{
FetchResponseData* newResponse = create();
newResponse->m_type = m_type;
if (m_terminationReason) {
newResponse->m_terminationReason = adoptPtr(new TerminationReason);
*newResponse->m_terminationReason = *m_terminationReason;
}
newResponse->m_url = m_url;
newResponse->m_status = m_status;
newResponse->m_statusMessage = m_statusMessage;
newResponse->m_headerList = m_headerList->clone();
newResponse->m_mimeType = m_mimeType;
newResponse->m_responseTime = m_responseTime;
newResponse->m_cacheStorageCacheName = m_cacheStorageCacheName;
switch (m_type) {
case BasicType:
case CORSType:
ASSERT(m_internalResponse);
ASSERT(m_buffer == m_internalResponse->m_buffer);
ASSERT(m_internalResponse->m_type == DefaultType);
newResponse->m_internalResponse = m_internalResponse->clone(executionContext);
m_buffer = m_internalResponse->m_buffer;
newResponse->m_buffer = newResponse->m_internalResponse->m_buffer;
break;
case DefaultType: {
ASSERT(!m_internalResponse);
if (m_buffer) {
OwnPtr<WebDataConsumerHandle> handle1, handle2;
DataConsumerTee::create(executionContext, m_buffer->releaseHandle(executionContext), &handle1, &handle2);
m_buffer = new BodyStreamBuffer(createFetchDataConsumerHandleFromWebHandle(handle1.release()));
newResponse->m_buffer = new BodyStreamBuffer(createFetchDataConsumerHandleFromWebHandle(handle2.release()));
}
break;
}
case ErrorType:
ASSERT(!m_internalResponse);
ASSERT(!m_buffer);
break;
case OpaqueType:
case OpaqueRedirectType:
ASSERT(m_internalResponse);
ASSERT(!m_buffer);
ASSERT(m_internalResponse->m_type == DefaultType);
newResponse->m_internalResponse = m_internalResponse->clone(executionContext);
break;
}
return newResponse;
}
void FetchResponseData::populateWebServiceWorkerResponse(WebServiceWorkerResponse& response)
{
if (m_internalResponse) {
m_internalResponse->populateWebServiceWorkerResponse(response);
response.setResponseType(fetchTypeToWebType(m_type));
return;
}
response.setURL(url());
response.setStatus(status());
response.setStatusText(statusMessage());
response.setResponseType(fetchTypeToWebType(m_type));
response.setResponseTime(responseTime());
response.setCacheStorageCacheName(cacheStorageCacheName());
for (size_t i = 0; i < headerList()->size(); ++i) {
const FetchHeaderList::Header* header = headerList()->list()[i].get();
response.appendHeader(header->first, header->second);
}
}
FetchResponseData::FetchResponseData(Type type, unsigned short status, AtomicString statusMessage)
: m_type(type)
, m_status(status)
, m_statusMessage(statusMessage)
, m_headerList(FetchHeaderList::create())
, m_responseTime(0)
{
}
void FetchResponseData::replaceBodyStreamBuffer(BodyStreamBuffer* buffer)
{
if (m_type == BasicType || m_type == CORSType) {
ASSERT(m_internalResponse);
m_internalResponse->m_buffer = buffer;
m_buffer = buffer;
} else if (m_type == DefaultType) {
ASSERT(!m_internalResponse);
m_buffer = buffer;
}
}
DEFINE_TRACE(FetchResponseData)
{
visitor->trace(m_headerList);
visitor->trace(m_internalResponse);
visitor->trace(m_buffer);
}
} // namespace blink