OLD | NEW |
| (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 | |
OLD | NEW |