FetchResponseData* FetchResponseData::createCORSFilteredResponse( const HTTPHeaderSet& exposedHeaders) const { DCHECK_EQ(m_type, DefaultType); // "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->setURLList(m_urlList); for (size_t i = 0; i < m_headerList->size(); ++i) { const FetchHeaderList::Header* header = m_headerList->list()[i].get(); const String& name = header->first; const bool explicitlyExposed = exposedHeaders.contains(name); if (isOnAccessControlResponseHeaderWhitelist(name) || (explicitlyExposed && !FetchUtils::isForbiddenResponseHeaderName(name))) { if (explicitlyExposed) response->m_corsExposedHeaderNames.add(name); response->m_headerList->append(name, header->second); } } response->m_buffer = m_buffer; response->m_mimeType = m_mimeType; response->m_internalResponse = const_cast<FetchResponseData*>(this); return response; }
TEST_F(FetchResponseDataTest, ToWebServiceWorkerDefaultType) { WebServiceWorkerResponse webResponse; FetchResponseData* internalResponse = createInternalResponse(); internalResponse->populateWebServiceWorkerResponse(webResponse); EXPECT_EQ(WebServiceWorkerResponseTypeDefault, webResponse.responseType()); CheckHeaders(webResponse); }
TEST_F(FetchResponseDataTest, ToWebServiceWorkerOpaqueRedirectType) { WebServiceWorkerResponse webResponse; FetchResponseData* internalResponse = createInternalResponse(); FetchResponseData* opaqueRedirectResponseData = internalResponse->createOpaqueRedirectFilteredResponse(); opaqueRedirectResponseData->populateWebServiceWorkerResponse(webResponse); EXPECT_EQ(WebServiceWorkerResponseTypeOpaqueRedirect, webResponse.responseType()); CheckHeaders(webResponse); }
TEST_F(FetchResponseDataTest, ToWebServiceWorkerBasicType) { WebServiceWorkerResponse webResponse; FetchResponseData* internalResponse = createInternalResponse(); FetchResponseData* basicResponseData = internalResponse->createBasicFilteredResponse(); basicResponseData->populateWebServiceWorkerResponse(webResponse); EXPECT_EQ(WebServiceWorkerResponseTypeBasic, webResponse.responseType()); CheckHeaders(webResponse); }
TEST_F(FetchResponseDataTest, ToWebServiceWorkerCORSType) { WebServiceWorkerResponse webResponse; FetchResponseData* internalResponse = createInternalResponse(); FetchResponseData* corsResponseData = internalResponse->createCORSFilteredResponse(); corsResponseData->populateWebServiceWorkerResponse(webResponse); EXPECT_EQ(WebServiceWorkerResponseTypeCORS, webResponse.responseType()); CheckHeaders(webResponse); }
void FetchManager::Loader::didFinishLoading(unsigned long, double) { if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) return; OwnPtr<BlobData> blobData = BlobData::create(); String filePath = m_response.downloadedFilePath(); if (!filePath.isEmpty() && m_downloadedBlobLength) { blobData->appendFile(filePath); blobData->setContentType(m_response.mimeType()); } FetchResponseData* response = FetchResponseData::create(); response->setStatus(m_response.httpStatusCode()); response->setStatusMessage(m_response.httpStatusText()); HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end(); for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it != end; ++it) { response->headerList()->append(it->key, it->value); } response->setBlobDataHandle(BlobDataHandle::create(blobData.release(), m_downloadedBlobLength)); response->setURL(m_request->url()); switch (m_request->tainting()) { case FetchRequestData::BasicTainting: response = response->createBasicFilteredResponse(); break; case FetchRequestData::CORSTainting: response = response->createCORSFilteredResponse(); break; case FetchRequestData::OpaqueTainting: response = response->createOpaqueFilteredResponse(); break; } m_resolver->resolve(Response::create(m_resolver->executionContext(), response)); notifyFinished(); }
Response* Response::clone(ExceptionState& exceptionState) { if (isBodyLocked() || bodyUsed()) { exceptionState.throwTypeError("Response body is already used"); return nullptr; } FetchResponseData* response = m_response->clone(executionContext()); Headers* headers = Headers::create(response->headerList()); headers->setGuard(m_headers->guard()); return new Response(executionContext(), response, headers); }
FetchResponseData* FetchResponseData::clone(ScriptState* scriptState) { FetchResponseData* newResponse = create(); newResponse->m_type = m_type; if (m_terminationReason) { newResponse->m_terminationReason = WTF::wrapUnique(new TerminationReason); *newResponse->m_terminationReason = *m_terminationReason; } newResponse->setURLList(m_urlList); 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; newResponse->m_corsExposedHeaderNames = m_corsExposedHeaderNames; 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(scriptState); m_buffer = m_internalResponse->m_buffer; newResponse->m_buffer = newResponse->m_internalResponse->m_buffer; break; case DefaultType: { ASSERT(!m_internalResponse); if (m_buffer) { BodyStreamBuffer* new1 = nullptr; BodyStreamBuffer* new2 = nullptr; m_buffer->tee(&new1, &new2); m_buffer = new1; newResponse->m_buffer = new2; } 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(scriptState); break; } return newResponse; }
FetchResponseData* FetchResponseData::createOpaqueRedirectFilteredResponse() const { DCHECK_EQ(m_type, DefaultType); // "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->setURLList(m_urlList); response->m_internalResponse = const_cast<FetchResponseData*>(this); return response; }
TEST_F(FetchResponseDataTest, HeaderList) { FetchResponseData* responseData = createInternalResponse(); Vector<String> setCookieValues; responseData->headerList()->getAll("set-cookie", setCookieValues); ASSERT_EQ(1U, setCookieValues.size()); EXPECT_EQ("foo", setCookieValues[0]); Vector<String> barValues; responseData->headerList()->getAll("bar", barValues); ASSERT_EQ(1U, barValues.size()); EXPECT_EQ("bar", barValues[0]); Vector<String> cacheControlValues; responseData->headerList()->getAll("cache-control", cacheControlValues); ASSERT_EQ(1U, cacheControlValues.size()); EXPECT_EQ("no-cache", cacheControlValues[0]); }
FetchResponseData* FetchResponseData::createBasicFilteredResponse() const { DCHECK_EQ(m_type, DefaultType); // "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->setURLList(m_urlList); 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 = const_cast<FetchResponseData*>(this); return response; }
void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceResponse& response, PassOwnPtr<WebDataConsumerHandle> handle) { ASSERT(handle); m_responseHttpStatusCode = response.httpStatusCode(); // Recompute the tainting if the request was redirected to a different // origin. if (!SecurityOrigin::create(response.url())->isSameSchemeHostPort(m_request->origin().get())) { switch (m_request->mode()) { case WebURLRequest::FetchRequestModeSameOrigin: ASSERT_NOT_REACHED(); break; case WebURLRequest::FetchRequestModeNoCORS: m_request->setResponseTainting(FetchRequestData::OpaqueTainting); break; case WebURLRequest::FetchRequestModeCORS: case WebURLRequest::FetchRequestModeCORSWithForcedPreflight: m_request->setResponseTainting(FetchRequestData::CORSTainting); break; } } FetchResponseData* responseData = FetchResponseData::createWithBuffer(BodyStreamBuffer::create(createFetchDataConsumerHandleFromWebHandle(handle))); responseData->setStatus(response.httpStatusCode()); responseData->setStatusMessage(response.httpStatusText()); for (auto& it : response.httpHeaderFields()) responseData->headerList()->append(it.key, it.value); responseData->setURL(response.url()); responseData->setMIMEType(response.mimeType()); FetchResponseData* taintedResponse = responseData; switch (m_request->tainting()) { case FetchRequestData::BasicTainting: taintedResponse = responseData->createBasicFilteredResponse(); break; case FetchRequestData::CORSTainting: taintedResponse = responseData->createCORSFilteredResponse(); break; case FetchRequestData::OpaqueTainting: taintedResponse = responseData->createOpaqueFilteredResponse(); break; } Response* r = Response::create(m_resolver->executionContext(), taintedResponse); r->headers()->setGuard(Headers::ImmutableGuard); m_resolver->resolve(r); m_resolver.clear(); }
TEST_F(FetchResponseDataTest, OpaqueFilterOnResponseWithAccessControlExposeHeaders) { FetchResponseData* internalResponse = createInternalResponse(); internalResponse->headerList()->append("access-control-expose-headers", "set-cookie, bar"); FetchResponseData* opaqueResponseData = internalResponse->createOpaqueFilteredResponse(); EXPECT_FALSE(opaqueResponseData->headerList()->has("set-cookie")); EXPECT_FALSE(opaqueResponseData->headerList()->has("bar")); EXPECT_FALSE(opaqueResponseData->headerList()->has("cache-control")); }
TEST_F(FetchResponseDataTest, OpaqueFilter) { FetchResponseData* internalResponse = createInternalResponse(); FetchResponseData* opaqueResponseData = internalResponse->createOpaqueFilteredResponse(); EXPECT_FALSE(opaqueResponseData->headerList()->has("set-cookie")); EXPECT_FALSE(opaqueResponseData->headerList()->has("bar")); EXPECT_FALSE(opaqueResponseData->headerList()->has("cache-control")); }
FetchResponseData* createInternalResponse() { FetchResponseData* internalResponse = FetchResponseData::create(); internalResponse->setStatus(200); internalResponse->setURL(KURL(ParsedURLString, "http://www.example.com")); internalResponse->headerList()->append("set-cookie", "foo"); internalResponse->headerList()->append("bar", "bar"); internalResponse->headerList()->append("cache-control", "no-cache"); return internalResponse; }
TEST_F(FetchResponseDataTest, CORSFilter) { FetchResponseData* internalResponse = createInternalResponse(); FetchResponseData* corsResponseData = internalResponse->createCORSFilteredResponse(); EXPECT_FALSE(corsResponseData->headerList()->has("set-cookie")); EXPECT_FALSE(corsResponseData->headerList()->has("bar")); Vector<String> cacheControlValues; corsResponseData->headerList()->getAll("cache-control", cacheControlValues); ASSERT_EQ(1U, cacheControlValues.size()); EXPECT_EQ("no-cache", cacheControlValues[0]); }
TEST_F(FetchResponseDataTest, CORSFilterOnResponseWithAccessControlExposeHeaders) { FetchResponseData* internalResponse = createInternalResponse(); internalResponse->headerList()->append("access-control-expose-headers", "set-cookie, bar"); FetchResponseData* corsResponseData = internalResponse->createCORSFilteredResponse(); EXPECT_FALSE(corsResponseData->headerList()->has("set-cookie")); Vector<String> barValues; corsResponseData->headerList()->getAll("bar", barValues); ASSERT_EQ(1U, barValues.size()); EXPECT_EQ("bar", barValues[0]); }