Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: Source/modules/serviceworkers/FetchManager.cpp

Issue 795323003: Move Fetch API releted code to modules/fetch. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/frame/csp/ContentSecurityPolicy.h"
16 #include "core/loader/ThreadableLoader.h"
17 #include "core/loader/ThreadableLoaderClient.h"
18 #include "modules/serviceworkers/Body.h"
19 #include "modules/serviceworkers/BodyStreamBuffer.h"
20 #include "modules/serviceworkers/FetchRequestData.h"
21 #include "modules/serviceworkers/Response.h"
22 #include "modules/serviceworkers/ResponseInit.h"
23 #include "platform/network/ResourceRequest.h"
24 #include "platform/weborigin/SecurityOrigin.h"
25 #include "public/platform/WebURLRequest.h"
26 #include "wtf/HashSet.h"
27
28 namespace blink {
29
30 class FetchManager::Loader : public ThreadableLoaderClient {
31 public:
32 Loader(ExecutionContext*, FetchManager*, PassRefPtr<ScriptPromiseResolver>, const FetchRequestData*);
33 ~Loader() override;
34 void didReceiveResponse(unsigned long, const ResourceResponse&, PassOwnPtr<W ebDataConsumerHandle>) override;
35 void didReceiveData(const char*, unsigned) override;
36 void didFinishLoading(unsigned long, double) override;
37 void didFail(const ResourceError&) override;
38 void didFailAccessControlCheck(const ResourceError&) override;
39 void didFailRedirectCheck() override;
40
41 void start();
42 void cleanup();
43
44 private:
45 void performBasicFetch();
46 void performNetworkError(const String& message);
47 void performHTTPFetch();
48 void failed(const String& message);
49 void notifyFinished();
50
51 ExecutionContext* m_executionContext;
52 FetchManager* m_fetchManager;
53 RefPtr<ScriptPromiseResolver> m_resolver;
54 Persistent<FetchRequestData> m_request;
55 Persistent<BodyStreamBuffer> m_responseBuffer;
56 RefPtr<ThreadableLoader> m_loader;
57 bool m_corsFlag;
58 bool m_corsPreflightFlag;
59 bool m_failed;
60 };
61
62 FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* f etchManager, PassRefPtr<ScriptPromiseResolver> resolver, const FetchRequestData* request)
63 : m_executionContext(executionContext)
64 , m_fetchManager(fetchManager)
65 , m_resolver(resolver)
66 , m_request(request->createCopy())
67 , m_corsFlag(false)
68 , m_corsPreflightFlag(false)
69 , m_failed(false)
70 {
71 }
72
73 FetchManager::Loader::~Loader()
74 {
75 if (m_loader)
76 m_loader->cancel();
77 }
78
79 void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceRespo nse& response, PassOwnPtr<WebDataConsumerHandle> handle)
80 {
81 // FIXME: Use |handle|.
82 ASSERT_UNUSED(handle, !handle);
83 m_responseBuffer = new BodyStreamBuffer();
84 FetchResponseData* responseData = FetchResponseData::createWithBuffer(m_resp onseBuffer);
85 responseData->setStatus(response.httpStatusCode());
86 responseData->setStatusMessage(response.httpStatusText());
87 for (auto& it : response.httpHeaderFields())
88 responseData->headerList()->append(it.key, it.value);
89 responseData->setURL(m_request->url());
90 responseData->setContentTypeForBuffer(response.mimeType());
91
92 FetchResponseData* taintedResponse = responseData;
93 switch (m_request->tainting()) {
94 case FetchRequestData::BasicTainting:
95 taintedResponse = responseData->createBasicFilteredResponse();
96 break;
97 case FetchRequestData::CORSTainting:
98 taintedResponse = responseData->createCORSFilteredResponse();
99 break;
100 case FetchRequestData::OpaqueTainting:
101 taintedResponse = responseData->createOpaqueFilteredResponse();
102 break;
103 }
104 m_resolver->resolve(Response::create(m_resolver->executionContext(), tainted Response));
105 m_resolver.clear();
106 }
107
108 void FetchManager::Loader::didReceiveData(const char* data, unsigned size)
109 {
110 m_responseBuffer->write(DOMArrayBuffer::create(data, size));
111 }
112
113 void FetchManager::Loader::didFinishLoading(unsigned long, double)
114 {
115 ASSERT(m_responseBuffer);
116 m_responseBuffer->close();
117 m_responseBuffer.clear();
118 notifyFinished();
119 }
120
121 void FetchManager::Loader::didFail(const ResourceError& error)
122 {
123 failed("Fetch API cannot load " + error.failingURL() + ". " + error.localize dDescription());
124 }
125
126 void FetchManager::Loader::didFailAccessControlCheck(const ResourceError& error)
127 {
128 failed("Fetch API cannot load " + error.failingURL() + ". " + error.localize dDescription());
129 }
130
131 void FetchManager::Loader::didFailRedirectCheck()
132 {
133 failed("Fetch API cannot load " + m_request->url().string() + ". Redirects a re not yet supported.");
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 // We do mixed content checking in ResourceFetcher.
157
158 // "- should fetching |request| be blocked as content security returns
159 // blocked"
160 if (!ContentSecurityPolicy::shouldBypassMainWorld(m_executionContext) && !m_ executionContext->contentSecurityPolicy()->allowConnectToSource(m_request->url() )) {
161 // "A network error."
162 performNetworkError("Refused to connect to '" + m_request->url().elidedS tring() + "' because it violates the document's Content Security Policy.");
163 return;
164 }
165
166 // "- |request|'s url's origin is |request|'s origin and the |CORS flag| is
167 // unset"
168 // "- |request|'s url's scheme is 'data' and |request|'s same-origin data
169 // URL flag is set"
170 // "- |request|'s url's scheme is 'about'"
171 if ((SecurityOrigin::create(m_request->url())->isSameSchemeHostPort(m_reques t->origin().get()) && !m_corsFlag)
172 || (m_request->url().protocolIsData() && m_request->sameOriginDataURLFla g())
173 || (m_request->url().protocolIsAbout())) {
174 // "The result of performing a basic fetch using request."
175 performBasicFetch();
176 return;
177 }
178
179 // "- |request|'s mode is |same-origin|"
180 if (m_request->mode() == WebURLRequest::FetchRequestModeSameOrigin) {
181 // "A network error."
182 performNetworkError("Fetch API cannot load " + m_request->url().string() + ". Request mode is \"same-origin\" but the URL\'s origin is not same as the r equest origin " + m_request->origin()->toString() + ".");
183 return;
184 }
185
186 // "- |request|'s mode is |no CORS|"
187 if (m_request->mode() == WebURLRequest::FetchRequestModeNoCORS) {
188 // "Set |request|'s response tainting to |opaque|."
189 m_request->setResponseTainting(FetchRequestData::OpaqueTainting);
190 // "The result of performing a basic fetch using |request|."
191 performBasicFetch();
192 return;
193 }
194
195 // "- |request|'s url's scheme is not one of 'http' and 'https'"
196 if (!m_request->url().protocolIsInHTTPFamily()) {
197 // "A network error."
198 performNetworkError("Fetch API cannot load " + m_request->url().string() + ". URL scheme must be \"http\" or \"https\" for CORS request.");
199 return;
200 }
201
202 // "- |request|'s mode is |CORS-with-forced-preflight|.
203 // "- |request|'s unsafe request flag is set and either |request|'s method
204 // is not a simple method or a header in |request|'s header list is not a
205 // simple header"
206 if (m_request->mode() == WebURLRequest::FetchRequestModeCORSWithForcedPrefli ght
207 || (m_request->unsafeRequestFlag()
208 && (!FetchUtils::isSimpleMethod(m_request->method())
209 || m_request->headerList()->containsNonSimpleHeader()))) {
210 // "Set |request|'s response tainting to |CORS|."
211 m_request->setResponseTainting(FetchRequestData::CORSTainting);
212 // "The result of performing an HTTP fetch using |request| with the
213 // |CORS flag| and |CORS preflight flag| set."
214 m_corsFlag = true;
215 m_corsPreflightFlag = true;
216 performHTTPFetch();
217 return;
218 }
219
220 // "- Otherwise
221 // Set |request|'s response tainting to |CORS|."
222 m_request->setResponseTainting(FetchRequestData::CORSTainting);
223 // "The result of performing an HTTP fetch using |request| with the
224 // |CORS flag| set."
225 m_corsFlag = true;
226 m_corsPreflightFlag = false;
227 performHTTPFetch();
228 }
229
230 void FetchManager::Loader::cleanup()
231 {
232 // Prevent notification
233 m_fetchManager = 0;
234
235 if (m_loader) {
236 m_loader->cancel();
237 m_loader.clear();
238 }
239 }
240
241 void FetchManager::Loader::performBasicFetch()
242 {
243 // "To perform a basic fetch using |request|, switch on |request|'s url's
244 // scheme, and run the associated steps:"
245 if (m_request->url().protocolIsInHTTPFamily()) {
246 // "Return the result of performing an HTTP fetch using |request|."
247 m_corsFlag = false;
248 m_corsPreflightFlag = false;
249 performHTTPFetch();
250 } else {
251 // FIXME: implement other protocols.
252 performNetworkError("Fetch API cannot load " + m_request->url().string() + ". URL scheme \"" + m_request->url().protocol() + "\" is not supported.");
253 }
254 }
255
256 void FetchManager::Loader::performNetworkError(const String& message)
257 {
258 failed(message);
259 }
260
261 void FetchManager::Loader::performHTTPFetch()
262 {
263 ASSERT(m_request->url().protocolIsInHTTPFamily());
264 // CORS preflight fetch procedure is implemented inside DocumentThreadableLo ader.
265
266 // "1. Let |HTTPRequest| be a copy of |request|, except that |HTTPRequest|'s
267 // body is a tee of |request|'s body."
268 // We use ResourceRequest class for HTTPRequest.
269 // FIXME: Support body.
270 ResourceRequest request(m_request->url());
271 request.setRequestContext(WebURLRequest::RequestContextFetch);
272 request.setHTTPMethod(m_request->method());
273 const Vector<OwnPtr<FetchHeaderList::Header> >& list = m_request->headerList ()->list();
274 for (size_t i = 0; i < list.size(); ++i) {
275 request.addHTTPHeaderField(AtomicString(list[i]->first), AtomicString(li st[i]->second));
276 }
277
278 if (m_request->method() != "GET" && m_request->method() != "HEAD") {
279 RefPtr<BlobDataHandle> blobDataHandle = m_request->blobDataHandle();
280 if (blobDataHandle.get()) {
281 RefPtr<FormData> httpBody(FormData::create());
282 httpBody->appendBlob(blobDataHandle->uuid(), blobDataHandle);
283 request.setHTTPBody(httpBody);
284 }
285 }
286
287 // "2. Append `Referer`/empty byte sequence, if |HTTPRequest|'s |referrer|
288 // is none, and `Referer`/|HTTPRequest|'s referrer, serialized and utf-8
289 // encoded, otherwise, to HTTPRequest's header list.
290 // We set the referrer using workerGlobalScope's URL in
291 // WorkerThreadableLoader.
292
293 // "3. Append `Host`, ..."
294 // FIXME: Implement this when the spec is fixed.
295
296 // "4.If |HTTPRequest|'s force Origin header flag is set, append `Origin`/
297 // |HTTPRequest|'s origin, serialized and utf-8 encoded, to |HTTPRequest|'s
298 // header list."
299 // We set Origin header in updateRequestForAccessControl() called from
300 // DocumentThreadableLoader::makeCrossOriginAccessRequest
301
302 // "5. Let |credentials flag| be set if either |HTTPRequest|'s credentials
303 // mode is |include|, or |HTTPRequest|'s credentials mode is |same-origin|
304 // and the |CORS flag| is unset, and unset otherwise.
305 ResourceLoaderOptions resourceLoaderOptions;
306 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
307 if (m_request->credentials() == WebURLRequest::FetchCredentialsModeInclude
308 || (m_request->credentials() == WebURLRequest::FetchCredentialsModeSameO rigin && !m_corsFlag)) {
309 resourceLoaderOptions.allowCredentials = AllowStoredCredentials;
310 }
311
312 ThreadableLoaderOptions threadableLoaderOptions;
313 threadableLoaderOptions.contentSecurityPolicyEnforcement = ContentSecurityPo licy::shouldBypassMainWorld(m_executionContext) ? DoNotEnforceContentSecurityPol icy : EnforceConnectSrcDirective;
314 if (m_corsPreflightFlag)
315 threadableLoaderOptions.preflightPolicy = ForcePreflight;
316 if (m_corsFlag)
317 threadableLoaderOptions.crossOriginRequestPolicy = UseAccessControl;
318 else
319 threadableLoaderOptions.crossOriginRequestPolicy = AllowCrossOriginReque sts;
320
321 m_loader = ThreadableLoader::create(*m_executionContext, this, request, thre adableLoaderOptions, resourceLoaderOptions);
322 }
323
324 void FetchManager::Loader::failed(const String& message)
325 {
326 if (m_failed)
327 return;
328 m_failed = true;
329 if (m_responseBuffer) {
330 m_responseBuffer->error(DOMException::create(NetworkError, message));
331 m_responseBuffer.clear();
332 } else if (m_resolver) {
333 if (!m_resolver->executionContext() || m_resolver->executionContext()->a ctiveDOMObjectsAreStopped())
334 return;
335 ScriptState* state = m_resolver->scriptState();
336 ScriptState::Scope scope(state);
337 m_resolver->reject(V8ThrowException::createTypeError(state->isolate(), m essage));
338 }
339 notifyFinished();
340 }
341
342 void FetchManager::Loader::notifyFinished()
343 {
344 if (m_fetchManager)
345 m_fetchManager->onLoaderFinished(this);
346 }
347
348 FetchManager::FetchManager(ExecutionContext* executionContext)
349 : m_executionContext(executionContext)
350 , m_isStopped(false)
351 {
352 }
353
354 FetchManager::~FetchManager()
355 {
356 if (!m_isStopped)
357 stop();
358 }
359
360 ScriptPromise FetchManager::fetch(ScriptState* scriptState, const FetchRequestDa ta* request)
361 {
362 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scrip tState);
363 ScriptPromise promise = resolver->promise();
364
365 OwnPtr<Loader> ownLoader(adoptPtr(new Loader(m_executionContext, this, resol ver.release(), request)));
366 Loader* loader = m_loaders.add(ownLoader.release()).storedValue->get();
367 loader->start();
368 return promise;
369 }
370
371 void FetchManager::stop()
372 {
373 ASSERT(!m_isStopped);
374 m_isStopped = true;
375 for (auto& loader : m_loaders) {
376 loader->cleanup();
377 }
378 }
379
380 void FetchManager::onLoaderFinished(Loader* loader)
381 {
382 // We don't use remove here, becuase it may cause recursive deletion.
383 OwnPtr<Loader> p = m_loaders.take(loader);
384 }
385
386 } // namespace blink
OLDNEW
« no previous file with comments | « Source/modules/serviceworkers/FetchManager.h ('k') | Source/modules/serviceworkers/FetchRequestData.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698