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

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

Issue 399543002: [ServiceWorker] Make fetch() method better conformance with the spec. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 6 years, 5 months 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "config.h" 5 #include "config.h"
6 #include "FetchManager.h" 6 #include "FetchManager.h"
7 7
8 #include "bindings/core/v8/ExceptionState.h" 8 #include "bindings/core/v8/ExceptionState.h"
9 #include "bindings/core/v8/ScriptPromiseResolver.h" 9 #include "bindings/core/v8/ScriptPromiseResolver.h"
10 #include "bindings/core/v8/ScriptState.h" 10 #include "bindings/core/v8/ScriptState.h"
11 #include "bindings/core/v8/V8ThrowException.h" 11 #include "bindings/core/v8/V8ThrowException.h"
12 #include "core/dom/ExceptionCode.h" 12 #include "core/dom/ExceptionCode.h"
13 #include "core/fileapi/Blob.h" 13 #include "core/fileapi/Blob.h"
14 #include "core/loader/ThreadableLoader.h" 14 #include "core/loader/ThreadableLoader.h"
15 #include "core/loader/ThreadableLoaderClient.h" 15 #include "core/loader/ThreadableLoaderClient.h"
16 #include "core/xml/XMLHttpRequest.h" 16 #include "core/xml/XMLHttpRequest.h"
17 #include "modules/serviceworkers/FetchRequestData.h"
17 #include "modules/serviceworkers/Response.h" 18 #include "modules/serviceworkers/Response.h"
18 #include "modules/serviceworkers/ResponseInit.h" 19 #include "modules/serviceworkers/ResponseInit.h"
19 #include "platform/network/ResourceRequest.h" 20 #include "platform/network/ResourceRequest.h"
21 #include "platform/weborigin/SecurityOrigin.h"
22 #include "public/platform/WebURLRequest.h"
20 #include "wtf/HashSet.h" 23 #include "wtf/HashSet.h"
21 24
22 namespace WebCore { 25 namespace WebCore {
23 26
24 class FetchManager::Loader : public ThreadableLoaderClient { 27 class FetchManager::Loader : public ThreadableLoaderClient {
25 public: 28 public:
26 Loader(ExecutionContext*, FetchManager*, PassRefPtr<ScriptPromiseResolver>, PassOwnPtr<ResourceRequest>); 29 Loader(ExecutionContext*, FetchManager*, PassRefPtr<ScriptPromiseResolver>, PassRefPtr<FetchRequestData>);
27 ~Loader(); 30 ~Loader();
28 virtual void didReceiveResponse(unsigned long, const ResourceResponse&); 31 virtual void didReceiveResponse(unsigned long, const ResourceResponse&);
29 virtual void didFinishLoading(unsigned long, double); 32 virtual void didFinishLoading(unsigned long, double);
30 virtual void didFail(const ResourceError&); 33 virtual void didFail(const ResourceError&);
31 virtual void didFailAccessControlCheck(const ResourceError&); 34 virtual void didFailAccessControlCheck(const ResourceError&);
32 virtual void didFailRedirectCheck(); 35 virtual void didFailRedirectCheck();
33 virtual void didDownloadData(int); 36 virtual void didDownloadData(int);
34 37
35 void start(); 38 void start();
36 void cleanup(); 39 void cleanup();
37 40
38 private: 41 private:
42 void performBasicFetch();
43 void performNetworkError();
44 void performeHTTPFetch();
39 void failed(); 45 void failed();
40 void notifyFinished(); 46 void notifyFinished();
41 47
42 ExecutionContext* m_executionContext; 48 ExecutionContext* m_executionContext;
43 FetchManager* m_fetchManager; 49 FetchManager* m_fetchManager;
44 RefPtr<ScriptPromiseResolver> m_resolver; 50 RefPtr<ScriptPromiseResolver> m_resolver;
45 OwnPtr<ResourceRequest> m_request; 51 RefPtr<FetchRequestData> m_request;
46 RefPtr<ThreadableLoader> m_loader; 52 RefPtr<ThreadableLoader> m_loader;
47 ResourceResponse m_response; 53 ResourceResponse m_response;
48 long long m_downloadedBlobLength; 54 long long m_downloadedBlobLength;
55 bool m_corsFlag;
56 bool m_corsPreflightFlag;
49 bool m_failed; 57 bool m_failed;
50 }; 58 };
51 59
52 FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* f etchManager, PassRefPtr<ScriptPromiseResolver> resolver, PassOwnPtr<ResourceRequ est> request) 60 FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* f etchManager, PassRefPtr<ScriptPromiseResolver> resolver, PassRefPtr<FetchRequest Data> request)
53 : m_executionContext(executionContext) 61 : m_executionContext(executionContext)
54 , m_fetchManager(fetchManager) 62 , m_fetchManager(fetchManager)
55 , m_resolver(resolver) 63 , m_resolver(resolver)
56 , m_request(request) 64 , m_request(request->createCopy())
57 , m_downloadedBlobLength(0) 65 , m_downloadedBlobLength(0)
66 , m_corsFlag(false)
67 , m_corsPreflightFlag(false)
58 , m_failed(false) 68 , m_failed(false)
59 { 69 {
60 } 70 }
61 71
62 FetchManager::Loader::~Loader() 72 FetchManager::Loader::~Loader()
63 { 73 {
64 if (m_loader) 74 if (m_loader)
65 m_loader->cancel(); 75 m_loader->cancel();
66 } 76 }
67 77
68 void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceRespo nse& response) 78 void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceRespo nse& response)
69 { 79 {
70 m_response = response; 80 m_response = response;
71 } 81 }
72 82
73 void FetchManager::Loader::didFinishLoading(unsigned long, double) 83 void FetchManager::Loader::didFinishLoading(unsigned long, double)
74 { 84 {
75 OwnPtr<BlobData> blobData = BlobData::create(); 85 OwnPtr<BlobData> blobData = BlobData::create();
76 String filePath = m_response.downloadedFilePath(); 86 String filePath = m_response.downloadedFilePath();
77 if (!filePath.isEmpty() && m_downloadedBlobLength) { 87 if (!filePath.isEmpty() && m_downloadedBlobLength) {
78 blobData->appendFile(filePath); 88 blobData->appendFile(filePath);
79 blobData->setContentType(m_response.mimeType()); 89 blobData->setContentType(m_response.mimeType());
80 } 90 }
81 ResponseInit responseInit; 91 RefPtr<FetchResponseData> response(FetchResponseData::create());
82 // FIXME: We may have to filter the status when we support CORS. 92 response->setStatus(m_response.httpStatusCode());
83 // http://fetch.spec.whatwg.org/#concept-filtered-response-opaque 93 response->setStatusMessage(m_response.httpStatusText());
84 responseInit.status = m_response.httpStatusCode(); 94 HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
85 responseInit.statusText = m_response.httpStatusText(); 95 for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin( ); it != end; ++it) {
86 // FIXME: fill options. 96 response->headerList()->append(it->key, it->value);
87 RefPtrWillBeRawPtr<Blob> blob = Blob::create(BlobDataHandle::create(blobData .release(), m_downloadedBlobLength)); 97 }
88 // FIXME: Handle response status correctly. 98 response->setBlobDataHandle(BlobDataHandle::create(blobData.release(), m_dow nloadedBlobLength));
89 NonThrowableExceptionState exceptionState; 99 response->setURL(m_request->url());
90 m_resolver->resolve(Response::create(blob.get(), responseInit, exceptionStat e)); 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()));
91 notifyFinished(); 113 notifyFinished();
92 } 114 }
93 115
94 void FetchManager::Loader::didFail(const ResourceError& error) 116 void FetchManager::Loader::didFail(const ResourceError& error)
95 { 117 {
96 failed(); 118 failed();
97 } 119 }
98 120
99 void FetchManager::Loader::didFailAccessControlCheck(const ResourceError& error) 121 void FetchManager::Loader::didFailAccessControlCheck(const ResourceError& error)
100 { 122 {
101 failed(); 123 failed();
102 } 124 }
103 125
104 void FetchManager::Loader::didFailRedirectCheck() 126 void FetchManager::Loader::didFailRedirectCheck()
105 { 127 {
106 failed(); 128 failed();
107 } 129 }
108 130
109 void FetchManager::Loader::didDownloadData(int dataLength) 131 void FetchManager::Loader::didDownloadData(int dataLength)
110 { 132 {
111 m_downloadedBlobLength += dataLength; 133 m_downloadedBlobLength += dataLength;
112 } 134 }
113 135
114 void FetchManager::Loader::start() 136 void FetchManager::Loader::start()
115 { 137 {
116 m_request->setDownloadToFile(true); 138
117 ThreadableLoaderOptions options; 139 // "1. If |request|'s url contains a Known HSTS Host, modify it perthe
118 // FIXME: Fill options. 140 // requirements of the 'URI [sic] Loading and Port Mapping' chapter of HTTP
119 ResourceLoaderOptions resourceLoaderOptions; 141 // Strict Transport Security."
120 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; 142 // FIXME: Implement this.
121 // FIXME: Fill resourceLoaderOptions. 143
122 m_loader = ThreadableLoader::create(*m_executionContext, this, *m_request, o ptions, resourceLoaderOptions); 144 // "2. If |request|'s referrer is not none, set |request|'s referrer to the
145 // result of invoking determine |request|'s referrer."
146 // We set the referrer using workerGlobalScope's URL in
147 // WorkerThreadableLoader.
148
149 // "3. If |request|'s synchronous flag is unset and fetch is not invoked
150 // recursively, run the remaining steps asynchronously."
151 // We don't support synchronous flag.
152
153 // "4. Let response be the value corresponding to the first matching
154 // statement:"
155
156 // "- should fetching |request| be blocked as mixed content returns blocked
157 // - should fetching |request| be blocked as content security returns
158 // blocked
159 // A network error."
160 // We do mixed content checking and CSP checking in ResourceFetcher.
161
162 // "- |request|'s url's origin is |request|'s origin and the |CORS flag| is
163 // unset"
164 // "- |request|'s url's scheme is 'data' and |request|'s same-origin data
165 // URL flag is set"
166 // "- |request|'s url's scheme is 'about'"
167 if ((SecurityOrigin::create(m_request->url())->isSameSchemeHostPort(m_reques t->origin().get()) && !m_corsFlag)
168 || (m_request->url().protocol() == "data" && m_request->sameOriginDataUR LFlag())
169 || (m_request->url().protocol() == "about")) {
170 // "The result of performing a basic fetch using request."
171 performBasicFetch();
172 return;
173 }
174
175 // "- |request|'s mode is |same-origin|"
176 if (m_request->mode() == FetchRequestData::SameOriginMode) {
177 // "A network error."
178 performNetworkError();
179 return;
180 }
181
182 // "- |request|'s mode is |no CORS|"
183 if (m_request->mode() == FetchRequestData::NoCORSMode) {
184 // "Set |request|'s response tainting to |opaque|."
185 m_request->setResponseTainting(FetchRequestData::OpaqueTainting);
186 // "The result of performing a basic fetch using |request|."
187 performBasicFetch();
188 return;
189 }
190
191 // "- |request|'s url's scheme is not one of 'http' and 'https'"
192 if (m_request->url().protocol() != "http" && m_request->url().protocol() != "https") {
193 // "A network error."
194 performNetworkError();
195 return;
196 }
197
198 // "- |request|'s mode is |CORS-with-forced-preflight|.
199 if (m_request->mode() == FetchRequestData::CORSWithForcedPreflight) {
200 // "Set |request|'s response tainting to |CORS|."
201 m_request->setResponseTainting(FetchRequestData::CORSTainting);
202 // "The result of performing an HTTP fetch using |request| with the
203 // |CORS flag| and |CORS preflight flag| set."
204 m_corsFlag = true;
205 m_corsPreflightFlag = true;
206 performeHTTPFetch();
207 return;
208 }
209
210 // "- |request|'s unsafe request flag is set and either |request|'s method
211 // is not a simple method or a header in |request|'s header list is not a
212 // simple header"
213 if (m_request->unsafeRequestFlag()
yhirano 2014/07/16 13:12:27 Can you merge this block into the above one?
horo 2014/07/18 09:12:18 Done.
214 && (!isSimpleMethod(m_request->method())
215 || m_request->headerList()->containsNonSimpleHeader())) {
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| and |CORS preflight flag| set."
220 m_corsFlag = true;
221 m_corsPreflightFlag = true;
222 performeHTTPFetch();
223 return;
224 }
225
226 // "- Otherwise
227 // Set |request|'s response tainting to |CORS|."
228 m_request->setResponseTainting(FetchRequestData::CORSTainting);
229 // "The result of performing an HTTP fetch using |request| with the
230 // |CORS flag| set."
231 m_corsFlag = true;
232 m_corsPreflightFlag = false;
233 performeHTTPFetch();
123 } 234 }
124 235
125 void FetchManager::Loader::cleanup() 236 void FetchManager::Loader::cleanup()
126 { 237 {
127 // Prevent notification 238 // Prevent notification
128 m_fetchManager = 0; 239 m_fetchManager = 0;
129 240
130 if (m_loader) { 241 if (m_loader) {
131 m_loader->cancel(); 242 m_loader->cancel();
132 m_loader.clear(); 243 m_loader.clear();
133 } 244 }
134 } 245 }
135 246
247 void FetchManager::Loader::performBasicFetch()
248 {
249 // "To perform a basic fetch using |request|, switch on |request|'s url's
250 // scheme, and run the associated steps:"
251 String protocol(m_request->url().protocol());
252 if (protocol == "about") {
253 // FIXME: implement this.
254 performNetworkError();
255 } else if (protocol == "blob") {
256 // FIXME: implement this.
257 performNetworkError();
258 } else if (protocol == "data") {
259 // FIXME: implement this.
260 performNetworkError();
261 } else if (protocol == "file" || protocol == "ftp") {
262 // "For now, unfortunate as it is, file and ftp URLs are left as an
263 // exercise for the reader. When in doubt, return a network error."
264 // FIXME: implement this.
265 performNetworkError();
266 } else if (protocol == "http" || protocol == "https") {
267 // "Return the result of performing an HTTP fetch using |request|."
268 m_corsFlag = false;
269 m_corsPreflightFlag = false;
270 performeHTTPFetch();
271 } else {
272 // "Return a network error."
273 performNetworkError();
274 }
275 }
276
277 void FetchManager::Loader::performNetworkError()
278 {
279 failed();
280 }
281
282 void FetchManager::Loader::performeHTTPFetch()
283 {
284 // "1. Let |HTTPRequest| be a copy of |request|, except that |HTTPRequest|'s
yhirano 2014/07/16 13:12:27 This is HTTP network and cache fetch. Shouldn't it
horo 2014/07/18 09:12:18 CORS preflight fetch procedure is implemented insi
285 // body is a tee of |request|'s body."
286 // We use ResourceRequest class for HTTPRequest.
287 // FIXME: Support body.
288 ResourceRequest request(m_request->url());
289 request.setRequestContext(blink::WebURLRequest::RequestContextFetch);
290 request.setDownloadToFile(true);
291 request.setHTTPMethod(m_request->method());
292 const Vector<OwnPtr<FetchHeaderList::Header> >& list = m_request->headerList ()->list();
293 for (size_t i = 0; i < list.size(); ++i) {
294 request.addHTTPHeaderField(AtomicString(list[i]->first), AtomicString(li st[i]->second));
295 }
296
297 // "2. Append `Referer`/empty byte sequence, if |HTTPRequest|'s |referrer|
298 // is none, and `Referer`/|HTTPRequest|'s referrer, serialized and utf-8
299 // encoded, otherwise, to HTTPRequest's header list.
300 // We set the referrer using workerGlobalScope's URL in
301 // WorkerThreadableLoader.
302
303 // "3. Append `Host`, ..."
304 // FIXME: Implement this when the spec is fixed.
305
306 // "4.If |HTTPRequest|'s force Origin header flag is set, append `Origin`/
307 // |HTTPRequest|'s origin, serialized and utf-8 encoded, to |HTTPRequest|'s
308 // header list."
309 // We set Origin header in updateRequestForAccessControl() called from
310 // DocumentThreadableLoader::makeCrossOriginAccessRequest
311
312 // "5. Let |credentials flag| be set if either |HTTPRequest|'s credentials
313 // mode is |include|, or |HTTPRequest|'s credentials mode is |same-origin|
314 // and the |CORS flag| is unset, and unset otherwise.
315 ResourceLoaderOptions resourceLoaderOptions;
316 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
317 if (m_request->credentials() == FetchRequestData::IncludeCredentials
318 || (m_request->credentials() == FetchRequestData::SameOriginCredentials && !m_corsFlag)) {
319 resourceLoaderOptions.allowCredentials = AllowStoredCredentials;
320 }
321
322 ThreadableLoaderOptions threadableLoaderOptions;
323 if (m_corsPreflightFlag)
324 threadableLoaderOptions.preflightPolicy = ForcePreflight;
325 if (m_corsFlag)
326 threadableLoaderOptions.crossOriginRequestPolicy = UseAccessControl;
327 else
328 threadableLoaderOptions.crossOriginRequestPolicy = AllowCrossOriginReque sts;
329
330
331 m_loader = ThreadableLoader::create(*m_executionContext, this, request, thre adableLoaderOptions, resourceLoaderOptions);
332 }
333
136 void FetchManager::Loader::failed() 334 void FetchManager::Loader::failed()
137 { 335 {
138 if (m_failed) 336 if (m_failed)
139 return; 337 return;
140 m_failed = true; 338 m_failed = true;
141 ScriptState* state = m_resolver->scriptState(); 339 ScriptState* state = m_resolver->scriptState();
142 ScriptState::Scope scope(state); 340 ScriptState::Scope scope(state);
143 m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", stat e->isolate())); 341 m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", stat e->isolate()));
144 notifyFinished(); 342 notifyFinished();
145 } 343 }
146 344
147 void FetchManager::Loader::notifyFinished() 345 void FetchManager::Loader::notifyFinished()
148 { 346 {
149 if (m_fetchManager) 347 if (m_fetchManager)
150 m_fetchManager->onLoaderFinished(this); 348 m_fetchManager->onLoaderFinished(this);
151 } 349 }
152 350
153 FetchManager::FetchManager(ExecutionContext* executionContext) 351 FetchManager::FetchManager(ExecutionContext* executionContext)
154 : m_executionContext(executionContext) 352 : m_executionContext(executionContext)
155 { 353 {
156 } 354 }
157 355
158 FetchManager::~FetchManager() 356 FetchManager::~FetchManager()
159 { 357 {
160 for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_load ers.end(); ++it) { 358 for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_load ers.end(); ++it) {
161 (*it)->cleanup(); 359 (*it)->cleanup();
162 } 360 }
163 } 361 }
164 362
165 ScriptPromise FetchManager::fetch(ScriptState* scriptState, PassOwnPtr<ResourceR equest> request) 363 ScriptPromise FetchManager::fetch(ScriptState* scriptState, PassRefPtr<FetchRequ estData> request)
166 { 364 {
167 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scrip tState); 365 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scrip tState);
168 ScriptPromise promise = resolver->promise(); 366 ScriptPromise promise = resolver->promise();
169 367
170 OwnPtr<Loader> loader(adoptPtr(new Loader(m_executionContext, this, resolver .release(), request))); 368 OwnPtr<Loader> ownLoader(adoptPtr(new Loader(m_executionContext, this, resol ver.release(), request)));
171 (*m_loaders.add(loader.release()).storedValue)->start(); 369 Loader* loader = m_loaders.add(ownLoader.release()).storedValue->get();
370 loader->start();
172 return promise; 371 return promise;
173 } 372 }
174 373
175 void FetchManager::onLoaderFinished(Loader* loader) 374 void FetchManager::onLoaderFinished(Loader* loader)
176 { 375 {
177 m_loaders.remove(loader); 376 m_loaders.remove(loader);
178 } 377 }
179 378
180 bool FetchManager::isSimpleMethod(const String& method) 379 bool FetchManager::isSimpleMethod(const String& method)
181 { 380 {
182 // "A simple method is a method that is `GET`, `HEAD`, or `POST`." 381 // "A simple method is a method that is `GET`, `HEAD`, or `POST`."
183 return isOnAccessControlSimpleRequestMethodWhitelist(method); 382 return isOnAccessControlSimpleRequestMethodWhitelist(method);
184 } 383 }
185 384
186 bool FetchManager::isForbiddenMethod(const String& method) 385 bool FetchManager::isForbiddenMethod(const String& method)
187 { 386 {
188 // "A forbidden method is a method that is a byte case-insensitive match for one of `CONNECT`, `TRACE`, and `TRACK`." 387 // "A forbidden method is a method that is a byte case-insensitive match for
388 // one of `CONNECT`, `TRACE`, and `TRACK`."
189 return !XMLHttpRequest::isAllowedHTTPMethod(method); 389 return !XMLHttpRequest::isAllowedHTTPMethod(method);
190 } 390 }
191 391
192 bool FetchManager::isUsefulMethod(const String& method) 392 bool FetchManager::isUsefulMethod(const String& method)
193 { 393 {
194 // "A useful method is a method that is not a forbidden method." 394 // "A useful method is a method that is not a forbidden method."
195 // "A forbidden method is a method that is a byte case-insensitive match for one of `CONNECT`, `TRACE`, and `TRACK`." 395 // "A forbidden method is a method that is a byte case-insensitive match for
396 // one of `CONNECT`, `TRACE`, and `TRACK`."
196 return XMLHttpRequest::isAllowedHTTPMethod(method); 397 return XMLHttpRequest::isAllowedHTTPMethod(method);
197 } 398 }
198 399
199 } // namespace WebCore 400 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698