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)
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)
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