blob: 2ea63f027bc8ca92409230160756f116d5b4d47f [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 "third_party/blink/renderer/core/fetch/fetch_response_data.h"
#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_response.h"
#include "third_party/blink/renderer/core/fetch/fetch_header_list.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/loader/fetch/fetch_utils.h"
#include "third_party/blink/renderer/platform/network/http_names.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
using Type = network::mojom::FetchResponseType;
namespace blink {
namespace {
WebVector<WebString> HeaderSetToWebVector(const WebHTTPHeaderSet& headers) {
// Can't just pass *headers to the WebVector constructor because HashSet
// iterators are not stl iterator compatible.
WebVector<WebString> result(static_cast<size_t>(headers.size()));
int idx = 0;
for (const auto& header : headers)
result[idx++] = WebString::FromASCII(header);
return result;
}
Vector<String> HeaderSetToVector(const WebHTTPHeaderSet& headers) {
Vector<String> result;
result.ReserveInitialCapacity(SafeCast<wtf_size_t>(headers.size()));
// WebHTTPHeaderSet stores headers using Latin1 encoding.
for (const auto& header : headers)
result.push_back(String(header.data(), header.size()));
return result;
}
} // namespace
FetchResponseData* FetchResponseData::Create() {
// "Unless stated otherwise, a response's url is null, status is 200, status
// message is the empty byte sequence, header list is an empty header list,
// and body is null."
return new FetchResponseData(Type::kDefault, 200, g_empty_atom);
}
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(Type::kError, 0, g_empty_atom);
}
FetchResponseData* FetchResponseData::CreateWithBuffer(
BodyStreamBuffer* buffer) {
FetchResponseData* response = FetchResponseData::Create();
response->buffer_ = buffer;
return response;
}
FetchResponseData* FetchResponseData::CreateBasicFilteredResponse() const {
DCHECK_EQ(type_, Type::kDefault);
// "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(Type::kBasic, status_, status_message_);
response->SetURLList(url_list_);
for (const auto& header : header_list_->List()) {
if (FetchUtils::IsForbiddenResponseHeaderName(header.first))
continue;
response->header_list_->Append(header.first, header.second);
}
response->buffer_ = buffer_;
response->mime_type_ = mime_type_;
response->internal_response_ = const_cast<FetchResponseData*>(this);
return response;
}
FetchResponseData* FetchResponseData::CreateCORSFilteredResponse(
const WebHTTPHeaderSet& exposed_headers) const {
DCHECK_EQ(type_, Type::kDefault);
// "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(Type::kCORS, status_, status_message_);
response->SetURLList(url_list_);
for (const auto& header : header_list_->List()) {
const String& name = header.first;
if (WebCORS::IsOnAccessControlResponseHeaderWhitelist(name) ||
(exposed_headers.find(name.Ascii().data()) != exposed_headers.end() &&
!FetchUtils::IsForbiddenResponseHeaderName(name))) {
response->header_list_->Append(name, header.second);
}
}
response->cors_exposed_header_names_ = exposed_headers;
response->buffer_ = buffer_;
response->mime_type_ = mime_type_;
response->internal_response_ = const_cast<FetchResponseData*>(this);
return response;
}
FetchResponseData* FetchResponseData::CreateOpaqueFilteredResponse() const {
DCHECK_EQ(type_, Type::kDefault);
// "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(Type::kOpaque, 0, g_empty_atom);
response->internal_response_ = const_cast<FetchResponseData*>(this);
return response;
}
FetchResponseData* FetchResponseData::CreateOpaqueRedirectFilteredResponse()
const {
DCHECK_EQ(type_, Type::kDefault);
// "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(Type::kOpaqueRedirect, 0, g_empty_atom);
response->SetURLList(url_list_);
response->internal_response_ = const_cast<FetchResponseData*>(this);
return response;
}
const KURL* FetchResponseData::Url() const {
// "A response has an associated url. It is a pointer to the last response URL
// in response’s url list and null if response’s url list is the empty list."
if (url_list_.IsEmpty())
return nullptr;
return &url_list_.back();
}
String FetchResponseData::MimeType() const {
return mime_type_;
}
BodyStreamBuffer* FetchResponseData::InternalBuffer() const {
if (internal_response_) {
return internal_response_->buffer_;
}
return buffer_;
}
String FetchResponseData::InternalMIMEType() const {
if (internal_response_) {
return internal_response_->MimeType();
}
return mime_type_;
}
void FetchResponseData::SetURLList(const Vector<KURL>& url_list) {
url_list_ = url_list;
}
const Vector<KURL>& FetchResponseData::InternalURLList() const {
if (internal_response_) {
return internal_response_->url_list_;
}
return url_list_;
}
FetchResponseData* FetchResponseData::Clone(ScriptState* script_state,
ExceptionState& exception_state) {
FetchResponseData* new_response = Create();
new_response->type_ = type_;
if (termination_reason_) {
new_response->termination_reason_ = std::make_unique<TerminationReason>();
*new_response->termination_reason_ = *termination_reason_;
}
new_response->SetURLList(url_list_);
new_response->status_ = status_;
new_response->status_message_ = status_message_;
new_response->header_list_ = header_list_->Clone();
new_response->mime_type_ = mime_type_;
new_response->response_time_ = response_time_;
new_response->cache_storage_cache_name_ = cache_storage_cache_name_;
new_response->cors_exposed_header_names_ = cors_exposed_header_names_;
switch (type_) {
case Type::kBasic:
case Type::kCORS:
DCHECK(internal_response_);
DCHECK_EQ(buffer_, internal_response_->buffer_);
DCHECK_EQ(internal_response_->type_, Type::kDefault);
new_response->internal_response_ =
internal_response_->Clone(script_state, exception_state);
if (exception_state.HadException())
return nullptr;
buffer_ = internal_response_->buffer_;
new_response->buffer_ = new_response->internal_response_->buffer_;
break;
case Type::kDefault: {
DCHECK(!internal_response_);
if (buffer_) {
BodyStreamBuffer* new1 = nullptr;
BodyStreamBuffer* new2 = nullptr;
buffer_->Tee(&new1, &new2, exception_state);
if (exception_state.HadException())
return nullptr;
buffer_ = new1;
new_response->buffer_ = new2;
}
break;
}
case Type::kError:
DCHECK(!internal_response_);
DCHECK(!buffer_);
break;
case Type::kOpaque:
case Type::kOpaqueRedirect:
DCHECK(internal_response_);
DCHECK(!buffer_);
DCHECK_EQ(internal_response_->type_, Type::kDefault);
new_response->internal_response_ =
internal_response_->Clone(script_state, exception_state);
if (exception_state.HadException())
return nullptr;
break;
}
return new_response;
}
void FetchResponseData::PopulateWebServiceWorkerResponse(
WebServiceWorkerResponse& response) {
if (internal_response_) {
internal_response_->PopulateWebServiceWorkerResponse(response);
response.SetResponseType(type_);
response.SetCorsExposedHeaderNames(
HeaderSetToWebVector(cors_exposed_header_names_));
return;
}
response.SetURLList(url_list_);
response.SetStatus(Status());
response.SetStatusText(StatusMessage());
response.SetResponseType(type_);
response.SetResponseTime(ResponseTime());
response.SetCacheStorageCacheName(CacheStorageCacheName());
response.SetCorsExposedHeaderNames(
HeaderSetToWebVector(cors_exposed_header_names_));
for (const auto& header : HeaderList()->List()) {
response.AppendHeader(header.first, header.second);
}
}
mojom::blink::FetchAPIResponsePtr
FetchResponseData::PopulateFetchAPIResponse() {
if (internal_response_) {
mojom::blink::FetchAPIResponsePtr response =
internal_response_->PopulateFetchAPIResponse();
response->response_type = type_;
response->cors_exposed_header_names =
HeaderSetToVector(cors_exposed_header_names_);
return response;
}
mojom::blink::FetchAPIResponsePtr response =
mojom::blink::FetchAPIResponse::New();
response->url_list = url_list_;
response->status_code = status_;
response->status_text = status_message_;
response->response_type = type_;
response->response_time = response_time_;
response->cache_storage_cache_name = cache_storage_cache_name_;
response->cors_exposed_header_names =
HeaderSetToVector(cors_exposed_header_names_);
for (const auto& header : HeaderList()->List())
response->headers.insert(header.first, header.second);
return response;
}
FetchResponseData::FetchResponseData(Type type,
unsigned short status,
AtomicString status_message)
: type_(type),
status_(status),
status_message_(status_message),
header_list_(FetchHeaderList::Create()),
response_time_(base::Time::Now()) {}
void FetchResponseData::ReplaceBodyStreamBuffer(BodyStreamBuffer* buffer) {
if (type_ == Type::kBasic || type_ == Type::kCORS) {
DCHECK(internal_response_);
internal_response_->buffer_ = buffer;
buffer_ = buffer;
} else if (type_ == Type::kDefault) {
DCHECK(!internal_response_);
buffer_ = buffer;
}
}
void FetchResponseData::Trace(blink::Visitor* visitor) {
visitor->Trace(header_list_);
visitor->Trace(internal_response_);
visitor->Trace(buffer_);
}
} // namespace blink