Skip to content

Commit

Permalink
Bump httpx and h11 (#2048)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kludex committed Jul 17, 2023
1 parent cd18c3b commit 9be3116
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 51 deletions.
6 changes: 5 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
-e .[standard]

# TODO: Remove this after h11 makes a release. By this writing, h11 was on 0.14.0.
# Core dependencies
h11 @ git+https://github.com/python-hyper/h11.git@master

# Explicit optionals
a2wsgi==1.7.0
wsproto==1.2.0
Expand All @@ -21,7 +25,7 @@ trustme==0.9.0
cryptography==41.0.2
coverage==7.2.7
coverage-conditional-plugin==0.9.0
httpx==0.23.0
httpx==0.24.1
watchgod==0.8.2

# Documentation
Expand Down
81 changes: 31 additions & 50 deletions uvicorn/protocols/http/h11_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
Literal,
Optional,
Tuple,
Union,
cast,
)
from urllib.parse import unquote
Expand All @@ -21,7 +20,6 @@
ASGI3Application,
ASGIReceiveEvent,
ASGISendEvent,
HTTPDisconnectEvent,
HTTPRequestEvent,
HTTPResponseBodyEvent,
HTTPResponseStartEvent,
Expand All @@ -44,15 +42,6 @@
)
from uvicorn.server import ServerState

H11Event = Union[
h11.Request,
h11.InformationalResponse,
h11.Response,
h11.Data,
h11.EndOfMessage,
h11.ConnectionClosed,
]


def _get_status_phrase(status_code: int) -> bytes:
try:
Expand Down Expand Up @@ -201,20 +190,19 @@ def handle_events(self) -> None:
self.logger.warning(msg)
self.send_400_response(msg)
return
event_type = type(event)

if event_type is h11.NEED_DATA:
if event is h11.NEED_DATA:
break

elif event_type is h11.PAUSED:
elif event is h11.PAUSED:
# This case can occur in HTTP pipelining, so we need to
# stop reading any more data, and ensure that at the end
# of the active request/response cycle we handle any
# events that have been buffered up.
self.flow.pause_reading()
break

elif event_type is h11.Request:
elif isinstance(event, h11.Request):
self.headers = [(key.lower(), value) for key, value in event.headers]
raw_path, _, query_string = event.target.partition(b"?")
self.scope = {
Expand Down Expand Up @@ -268,23 +256,23 @@ def handle_events(self) -> None:
task.add_done_callback(self.tasks.discard)
self.tasks.add(task)

elif event_type is h11.Data:
elif isinstance(event, h11.Data):
if self.conn.our_state is h11.DONE:
continue
self.cycle.body += event.data
if len(self.cycle.body) > HIGH_WATER_LIMIT:
self.flow.pause_reading()
self.cycle.message_event.set()

elif event_type is h11.EndOfMessage:
elif isinstance(event, h11.EndOfMessage):
if self.conn.our_state is h11.DONE:
self.transport.resume_reading()
self.conn.start_next_cycle()
continue
self.cycle.more_body = False
self.cycle.message_event.set()

def handle_websocket_upgrade(self, event: H11Event) -> None:
def handle_websocket_upgrade(self, event: h11.Request) -> None:
if self.logger.level <= TRACE_LOG_LEVEL:
prefix = "%s:%d - " % self.client if self.client else ""
self.logger.log(TRACE_LOG_LEVEL, "%sUpgrading to WebSocket", prefix)
Expand All @@ -305,19 +293,20 @@ def handle_websocket_upgrade(self, event: H11Event) -> None:

def send_400_response(self, msg: str) -> None:
reason = STATUS_PHRASES[400]
headers = [
headers: List[Tuple[bytes, bytes]] = [
(b"content-type", b"text/plain; charset=utf-8"),
(b"connection", b"close"),
]
event = h11.Response(status_code=400, headers=headers, reason=reason)
output = self.conn.send(event)
self.transport.write(output)
event = h11.Data(data=msg.encode("ascii"))
output = self.conn.send(event)

output = self.conn.send(event=h11.Data(data=msg.encode("ascii")))
self.transport.write(output)
event = h11.EndOfMessage()
output = self.conn.send(event)

output = self.conn.send(event=h11.EndOfMessage())
self.transport.write(output)

self.transport.close()

def on_response_complete(self) -> None:
Expand Down Expand Up @@ -479,7 +468,7 @@ async def send(self, message: "ASGISendEvent") -> None:
self.response_started = True
self.waiting_for_100_continue = False

status_code = message["status"]
status = message["status"]
headers = self.default_headers + list(message.get("headers", []))

if CLOSE_HEADER in self.scope["headers"] and CLOSE_HEADER not in headers:
Expand All @@ -492,15 +481,13 @@ async def send(self, message: "ASGISendEvent") -> None:
self.scope["method"],
get_path_with_query_string(self.scope),
self.scope["http_version"],
status_code,
status,
)

# Write response status line and headers
reason = STATUS_PHRASES[status_code]
event = h11.Response(
status_code=status_code, headers=headers, reason=reason
)
output = self.conn.send(event)
reason = STATUS_PHRASES[status]
response = h11.Response(status_code=status, headers=headers, reason=reason)
output = self.conn.send(event=response)
self.transport.write(output)

elif not self.response_complete:
Expand All @@ -514,19 +501,15 @@ async def send(self, message: "ASGISendEvent") -> None:
more_body = message.get("more_body", False)

# Write response body
if self.scope["method"] == "HEAD":
event = h11.Data(data=b"")
else:
event = h11.Data(data=body)
output = self.conn.send(event)
data = b"" if self.scope["method"] == "HEAD" else body
output = self.conn.send(event=h11.Data(data=data))
self.transport.write(output)

# Handle response completion
if not more_body:
self.response_complete = True
self.message_event.set()
event = h11.EndOfMessage()
output = self.conn.send(event)
output = self.conn.send(event=h11.EndOfMessage())
self.transport.write(output)

else:
Expand All @@ -536,17 +519,17 @@ async def send(self, message: "ASGISendEvent") -> None:

if self.response_complete:
if self.conn.our_state is h11.MUST_CLOSE or not self.keep_alive:
event = h11.ConnectionClosed()
self.conn.send(event)
self.conn.send(event=h11.ConnectionClosed())
self.transport.close()
self.on_response()

async def receive(self) -> "ASGIReceiveEvent":
if self.waiting_for_100_continue and not self.transport.is_closing():
headers: List[Tuple[str, str]] = []
event = h11.InformationalResponse(
status_code=100, headers=[], reason="Continue"
status_code=100, headers=headers, reason="Continue"
)
output = self.conn.send(event)
output = self.conn.send(event=event)
self.transport.write(output)
self.waiting_for_100_continue = False

Expand All @@ -555,15 +538,13 @@ async def receive(self) -> "ASGIReceiveEvent":
await self.message_event.wait()
self.message_event.clear()

message: "Union[HTTPDisconnectEvent, HTTPRequestEvent]"
if self.disconnected or self.response_complete:
message = {"type": "http.disconnect"}
else:
message = {
"type": "http.request",
"body": self.body,
"more_body": self.more_body,
}
self.body = b""
return {"type": "http.disconnect"}

message: "HTTPRequestEvent" = {
"type": "http.request",
"body": self.body,
"more_body": self.more_body,
}
self.body = b""
return message

0 comments on commit 9be3116

Please sign in to comment.