| 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 |