Compare commits
	
		
			2 Commits
		
	
	
		
			h3-cleanup
			...
			bare-cooki
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | a202435283 | ||
|   | 6d433af406 | 
| @@ -73,12 +73,16 @@ def parse_cookie(raw: str) -> Dict[str, List[str]]: | ||||
|     cookies: Dict[str, List[str]] = {} | ||||
|  | ||||
|     for token in raw.split(";"): | ||||
|         name, __, value = token.partition("=") | ||||
|         name, sep, value = token.partition("=") | ||||
|         name = name.strip() | ||||
|         value = value.strip() | ||||
|  | ||||
|         # Support cookies =value or plain value with no name | ||||
|         # https://github.com/httpwg/http-extensions/issues/159 | ||||
|         if not sep: | ||||
|             if not name: | ||||
|             continue | ||||
|                 continue  # Empty value like ;; or a cookie header with no value | ||||
|             name, value = "", name | ||||
|  | ||||
|         if COOKIE_NAME_RESERVED_CHARS.search(name):  # no cov | ||||
|             continue | ||||
|   | ||||
| @@ -152,20 +152,24 @@ class HTTPReceiver(Receiver, Stream): | ||||
|         size = len(response.body) if response.body else 0 | ||||
|         headers = response.headers | ||||
|         status = response.status | ||||
|         want_body = ( | ||||
|  | ||||
|         if not has_message_body(status) and ( | ||||
|             size | ||||
|             or "content-length" in headers | ||||
|             or "transfer-encoding" in headers | ||||
|         ) | ||||
|         headers.pop("transfer-encoding", None)  # Not used with HTTP/3 | ||||
|         if want_body and not has_message_body(status): | ||||
|         ): | ||||
|             headers.pop("content-length", None) | ||||
|             headers.pop("transfer-encoding", None) | ||||
|             logger.warning(  # no cov | ||||
|                 f"Message body set in response on {self.request.path}. " | ||||
|                 f"A {status} response may only have headers, no body." | ||||
|             ) | ||||
|         elif size and "content-length" not in headers: | ||||
|         elif "content-length" not in headers: | ||||
|             if size: | ||||
|                 headers["content-length"] = size | ||||
|             else: | ||||
|                 headers["transfer-encoding"] = "chunked" | ||||
|  | ||||
|         headers = [ | ||||
|             (b":status", str(response.status).encode()), | ||||
|             *response.processed_headers, | ||||
| @@ -191,8 +195,18 @@ class HTTPReceiver(Receiver, Stream): | ||||
|         self.headers_sent = True | ||||
|         self.stage = Stage.RESPONSE | ||||
|  | ||||
|         if self.response.body and not self.head_only: | ||||
|             self._send(self.response.body, False) | ||||
|         elif self.head_only: | ||||
|             self.future.cancel() | ||||
|  | ||||
|     def respond(self, response: BaseHTTPResponse) -> BaseHTTPResponse: | ||||
|         """Prepare response to client""" | ||||
|         logger.debug(  # no cov | ||||
|             f"{Colors.BLUE}[respond]:{Colors.END} {response}", | ||||
|             extra={"verbosity": 2}, | ||||
|         ) | ||||
|  | ||||
|         if self.stage is not Stage.HANDLER: | ||||
|             self.stage = Stage.FAILED | ||||
|             raise RuntimeError("Response already started") | ||||
| @@ -215,14 +229,38 @@ class HTTPReceiver(Receiver, Stream): | ||||
|  | ||||
|     async def send(self, data: bytes, end_stream: bool) -> None: | ||||
|         """Send data to client""" | ||||
|         logger.debug(  # no cov | ||||
|             f"{Colors.BLUE}[send]: {Colors.GREEN}data={data.decode()} " | ||||
|             f"end_stream={end_stream}{Colors.END}", | ||||
|             extra={"verbosity": 2}, | ||||
|         ) | ||||
|         self._send(data, end_stream) | ||||
|  | ||||
|     def _send(self, data: bytes, end_stream: bool) -> None: | ||||
|         if not self.headers_sent: | ||||
|             self.send_headers() | ||||
|         if self.stage is not Stage.RESPONSE: | ||||
|             raise ServerError(f"not ready to send: {self.stage}") | ||||
|  | ||||
|         if data and self.head_only: | ||||
|             data = b"" | ||||
|         # Chunked | ||||
|         if ( | ||||
|             self.response | ||||
|             and self.response.headers.get("transfer-encoding") == "chunked" | ||||
|         ): | ||||
|             size = len(data) | ||||
|             if end_stream: | ||||
|                 data = ( | ||||
|                     b"%x\r\n%b\r\n0\r\n\r\n" % (size, data) | ||||
|                     if size | ||||
|                     else b"0\r\n\r\n" | ||||
|                 ) | ||||
|             elif size: | ||||
|                 data = b"%x\r\n%b\r\n" % (size, data) | ||||
|  | ||||
|         logger.debug(  # no cov | ||||
|             f"{Colors.BLUE}[transmitting]{Colors.END}", | ||||
|             extra={"verbosity": 2}, | ||||
|         ) | ||||
|         self.protocol.connection.send_data( | ||||
|             stream_id=self.request.stream_id, | ||||
|             data=data, | ||||
|   | ||||
| @@ -11,6 +11,20 @@ from sanic.cookies.request import CookieRequestParameters | ||||
| from sanic.exceptions import ServerError | ||||
| from sanic.response import text | ||||
| from sanic.response.convenience import json | ||||
| from sanic.cookies.request import parse_cookie | ||||
|  | ||||
| def test_request_cookies(): | ||||
|     cdict = parse_cookie("foo=one; foo=two; abc = xyz;;bare;=bare2") | ||||
|     assert cdict == { | ||||
|         "foo": ["one", "two"], | ||||
|         "abc": ["xyz"], | ||||
|         "": ["bare", "bare2"], | ||||
|     } | ||||
|     c = CookieRequestParameters(cdict) | ||||
|     assert c.getlist("foo") == ["one", "two"] | ||||
|     assert c.getlist("abc") == ["xyz"] | ||||
|     assert c.getlist("") == ["bare", "bare2"] | ||||
|     assert c.getlist("bare") == None   # [] might be sensible but we got None for now | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------ # | ||||
|   | ||||
		Reference in New Issue
	
	Block a user