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

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 falken'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 // "1. If |request|'s url contains a Known HSTS Host, modify it per the
117 ThreadableLoaderOptions options; 139 // requirements of the 'URI [sic] Loading and Port Mapping' chapter of HTTP
118 // FIXME: Fill options. 140 // Strict Transport Security."
119 ResourceLoaderOptions resourceLoaderOptions; 141 // FIXME: Implement this.
120 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; 142
121 // FIXME: Fill resourceLoaderOptions. 143 // "2. If |request|'s referrer is not none, set |request|'s referrer to the
122 m_loader = ThreadableLoader::create(*m_executionContext, this, *m_request, o ptions, resourceLoaderOptions); 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_reques t->origin().get()) && !m_corsFlag)
167 || (m_request->url().protocolIsData() && m_request->sameOriginDataURLFla g())
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 && (!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();
123 } 223 }
124 224
125 void FetchManager::Loader::cleanup() 225 void FetchManager::Loader::cleanup()
126 { 226 {
127 // Prevent notification 227 // Prevent notification
128 m_fetchManager = 0; 228 m_fetchManager = 0;
129 229
130 if (m_loader) { 230 if (m_loader) {
131 m_loader->cancel(); 231 m_loader->cancel();
132 m_loader.clear(); 232 m_loader.clear();
133 } 233 }
134 } 234 }
135 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 DocumentThreadableLo ader.
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(blink::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(li st[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 = AllowCrossOriginReque sts;
305
306
307 m_loader = ThreadableLoader::create(*m_executionContext, this, request, thre adableLoaderOptions, resourceLoaderOptions);
308 }
309
136 void FetchManager::Loader::failed() 310 void FetchManager::Loader::failed()
137 { 311 {
138 if (m_failed) 312 if (m_failed)
139 return; 313 return;
140 m_failed = true; 314 m_failed = true;
141 ScriptState* state = m_resolver->scriptState(); 315 ScriptState* state = m_resolver->scriptState();
142 ScriptState::Scope scope(state); 316 ScriptState::Scope scope(state);
143 m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", stat e->isolate())); 317 m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", stat e->isolate()));
144 notifyFinished(); 318 notifyFinished();
145 } 319 }
146 320
147 void FetchManager::Loader::notifyFinished() 321 void FetchManager::Loader::notifyFinished()
148 { 322 {
149 if (m_fetchManager) 323 if (m_fetchManager)
150 m_fetchManager->onLoaderFinished(this); 324 m_fetchManager->onLoaderFinished(this);
151 } 325 }
152 326
153 FetchManager::FetchManager(ExecutionContext* executionContext) 327 FetchManager::FetchManager(ExecutionContext* executionContext)
154 : m_executionContext(executionContext) 328 : m_executionContext(executionContext)
155 { 329 {
156 } 330 }
157 331
158 FetchManager::~FetchManager() 332 FetchManager::~FetchManager()
159 { 333 {
160 for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_load ers.end(); ++it) { 334 for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_load ers.end(); ++it) {
161 (*it)->cleanup(); 335 (*it)->cleanup();
162 } 336 }
163 } 337 }
164 338
165 ScriptPromise FetchManager::fetch(ScriptState* scriptState, PassOwnPtr<ResourceR equest> request) 339 ScriptPromise FetchManager::fetch(ScriptState* scriptState, PassRefPtr<FetchRequ estData> request)
166 { 340 {
167 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scrip tState); 341 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scrip tState);
168 ScriptPromise promise = resolver->promise(); 342 ScriptPromise promise = resolver->promise();
169 343
170 OwnPtr<Loader> loader(adoptPtr(new Loader(m_executionContext, this, resolver .release(), request))); 344 OwnPtr<Loader> ownLoader(adoptPtr(new Loader(m_executionContext, this, resol ver.release(), request)));
171 (*m_loaders.add(loader.release()).storedValue)->start(); 345 Loader* loader = m_loaders.add(ownLoader.release()).storedValue->get();
346 loader->start();
172 return promise; 347 return promise;
173 } 348 }
174 349
175 void FetchManager::onLoaderFinished(Loader* loader) 350 void FetchManager::onLoaderFinished(Loader* loader)
176 { 351 {
177 m_loaders.remove(loader); 352 m_loaders.remove(loader);
178 } 353 }
179 354
180 bool FetchManager::isSimpleMethod(const String& method) 355 bool FetchManager::isSimpleMethod(const String& method)
181 { 356 {
182 // "A simple method is a method that is `GET`, `HEAD`, or `POST`." 357 // "A simple method is a method that is `GET`, `HEAD`, or `POST`."
183 return isOnAccessControlSimpleRequestMethodWhitelist(method); 358 return isOnAccessControlSimpleRequestMethodWhitelist(method);
184 } 359 }
185 360
186 bool FetchManager::isForbiddenMethod(const String& method) 361 bool FetchManager::isForbiddenMethod(const String& method)
187 { 362 {
188 // "A forbidden method is a method that is a byte case-insensitive match for one of `CONNECT`, `TRACE`, and `TRACK`." 363 // "A forbidden method is a method that is a byte case-insensitive match for
364 // one of `CONNECT`, `TRACE`, and `TRACK`."
189 return !XMLHttpRequest::isAllowedHTTPMethod(method); 365 return !XMLHttpRequest::isAllowedHTTPMethod(method);
190 } 366 }
191 367
192 bool FetchManager::isUsefulMethod(const String& method) 368 bool FetchManager::isUsefulMethod(const String& method)
193 { 369 {
194 // "A useful method is a method that is not a forbidden method." 370 // "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`." 371 // "A forbidden method is a method that is a byte case-insensitive match for
372 // one of `CONNECT`, `TRACE`, and `TRACK`."
196 return XMLHttpRequest::isAllowedHTTPMethod(method); 373 return XMLHttpRequest::isAllowedHTTPMethod(method);
197 } 374 }
198 375
199 } // namespace blink 376 } // 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