|
1 | 1 | import re
|
2 | 2 | import time
|
| 3 | +from functools import cached_property |
3 | 4 | from http.server import BaseHTTPRequestHandler
|
4 | 5 | from urllib.parse import parse_qs, unquote, urlsplit
|
5 | 6 |
|
6 |
| -from httptools.parser import HttpRequestParser |
| 7 | +from h11 import SERVER, Connection, Data |
| 8 | +from h11 import Request as H11Request |
7 | 9 |
|
8 | 10 | from .compat import ENCODING, decode_from_bytes, do_the_magic, encode_to_bytes
|
9 | 11 | from .mocket import Mocket, MocketEntry
|
|
19 | 21 | ASCII = "ascii"
|
20 | 22 |
|
21 | 23 |
|
22 |
| -class Protocol: |
23 |
| - def __init__(self): |
24 |
| - self.url = None |
25 |
| - self.body = None |
26 |
| - self.headers = {} |
27 |
| - |
28 |
| - def on_header(self, name: bytes, value: bytes): |
29 |
| - self.headers[name.decode(ASCII)] = value.decode(ASCII) |
30 |
| - |
31 |
| - def on_body(self, body: bytes): |
32 |
| - try: |
33 |
| - self.body = body.decode(ENCODING) |
34 |
| - except UnicodeDecodeError: |
35 |
| - self.body = body |
36 |
| - |
37 |
| - def on_url(self, url: bytes): |
38 |
| - self.url = url.decode(ASCII) |
39 |
| - |
40 |
| - |
41 | 24 | class Request:
|
42 |
| - _protocol = None |
43 | 25 | _parser = None
|
| 26 | + _event = None |
44 | 27 |
|
45 | 28 | def __init__(self, data):
|
46 |
| - self._protocol = Protocol() |
47 |
| - self._parser = HttpRequestParser(self._protocol) |
| 29 | + self._parser = Connection(SERVER) |
48 | 30 | self.add_data(data)
|
49 | 31 |
|
50 | 32 | def add_data(self, data):
|
51 |
| - self._parser.feed_data(data) |
| 33 | + self._parser.receive_data(data) |
52 | 34 |
|
53 | 35 | @property
|
| 36 | + def event(self): |
| 37 | + if not self._event: |
| 38 | + event = self._parser.next_event() |
| 39 | + self._event = event |
| 40 | + return self._event |
| 41 | + |
| 42 | + @cached_property |
54 | 43 | def method(self):
|
55 |
| - return self._parser.get_method().decode(ASCII) |
| 44 | + return self.event.method.decode(ASCII) |
56 | 45 |
|
57 |
| - @property |
| 46 | + @cached_property |
58 | 47 | def path(self):
|
59 |
| - return self._protocol.url |
| 48 | + return self.event.target.decode(ASCII) |
60 | 49 |
|
61 |
| - @property |
| 50 | + @cached_property |
62 | 51 | def headers(self):
|
63 |
| - return self._protocol.headers |
| 52 | + return {k.decode(ASCII): v.decode(ASCII) for k, v in self.event.headers} |
64 | 53 |
|
65 |
| - @property |
| 54 | + @cached_property |
66 | 55 | def querystring(self):
|
67 |
| - parts = self._protocol.url.split("?", 1) |
| 56 | + parts = self.path.split("?", 1) |
68 | 57 | return (
|
69 | 58 | parse_qs(unquote(parts[1]), keep_blank_values=True)
|
70 | 59 | if len(parts) == 2
|
71 | 60 | else {}
|
72 | 61 | )
|
73 | 62 |
|
74 |
| - @property |
| 63 | + @cached_property |
75 | 64 | def body(self):
|
76 |
| - return self._protocol.body |
| 65 | + while True: |
| 66 | + event = self._parser.next_event() |
| 67 | + if isinstance(event, H11Request): |
| 68 | + self._event = event |
| 69 | + elif isinstance(event, Data): |
| 70 | + break |
| 71 | + return event.data.decode(ENCODING) |
77 | 72 |
|
78 | 73 | def __str__(self):
|
79 | 74 | return f"{self.method} - {self.path} - {self.headers}"
|
|
0 commit comments