| // 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 "core/fetch/Response.h" |
| |
| #include <memory> |
| #include "bindings/core/v8/ExceptionState.h" |
| #include "bindings/core/v8/V8BindingForTesting.h" |
| #include "core/dom/ExecutionContext.h" |
| #include "core/fetch/BodyStreamBuffer.h" |
| #include "core/fetch/BytesConsumer.h" |
| #include "core/fetch/BytesConsumerTestUtil.h" |
| #include "core/fetch/DataConsumerHandleTestUtil.h" |
| #include "core/fetch/FetchResponseData.h" |
| #include "core/frame/Frame.h" |
| #include "core/testing/DummyPageHolder.h" |
| #include "platform/bindings/ScriptState.h" |
| #include "platform/blob/BlobData.h" |
| #include "platform/testing/UnitTestHelpers.h" |
| #include "platform/wtf/Vector.h" |
| #include "public/platform/modules/fetch/fetch_api_request.mojom-blink.h" |
| #include "public/platform/modules/serviceworker/WebServiceWorkerResponse.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace blink { |
| namespace { |
| |
| std::unique_ptr<WebServiceWorkerResponse> CreateTestWebServiceWorkerResponse() { |
| const KURL url("http://www.webresponse.com/"); |
| const unsigned short kStatus = 200; |
| const String status_text = "the best status text"; |
| struct { |
| const char* key; |
| const char* value; |
| } headers[] = {{"cache-control", "no-cache"}, |
| {"set-cookie", "foop"}, |
| {"foo", "bar"}, |
| {nullptr, nullptr}}; |
| Vector<WebURL> url_list; |
| url_list.push_back(url); |
| std::unique_ptr<WebServiceWorkerResponse> web_response = |
| std::make_unique<WebServiceWorkerResponse>(); |
| web_response->SetURLList(url_list); |
| web_response->SetStatus(kStatus); |
| web_response->SetStatusText(status_text); |
| web_response->SetResponseType(network::mojom::FetchResponseType::kDefault); |
| for (int i = 0; headers[i].key; ++i) { |
| web_response->SetHeader(WebString::FromUTF8(headers[i].key), |
| WebString::FromUTF8(headers[i].value)); |
| } |
| return web_response; |
| } |
| |
| TEST(ServiceWorkerResponseTest, FromFetchResponseData) { |
| std::unique_ptr<DummyPageHolder> page = |
| DummyPageHolder::Create(IntSize(1, 1)); |
| const KURL url("http://www.response.com"); |
| |
| FetchResponseData* fetch_response_data = FetchResponseData::Create(); |
| Vector<KURL> url_list; |
| url_list.push_back(url); |
| fetch_response_data->SetURLList(url_list); |
| Response* response = |
| Response::Create(&page->GetDocument(), fetch_response_data); |
| DCHECK(response); |
| EXPECT_EQ(url, response->url()); |
| } |
| |
| TEST(ServiceWorkerResponseTest, FromWebServiceWorkerResponse) { |
| V8TestingScope scope; |
| std::unique_ptr<WebServiceWorkerResponse> web_response = |
| CreateTestWebServiceWorkerResponse(); |
| Response* response = Response::Create(scope.GetScriptState(), *web_response); |
| DCHECK(response); |
| ASSERT_EQ(1u, web_response->UrlList().size()); |
| EXPECT_EQ(web_response->UrlList()[0], response->url()); |
| EXPECT_EQ(web_response->Status(), response->status()); |
| EXPECT_STREQ(web_response->StatusText().Utf8().c_str(), |
| response->statusText().Utf8().data()); |
| |
| Headers* response_headers = response->headers(); |
| |
| WebVector<WebString> keys = web_response->GetHeaderKeys(); |
| EXPECT_EQ(keys.size(), response_headers->HeaderList()->size()); |
| for (size_t i = 0, max = keys.size(); i < max; ++i) { |
| WebString key = keys[i]; |
| DummyExceptionStateForTesting exception_state; |
| EXPECT_STREQ(web_response->GetHeader(key).Utf8().c_str(), |
| response_headers->get(key, exception_state).Utf8().data()); |
| EXPECT_FALSE(exception_state.HadException()); |
| } |
| } |
| |
| TEST(ServiceWorkerResponseTest, FromWebServiceWorkerResponseDefault) { |
| V8TestingScope scope; |
| std::unique_ptr<WebServiceWorkerResponse> web_response = |
| CreateTestWebServiceWorkerResponse(); |
| web_response->SetResponseType(network::mojom::FetchResponseType::kDefault); |
| Response* response = Response::Create(scope.GetScriptState(), *web_response); |
| |
| Headers* response_headers = response->headers(); |
| DummyExceptionStateForTesting exception_state; |
| EXPECT_STREQ( |
| "foop", |
| response_headers->get("set-cookie", exception_state).Utf8().data()); |
| EXPECT_STREQ("bar", |
| response_headers->get("foo", exception_state).Utf8().data()); |
| EXPECT_STREQ( |
| "no-cache", |
| response_headers->get("cache-control", exception_state).Utf8().data()); |
| EXPECT_FALSE(exception_state.HadException()); |
| } |
| |
| TEST(ServiceWorkerResponseTest, FromWebServiceWorkerResponseBasic) { |
| V8TestingScope scope; |
| std::unique_ptr<WebServiceWorkerResponse> web_response = |
| CreateTestWebServiceWorkerResponse(); |
| web_response->SetResponseType(network::mojom::FetchResponseType::kBasic); |
| Response* response = Response::Create(scope.GetScriptState(), *web_response); |
| |
| Headers* response_headers = response->headers(); |
| DummyExceptionStateForTesting exception_state; |
| EXPECT_STREQ( |
| "", response_headers->get("set-cookie", exception_state).Utf8().data()); |
| EXPECT_STREQ("bar", |
| response_headers->get("foo", exception_state).Utf8().data()); |
| EXPECT_STREQ( |
| "no-cache", |
| response_headers->get("cache-control", exception_state).Utf8().data()); |
| EXPECT_FALSE(exception_state.HadException()); |
| } |
| |
| TEST(ServiceWorkerResponseTest, FromWebServiceWorkerResponseCORS) { |
| V8TestingScope scope; |
| std::unique_ptr<WebServiceWorkerResponse> web_response = |
| CreateTestWebServiceWorkerResponse(); |
| web_response->SetResponseType(network::mojom::FetchResponseType::kCORS); |
| Response* response = Response::Create(scope.GetScriptState(), *web_response); |
| |
| Headers* response_headers = response->headers(); |
| DummyExceptionStateForTesting exception_state; |
| EXPECT_STREQ( |
| "", response_headers->get("set-cookie", exception_state).Utf8().data()); |
| EXPECT_STREQ("", response_headers->get("foo", exception_state).Utf8().data()); |
| EXPECT_STREQ( |
| "no-cache", |
| response_headers->get("cache-control", exception_state).Utf8().data()); |
| EXPECT_FALSE(exception_state.HadException()); |
| } |
| |
| TEST(ServiceWorkerResponseTest, FromWebServiceWorkerResponseOpaque) { |
| V8TestingScope scope; |
| std::unique_ptr<WebServiceWorkerResponse> web_response = |
| CreateTestWebServiceWorkerResponse(); |
| web_response->SetResponseType(network::mojom::FetchResponseType::kOpaque); |
| Response* response = Response::Create(scope.GetScriptState(), *web_response); |
| |
| Headers* response_headers = response->headers(); |
| DummyExceptionStateForTesting exception_state; |
| EXPECT_STREQ( |
| "", response_headers->get("set-cookie", exception_state).Utf8().data()); |
| EXPECT_STREQ("", response_headers->get("foo", exception_state).Utf8().data()); |
| EXPECT_STREQ( |
| "", |
| response_headers->get("cache-control", exception_state).Utf8().data()); |
| EXPECT_FALSE(exception_state.HadException()); |
| } |
| |
| void CheckResponseStream(ScriptState* script_state, |
| Response* response, |
| bool check_response_body_stream_buffer) { |
| BodyStreamBuffer* original_internal = response->InternalBodyBuffer(); |
| if (check_response_body_stream_buffer) { |
| EXPECT_EQ(response->BodyBuffer(), original_internal); |
| } else { |
| EXPECT_FALSE(response->BodyBuffer()); |
| } |
| |
| DummyExceptionStateForTesting exception_state; |
| Response* cloned_response = response->clone(script_state, exception_state); |
| EXPECT_FALSE(exception_state.HadException()); |
| |
| if (!response->InternalBodyBuffer()) |
| FAIL() << "internalBodyBuffer() must not be null."; |
| if (!cloned_response->InternalBodyBuffer()) |
| FAIL() << "internalBodyBuffer() must not be null."; |
| EXPECT_TRUE(response->InternalBodyBuffer()); |
| EXPECT_TRUE(cloned_response->InternalBodyBuffer()); |
| EXPECT_TRUE(response->InternalBodyBuffer()); |
| EXPECT_TRUE(cloned_response->InternalBodyBuffer()); |
| EXPECT_NE(response->InternalBodyBuffer(), original_internal); |
| EXPECT_NE(cloned_response->InternalBodyBuffer(), original_internal); |
| EXPECT_NE(response->InternalBodyBuffer(), |
| cloned_response->InternalBodyBuffer()); |
| if (check_response_body_stream_buffer) { |
| EXPECT_EQ(response->BodyBuffer(), response->InternalBodyBuffer()); |
| EXPECT_EQ(cloned_response->BodyBuffer(), |
| cloned_response->InternalBodyBuffer()); |
| } else { |
| EXPECT_FALSE(response->BodyBuffer()); |
| EXPECT_FALSE(cloned_response->BodyBuffer()); |
| } |
| BytesConsumerTestUtil::MockFetchDataLoaderClient* client1 = |
| new BytesConsumerTestUtil::MockFetchDataLoaderClient(); |
| BytesConsumerTestUtil::MockFetchDataLoaderClient* client2 = |
| new BytesConsumerTestUtil::MockFetchDataLoaderClient(); |
| EXPECT_CALL(*client1, DidFetchDataLoadedString(String("Hello, world"))); |
| EXPECT_CALL(*client2, DidFetchDataLoadedString(String("Hello, world"))); |
| |
| response->InternalBodyBuffer()->StartLoading( |
| FetchDataLoader::CreateLoaderAsString(), client1); |
| cloned_response->InternalBodyBuffer()->StartLoading( |
| FetchDataLoader::CreateLoaderAsString(), client2); |
| blink::testing::RunPendingTasks(); |
| } |
| |
| BodyStreamBuffer* CreateHelloWorldBuffer(ScriptState* script_state) { |
| using BytesConsumerCommand = BytesConsumerTestUtil::Command; |
| BytesConsumerTestUtil::ReplayingBytesConsumer* src = |
| new BytesConsumerTestUtil::ReplayingBytesConsumer( |
| ExecutionContext::From(script_state)); |
| src->Add(BytesConsumerCommand(BytesConsumerCommand::kData, "Hello, ")); |
| src->Add(BytesConsumerCommand(BytesConsumerCommand::kData, "world")); |
| src->Add(BytesConsumerCommand(BytesConsumerCommand::kDone)); |
| return new BodyStreamBuffer(script_state, src, nullptr); |
| } |
| |
| TEST(ServiceWorkerResponseTest, BodyStreamBufferCloneDefault) { |
| V8TestingScope scope; |
| BodyStreamBuffer* buffer = CreateHelloWorldBuffer(scope.GetScriptState()); |
| FetchResponseData* fetch_response_data = |
| FetchResponseData::CreateWithBuffer(buffer); |
| Vector<KURL> url_list; |
| url_list.push_back(KURL("http://www.response.com")); |
| fetch_response_data->SetURLList(url_list); |
| Response* response = |
| Response::Create(scope.GetExecutionContext(), fetch_response_data); |
| EXPECT_EQ(response->InternalBodyBuffer(), buffer); |
| CheckResponseStream(scope.GetScriptState(), response, true); |
| } |
| |
| TEST(ServiceWorkerResponseTest, BodyStreamBufferCloneBasic) { |
| V8TestingScope scope; |
| BodyStreamBuffer* buffer = CreateHelloWorldBuffer(scope.GetScriptState()); |
| FetchResponseData* fetch_response_data = |
| FetchResponseData::CreateWithBuffer(buffer); |
| Vector<KURL> url_list; |
| url_list.push_back(KURL("http://www.response.com")); |
| fetch_response_data->SetURLList(url_list); |
| fetch_response_data = fetch_response_data->CreateBasicFilteredResponse(); |
| Response* response = |
| Response::Create(scope.GetExecutionContext(), fetch_response_data); |
| EXPECT_EQ(response->InternalBodyBuffer(), buffer); |
| CheckResponseStream(scope.GetScriptState(), response, true); |
| } |
| |
| TEST(ServiceWorkerResponseTest, BodyStreamBufferCloneCORS) { |
| V8TestingScope scope; |
| BodyStreamBuffer* buffer = CreateHelloWorldBuffer(scope.GetScriptState()); |
| FetchResponseData* fetch_response_data = |
| FetchResponseData::CreateWithBuffer(buffer); |
| Vector<KURL> url_list; |
| url_list.push_back(KURL("http://www.response.com")); |
| fetch_response_data->SetURLList(url_list); |
| fetch_response_data = fetch_response_data->CreateCORSFilteredResponse({}); |
| Response* response = |
| Response::Create(scope.GetExecutionContext(), fetch_response_data); |
| EXPECT_EQ(response->InternalBodyBuffer(), buffer); |
| CheckResponseStream(scope.GetScriptState(), response, true); |
| } |
| |
| TEST(ServiceWorkerResponseTest, BodyStreamBufferCloneOpaque) { |
| V8TestingScope scope; |
| BodyStreamBuffer* buffer = CreateHelloWorldBuffer(scope.GetScriptState()); |
| FetchResponseData* fetch_response_data = |
| FetchResponseData::CreateWithBuffer(buffer); |
| Vector<KURL> url_list; |
| url_list.push_back(KURL("http://www.response.com")); |
| fetch_response_data->SetURLList(url_list); |
| fetch_response_data = fetch_response_data->CreateOpaqueFilteredResponse(); |
| Response* response = |
| Response::Create(scope.GetExecutionContext(), fetch_response_data); |
| EXPECT_EQ(response->InternalBodyBuffer(), buffer); |
| CheckResponseStream(scope.GetScriptState(), response, false); |
| } |
| |
| TEST(ServiceWorkerResponseTest, BodyStreamBufferCloneError) { |
| V8TestingScope scope; |
| BodyStreamBuffer* buffer = new BodyStreamBuffer( |
| scope.GetScriptState(), |
| BytesConsumer::CreateErrored(BytesConsumer::Error()), nullptr); |
| FetchResponseData* fetch_response_data = |
| FetchResponseData::CreateWithBuffer(buffer); |
| Vector<KURL> url_list; |
| url_list.push_back(KURL("http://www.response.com")); |
| fetch_response_data->SetURLList(url_list); |
| Response* response = |
| Response::Create(scope.GetExecutionContext(), fetch_response_data); |
| DummyExceptionStateForTesting exception_state; |
| Response* cloned_response = |
| response->clone(scope.GetScriptState(), exception_state); |
| EXPECT_FALSE(exception_state.HadException()); |
| |
| BytesConsumerTestUtil::MockFetchDataLoaderClient* client1 = |
| new BytesConsumerTestUtil::MockFetchDataLoaderClient(); |
| BytesConsumerTestUtil::MockFetchDataLoaderClient* client2 = |
| new BytesConsumerTestUtil::MockFetchDataLoaderClient(); |
| EXPECT_CALL(*client1, DidFetchDataLoadFailed()); |
| EXPECT_CALL(*client2, DidFetchDataLoadFailed()); |
| |
| response->InternalBodyBuffer()->StartLoading( |
| FetchDataLoader::CreateLoaderAsString(), client1); |
| cloned_response->InternalBodyBuffer()->StartLoading( |
| FetchDataLoader::CreateLoaderAsString(), client2); |
| blink::testing::RunPendingTasks(); |
| } |
| |
| } // namespace |
| } // namespace blink |