blob: fd334f28b7d09a581ef80e95d3275c13f71354a2 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "Request.h"
#include "bindings/core/v8/Dictionary.h"
#include "core/dom/ExecutionContext.h"
#include "core/fetch/FetchUtils.h"
#include "core/fetch/ResourceLoaderOptions.h"
#include "core/loader/ThreadableLoader.h"
#include "modules/serviceworkers/FetchManager.h"
#include "modules/serviceworkers/RequestInit.h"
#include "platform/network/HTTPParsers.h"
#include "platform/network/ResourceRequest.h"
#include "platform/weborigin/Referrer.h"
#include "public/platform/WebServiceWorkerRequest.h"
#include "public/platform/WebURLRequest.h"
namespace blink {
Request* Request::createRequestWithRequestData(ExecutionContext* context, FetchRequestData* request, const RequestInit& init, WebURLRequest::FetchRequestMode mode, WebURLRequest::FetchCredentialsMode credentials, ExceptionState& exceptionState)
{
// "7. Let |mode| be |init|'s mode member if it is present, and
// |fallbackMode| otherwise."
// "8. 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(mode = WebURLRequest::FetchRequestModeNoCORS);
} else if (init.mode == "cors") {
request->setMode(WebURLRequest::FetchRequestModeCORS);
} else {
// Instead of using null as a special fallback value, we pass the
// current mode in Request::create(). So we just set here.
request->setMode(mode);
}
// "9. Let |credentials| be |init|'s credentials member if it is present,
// and |fallbackCredentials| otherwise."
// "10. 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 {
// Instead of using null as a special fallback value, we pass the
// current credentials in Request::create(). So we just set here.
request->setCredentials(credentials);
}
// "11. If |init|'s method member is present, let |method| be it and run
// these substeps:"
if (!init.method.isEmpty()) {
// "1. If |method| is not a useful method, throw a TypeError."
if (!FetchUtils::isUsefulMethod(init.method)) {
exceptionState.throwTypeError("'" + init.method + "' HTTP method is unsupported.");
return 0;
}
if (!isValidHTTPToken(init.method)) {
exceptionState.throwTypeError("'" + init.method + "' is not a valid HTTP method.");
return 0;
}
// FIXME: "2. Add case correction as in XMLHttpRequest?"
// "3. Set |request|'s method to |method|."
request->setMethod(FetchUtils::normalizeMethod(AtomicString(init.method)));
}
// "12. Let |r| be a new Request object associated with |request|, Headers
// object."
Request* r = Request::create(context, request);
// "13. Let |headers| be a copy of |r|'s Headers object."
// "14. 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()->createCopy();
}
// "15. Empty |r|'s request's header list."
r->clearHeaderList();
// "16. 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);
}
// "17. 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;
// "18. If |init|'s body member is present, run these substeps:"
if (init.bodyBlobHandle) {
// "1. Let |stream| and |Content-Type| be the result of extracting
// |init|'s body member."
// "2. Set |r|'s request's body to |stream|."
// "3.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."
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;
}
// "19. Set |r|'s MIME type to the result of extracting a MIME type from
// |r|'s request's header list."
// FIXME: We don't have MIME type in Request object yet.
// "20. Return |r|."
return r;
}
Request* Request::create(ExecutionContext* context, const String& input, ExceptionState& exceptionState)
{
return create(context, input, Dictionary(), exceptionState);
}
Request* Request::create(ExecutionContext* context, const String& input, const Dictionary& init, ExceptionState& exceptionState)
{
// "2. Let |request| be |input|'s associated request, if |input| is a
// Request object, and a new request otherwise."
FetchRequestData* request(FetchRequestData::create(context));
// "3. Set |request| to a restricted copy of itself."
request = request->createRestrictedCopy(context, SecurityOrigin::create(context->url()));
// "6. If |input| is a string, run these substeps:"
// "1. Let |parsedURL| be the result of parsing |input| with entry settings
// object's API base URL."
KURL parsedURL = context->completeURL(input);
// "2. If |parsedURL| is failure, throw a TypeError."
if (!parsedURL.isValid()) {
exceptionState.throwTypeError("Invalid URL");
return 0;
}
// "3. Set |request|'s url to |parsedURL|."
request->setURL(parsedURL);
// "4. Set |fallbackMode| to CORS."
// "5. Set |fallbackCredentials| to omit."
return createRequestWithRequestData(context, request, RequestInit(context, init, exceptionState), WebURLRequest::FetchRequestModeCORS, WebURLRequest::FetchCredentialsModeOmit, exceptionState);
}
Request* Request::create(ExecutionContext* context, Request* input, ExceptionState& exceptionState)
{
return create(context, input, Dictionary(), exceptionState);
}
Request* Request::create(ExecutionContext* context, Request* input, const Dictionary& init, ExceptionState& exceptionState)
{
// "1. If input is a Request object, run these substeps:"
// " 1. If input's used flag is set, throw a TypeError."
// " 2. Set input's used flag."
if (input->bodyUsed()) {
exceptionState.throwTypeError(
"Cannot construct a Request with a Request object that has already been used.");
return 0;
}
input->setBodyUsed();
// "2. Let |request| be |input|'s associated request, if |input| is a
// Request object, and a new request otherwise."
// "3. Set |request| to a restricted copy of itself."
FetchRequestData* request(input->request()->createRestrictedCopy(context, SecurityOrigin::create(context->url())));
// "4. Let |fallbackMode| be null."
// "5. Let |fallbackCredentials| be null."
// Instead of using null as a special fallback value, just pass the current
// mode and credentials; it has the same effect.
const WebURLRequest::FetchRequestMode currentMode = request->mode();
const WebURLRequest::FetchCredentialsMode currentCredentials = request->credentials();
return createRequestWithRequestData(context, request, RequestInit(context, init, exceptionState), currentMode, currentCredentials, exceptionState);
}
Request* Request::create(ExecutionContext* context, FetchRequestData* request)
{
Request* r = new Request(context, request);
r->suspendIfNeeded();
return r;
}
Request::Request(ExecutionContext* context, FetchRequestData* request)
: Body(context)
, m_request(request)
, m_headers(Headers::create(m_request->headerList()))
{
m_headers->setGuard(Headers::RequestGuard);
}
Request* Request::create(ExecutionContext* context, const WebServiceWorkerRequest& webRequest)
{
Request* r = new Request(context, webRequest);
r->suspendIfNeeded();
return r;
}
Request* Request::create(const Request& copyFrom)
{
Request* r = new Request(copyFrom);
r->suspendIfNeeded();
return r;
}
Request::Request(ExecutionContext* context, const WebServiceWorkerRequest& webRequest)
: Body(context)
, m_request(FetchRequestData::create(webRequest))
, m_headers(Headers::create(m_request->headerList()))
{
m_headers->setGuard(Headers::RequestGuard);
}
Request::Request(const Request& copy_from)
: Body(copy_from)
, m_request(copy_from.m_request)
, m_headers(copy_from.m_headers->createCopy())
{
}
String Request::method() const
{
// "The method attribute's getter must return request's method."
return m_request->method();
}
String Request::url() const
{
// The url attribute's getter must return request's url, serialized with the exclude fragment flag set.
if (!m_request->url().hasFragmentIdentifier())
return m_request->url();
KURL url(m_request->url());
url.removeFragmentIdentifier();
return url;
}
String Request::referrer() const
{
// "The referrer attribute's getter must return the empty string if
// request's referrer is none, and request's referrer, serialized,
// otherwise."
return m_request->referrer().referrer().referrer;
}
String Request::mode() const
{
// "The mode attribute's getter must return the value corresponding to the
// first matching statement, switching on request's mode:"
switch (m_request->mode()) {
case WebURLRequest::FetchRequestModeSameOrigin:
return "same-origin";
case WebURLRequest::FetchRequestModeNoCORS:
return "no-cors";
case WebURLRequest::FetchRequestModeCORS:
case WebURLRequest::FetchRequestModeCORSWithForcedPreflight:
return "cors";
}
ASSERT_NOT_REACHED();
return "";
}
String Request::credentials() const
{
// "The credentials attribute's getter must return the value corresponding
// to the first matching statement, switching on request's credentials
// mode:"
switch (m_request->credentials()) {
case WebURLRequest::FetchCredentialsModeOmit:
return "omit";
case WebURLRequest::FetchCredentialsModeSameOrigin:
return "same-origin";
case WebURLRequest::FetchCredentialsModeInclude:
return "include";
}
ASSERT_NOT_REACHED();
return "";
}
Request* Request::clone() const
{
return Request::create(*this);
}
void Request::populateWebServiceWorkerRequest(WebServiceWorkerRequest& webRequest) const
{
webRequest.setMethod(method());
webRequest.setURL(m_request->url());
const FetchHeaderList* headerList = m_headers->headerList();
for (size_t i = 0, size = headerList->size(); i < size; ++i) {
const FetchHeaderList::Header& header = headerList->entry(i);
webRequest.appendHeader(header.first, header.second);
}
webRequest.setReferrer(m_request->referrer().referrer().referrer, static_cast<WebReferrerPolicy>(m_request->referrer().referrer().referrerPolicy));
// FIXME: How can we set isReload properly? What is the correct place to load it in to the Request object? We should investigate the right way
// to plumb this information in to here.
}
void Request::setBodyBlobHandle(PassRefPtr<BlobDataHandle> blobDataHandle)
{
m_request->setBlobDataHandle(blobDataHandle);
}
void Request::clearHeaderList()
{
m_request->headerList()->clearList();
}
PassRefPtr<BlobDataHandle> Request::blobDataHandle()
{
return m_request->blobDataHandle();
}
void Request::trace(Visitor* visitor)
{
Body::trace(visitor);
visitor->trace(m_request);
visitor->trace(m_headers);
}
} // namespace blink