|
4 | 4 | import traceback |
5 | 5 | from spin_sdk.http import poll_loop |
6 | 6 | from spin_sdk.http.poll_loop import PollLoop, Sink, Stream |
7 | | -from spin_sdk.wit import exports |
8 | 7 | from spin_sdk.wit.types import Ok, Err |
9 | 8 | from spin_sdk.wit.imports.types import ( |
10 | 9 | IncomingResponse, Method, Method_Get, Method_Head, Method_Post, Method_Put, Method_Delete, Method_Connect, Method_Options, |
@@ -32,96 +31,105 @@ class Response: |
32 | 31 | headers: MutableMapping[str, str] |
33 | 32 | body: Optional[bytes] |
34 | 33 |
|
35 | | -class IncomingHandler(exports.IncomingHandler): |
36 | | - """Simplified handler for incoming HTTP requests using blocking, buffered I/O.""" |
| 34 | +try: |
| 35 | + from spin_sdk.wit import exports |
| 36 | + from spin_sdk.wit.exports import IncomingHandler as Base |
37 | 37 |
|
38 | | - def handle_request(self, request: Request) -> Response: |
39 | | - """Handle an incoming HTTP request and return a response or raise an error""" |
40 | | - raise NotImplementedError |
| 38 | + class IncomingHandler(Base): |
| 39 | + """Simplified handler for incoming HTTP requests using blocking, buffered I/O.""" |
| 40 | + |
| 41 | + def handle_request(self, request: Request) -> Response: |
| 42 | + """Handle an incoming HTTP request and return a response or raise an error""" |
| 43 | + raise NotImplementedError |
| 44 | + |
| 45 | + def handle(self, request: IncomingRequest, response_out: ResponseOutparam): |
| 46 | + method = request.method() |
| 47 | + |
| 48 | + if isinstance(method, Method_Get): |
| 49 | + method_str = "GET" |
| 50 | + elif isinstance(method, Method_Head): |
| 51 | + method_str = "HEAD" |
| 52 | + elif isinstance(method, Method_Post): |
| 53 | + method_str = "POST" |
| 54 | + elif isinstance(method, Method_Put): |
| 55 | + method_str = "PUT" |
| 56 | + elif isinstance(method, Method_Delete): |
| 57 | + method_str = "DELETE" |
| 58 | + elif isinstance(method, Method_Connect): |
| 59 | + method_str = "CONNECT" |
| 60 | + elif isinstance(method, Method_Options): |
| 61 | + method_str = "OPTIONS" |
| 62 | + elif isinstance(method, Method_Trace): |
| 63 | + method_str = "TRACE" |
| 64 | + elif isinstance(method, Method_Patch): |
| 65 | + method_str = "PATCH" |
| 66 | + elif isinstance(method, Method_Other): |
| 67 | + method_str = method.value |
| 68 | + else: |
| 69 | + raise AssertionError |
| 70 | + |
| 71 | + request_body = request.consume() |
| 72 | + request_stream = request_body.stream() |
| 73 | + body = bytearray() |
| 74 | + while True: |
| 75 | + try: |
| 76 | + body += request_stream.blocking_read(16 * 1024) |
| 77 | + except Err as e: |
| 78 | + if isinstance(e.value, StreamError_Closed): |
| 79 | + request_stream.__exit__() |
| 80 | + IncomingBody.finish(request_body) |
| 81 | + break |
| 82 | + else: |
| 83 | + raise e |
| 84 | + |
| 85 | + request_uri = request.path_with_query() |
| 86 | + if request_uri is None: |
| 87 | + uri = "/" |
| 88 | + else: |
| 89 | + uri = request_uri |
41 | 90 |
|
42 | | - def handle(self, request: IncomingRequest, response_out: ResponseOutparam): |
43 | | - method = request.method() |
44 | | - |
45 | | - if isinstance(method, Method_Get): |
46 | | - method_str = "GET" |
47 | | - elif isinstance(method, Method_Head): |
48 | | - method_str = "HEAD" |
49 | | - elif isinstance(method, Method_Post): |
50 | | - method_str = "POST" |
51 | | - elif isinstance(method, Method_Put): |
52 | | - method_str = "PUT" |
53 | | - elif isinstance(method, Method_Delete): |
54 | | - method_str = "DELETE" |
55 | | - elif isinstance(method, Method_Connect): |
56 | | - method_str = "CONNECT" |
57 | | - elif isinstance(method, Method_Options): |
58 | | - method_str = "OPTIONS" |
59 | | - elif isinstance(method, Method_Trace): |
60 | | - method_str = "TRACE" |
61 | | - elif isinstance(method, Method_Patch): |
62 | | - method_str = "PATCH" |
63 | | - elif isinstance(method, Method_Other): |
64 | | - method_str = method.value |
65 | | - else: |
66 | | - raise AssertionError |
67 | | - |
68 | | - request_body = request.consume() |
69 | | - request_stream = request_body.stream() |
70 | | - body = bytearray() |
71 | | - while True: |
72 | 91 | try: |
73 | | - body += request_stream.blocking_read(16 * 1024) |
74 | | - except Err as e: |
75 | | - if isinstance(e.value, StreamError_Closed): |
76 | | - request_stream.__exit__() |
77 | | - IncomingBody.finish(request_body) |
78 | | - break |
79 | | - else: |
80 | | - raise e |
81 | | - |
82 | | - request_uri = request.path_with_query() |
83 | | - if request_uri is None: |
84 | | - uri = "/" |
85 | | - else: |
86 | | - uri = request_uri |
87 | | - |
88 | | - try: |
89 | | - simple_response = self.handle_request(Request( |
90 | | - method_str, |
91 | | - uri, |
92 | | - dict(map(lambda pair: (pair[0], str(pair[1], "utf-8")), request.headers().entries())), |
93 | | - bytes(body) |
94 | | - )) |
95 | | - except: |
96 | | - traceback.print_exc() |
97 | | - |
98 | | - response = OutgoingResponse(Fields()) |
99 | | - response.set_status_code(500) |
100 | | - ResponseOutparam.set(response_out, Ok(response)) |
101 | | - return |
102 | | - |
103 | | - if simple_response.headers.get('content-length') is None: |
104 | | - content_length = len(simple_response.body) if simple_response.body is not None else 0 |
105 | | - simple_response.headers['content-length'] = str(content_length) |
106 | | - |
107 | | - response = OutgoingResponse(Fields.from_list(list(map( |
108 | | - lambda pair: (pair[0], bytes(pair[1], "utf-8")), |
109 | | - simple_response.headers.items() |
110 | | - )))) |
111 | | - response_body = response.body() |
112 | | - response.set_status_code(simple_response.status) |
113 | | - ResponseOutparam.set(response_out, Ok(response)) |
114 | | - response_stream = response_body.write() |
115 | | - if simple_response.body is not None: |
116 | | - MAX_BLOCKING_WRITE_SIZE = 4096 |
117 | | - offset = 0 |
118 | | - while offset < len(simple_response.body): |
119 | | - count = min(len(simple_response.body) - offset, MAX_BLOCKING_WRITE_SIZE) |
120 | | - response_stream.blocking_write_and_flush(simple_response.body[offset:offset+count]) |
121 | | - offset += count |
122 | | - response_stream.__exit__() |
123 | | - OutgoingBody.finish(response_body, None) |
| 92 | + simple_response = self.handle_request(Request( |
| 93 | + method_str, |
| 94 | + uri, |
| 95 | + dict(map(lambda pair: (pair[0], str(pair[1], "utf-8")), request.headers().entries())), |
| 96 | + bytes(body) |
| 97 | + )) |
| 98 | + except: |
| 99 | + traceback.print_exc() |
124 | 100 |
|
| 101 | + response = OutgoingResponse(Fields()) |
| 102 | + response.set_status_code(500) |
| 103 | + ResponseOutparam.set(response_out, Ok(response)) |
| 104 | + return |
| 105 | + |
| 106 | + if simple_response.headers.get('content-length') is None: |
| 107 | + content_length = len(simple_response.body) if simple_response.body is not None else 0 |
| 108 | + simple_response.headers['content-length'] = str(content_length) |
| 109 | + |
| 110 | + response = OutgoingResponse(Fields.from_list(list(map( |
| 111 | + lambda pair: (pair[0], bytes(pair[1], "utf-8")), |
| 112 | + simple_response.headers.items() |
| 113 | + )))) |
| 114 | + response_body = response.body() |
| 115 | + response.set_status_code(simple_response.status) |
| 116 | + ResponseOutparam.set(response_out, Ok(response)) |
| 117 | + response_stream = response_body.write() |
| 118 | + if simple_response.body is not None: |
| 119 | + MAX_BLOCKING_WRITE_SIZE = 4096 |
| 120 | + offset = 0 |
| 121 | + while offset < len(simple_response.body): |
| 122 | + count = min(len(simple_response.body) - offset, MAX_BLOCKING_WRITE_SIZE) |
| 123 | + response_stream.blocking_write_and_flush(simple_response.body[offset:offset+count]) |
| 124 | + offset += count |
| 125 | + response_stream.__exit__() |
| 126 | + OutgoingBody.finish(response_body, None) |
| 127 | + |
| 128 | +except ImportError: |
| 129 | + # `spin_sdk.wit.exports` won't exist if the use is targeting `spin-imports`, |
| 130 | + # so just skip this part |
| 131 | + pass |
| 132 | + |
125 | 133 | def send(request: Request) -> Response: |
126 | 134 | """Send an HTTP request and return a response or raise an error""" |
127 | 135 | loop = PollLoop() |
|
0 commit comments