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(); if (!isOnAccessControlResponseHeaderWhitelist(header->first) && !accessControlExposeHeaderSet.contains(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; }
void parseAccessControlExposeHeadersAllowList(const String& headerValue, HTTPHeaderSet& headerSet) { Vector<String> headers; headerValue.split(',', false, headers); for (unsigned headerCount = 0; headerCount < headers.size(); headerCount++) { String strippedHeader = headers[headerCount].stripWhiteSpace(); if (!strippedHeader.isEmpty()) headerSet.add(strippedHeader); } }
void WebAssociatedURLLoaderImpl::ClientAdapter::didReceiveResponse( unsigned long, const ResourceResponse& response, std::unique_ptr<WebDataConsumerHandle> handle) { ALLOW_UNUSED_LOCAL(handle); DCHECK(!handle); if (!m_client) return; if (m_options.exposeAllResponseHeaders || m_options.crossOriginRequestPolicy != WebAssociatedURLLoaderOptions:: CrossOriginRequestPolicyUseAccessControl) { // Use the original ResourceResponse. m_client->didReceiveResponse(WrappedResourceResponse(response)); return; } HTTPHeaderSet exposedHeaders; extractCorsExposedHeaderNamesList(response, exposedHeaders); HTTPHeaderSet blockedHeaders; for (const auto& header : response.httpHeaderFields()) { if (FetchUtils::isForbiddenResponseHeaderName(header.key) || (!isOnAccessControlResponseHeaderWhitelist(header.key) && !exposedHeaders.contains(header.key))) blockedHeaders.add(header.key); } if (blockedHeaders.isEmpty()) { // Use the original ResourceResponse. m_client->didReceiveResponse(WrappedResourceResponse(response)); return; } // If there are blocked headers, copy the response so we can remove them. WebURLResponse validatedResponse = WrappedResourceResponse(response); for (const auto& header : blockedHeaders) validatedResponse.clearHTTPHeaderField(header); m_client->didReceiveResponse(validatedResponse); }
void ForeignFetchRespondWithObserver::responseWasFulfilled( const ScriptValue& value) { ASSERT(getExecutionContext()); ExceptionState exceptionState(value.isolate(), ExceptionState::UnknownContext, "ForeignFetchEvent", "respondWith"); ForeignFetchResponse foreignFetchResponse = ScriptValue::to<ForeignFetchResponse>(toIsolate(getExecutionContext()), value, exceptionState); if (exceptionState.hadException()) { exceptionState.clearException(); responseWasRejected(WebServiceWorkerResponseErrorNoForeignFetchResponse); return; } Response* response = foreignFetchResponse.response(); const FetchResponseData* internalResponse = response->response(); const bool isOpaque = internalResponse->getType() == FetchResponseData::OpaqueType || internalResponse->getType() == FetchResponseData::OpaqueRedirectType; if (internalResponse->getType() != FetchResponseData::DefaultType) internalResponse = internalResponse->internalResponse(); if (!foreignFetchResponse.hasOrigin()) { if (foreignFetchResponse.hasHeaders() && !foreignFetchResponse.headers().isEmpty()) { responseWasRejected( WebServiceWorkerResponseErrorForeignFetchHeadersWithoutOrigin); return; } // If response isn't already opaque, make it opaque. if (!isOpaque) { FetchResponseData* opaqueData = internalResponse->createOpaqueFilteredResponse(); response = Response::create(getExecutionContext(), opaqueData); } } else if (m_requestOrigin->toString() != foreignFetchResponse.origin()) { responseWasRejected( WebServiceWorkerResponseErrorForeignFetchMismatchedOrigin); return; } else if (!isOpaque) { HTTPHeaderSet headers; if (foreignFetchResponse.hasHeaders()) { for (const String& header : foreignFetchResponse.headers()) headers.add(header); if (response->response()->getType() == FetchResponseData::CORSType) { const HTTPHeaderSet& existingHeaders = response->response()->corsExposedHeaderNames(); HTTPHeaderSet headersToRemove; for (HTTPHeaderSet::iterator it = headers.begin(); it != headers.end(); ++it) { if (!existingHeaders.contains(*it)) headersToRemove.add(*it); } headers.removeAll(headersToRemove); } } FetchResponseData* responseData = internalResponse->createCORSFilteredResponse(headers); response = Response::create(getExecutionContext(), responseData); } RespondWithObserver::responseWasFulfilled( ScriptValue::from(value.getScriptState(), response)); }