ContentDispositionType contentDispositionType(const String& contentDisposition) { if (contentDisposition.isEmpty()) return ContentDispositionNone; Vector<String> parameters; contentDisposition.split(';', parameters); String dispositionType = parameters[0]; dispositionType.stripWhiteSpace(); if (equalIgnoringCase(dispositionType, "inline")) return ContentDispositionInline; // Some broken sites just send bogus headers like // // Content-Disposition: ; filename="file" // Content-Disposition: filename="file" // Content-Disposition: name="file" // // without a disposition token... screen those out. if (!isValidHTTPToken(dispositionType)) return ContentDispositionNone; // We have a content-disposition of "attachment" or unknown. // RFC 2183, section 2.8 says that an unknown disposition // value should be treated as "attachment" return ContentDispositionAttachment; }
bool FetchHeaders::has(const String& name, ExceptionCode& ec) const { if (!isValidHTTPToken(name)) { ec = TypeError; return false; } return m_headers.contains(name); }
String FetchHeaders::get(const String& name, ExceptionCode& ec) const { if (!isValidHTTPToken(name)) { ec = TypeError; return String(); } return m_headers.get(name); }
void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec) { internalAbort(); State previousState = m_state; m_state = UNSENT; m_error = false; m_uploadComplete = false; // clear stuff from possible previous load clearResponse(); clearRequest(); ASSERT(m_state == UNSENT); if (!isValidHTTPToken(method)) { ec = SYNTAX_ERR; return; } if (!isAllowedHTTPMethod(method)) { ec = SECURITY_ERR; return; } if (!scriptExecutionContext()->contentSecurityPolicy()->allowConnectFromSource(url)) { // FIXME: Should this be throwing an exception? ec = SECURITY_ERR; return; } // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated // attempt to discourage synchronous XHR use. responseType is one such piece of functionality. // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols // such as file: and data: still make sense to allow. if (!async && scriptExecutionContext()->isDocument() && url.protocolIsInHTTPFamily() && m_responseTypeCode != ResponseTypeDefault) { logConsoleError(scriptExecutionContext(), "Synchronous HTTP(S) requests made from the window context cannot have XMLHttpRequest.responseType set."); ec = INVALID_ACCESS_ERR; return; } m_method = uppercaseKnownHTTPMethod(method); m_url = url; m_async = async; ASSERT(!m_loader); // Check previous state to avoid dispatching readyState event // when calling open several times in a row. if (previousState != OPENED) changeState(OPENED); else m_state = OPENED; }
static bool setMethod(ResourceRequest& request, const String& initMethod) { if (!isValidHTTPToken(initMethod)) return false; String method = initMethod.convertToASCIIUppercase(); if (method == "CONNECT" || method == "TRACE" || method == "TRACK") return false; request.setHTTPMethod((method == "DELETE" || method == "GET" || method == "HEAD" || method == "OPTIONS" || method == "POST" || method == "PUT") ? method : initMethod); return true; }
void AssociatedURLLoader::loadAsynchronously(const WebURLRequest& request, WebURLLoaderClient* client) { ASSERT(!m_loader); ASSERT(!m_client); m_client = client; ASSERT(m_client); bool allowLoad = true; WebURLRequest newRequest(request); if (m_options.untrustedHTTP) { WebString method = newRequest.httpMethod(); allowLoad = isValidHTTPToken(method) && FetchUtils::isUsefulMethod(method); if (allowLoad) { newRequest.setHTTPMethod(FetchUtils::normalizeMethod(method)); HTTPRequestHeaderValidator validator; newRequest.visitHTTPHeaderFields(&validator); allowLoad = validator.isSafe(); } } m_clientAdapter = ClientAdapter::create(this, m_client, m_options); if (allowLoad) { ThreadableLoaderOptions options; options.preflightPolicy = static_cast<PreflightPolicy>(m_options.preflightPolicy); options.crossOriginRequestPolicy = static_cast<CrossOriginRequestPolicy>(m_options.crossOriginRequestPolicy); ResourceLoaderOptions resourceLoaderOptions; resourceLoaderOptions.allowCredentials = m_options.allowCredentials ? AllowStoredCredentials : DoNotAllowStoredCredentials; resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; const ResourceRequest& webcoreRequest = newRequest.toResourceRequest(); if (webcoreRequest.requestContext() == WebURLRequest::RequestContextUnspecified) { // FIXME: We load URLs without setting a TargetType (and therefore a request context) in several // places in content/ (P2PPortAllocatorSession::AllocateLegacyRelaySession, for example). Remove // this once those places are patched up. newRequest.setRequestContext(WebURLRequest::RequestContextInternal); } Document* webcoreDocument = m_frameImpl->frame()->document(); ASSERT(webcoreDocument); m_loader = DocumentThreadableLoader::create(*webcoreDocument, m_clientAdapter.get(), webcoreRequest, options, resourceLoaderOptions); } if (!m_loader) { // FIXME: return meaningful error codes. m_clientAdapter->setDelayedError(ResourceError()); } m_clientAdapter->enableErrorNotifications(); }
void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec) { internalAbort(); State previousState = m_state; m_state = UNSENT; m_error = false; m_responseTypeCode = ResponseTypeDefault; m_uploadComplete = false; // clear stuff from possible previous load clearResponse(); clearRequest(); ASSERT(m_state == UNSENT); if (!isValidHTTPToken(method)) { ec = SYNTAX_ERR; return; } if (!isAllowedHTTPMethod(method)) { ec = SECURITY_ERR; return; } if (!scriptExecutionContext()->contentSecurityPolicy()->allowConnectFromSource(url)) { // FIXME: Should this be throwing an exception? ec = SECURITY_ERR; return; } m_method = uppercaseKnownHTTPMethod(method); m_url = url; m_async = async; ASSERT(!m_loader); // Check previous state to avoid dispatching readyState event // when calling open several times in a row. if (previousState != OPENED) changeState(OPENED); else m_state = OPENED; }
static bool canWriteHeader(const String& name, const String& value, FetchHeaders::Guard guard, ExceptionCode& ec) { if (!isValidHTTPToken(name) || !isValidHTTPHeaderValue(value)) { ec = TypeError; return false; } if (guard == FetchHeaders::Guard::Immutable) { ec = TypeError; return false; } if (guard == FetchHeaders::Guard::Request && isForbiddenHeaderName(name)) return false; if (guard == FetchHeaders::Guard::RequestNoCors && !isSimpleHeader(name, value)) return false; if (guard == FetchHeaders::Guard::Response && isForbiddenResponseHeaderName(name)) return false; return true; }
void AssociatedURLLoader::loadAsynchronously(const WebURLRequest& request, WebURLLoaderClient* client) { ASSERT(!m_client); m_client = client; ASSERT(m_client); bool allowLoad = true; WebURLRequest newRequest(request); if (m_options.untrustedHTTP) { WebString method = newRequest.httpMethod(); allowLoad = isValidHTTPToken(method) && XMLHttpRequest::isAllowedHTTPMethod(method); if (allowLoad) { newRequest.setHTTPMethod(XMLHttpRequest::uppercaseKnownHTTPMethod(method)); HTTPRequestHeaderValidator validator; newRequest.visitHTTPHeaderFields(&validator); allowLoad = validator.isSafe(); } } m_clientAdapter = ClientAdapter::create(this, m_client, m_options); if (allowLoad) { ThreadableLoaderOptions options; options.sendLoadCallbacks = SendCallbacks; // Always send callbacks. options.sniffContent = m_options.sniffContent ? SniffContent : DoNotSniffContent; options.allowCredentials = m_options.allowCredentials ? AllowStoredCredentials : DoNotAllowStoredCredentials; options.preflightPolicy = m_options.forcePreflight ? ForcePreflight : ConsiderPreflight; options.crossOriginRequestPolicy = static_cast<WebCore::CrossOriginRequestPolicy>(m_options.crossOriginRequestPolicy); options.dataBufferingPolicy = DoNotBufferData; const ResourceRequest& webcoreRequest = newRequest.toResourceRequest(); Document* webcoreDocument = m_frameImpl->frame()->document(); m_loader = DocumentThreadableLoader::create(webcoreDocument, m_clientAdapter.get(), webcoreRequest, options); } else { // FIXME: return meaningful error codes. m_clientAdapter->setDelayedError(ResourceError()); } m_clientAdapter->enableErrorNotifications(); }
void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec) { internalAbort(); State previousState = m_state; m_state = UNSENT; m_error = false; m_uploadComplete = false; // clear stuff from possible previous load clearResponse(); clearRequest(); ASSERT(m_state == UNSENT); if (!isValidHTTPToken(method)) { ec = SYNTAX_ERR; return; } if (!isAllowedHTTPMethod(method)) { ec = SECURITY_ERR; return; } // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. bool shouldBypassMainWorldContentSecurityPolicy = false; if (scriptExecutionContext()->isDocument()) { Document* document = static_cast<Document*>(scriptExecutionContext()); shouldBypassMainWorldContentSecurityPolicy = document->frame()->script()->shouldBypassMainWorldContentSecurityPolicy(); } if (!shouldBypassMainWorldContentSecurityPolicy && !scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(url)) { // FIXME: Should this be throwing an exception? ec = SECURITY_ERR; return; } if (!async && scriptExecutionContext()->isDocument()) { if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) { logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests are disabled for this page."); ec = INVALID_ACCESS_ERR; return; } // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated // attempt to discourage synchronous XHR use. responseType is one such piece of functionality. // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols // such as file: and data: still make sense to allow. if (url.protocolIsInHTTPFamily() && m_responseTypeCode != ResponseTypeDefault) { logConsoleError(scriptExecutionContext(), "Synchronous HTTP(S) requests made from the window context cannot have XMLHttpRequest.responseType set."); ec = INVALID_ACCESS_ERR; return; } #if ENABLE(XHR_TIMEOUT) // Similarly, timeouts are disabled for synchronous requests as well. if (m_timeoutMilliseconds > 0) { logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests must not have a timeout value set."); ec = INVALID_ACCESS_ERR; return; } #endif } m_method = uppercaseKnownHTTPMethod(method); m_url = url; m_async = async; ASSERT(!m_loader); // Check previous state to avoid dispatching readyState event // when calling open several times in a row. if (previousState != OPENED) changeState(OPENED); else m_state = OPENED; }
void XMLHttpRequest::open(const String& method, const URL& url, bool async, ExceptionCode& ec) { if (!internalAbort()) return; State previousState = m_state; m_state = UNSENT; m_error = false; m_sendFlag = false; m_uploadComplete = false; // clear stuff from possible previous load clearResponse(); clearRequest(); ASSERT(m_state == UNSENT); if (!isValidHTTPToken(method)) { ec = SYNTAX_ERR; return; } if (!isAllowedHTTPMethod(method)) { ec = SECURITY_ERR; return; } if (!async && scriptExecutionContext()->isDocument()) { if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) { logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests are disabled for this page."); ec = INVALID_ACCESS_ERR; return; } // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated // attempt to discourage synchronous XHR use. responseType is one such piece of functionality. // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols // such as file: and data: still make sense to allow. if (url.protocolIsInHTTPFamily() && m_responseType != ResponseType::EmptyString) { logConsoleError(scriptExecutionContext(), "Synchronous HTTP(S) requests made from the window context cannot have XMLHttpRequest.responseType set."); ec = INVALID_ACCESS_ERR; return; } // Similarly, timeouts are disabled for synchronous requests as well. if (m_timeoutMilliseconds > 0) { logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests must not have a timeout value set."); ec = INVALID_ACCESS_ERR; return; } } m_method = uppercaseKnownHTTPMethod(method); m_url = url; scriptExecutionContext()->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(m_url, ContentSecurityPolicy::InsecureRequestType::Load); m_async = async; ASSERT(!m_loader); // Check previous state to avoid dispatching readyState event // when calling open several times in a row. if (previousState != OPENED) changeState(OPENED); else m_state = OPENED; }
Request* Request::createRequestWithRequestOrString(ExecutionContext* context, Request* inputRequest, const String& inputString, const RequestInit& init, ExceptionState& exceptionState) { // "1. If input is a Request object, run these substeps:" if (inputRequest) { // "1. If input's used flag is set, throw a TypeError." if (inputRequest->bodyUsed()) { exceptionState.throwTypeError("Cannot construct a Request with a Request object that has already been used."); return 0; } // "2. Set input's used flag." inputRequest->lockBody(PassBody); } // "2. Let |request| be |input|'s associated request, if |input| is a // Request object, and a new request otherwise." // "3. Set |request| to a new request whose url is |request|'s url, method // is |request|'s method, header list is a copy of |request|'s header list, // unsafe request flag is set, body is |request|'s body, client is entry // settings object, origin is entry settings object's origin, force Origin // header flag is set, same-origin data URL flag is set, referrer is client, // context is fetch, mode is |request|'s mode, credentials mode is // |request|'s credentials mode, and cache mode is |request|'s cache mode." FetchRequestData* request = createCopyOfFetchRequestDataForFetch(context, inputRequest ? inputRequest->request() : FetchRequestData::create()); if (inputRequest && inputRequest->blobDataHandle() && inputRequest->isBodyConsumed()) { // Currently the only methods that can consume body data without // setting 'body passed' flag consume entire body (e.g. text()). Thus // we can set an empty blob to the new request instead of creating a // draining stream. // TODO(yhirano): Fix this once Request.body is introduced. OwnPtr<BlobData> blobData = BlobData::create(); blobData->setContentType(inputRequest->blobDataHandle()->type()); request->setBlobDataHandle(BlobDataHandle::create(blobData.release(), 0)); } // "4. Let |fallbackMode| be null." // "5. Let |fallbackCredentials| be null." // "6. Let |fallbackCache| be null." // We don't use fallback values. We set these flags directly in below. // "7. If |input| is a string, run these substeps:" if (!inputRequest) { // "1. Let |parsedURL| be the result of parsing |input| with entry // settings object's API base URL." KURL parsedURL = context->completeURL(inputString); // "2. If |parsedURL| is failure, throw a TypeError." if (!parsedURL.isValid()) { exceptionState.throwTypeError("Failed to parse URL from " + inputString); return 0; } // "3. Set |request|'s url to |parsedURL|." request->setURL(parsedURL); // "4. Set |fallbackMode| to CORS." // "5. Set |fallbackCredentials| to omit." // "6. Set |fallbackCache| to default." // We don't use fallback values. We set these flags directly in below. } // "8. Let |mode| be |init|'s mode member if it is present, and // |fallbackMode| otherwise." // "9. If |mode| is non-null, set |request|'s mode to |mode|." if (init.mode == "same-origin") { request->setMode(WebURLRequest::FetchRequestModeSameOrigin); } else if (init.mode == "no-cors") { request->setMode(WebURLRequest::FetchRequestModeNoCORS); } else if (init.mode == "cors") { request->setMode(WebURLRequest::FetchRequestModeCORS); } else { if (!inputRequest) request->setMode(WebURLRequest::FetchRequestModeCORS); } // "10. Let |credentials| be |init|'s credentials member if it is present, // and |fallbackCredentials| otherwise." // "11. If |credentials| is non-null, set |request|'s credentials mode to // |credentials|. if (init.credentials == "omit") { request->setCredentials(WebURLRequest::FetchCredentialsModeOmit); } else if (init.credentials == "same-origin") { request->setCredentials(WebURLRequest::FetchCredentialsModeSameOrigin); } else if (init.credentials == "include") { request->setCredentials(WebURLRequest::FetchCredentialsModeInclude); } else { if (!inputRequest) request->setCredentials(WebURLRequest::FetchCredentialsModeOmit); } // FIXME: "12. Let |cache| be |init|'s cache member if it is present, and // |fallbackCache| otherwise." // FIXME: "13. If |cache| is non-null, set |request|'s cache mode to // |cache|." // "14. If |init|'s method member is present, let |method| be it and run // these substeps:" if (!init.method.isNull()) { // "1. If |method| is not a method or method is a forbidden method, // throw a TypeError." if (!isValidHTTPToken(init.method)) { exceptionState.throwTypeError("'" + init.method + "' is not a valid HTTP method."); return 0; } if (FetchUtils::isForbiddenMethod(init.method)) { exceptionState.throwTypeError("'" + init.method + "' HTTP method is unsupported."); return 0; } // "2. Normalize |method|." // "3. Set |request|'s method to |method|." request->setMethod(FetchUtils::normalizeMethod(AtomicString(init.method))); } // "15. Let |r| be a new Request object associated with |request|, and a new // Headers object." Request* r = Request::create(context, request); // "16. Let |headers| be a copy of |r|'s Headers object." // "17. If |init|'s headers member is present, set |headers| to |init|'s // headers member." // We don't create a copy of r's Headers object when init's headers member // is present. Headers* headers = 0; if (!init.headers && init.headersDictionary.isUndefinedOrNull()) { headers = r->headers()->clone(); } // "18. Empty |r|'s request's header list." r->clearHeaderList(); // "19. If |r|'s request's mode is no CORS, run these substeps: if (r->request()->mode() == WebURLRequest::FetchRequestModeNoCORS) { // "1. If |r|'s request's method is not a simple method, throw a // TypeError." if (!FetchUtils::isSimpleMethod(r->request()->method())) { exceptionState.throwTypeError("'" + r->request()->method() + "' is unsupported in no-cors mode."); return 0; } // "Set |r|'s Headers object's guard to |request-no-CORS|. r->headers()->setGuard(Headers::RequestNoCORSGuard); } // "20. Fill |r|'s Headers object with |headers|. Rethrow any exceptions." if (init.headers) { ASSERT(init.headersDictionary.isUndefinedOrNull()); r->headers()->fillWith(init.headers.get(), exceptionState); } else if (!init.headersDictionary.isUndefinedOrNull()) { r->headers()->fillWith(init.headersDictionary, exceptionState); } else { ASSERT(headers); r->headers()->fillWith(headers, exceptionState); } if (exceptionState.hadException()) return 0; // "21. If |init|'s body member is present, run these substeps:" if (init.bodyBlobHandle) { // "1. If request's method is `GET` or `HEAD`, throw a TypeError." // "2. Let |stream| and |Content-Type| be the result of extracting // |init|'s body member." // "3. Set |r|'s request's body to |stream|." // "4. If |Content-Type| is non-null and |r|'s request's header list // contains no header named `Content-Type`, append // `Content-Type`/|Content-Type| to |r|'s Headers object. Rethrow any // exception." if (request->method() == "GET" || request->method() == "HEAD") { exceptionState.throwTypeError("Request with GET/HEAD method cannot have body."); return 0; } r->setBodyBlobHandle(init.bodyBlobHandle); if (!init.bodyBlobHandle->type().isEmpty() && !r->headers()->has("Content-Type", exceptionState)) { r->headers()->append("Content-Type", init.bodyBlobHandle->type(), exceptionState); } if (exceptionState.hadException()) return 0; } // "22. Set |r|'s MIME type to the result of extracting a MIME type from // |r|'s request's header list." r->m_request->setMIMEType(r->m_request->headerList()->extractMIMEType()); // "23. Return |r|." return r; }
void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, ExceptionState& exceptionState) { WTF_LOG(Network, "XMLHttpRequest %p open('%s', '%s', %d)", this, method.string().utf8().data(), url.elidedString().utf8().data(), async); if (!internalAbort()) return; State previousState = m_state; m_state = UNSENT; m_error = false; m_uploadComplete = false; // clear stuff from possible previous load clearResponse(); clearRequest(); ASSERT(m_state == UNSENT); if (!isValidHTTPToken(method)) { exceptionState.throwDOMException(SyntaxError, "'" + method + "' is not a valid HTTP method."); return; } if (!isAllowedHTTPMethod(method)) { exceptionState.throwSecurityError("'" + method + "' HTTP method is unsupported."); return; } if (!ContentSecurityPolicy::shouldBypassMainWorld(executionContext()) && !executionContext()->contentSecurityPolicy()->allowConnectToSource(url)) { // We can safely expose the URL to JavaScript, as these checks happen synchronously before redirection. JavaScript receives no new information. exceptionState.throwSecurityError("Refused to connect to '" + url.elidedString() + "' because it violates the document's Content Security Policy."); return; } if (!async && executionContext()->isDocument()) { if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) { exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests are disabled for this page."); return; } // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated // attempt to discourage synchronous XHR use. responseType is one such piece of functionality. // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols // such as file: and data: still make sense to allow. if (url.protocolIsInHTTPFamily() && m_responseTypeCode != ResponseTypeDefault) { exceptionState.throwDOMException(InvalidAccessError, "Synchronous HTTP requests from a document must not set a response type."); return; } // Similarly, timeouts are disabled for synchronous requests as well. if (m_timeoutMilliseconds > 0) { exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests must not set a timeout."); return; } } m_method = uppercaseKnownHTTPMethod(method); m_url = url; m_async = async; ASSERT(!m_loader); // Check previous state to avoid dispatching readyState event // when calling open several times in a row. if (previousState != OPENED) changeState(OPENED); else m_state = OPENED; }
bool FetchHeaderList::isValidHeaderName(const String& name) { // "A name is a case-insensitive byte sequence that matches the field-name // token production." return isValidHTTPToken(name); }