Skip to content

Commit 267d2c1

Browse files
committed
[Refactor] http.model: better body
1 parent 805898d commit 267d2c1

File tree

5 files changed

+181
-99
lines changed

5 files changed

+181
-99
lines changed

examples/client-gzip.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import asyncio
2+
from extra.client import HTTPClient
3+
from extra.http.model import HTTPBodyBlob
4+
5+
import zlib
6+
7+
8+
class GzipDecoder:
9+
def __init__(self):
10+
self.decompressor = zlib.decompressobj(wbits=zlib.MAX_WBITS | 32)
11+
self.buffer = io.BytesIO()
12+
13+
def feed(self, chunk: bytes) -> bytes | None:
14+
return self.decompressor.decompress(chunk)
15+
16+
def flush(self) -> bytes | None:
17+
return self.decompressor.flush()
18+
19+
20+
# NOTE: Start "examples/sse.py"
21+
async def main(path: str, host: str = "127.0.0.1", port: int = 443, ssl: bool = True):
22+
transform = GzipDecoder()
23+
24+
with open("/dev/stdout", "wb") as f:
25+
async for atom in HTTPClient.Request(
26+
host=host,
27+
method="GET",
28+
port=port,
29+
path=path,
30+
timeout=11.0,
31+
streaming=False,
32+
headers={"Accept-Encoding": "gzip"},
33+
ssl=ssl,
34+
):
35+
if isinstance(atom, HTTPBodyBlob):
36+
f.write(transform.feed(atom.payload) or b"")
37+
f.write(transform.flush() or b"")
38+
39+
40+
if __name__ == "__main__":
41+
import sys
42+
43+
args = sys.argv[2:] or ["/index"]
44+
n = len(args)
45+
asyncio.run(
46+
main(
47+
path="/gh/lodash/lodash/4.17.15-npm/lodash.min.js",
48+
host="cdn.statically.io",
49+
)
50+
)
51+
# EOF

src/py/extra/client.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
from .http.model import (
1111
HTTPRequest,
1212
HTTPResponse,
13-
HTTPResponseStream,
14-
HTTPResponseAsyncStream,
13+
HTTPBodyStream,
14+
HTTPBodyAsyncStream,
1515
HTTPBodyBlob,
16-
HTTPResponseFile,
16+
HTTPBodyFile,
1717
HTTPHeaders,
18-
HTTPRequestBody,
18+
HTTPBody,
19+
HTTPReaderBody,
1920
HTTPAtom,
2021
HTTPProcessingStatus,
2122
)
@@ -348,15 +349,14 @@ async def OnRequest(
348349
head: dict[str, str] = request.headers
349350
if "Host" not in head:
350351
head["Host"] = host
351-
body = request.body
352+
body = request._body
352353
if not streaming and "Content-Length" not in head:
353354
head["Content-Length"] = (
354355
"0"
355356
if body is None
356357
else (
357358
str(body.length)
358-
if isinstance(body, HTTPBodyBlob)
359-
or isinstance(body, HTTPResponseFile)
359+
if isinstance(body, HTTPBodyBlob) or isinstance(body, HTTPBodyFile)
360360
else str(body.expected or "0")
361361
)
362362
)
@@ -371,7 +371,7 @@ async def OnRequest(
371371
# And send the request
372372
if isinstance(body, HTTPBodyBlob):
373373
cxn.writer.write(body.payload)
374-
elif isinstance(body, HTTPResponseFile):
374+
elif isinstance(body, HTTPBodyFile):
375375
fd: int = -1
376376
try:
377377
fd = os.open(str(body.path), os.O_RDONLY)
@@ -384,13 +384,13 @@ async def OnRequest(
384384
finally:
385385
if fd > 0:
386386
os.close(fd)
387-
elif isinstance(body, HTTPResponseStream):
387+
elif isinstance(body, HTTPBodyStream):
388388
# No keep alive with streaming as these are long
389389
# lived requests.
390390
for chunk in body.stream:
391391
cxn.writer.write(asWritable(chunk))
392392
await cxn.writer.drain()
393-
elif isinstance(body, HTTPResponseAsyncStream):
393+
elif isinstance(body, HTTPBodyAsyncStream):
394394
# No keep alive with streaming as these are long
395395
# lived requests.
396396
async for chunk in body.stream:
@@ -435,13 +435,19 @@ async def OnRequest(
435435
status = atom
436436
elif isinstance(atom, HTTPResponse):
437437
res = atom
438+
if res.body:
439+
yield res.body
438440
break
439441
else:
440442
yield atom
441443
iteration += 1
442444
if (
445+
# We continue if we have streaming
443446
streaming is True
444447
or res
448+
and res.body
449+
and HTTPBody.HasRemaining(res.body)
450+
or res
445451
and res.headers.contentType in {"text/event-stream"}
446452
):
447453
# TODO: We should swap out the body for a streaming body
@@ -475,7 +481,7 @@ async def Request(
475481
*,
476482
port: int | None = None,
477483
headers: dict[str, str] | None = None,
478-
body: HTTPRequestBody | HTTPBodyBlob | None = None,
484+
body: HTTPReaderBody | HTTPBodyBlob | None = None,
479485
params: dict[str, str] | str | None = None,
480486
ssl: bool = True,
481487
verified: bool = True,
@@ -587,7 +593,7 @@ async def request(
587593
*,
588594
port: int | None = None,
589595
headers: dict[str, str] | None = None,
590-
body: HTTPRequestBody | HTTPBodyBlob | None = None,
596+
body: HTTPReaderBody | HTTPBodyBlob | None = None,
591597
params: dict[str, str] | str | None = None,
592598
ssl: bool = True,
593599
verified: bool = True,

src/py/extra/handler.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
from .http.model import (
1212
HTTPRequest,
1313
HTTPHeaders,
14-
HTTPResponseFile,
15-
HTTPResponseStream,
16-
HTTPResponseAsyncStream,
14+
HTTPBodyFile,
15+
HTTPBodyStream,
16+
HTTPBodyAsyncStream,
1717
HTTPBodyBlob,
1818
HTTPResponse,
1919
headername,
@@ -144,18 +144,18 @@ async def FromResponse(response: HTTPResponse) -> dict[str, Any]:
144144
pass
145145
elif isinstance(response.body, HTTPBodyBlob):
146146
buffer.write(response.body.payload or b"")
147-
elif isinstance(response.body, HTTPResponseFile):
147+
elif isinstance(response.body, HTTPBodyFile):
148148
with open(response.body.path, "rb") as f:
149149
while data := f.read(32_000):
150150
buffer.write(data)
151-
elif isinstance(response.body, HTTPResponseStream):
151+
elif isinstance(response.body, HTTPBodyStream):
152152
# TODO: Should handle exception
153153
try:
154154
for chunk in response.body.stream:
155155
buffer.write(asWritable(chunk))
156156
finally:
157157
response.body.stream.close()
158-
elif isinstance(response.body, HTTPResponseAsyncStream):
158+
elif isinstance(response.body, HTTPBodyAsyncStream):
159159
# No keep alive with streaming as these are long
160160
# lived requests.
161161
try:

0 commit comments

Comments
 (0)