Esempio n. 1
0
def get_cors_headers(options, request_headers, request_method):
    origins_to_set = get_cors_origins(options, request_headers.get('Origin'))
    headers = CIMultiDict()

    if not origins_to_set:  # CORS is not enabled for this route
        return headers

    for origin in origins_to_set:
        # TODO, with CIDict, with will only allow one origin
        # With CIMultiDict it should work with multiple
        headers[ACL_ORIGIN] = origin

    headers[ACL_EXPOSE_HEADERS] = options.get('expose_headers')

    if options.get('supports_credentials'):
        headers[ACL_CREDENTIALS] = 'true'  # case sensative

    # This is a preflight request
    # http://www.w3.org/TR/cors/#resource-preflight-requests
    if request_method == 'OPTIONS':
        acl_request_method = request_headers.get(ACL_REQUEST_METHOD,
                                                 '').upper()

        # If there is no Access-Control-Request-Method header or if parsing
        # failed, do not set any additional headers
        if acl_request_method and acl_request_method in options.get('methods'):

            # If method is not a case-sensitive match for any of the values in
            # list of methods do not set any additional headers and terminate
            # this set of steps.
            headers[ACL_ALLOW_HEADERS] = get_allow_headers(
                options, request_headers.get(ACL_REQUEST_HEADERS))
            headers[ACL_MAX_AGE] = str(options.get(
                'max_age'))  # sanic cannot handle integers in header values.
            headers[ACL_METHODS] = options.get('methods')
        else:
            LOG.info(
                "The request's Access-Control-Request-Method header does not match allowed methods. "
                "CORS headers will not be applied.")

    # http://www.w3.org/TR/cors/#resource-implementation
    if options.get('vary_header'):
        # Only set header if the origin returned will vary dynamically,
        # i.e. if we are not returning an asterisk, and there are multiple
        # origins that can be matched.
        if headers[ACL_ORIGIN] == '*':
            pass
        elif (len(options.get('origins')) > 1 or len(origins_to_set) > 1
              or any(map(probably_regex, options.get('origins')))):
            headers['Vary'] = 'Origin'

    return CIMultiDict((k, v) for k, v in headers.items() if v)
Esempio n. 2
0
    def on_headers_complete(self):
        remote_addr = self.transport.get_extra_info('peername')
        if remote_addr:
            self.headers.append(('Remote-Addr', '%s:%s' % remote_addr))

        self.request = Request(url_bytes=self.url,
                               headers=CIMultiDict(self.headers),
                               version=self.parser.get_http_version(),
                               method=self.parser.get_method().decode())
Esempio n. 3
0
def set_cors_headers(req, resp, context, options):
    """
    Performs the actual evaluation of Sanic-CORS options and actually
    modifies the response object.

    This function is used both in the decorator and the after_request
    callback
    :param sanic.request.Request req:

    """
    try:
        request_context = context.request[id(req)]
    except AttributeError:
        LOG.debug(
            "Cannot find the request context. Is request already finished?")
        return resp
    # If CORS has already been evaluated via the decorator, skip
    evaluated = request_context.get(SANIC_CORS_EVALUATED, False)
    if evaluated:
        LOG.debug('CORS have been already evaluated, skipping')
        return resp

    # `resp` can be None in the case of using Websockets
    # however this case should have been handled in the `extension` and `decorator` methods
    # before getting here. This is a final failsafe check to prevent crashing
    if resp is None:
        return None

    if resp.headers is None:
        resp.headers = CIMultiDict()

    headers_to_set = get_cors_headers(options, req.headers, req.method)

    LOG.debug('Settings CORS headers: %s', str(headers_to_set))

    # dict .extend() does not work on CIDict so
    # iterate over them and add them individually.
    try:
        resp.headers.extend(headers_to_set)
    except Exception as e1:
        for k, v in headers_to_set.items():
            try:
                resp.headers.add(k, v)
            except Exception as e2:
                resp.headers[k] = v
        return resp
Esempio n. 4
0
    def source(self, request):
        '''
        Pulls values off the request in the provided location
        :param request: The flask request object to parse arguments from
        '''
        if isinstance(self.location, six.string_types):
            value = getattr(request, self.location, CIMultiDict())
            if callable(value):
                value = value()
            if value is not None:
                return value
        else:
            values = CIMultiDict()
            for l in self.location:
                value = getattr(request, l, None)
                if callable(value):
                    value = value()
                if value is not None:
                    values.update(value)
            return values

        return CIMultiDict()
 def test_multiple_set_cookie_headers(request):
     resp = HTTPResponse(body="Foo bar baz")
     resp.headers = CIMultiDict()
     resp.headers['set-cookie'] = 'foo'
     resp.headers['set-cookie'] = 'bar'
     return resp
Esempio n. 6
0
 def test_existing_vary_headers(request):
     return HTTPResponse('',
                         status=200,
                         headers=CIMultiDict(
                             {'Vary': 'Accept-Encoding'}))
Esempio n. 7
0
    def handle_error(self, request, e):
        '''
        Error handler for the API transforms a raised exception into a Flask response,
        with the appropriate HTTP status code and body.

        :param Exception e: the raised Exception object

        '''
        context = restplus.get_context_from_spf(self.spf_reg)
        app = context.app
        headers = CIMultiDict()
        if e.__class__ in self.error_handlers:
            handler = self.error_handlers[e.__class__]
            result = handler(e)
            default_data, code, headers = unpack(
                result, HTTPStatus.INTERNAL_SERVER_ERROR)
        elif isinstance(e, SanicException):
            code = e.status_code
            if code is 200:
                status = b'OK'
            elif code is 404:
                status = b'Not Found'
            elif code is 500:
                status = b'Internal Server Error'
            else:
                code = HTTPStatus(code)
                status = ALL_STATUS_CODES.get(code.value)
            if status and isinstance(status, bytes):
                status = status.decode('ascii')
            default_data = {'message': getattr(e, 'message', status)}
            # headers = e.get_response().headers
        elif self._default_error_handler:
            result = self._default_error_handler(e)
            default_data, code, headers = unpack(
                result, HTTPStatus.INTERNAL_SERVER_ERROR)
        else:
            code = HTTPStatus.INTERNAL_SERVER_ERROR
            status = ALL_STATUS_CODES.get(code.value, str(e))
            if status and isinstance(status, bytes):
                status = status.decode('ascii')
            default_data = {
                'message': status,
            }

        default_data['message'] = default_data.get('message', str(e))
        data = getattr(e, 'data', default_data)
        fallback_mediatype = None

        if code >= HTTPStatus.INTERNAL_SERVER_ERROR:
            exc_info = sys.exc_info()
            if exc_info[1] is None:
                exc_info = None
            context.log(logging.ERROR, exc_info)

        elif code == HTTPStatus.NOT_FOUND and app.config.get(
                "ERROR_404_HELP", True):
            data['message'] = self._help_on_404(request,
                                                data.get('message', None))

        elif code == HTTPStatus.NOT_ACCEPTABLE and self.default_mediatype is None:
            # if we are handling NotAcceptable (406), make sure that
            # make_response uses a representation we support as the
            # default mediatype (so that make_response doesn't throw
            # another NotAcceptable error).
            supported_mediatypes = list(self.representations.keys())
            fallback_mediatype = supported_mediatypes[
                0] if supported_mediatypes else "text/plain"

        # Remove blacklisted headers
        for header in HEADERS_BLACKLIST:
            headers.pop(header, None)

        resp = self.make_response(request,
                                  data,
                                  code,
                                  headers,
                                  fallback_mediatype=fallback_mediatype)

        if code == HTTPStatus.UNAUTHORIZED:
            resp = self.unauthorized(resp)
        return resp