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

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: incorporated yhirano's comment 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 blink { 25 namespace blink {
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 performHTTPFetch();
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())
jochen (gone - plz use gerrit) 2014/07/24 14:37:26 protocolIsData()
horo 2014/07/24 15:50:22 Done.
169 || (m_request->url().protocol() == "about")) {
jochen (gone - plz use gerrit) 2014/07/24 14:37:26 protocolIsAbout()
horo 2014/07/24 15:50:22 Done.
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") {
jochen (gone - plz use gerrit) 2014/07/24 14:37:26 protocolIsInHTTPFamily()
horo 2014/07/24 15:50:22 Done.
193 // "A network error."
194 performNetworkError();
195 return;
196 }
197
198 // "- |request|'s mode is |CORS-with-forced-preflight|.
199 // "- |request|'s unsafe request flag is set and either |request|'s method
200 // is not a simple method or a header in |request|'s header list is not a
201 // simple header"
202 if (m_request->mode() == FetchRequestData::CORSWithForcedPreflight
203 || (m_request->unsafeRequestFlag()
204 && (!isSimpleMethod(m_request->method())
205 || m_request->headerList()->containsNonSimpleHeader()))) {
206 // "Set |request|'s response tainting to |CORS|."
207 m_request->setResponseTainting(FetchRequestData::CORSTainting);
208 // "The result of performing an HTTP fetch using |request| with the
209 // |CORS flag| and |CORS preflight flag| set."
210 m_corsFlag = true;
211 m_corsPreflightFlag = true;
212 performHTTPFetch();
213 return;
214 }
215
216 // "- Otherwise
217 // Set |request|'s response tainting to |CORS|."
218 m_request->setResponseTainting(FetchRequestData::CORSTainting);
219 // "The result of performing an HTTP fetch using |request| with the
220 // |CORS flag| set."
221 m_corsFlag = true;
222 m_corsPreflightFlag = false;
223 performHTTPFetch();
123 } 224 }
124 225
125 void FetchManager::Loader::cleanup() 226 void FetchManager::Loader::cleanup()
126 { 227 {
127 // Prevent notification 228 // Prevent notification
128 m_fetchManager = 0; 229 m_fetchManager = 0;
129 230
130 if (m_loader) { 231 if (m_loader) {
131 m_loader->cancel(); 232 m_loader->cancel();
132 m_loader.clear(); 233 m_loader.clear();
133 } 234 }
134 } 235 }
135 236
237 void FetchManager::Loader::performBasicFetch()
238 {
239 // "To perform a basic fetch using |request|, switch on |request|'s url's
240 // scheme, and run the associated steps:"
241 String protocol(m_request->url().protocol());
242 if (protocol == "about") {
243 // FIXME: implement this.
244 performNetworkError();
245 } else if (protocol == "blob") {
246 // FIXME: implement this.
247 performNetworkError();
248 } else if (protocol == "data") {
249 // FIXME: implement this.
250 performNetworkError();
251 } else if (protocol == "file" || protocol == "ftp") {
252 // "For now, unfortunate as it is, file and ftp URLs are left as an
253 // exercise for the reader. When in doubt, return a network error."
254 // FIXME: implement this.
255 performNetworkError();
256 } else if (protocol == "http" || protocol == "https") {
257 // "Return the result of performing an HTTP fetch using |request|."
258 m_corsFlag = false;
259 m_corsPreflightFlag = false;
260 performHTTPFetch();
261 } else {
262 // "Return a network error."
263 performNetworkError();
264 }
265 }
266
267 void FetchManager::Loader::performNetworkError()
268 {
269 failed();
270 }
271
272 void FetchManager::Loader::performHTTPFetch()
273 {
274 // CORS preflight fetch procedure is implemented inside DocumentThreadableLo ader.
275
276 // "1. Let |HTTPRequest| be a copy of |request|, except that |HTTPRequest|'s
277 // body is a tee of |request|'s body."
278 // We use ResourceRequest class for HTTPRequest.
279 // FIXME: Support body.
280 ResourceRequest request(m_request->url());
281 request.setRequestContext(blink::WebURLRequest::RequestContextFetch);
282 request.setDownloadToFile(true);
283 request.setHTTPMethod(m_request->method());
284 const Vector<OwnPtr<FetchHeaderList::Header> >& list = m_request->headerList ()->list();
285 for (size_t i = 0; i < list.size(); ++i) {
286 request.addHTTPHeaderField(AtomicString(list[i]->first), AtomicString(li st[i]->second));
jochen (gone - plz use gerrit) 2014/07/24 14:37:26 will this copy the referrer header? If yes, we nee
horo 2014/07/24 15:50:22 No. There could not be a referrer header. Because
287 }
288
289 // "2. Append `Referer`/empty byte sequence, if |HTTPRequest|'s |referrer|
290 // is none, and `Referer`/|HTTPRequest|'s referrer, serialized and utf-8
291 // encoded, otherwise, to HTTPRequest's header list.
292 // We set the referrer using workerGlobalScope's URL in
293 // WorkerThreadableLoader.
294
295 // "3. Append `Host`, ..."
296 // FIXME: Implement this when the spec is fixed.
297
298 // "4.If |HTTPRequest|'s force Origin header flag is set, append `Origin`/
299 // |HTTPRequest|'s origin, serialized and utf-8 encoded, to |HTTPRequest|'s
300 // header list."
301 // We set Origin header in updateRequestForAccessControl() called from
302 // DocumentThreadableLoader::makeCrossOriginAccessRequest
303
304 // "5. Let |credentials flag| be set if either |HTTPRequest|'s credentials
305 // mode is |include|, or |HTTPRequest|'s credentials mode is |same-origin|
306 // and the |CORS flag| is unset, and unset otherwise.
307 ResourceLoaderOptions resourceLoaderOptions;
308 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
309 if (m_request->credentials() == FetchRequestData::IncludeCredentials
310 || (m_request->credentials() == FetchRequestData::SameOriginCredentials && !m_corsFlag)) {
311 resourceLoaderOptions.allowCredentials = AllowStoredCredentials;
312 }
313
314 ThreadableLoaderOptions threadableLoaderOptions;
315 if (m_corsPreflightFlag)
316 threadableLoaderOptions.preflightPolicy = ForcePreflight;
317 if (m_corsFlag)
318 threadableLoaderOptions.crossOriginRequestPolicy = UseAccessControl;
319 else
320 threadableLoaderOptions.crossOriginRequestPolicy = AllowCrossOriginReque sts;
321
322
323 m_loader = ThreadableLoader::create(*m_executionContext, this, request, thre adableLoaderOptions, resourceLoaderOptions);
324 }
325
136 void FetchManager::Loader::failed() 326 void FetchManager::Loader::failed()
137 { 327 {
138 if (m_failed) 328 if (m_failed)
139 return; 329 return;
140 m_failed = true; 330 m_failed = true;
141 ScriptState* state = m_resolver->scriptState(); 331 ScriptState* state = m_resolver->scriptState();
142 ScriptState::Scope scope(state); 332 ScriptState::Scope scope(state);
143 m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", stat e->isolate())); 333 m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", stat e->isolate()));
144 notifyFinished(); 334 notifyFinished();
145 } 335 }
146 336
147 void FetchManager::Loader::notifyFinished() 337 void FetchManager::Loader::notifyFinished()
148 { 338 {
149 if (m_fetchManager) 339 if (m_fetchManager)
150 m_fetchManager->onLoaderFinished(this); 340 m_fetchManager->onLoaderFinished(this);
151 } 341 }
152 342
153 FetchManager::FetchManager(ExecutionContext* executionContext) 343 FetchManager::FetchManager(ExecutionContext* executionContext)
154 : m_executionContext(executionContext) 344 : m_executionContext(executionContext)
155 { 345 {
156 } 346 }
157 347
158 FetchManager::~FetchManager() 348 FetchManager::~FetchManager()
159 { 349 {
160 for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_load ers.end(); ++it) { 350 for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_load ers.end(); ++it) {
161 (*it)->cleanup(); 351 (*it)->cleanup();
162 } 352 }
163 } 353 }
164 354
165 ScriptPromise FetchManager::fetch(ScriptState* scriptState, PassOwnPtr<ResourceR equest> request) 355 ScriptPromise FetchManager::fetch(ScriptState* scriptState, PassRefPtr<FetchRequ estData> request)
166 { 356 {
167 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scrip tState); 357 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scrip tState);
168 ScriptPromise promise = resolver->promise(); 358 ScriptPromise promise = resolver->promise();
169 359
170 OwnPtr<Loader> loader(adoptPtr(new Loader(m_executionContext, this, resolver .release(), request))); 360 OwnPtr<Loader> ownLoader(adoptPtr(new Loader(m_executionContext, this, resol ver.release(), request)));
171 (*m_loaders.add(loader.release()).storedValue)->start(); 361 Loader* loader = m_loaders.add(ownLoader.release()).storedValue->get();
362 loader->start();
172 return promise; 363 return promise;
173 } 364 }
174 365
175 void FetchManager::onLoaderFinished(Loader* loader) 366 void FetchManager::onLoaderFinished(Loader* loader)
176 { 367 {
177 m_loaders.remove(loader); 368 m_loaders.remove(loader);
178 } 369 }
179 370
180 bool FetchManager::isSimpleMethod(const String& method) 371 bool FetchManager::isSimpleMethod(const String& method)
181 { 372 {
182 // "A simple method is a method that is `GET`, `HEAD`, or `POST`." 373 // "A simple method is a method that is `GET`, `HEAD`, or `POST`."
183 return isOnAccessControlSimpleRequestMethodWhitelist(method); 374 return isOnAccessControlSimpleRequestMethodWhitelist(method);
184 } 375 }
185 376
186 bool FetchManager::isForbiddenMethod(const String& method) 377 bool FetchManager::isForbiddenMethod(const String& method)
187 { 378 {
188 // "A forbidden method is a method that is a byte case-insensitive match for one of `CONNECT`, `TRACE`, and `TRACK`." 379 // "A forbidden method is a method that is a byte case-insensitive match for
380 // one of `CONNECT`, `TRACE`, and `TRACK`."
189 return !XMLHttpRequest::isAllowedHTTPMethod(method); 381 return !XMLHttpRequest::isAllowedHTTPMethod(method);
190 } 382 }
191 383
192 bool FetchManager::isUsefulMethod(const String& method) 384 bool FetchManager::isUsefulMethod(const String& method)
193 { 385 {
194 // "A useful method is a method that is not a forbidden method." 386 // "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`." 387 // "A forbidden method is a method that is a byte case-insensitive match for
388 // one of `CONNECT`, `TRACE`, and `TRACK`."
196 return XMLHttpRequest::isAllowedHTTPMethod(method); 389 return XMLHttpRequest::isAllowedHTTPMethod(method);
197 } 390 }
198 391
199 } // namespace blink 392 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698