Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / serviceworkers / FetchManager.cpp
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.
4
5 #include "config.h"
6 #include "FetchManager.h"
7
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"
24
25 namespace blink {
26
27 class FetchManager::Loader : public ThreadableLoaderClient {
28 public:
29     Loader(ExecutionContext*, FetchManager*, PassRefPtr<ScriptPromiseResolver>, PassRefPtrWillBeRawPtr<FetchRequestData>);
30     ~Loader();
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);
37
38     void start();
39     void cleanup();
40
41 private:
42     void performBasicFetch();
43     void performNetworkError();
44     void performHTTPFetch();
45     void failed();
46     void notifyFinished();
47
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;
55     bool m_corsFlag;
56     bool m_corsPreflightFlag;
57     bool m_failed;
58 };
59
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)
66     , m_corsFlag(false)
67     , m_corsPreflightFlag(false)
68     , m_failed(false)
69 {
70 }
71
72 FetchManager::Loader::~Loader()
73 {
74     if (m_loader)
75         m_loader->cancel();
76 }
77
78 void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceResponse& response)
79 {
80     m_response = response;
81 }
82
83 void FetchManager::Loader::didFinishLoading(unsigned long, double)
84 {
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());
90     }
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);
97     }
98     response->setBlobDataHandle(BlobDataHandle::create(blobData.release(), m_downloadedBlobLength));
99     response->setURL(m_request->url());
100
101     switch (m_request->tainting()) {
102     case FetchRequestData::BasicTainting:
103         response = response->createBasicFilteredResponse();
104         break;
105     case FetchRequestData::CORSTainting:
106         response = response->createCORSFilteredResponse();
107         break;
108     case FetchRequestData::OpaqueTainting:
109         response = response->createOpaqueFilteredResponse();
110         break;
111     }
112     m_resolver->resolve(Response::create(response.release()));
113     notifyFinished();
114 }
115
116 void FetchManager::Loader::didFail(const ResourceError& error)
117 {
118     failed();
119 }
120
121 void FetchManager::Loader::didFailAccessControlCheck(const ResourceError& error)
122 {
123     failed();
124 }
125
126 void FetchManager::Loader::didFailRedirectCheck()
127 {
128     failed();
129 }
130
131 void FetchManager::Loader::didDownloadData(int dataLength)
132 {
133     m_downloadedBlobLength += dataLength;
134 }
135
136 void FetchManager::Loader::start()
137 {
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.
142
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.
147
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.
151
152     // "4. Let response be the value corresponding to the first matching
153     // statement:"
154
155     // "- should fetching |request| be blocked as mixed content returns blocked
156     //  - should fetching |request| be blocked as content security returns
157     //    blocked
158     //      A network error."
159     // We do mixed content checking and CSP checking in ResourceFetcher.
160
161     // "- |request|'s url's origin is |request|'s origin and the |CORS flag| is
162     //    unset"
163     // "- |request|'s url's scheme is 'data' and |request|'s same-origin data
164     //    URL flag is set"
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."
170         performBasicFetch();
171         return;
172     }
173
174     // "- |request|'s mode is |same-origin|"
175     if (m_request->mode() == FetchRequestData::SameOriginMode) {
176         // "A network error."
177         performNetworkError();
178         return;
179     }
180
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|."
186         performBasicFetch();
187         return;
188     }
189
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();
194         return;
195     }
196
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
200     // simple header"
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."
209         m_corsFlag = true;
210         m_corsPreflightFlag = true;
211         performHTTPFetch();
212         return;
213     }
214
215     // "- Otherwise
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
219     // |CORS flag| set."
220     m_corsFlag = true;
221     m_corsPreflightFlag = false;
222     performHTTPFetch();
223 }
224
225 void FetchManager::Loader::cleanup()
226 {
227     // Prevent notification
228     m_fetchManager = 0;
229
230     if (m_loader) {
231         m_loader->cancel();
232         m_loader.clear();
233     }
234 }
235
236 void FetchManager::Loader::performBasicFetch()
237 {
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|."
242         m_corsFlag = false;
243         m_corsPreflightFlag = false;
244         performHTTPFetch();
245     } else {
246         // FIXME: implement other protocols.
247         performNetworkError();
248     }
249 }
250
251 void FetchManager::Loader::performNetworkError()
252 {
253     failed();
254 }
255
256 void FetchManager::Loader::performHTTPFetch()
257 {
258     // CORS preflight fetch procedure is implemented inside DocumentThreadableLoader.
259
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));
271     }
272
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.
278
279     // "3. Append `Host`, ..."
280     // FIXME: Implement this when the spec is fixed.
281
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
284     // header list."
285     // We set Origin header in updateRequestForAccessControl() called from
286     // DocumentThreadableLoader::makeCrossOriginAccessRequest
287
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;
296     }
297
298     ThreadableLoaderOptions threadableLoaderOptions;
299     if (m_corsPreflightFlag)
300         threadableLoaderOptions.preflightPolicy = ForcePreflight;
301     if (m_corsFlag)
302         threadableLoaderOptions.crossOriginRequestPolicy = UseAccessControl;
303     else
304         threadableLoaderOptions.crossOriginRequestPolicy = AllowCrossOriginRequests;
305
306
307     m_loader = ThreadableLoader::create(*m_executionContext, this, request, threadableLoaderOptions, resourceLoaderOptions);
308 }
309
310 void FetchManager::Loader::failed()
311 {
312     if (m_failed)
313         return;
314     m_failed = true;
315     ScriptState* state = m_resolver->scriptState();
316     ScriptState::Scope scope(state);
317     m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", state->isolate()));
318     notifyFinished();
319 }
320
321 void FetchManager::Loader::notifyFinished()
322 {
323     if (m_fetchManager)
324         m_fetchManager->onLoaderFinished(this);
325 }
326
327 FetchManager::FetchManager(ExecutionContext* executionContext)
328     : m_executionContext(executionContext)
329 {
330 }
331
332 FetchManager::~FetchManager()
333 {
334     for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_loaders.end(); ++it) {
335         (*it)->cleanup();
336     }
337 }
338
339 ScriptPromise FetchManager::fetch(ScriptState* scriptState, PassRefPtrWillBeRawPtr<FetchRequestData> request)
340 {
341     RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
342     ScriptPromise promise = resolver->promise();
343
344     OwnPtr<Loader> ownLoader(adoptPtr(new Loader(m_executionContext, this, resolver.release(), request)));
345     Loader* loader = m_loaders.add(ownLoader.release()).storedValue->get();
346     loader->start();
347     return promise;
348 }
349
350 void FetchManager::onLoaderFinished(Loader* loader)
351 {
352     m_loaders.remove(loader);
353 }
354
355 } // namespace blink