示例#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)
示例#2
0
class BaseHTTPResponse:
    """
    The base class for all HTTP Responses
    """

    def __init__(self):
        self.asgi: bool = False
        self.body: Optional[bytes] = None
        self.content_type: Optional[str] = None
        self.stream: Http = None
        self.status: int = None
        self.headers = Header({})
        self._cookies: Optional[CookieJar] = None

    def _encode_body(self, data: Optional[AnyStr]):
        if data is None:
            return b""
        return (
            data.encode() if hasattr(data, "encode") else data  # type: ignore
        )

    @property
    def cookies(self) -> CookieJar:
        """
        The response cookies. Cookies should be set and written as follows:

        .. code-block:: python

                response.cookies["test"] = "It worked!"
                response.cookies["test"]["domain"] = ".yummy-yummy-cookie.com"
                response.cookies["test"]["httponly"] = True

        `See user guide
        <https://sanicframework.org/guide/basics/cookies.html>`_

        :return: the cookie jar
        :rtype: CookieJar
        """
        if self._cookies is None:
            self._cookies = CookieJar(self.headers)
        return self._cookies

    @property
    def processed_headers(self) -> Iterator[Tuple[bytes, bytes]]:
        """
        Obtain a list of header tuples encoded in bytes for sending.

        Add and remove headers based on status and content_type.

        :return: response headers
        :rtype: Tuple[Tuple[bytes, bytes], ...]
        """
        # TODO: Make a blacklist set of header names and then filter with that
        if self.status in (304, 412):  # Not Modified, Precondition Failed
            self.headers = remove_entity_headers(self.headers)
        if has_message_body(self.status):
            self.headers.setdefault("content-type", self.content_type)
        # Encode headers into bytes
        return (
            (name.encode("ascii"), f"{value}".encode(errors="surrogateescape"))
            for name, value in self.headers.items()
        )

    async def send(
        self,
        data: Optional[Union[AnyStr]] = None,
        end_stream: Optional[bool] = None,
    ) -> None:
        """
        Send any pending response headers and the given data as body.

        :param data: str or bytes to be written
        :param end_stream: whether to close the stream after this block
        """
        if data is None and end_stream is None:
            end_stream = True
        if end_stream and not data and self.stream.send is None:
            return
        data = (
            data.encode()  # type: ignore
            if hasattr(data, "encode")
            else data or b""
        )
        await self.stream.send(data, end_stream=end_stream)
示例#3
0
def make_mocked_request(method: str,
                        path: bytes,
                        headers=None,
                        *args,
                        version=(1, 1),
                        closing=False,
                        app=None,
                        writer=sentinel,
                        protocol=sentinel,
                        transport=sentinel,
                        payload=sentinel,
                        sslcontext=None,
                        client_max_size=1024**2,
                        loop=None,
                        stream=False):
    """Creates mocked web.Request testing purposes.
    Useful in unit tests, when spinning full web server is overkill or
    specific conditions and errors are hard to trigger.
    """

    task = mock.Mock()
    if loop is None:
        loop = mock.Mock()
        loop.create_future.return_value = ()

    if version < (1, 1):
        closing = True

    if headers:
        headers = MultiDict(headers)
        raw_hdrs = tuple(
            (k.encode('utf-8'), v.encode('utf-8')) for k, v in headers.items())
    else:
        headers = MultiDict()
        raw_hdrs = ()

    chunked = 'chunked' in headers.get('Transfer-Encoding', '').lower()

    # message = RawRequestMessage(
    #     method, path, version, headers,
    #     raw_hdrs, closing, False, False, chunked, URL(path))
    if app is None:
        app = _create_app_mock()

    if transport is sentinel:
        transport = _create_transport(sslcontext)

    if protocol is sentinel:
        protocol = mock.Mock()
        protocol.transport = transport

    if writer is sentinel:
        writer = mock.Mock()
        writer.write_headers = make_mocked_coro(None)
        writer.write = make_mocked_coro(None)
        writer.write_eof = make_mocked_coro(None)
        writer.drain = make_mocked_coro(None)
        writer.transport = transport

    protocol.transport = transport
    protocol.writer = writer

    if payload is sentinel:
        payload = b""

    if sanic_19_6 > sanic_version:
        req = Request(path, headers, version, method, transport=transport)
        req.app = app
    else:
        req = Request(path,
                      headers,
                      version,
                      method,
                      transport=transport,
                      app=app)
    if stream:
        mock_stream = mock.Mock()
        mock_stream.read = make_mocked_coro(payload)
        req.stream = mock_stream
    else:
        req.body_push(payload)
        req.body_finish()
    # req = Request(message, payload,
    #               protocol, writer, task, loop,
    #               client_max_size=client_max_size)

    return req