1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "FetchManager.h"
8 #include "bindings/core/v8/ExceptionState.h"
9 #include "bindings/core/v8/ScriptPromiseResolver.h"
10 #include "bindings/core/v8/ScriptState.h"
11 #include "bindings/core/v8/V8ThrowException.h"
12 #include "core/dom/ExceptionCode.h"
13 #include "core/fetch/FetchUtils.h"
14 #include "core/fileapi/Blob.h"
15 #include "core/loader/ThreadableLoader.h"
16 #include "core/loader/ThreadableLoaderClient.h"
17 #include "modules/serviceworkers/FetchRequestData.h"
18 #include "modules/serviceworkers/Response.h"
19 #include "modules/serviceworkers/ResponseInit.h"
20 #include "platform/network/ResourceRequest.h"
21 #include "platform/weborigin/SecurityOrigin.h"
22 #include "public/platform/WebURLRequest.h"
23 #include "wtf/HashSet.h"
27 class FetchManager::Loader : public ThreadableLoaderClient {
29 Loader(ExecutionContext*, FetchManager*, PassRefPtr<ScriptPromiseResolver>, PassRefPtrWillBeRawPtr<FetchRequestData>);
31 virtual void didReceiveResponse(unsigned long, const ResourceResponse&);
32 virtual void didFinishLoading(unsigned long, double);
33 virtual void didFail(const ResourceError&);
34 virtual void didFailAccessControlCheck(const ResourceError&);
35 virtual void didFailRedirectCheck();
36 virtual void didDownloadData(int);
42 void performBasicFetch();
43 void performNetworkError();
44 void performHTTPFetch();
46 void notifyFinished();
48 ExecutionContext* m_executionContext;
49 FetchManager* m_fetchManager;
50 RefPtr<ScriptPromiseResolver> m_resolver;
51 RefPtrWillBePersistent<FetchRequestData> m_request;
52 RefPtr<ThreadableLoader> m_loader;
53 ResourceResponse m_response;
54 long long m_downloadedBlobLength;
56 bool m_corsPreflightFlag;
60 FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* fetchManager, PassRefPtr<ScriptPromiseResolver> resolver, PassRefPtrWillBeRawPtr<FetchRequestData> request)
61 : m_executionContext(executionContext)
62 , m_fetchManager(fetchManager)
63 , m_resolver(resolver)
64 , m_request(request->createCopy())
65 , m_downloadedBlobLength(0)
67 , m_corsPreflightFlag(false)
72 FetchManager::Loader::~Loader()
78 void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceResponse& response)
80 m_response = response;
83 void FetchManager::Loader::didFinishLoading(unsigned long, double)
85 OwnPtr<BlobData> blobData = BlobData::create();
86 String filePath = m_response.downloadedFilePath();
87 if (!filePath.isEmpty() && m_downloadedBlobLength) {
88 blobData->appendFile(filePath);
89 blobData->setContentType(m_response.mimeType());
91 RefPtrWillBeRawPtr<FetchResponseData> response(FetchResponseData::create());
92 response->setStatus(m_response.httpStatusCode());
93 response->setStatusMessage(m_response.httpStatusText());
94 HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
95 for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it != end; ++it) {
96 response->headerList()->append(it->key, it->value);
98 response->setBlobDataHandle(BlobDataHandle::create(blobData.release(), m_downloadedBlobLength));
99 response->setURL(m_request->url());
101 switch (m_request->tainting()) {
102 case FetchRequestData::BasicTainting:
103 response = response->createBasicFilteredResponse();
105 case FetchRequestData::CORSTainting:
106 response = response->createCORSFilteredResponse();
108 case FetchRequestData::OpaqueTainting:
109 response = response->createOpaqueFilteredResponse();
112 m_resolver->resolve(Response::create(response.release()));
116 void FetchManager::Loader::didFail(const ResourceError& error)
121 void FetchManager::Loader::didFailAccessControlCheck(const ResourceError& error)
126 void FetchManager::Loader::didFailRedirectCheck()
131 void FetchManager::Loader::didDownloadData(int dataLength)
133 m_downloadedBlobLength += dataLength;
136 void FetchManager::Loader::start()
138 // "1. If |request|'s url contains a Known HSTS Host, modify it per the
139 // requirements of the 'URI [sic] Loading and Port Mapping' chapter of HTTP
140 // Strict Transport Security."
141 // FIXME: Implement this.
143 // "2. If |request|'s referrer is not none, set |request|'s referrer to the
144 // result of invoking determine |request|'s referrer."
145 // We set the referrer using workerGlobalScope's URL in
146 // WorkerThreadableLoader.
148 // "3. If |request|'s synchronous flag is unset and fetch is not invoked
149 // recursively, run the remaining steps asynchronously."
150 // We don't support synchronous flag.
152 // "4. Let response be the value corresponding to the first matching
155 // "- should fetching |request| be blocked as mixed content returns blocked
156 // - should fetching |request| be blocked as content security returns
159 // We do mixed content checking and CSP checking in ResourceFetcher.
161 // "- |request|'s url's origin is |request|'s origin and the |CORS flag| is
163 // "- |request|'s url's scheme is 'data' and |request|'s same-origin data
165 // "- |request|'s url's scheme is 'about'"
166 if ((SecurityOrigin::create(m_request->url())->isSameSchemeHostPort(m_request->origin().get()) && !m_corsFlag)
167 || (m_request->url().protocolIsData() && m_request->sameOriginDataURLFlag())
168 || (m_request->url().protocolIsAbout())) {
169 // "The result of performing a basic fetch using request."
174 // "- |request|'s mode is |same-origin|"
175 if (m_request->mode() == FetchRequestData::SameOriginMode) {
176 // "A network error."
177 performNetworkError();
181 // "- |request|'s mode is |no CORS|"
182 if (m_request->mode() == FetchRequestData::NoCORSMode) {
183 // "Set |request|'s response tainting to |opaque|."
184 m_request->setResponseTainting(FetchRequestData::OpaqueTainting);
185 // "The result of performing a basic fetch using |request|."
190 // "- |request|'s url's scheme is not one of 'http' and 'https'"
191 if (!m_request->url().protocolIsInHTTPFamily()) {
192 // "A network error."
193 performNetworkError();
197 // "- |request|'s mode is |CORS-with-forced-preflight|.
198 // "- |request|'s unsafe request flag is set and either |request|'s method
199 // is not a simple method or a header in |request|'s header list is not a
201 if (m_request->mode() == FetchRequestData::CORSWithForcedPreflight
202 || (m_request->unsafeRequestFlag()
203 && (!FetchUtils::isSimpleMethod(m_request->method())
204 || m_request->headerList()->containsNonSimpleHeader()))) {
205 // "Set |request|'s response tainting to |CORS|."
206 m_request->setResponseTainting(FetchRequestData::CORSTainting);
207 // "The result of performing an HTTP fetch using |request| with the
208 // |CORS flag| and |CORS preflight flag| set."
210 m_corsPreflightFlag = true;
216 // Set |request|'s response tainting to |CORS|."
217 m_request->setResponseTainting(FetchRequestData::CORSTainting);
218 // "The result of performing an HTTP fetch using |request| with the
221 m_corsPreflightFlag = false;
225 void FetchManager::Loader::cleanup()
227 // Prevent notification
236 void FetchManager::Loader::performBasicFetch()
238 // "To perform a basic fetch using |request|, switch on |request|'s url's
239 // scheme, and run the associated steps:"
240 if (m_request->url().protocolIsInHTTPFamily()) {
241 // "Return the result of performing an HTTP fetch using |request|."
243 m_corsPreflightFlag = false;
246 // FIXME: implement other protocols.
247 performNetworkError();
251 void FetchManager::Loader::performNetworkError()
256 void FetchManager::Loader::performHTTPFetch()
258 // CORS preflight fetch procedure is implemented inside DocumentThreadableLoader.
260 // "1. Let |HTTPRequest| be a copy of |request|, except that |HTTPRequest|'s
261 // body is a tee of |request|'s body."
262 // We use ResourceRequest class for HTTPRequest.
263 // FIXME: Support body.
264 ResourceRequest request(m_request->url());
265 request.setRequestContext(WebURLRequest::RequestContextFetch);
266 request.setDownloadToFile(true);
267 request.setHTTPMethod(m_request->method());
268 const Vector<OwnPtr<FetchHeaderList::Header> >& list = m_request->headerList()->list();
269 for (size_t i = 0; i < list.size(); ++i) {
270 request.addHTTPHeaderField(AtomicString(list[i]->first), AtomicString(list[i]->second));
273 // "2. Append `Referer`/empty byte sequence, if |HTTPRequest|'s |referrer|
274 // is none, and `Referer`/|HTTPRequest|'s referrer, serialized and utf-8
275 // encoded, otherwise, to HTTPRequest's header list.
276 // We set the referrer using workerGlobalScope's URL in
277 // WorkerThreadableLoader.
279 // "3. Append `Host`, ..."
280 // FIXME: Implement this when the spec is fixed.
282 // "4.If |HTTPRequest|'s force Origin header flag is set, append `Origin`/
283 // |HTTPRequest|'s origin, serialized and utf-8 encoded, to |HTTPRequest|'s
285 // We set Origin header in updateRequestForAccessControl() called from
286 // DocumentThreadableLoader::makeCrossOriginAccessRequest
288 // "5. Let |credentials flag| be set if either |HTTPRequest|'s credentials
289 // mode is |include|, or |HTTPRequest|'s credentials mode is |same-origin|
290 // and the |CORS flag| is unset, and unset otherwise.
291 ResourceLoaderOptions resourceLoaderOptions;
292 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
293 if (m_request->credentials() == FetchRequestData::IncludeCredentials
294 || (m_request->credentials() == FetchRequestData::SameOriginCredentials && !m_corsFlag)) {
295 resourceLoaderOptions.allowCredentials = AllowStoredCredentials;
298 ThreadableLoaderOptions threadableLoaderOptions;
299 if (m_corsPreflightFlag)
300 threadableLoaderOptions.preflightPolicy = ForcePreflight;
302 threadableLoaderOptions.crossOriginRequestPolicy = UseAccessControl;
304 threadableLoaderOptions.crossOriginRequestPolicy = AllowCrossOriginRequests;
307 m_loader = ThreadableLoader::create(*m_executionContext, this, request, threadableLoaderOptions, resourceLoaderOptions);
310 void FetchManager::Loader::failed()
315 ScriptState* state = m_resolver->scriptState();
316 ScriptState::Scope scope(state);
317 m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", state->isolate()));
321 void FetchManager::Loader::notifyFinished()
324 m_fetchManager->onLoaderFinished(this);
327 FetchManager::FetchManager(ExecutionContext* executionContext)
328 : m_executionContext(executionContext)
332 FetchManager::~FetchManager()
334 for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_loaders.end(); ++it) {
339 ScriptPromise FetchManager::fetch(ScriptState* scriptState, PassRefPtrWillBeRawPtr<FetchRequestData> request)
341 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
342 ScriptPromise promise = resolver->promise();
344 OwnPtr<Loader> ownLoader(adoptPtr(new Loader(m_executionContext, this, resolver.release(), request)));
345 Loader* loader = m_loaders.add(ownLoader.release()).storedValue->get();
350 void FetchManager::onLoaderFinished(Loader* loader)
352 m_loaders.remove(loader);