Home | History | Annotate | Download | only in serviceworkers
      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 "Request.h"
      7 
      8 #include "bindings/core/v8/Dictionary.h"
      9 #include "core/dom/ExecutionContext.h"
     10 #include "core/fetch/FetchUtils.h"
     11 #include "core/fetch/ResourceLoaderOptions.h"
     12 #include "core/loader/ThreadableLoader.h"
     13 #include "core/xml/XMLHttpRequest.h"
     14 #include "modules/serviceworkers/FetchManager.h"
     15 #include "modules/serviceworkers/HeadersForEachCallback.h"
     16 #include "modules/serviceworkers/RequestInit.h"
     17 #include "platform/NotImplemented.h"
     18 #include "platform/network/HTTPParsers.h"
     19 #include "platform/network/ResourceRequest.h"
     20 #include "platform/weborigin/Referrer.h"
     21 #include "public/platform/WebServiceWorkerRequest.h"
     22 
     23 namespace blink {
     24 
     25 namespace {
     26 
     27 class FillWebRequestHeaders : public HeadersForEachCallback {
     28 public:
     29     FillWebRequestHeaders(WebServiceWorkerRequest* webRequest) : m_webRequest(webRequest) { }
     30 
     31     virtual bool handleItem(ScriptValue, const String&, const String&, Headers*)
     32     {
     33         ASSERT_NOT_REACHED();
     34         return false;
     35     }
     36 
     37     virtual bool handleItem(const String& value, const String& key, Headers*)
     38     {
     39         m_webRequest->appendHeader(key, value);
     40         return true;
     41     }
     42 
     43 private:
     44     WebServiceWorkerRequest* m_webRequest;
     45 };
     46 
     47 } // namespace
     48 
     49 Request* Request::createRequestWithRequestData(ExecutionContext* context, FetchRequestData* request, const RequestInit& init, FetchRequestData::Mode mode, FetchRequestData::Credentials credentials, ExceptionState& exceptionState)
     50 {
     51     // "7. Let |mode| be |init|'s mode member if it is present, and
     52     // |fallbackMode| otherwise."
     53     // "8. If |mode| is non-null, set |request|'s mode to |mode|."
     54     if (init.mode == "same-origin") {
     55         request->setMode(FetchRequestData::SameOriginMode);
     56     } else if (init.mode == "no-cors") {
     57         request->setMode(mode = FetchRequestData::NoCORSMode);
     58     } else if (init.mode == "cors") {
     59         request->setMode(FetchRequestData::CORSMode);
     60     } else {
     61         // Instead of using null as a special fallback value, we pass the
     62         // current mode in Request::create(). So we just set here.
     63         request->setMode(mode);
     64     }
     65 
     66     // "9. Let |credentials| be |init|'s credentials member if it is present,
     67     // and |fallbackCredentials| otherwise."
     68     // "10. If |credentials| is non-null, set |request|'s credentials mode to
     69     // |credentials|.
     70     if (init.credentials == "omit") {
     71         request->setCredentials(FetchRequestData::OmitCredentials);
     72     } else if (init.credentials == "same-origin") {
     73         request->setCredentials(FetchRequestData::SameOriginCredentials);
     74     } else if (init.credentials == "include") {
     75         request->setCredentials(FetchRequestData::IncludeCredentials);
     76     } else {
     77         // Instead of using null as a special fallback value, we pass the
     78         // current credentials in Request::create(). So we just set here.
     79         request->setCredentials(credentials);
     80     }
     81 
     82     // "11. If |init|'s method member is present, let |method| be it and run
     83     // these substeps:"
     84     if (!init.method.isEmpty()) {
     85         // "1. If |method| is not a useful method, throw a TypeError."
     86         if (!FetchUtils::isUsefulMethod(init.method)) {
     87             exceptionState.throwTypeError("'" + init.method + "' HTTP method is unsupported.");
     88             return 0;
     89         }
     90         if (!isValidHTTPToken(init.method)) {
     91             exceptionState.throwTypeError("'" + init.method + "' is not a valid HTTP method.");
     92             return 0;
     93         }
     94         // FIXME: "2. Add case correction as in XMLHttpRequest?"
     95         // "3. Set |request|'s method to |method|."
     96         request->setMethod(XMLHttpRequest::uppercaseKnownHTTPMethod(AtomicString(init.method)));
     97     }
     98     // "12. Let |r| be a new Request object associated with |request|, Headers
     99     // object."
    100     Request* r = Request::create(context, request);
    101 
    102     // "13. Let |headers| be a copy of |r|'s Headers object."
    103     // "14. If |init|'s headers member is present, set |headers| to |init|'s
    104     // headers member."
    105     // We don't create a copy of r's Headers object when init's headers member
    106     // is present.
    107     Headers* headers = 0;
    108     if (!init.headers && init.headersDictionary.isUndefinedOrNull()) {
    109         headers = r->headers()->createCopy();
    110     }
    111     // "15. Empty |r|'s request's header list."
    112     r->clearHeaderList();
    113 
    114     // "16. If |r|'s request's mode is no CORS, run these substeps:
    115     if (r->request()->mode() == FetchRequestData::NoCORSMode) {
    116         // "1. If |r|'s request's method is not a simple method, throw a
    117         // TypeError."
    118         if (!FetchUtils::isSimpleMethod(r->request()->method())) {
    119             exceptionState.throwTypeError("'" + r->request()->method() + "' is unsupported in no-cors mode.");
    120             return 0;
    121         }
    122         // "Set |r|'s Headers object's guard to |request-no-CORS|.
    123         r->headers()->setGuard(Headers::RequestNoCORSGuard);
    124     }
    125 
    126     // "17. Fill |r|'s Headers object with |headers|. Rethrow any exceptions."
    127     if (init.headers) {
    128         ASSERT(init.headersDictionary.isUndefinedOrNull());
    129         r->headers()->fillWith(init.headers.get(), exceptionState);
    130     } else if (!init.headersDictionary.isUndefinedOrNull()) {
    131         r->headers()->fillWith(init.headersDictionary, exceptionState);
    132     } else {
    133         ASSERT(headers);
    134         r->headers()->fillWith(headers, exceptionState);
    135     }
    136     if (exceptionState.hadException())
    137         return 0;
    138     // "18. If |init|'s body member is present, run these substeps:"
    139     if (init.bodyBlobHandle) {
    140         // "1. Let |stream| and |Content-Type| be the result of extracting
    141         // |init|'s body member."
    142         // "2. Set |r|'s request's body to |stream|."
    143         // "3.If |Content-Type| is non-null and |r|'s request's header list
    144         //  contains no header named `Content-Type`, append
    145         // `Content-Type`/|Content-Type| to |r|'s Headers object. Rethrow any
    146         // exception."
    147         r->setBodyBlobHandle(init.bodyBlobHandle);
    148         if (!init.bodyBlobHandle->type().isEmpty() && !r->headers()->has("Content-Type", exceptionState)) {
    149             r->headers()->append("Content-Type", init.bodyBlobHandle->type(), exceptionState);
    150         }
    151         if (exceptionState.hadException())
    152             return 0;
    153     }
    154     // "19. Set |r|'s MIME type to the result of extracting a MIME type from
    155     // |r|'s request's header list."
    156     // FIXME: We don't have MIME type in Request object yet.
    157 
    158     // "20. Return |r|."
    159     return r;
    160 }
    161 
    162 Request* Request::create(ExecutionContext* context, const String& input, ExceptionState& exceptionState)
    163 {
    164     return create(context, input, Dictionary(), exceptionState);
    165 }
    166 
    167 Request* Request::create(ExecutionContext* context, const String& input, const Dictionary& init, ExceptionState& exceptionState)
    168 {
    169     // "2. Let |request| be |input|'s associated request, if |input| is a
    170     // Request object, and a new request otherwise."
    171     FetchRequestData* request(FetchRequestData::create(context));
    172     // "3. Set |request| to a restricted copy of itself."
    173     request = request->createRestrictedCopy(context, SecurityOrigin::create(context->url()));
    174     // "6. If |input| is a string, run these substeps:"
    175     // "1. Let |parsedURL| be the result of parsing |input| with entry settings
    176     // object's API base URL."
    177     KURL parsedURL = context->completeURL(input);
    178     // "2. If |parsedURL| is failure, throw a TypeError."
    179     if (!parsedURL.isValid()) {
    180         exceptionState.throwTypeError("Invalid URL");
    181         return 0;
    182     }
    183     // "3. Set |request|'s url to |parsedURL|."
    184     request->setURL(parsedURL);
    185     // "4. Set |fallbackMode| to CORS."
    186     // "5. Set |fallbackCredentials| to omit."
    187     return createRequestWithRequestData(context, request, RequestInit(context, init, exceptionState), FetchRequestData::CORSMode, FetchRequestData::OmitCredentials, exceptionState);
    188 }
    189 
    190 Request* Request::create(ExecutionContext* context, Request* input, ExceptionState& exceptionState)
    191 {
    192     return create(context, input, Dictionary(), exceptionState);
    193 }
    194 
    195 Request* Request::create(ExecutionContext* context, Request* input, const Dictionary& init, ExceptionState& exceptionState)
    196 {
    197     // "1. If input is a Request object, run these substeps:"
    198     // "  1. If input's used flag is set, throw a TypeError."
    199     // "  2. Set input's used flag."
    200     if (input->bodyUsed()) {
    201         exceptionState.throwTypeError(
    202             "Cannot construct a Request with a Request object that has already been used.");
    203         return 0;
    204     }
    205     input->setBodyUsed();
    206     // "2. Let |request| be |input|'s associated request, if |input| is a
    207     // Request object, and a new request otherwise."
    208     // "3. Set |request| to a restricted copy of itself."
    209     FetchRequestData* request(input->request()->createRestrictedCopy(context, SecurityOrigin::create(context->url())));
    210     // "4. Let |fallbackMode| be null."
    211     // "5. Let |fallbackCredentials| be null."
    212     // Instead of using null as a special fallback value, just pass the current
    213     // mode and credentials; it has the same effect.
    214     const FetchRequestData::Mode currentMode = request->mode();
    215     const FetchRequestData::Credentials currentCredentials = request->credentials();
    216     return createRequestWithRequestData(context, request, RequestInit(context, init, exceptionState), currentMode, currentCredentials, exceptionState);
    217 }
    218 
    219 Request* Request::create(ExecutionContext* context, FetchRequestData* request)
    220 {
    221     Request* r = new Request(context, request);
    222     r->suspendIfNeeded();
    223     return r;
    224 }
    225 
    226 Request::Request(ExecutionContext* context, FetchRequestData* request)
    227     : Body(context)
    228     , m_request(request)
    229     , m_headers(Headers::create(m_request->headerList()))
    230 {
    231     m_headers->setGuard(Headers::RequestGuard);
    232 }
    233 
    234 Request* Request::create(ExecutionContext* context, const WebServiceWorkerRequest& webRequest)
    235 {
    236     Request* r = new Request(context, webRequest);
    237     r->suspendIfNeeded();
    238     return r;
    239 }
    240 
    241 Request* Request::create(const Request& copyFrom)
    242 {
    243     Request* r = new Request(copyFrom);
    244     r->suspendIfNeeded();
    245     return r;
    246 }
    247 
    248 Request::Request(ExecutionContext* context, const WebServiceWorkerRequest& webRequest)
    249     : Body(context)
    250     , m_request(FetchRequestData::create(webRequest))
    251     , m_headers(Headers::create(m_request->headerList()))
    252 {
    253     m_headers->setGuard(Headers::RequestGuard);
    254 }
    255 
    256 Request::Request(const Request& copy_from)
    257     : Body(copy_from)
    258     , m_request(copy_from.m_request)
    259     , m_headers(copy_from.m_headers->createCopy())
    260 {
    261 }
    262 
    263 
    264 String Request::method() const
    265 {
    266     // "The method attribute's getter must return request's method."
    267     return m_request->method();
    268 }
    269 
    270 String Request::url() const
    271 {
    272     // The url attribute's getter must return request's url, serialized with the exclude fragment flag set.
    273     if (!m_request->url().hasFragmentIdentifier())
    274         return m_request->url();
    275     KURL url(m_request->url());
    276     url.removeFragmentIdentifier();
    277     return url;
    278 }
    279 
    280 String Request::referrer() const
    281 {
    282     // "The referrer attribute's getter must return the empty string if
    283     // request's referrer is none, and request's referrer, serialized,
    284     // otherwise."
    285     return m_request->referrer().referrer().referrer;
    286 }
    287 
    288 String Request::mode() const
    289 {
    290     // "The mode attribute's getter must return the value corresponding to the
    291     // first matching statement, switching on request's mode:"
    292     switch (m_request->mode()) {
    293     case FetchRequestData::SameOriginMode:
    294         return "same-origin";
    295     case FetchRequestData::NoCORSMode:
    296         return "no-cors";
    297     case FetchRequestData::CORSMode:
    298     case FetchRequestData::CORSWithForcedPreflight:
    299         return "cors";
    300     }
    301     ASSERT_NOT_REACHED();
    302     return "";
    303 }
    304 
    305 String Request::credentials() const
    306 {
    307     // "The credentials attribute's getter must return the value corresponding
    308     // to the first matching statement, switching on request's credentials
    309     // mode:"
    310     switch (m_request->credentials()) {
    311     case FetchRequestData::OmitCredentials:
    312         return "omit";
    313     case FetchRequestData::SameOriginCredentials:
    314         return "same-origin";
    315     case FetchRequestData::IncludeCredentials:
    316         return "include";
    317     }
    318     ASSERT_NOT_REACHED();
    319     return "";
    320 }
    321 
    322 Request* Request::clone() const
    323 {
    324     return Request::create(*this);
    325 }
    326 
    327 void Request::populateWebServiceWorkerRequest(WebServiceWorkerRequest& webRequest)
    328 {
    329     webRequest.setMethod(method());
    330     webRequest.setURL(m_request->url());
    331     m_headers->forEach(new FillWebRequestHeaders(&webRequest));
    332     webRequest.setReferrer(m_request->referrer().referrer().referrer, static_cast<WebReferrerPolicy>(m_request->referrer().referrer().referrerPolicy));
    333     // 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
    334     // to plumb this information in to here.
    335 }
    336 
    337 void Request::setBodyBlobHandle(PassRefPtr<BlobDataHandle> blobDataHandle)
    338 {
    339     m_request->setBlobDataHandle(blobDataHandle);
    340 }
    341 
    342 void Request::clearHeaderList()
    343 {
    344     m_request->headerList()->clearList();
    345 }
    346 
    347 PassRefPtr<BlobDataHandle> Request::blobDataHandle()
    348 {
    349     return m_request->blobDataHandle();
    350 }
    351 
    352 void Request::trace(Visitor* visitor)
    353 {
    354     Body::trace(visitor);
    355     visitor->trace(m_request);
    356     visitor->trace(m_headers);
    357 }
    358 
    359 } // namespace blink
    360