@@ -132,10 +132,14 @@ def _get_http_types(request):
132132def _get_world_helpers ():
133133 """Discover componentize-py's stream/future helpers on the world module.
134134
135- componentize-py generates ``byte_stream()`` (for ``stream<u8>``) and a
136- trailers-future constructor (for ``future<result<option<fields>,
137- error-code>>``) on the world module. The trailers-future function has
138- an auto-generated name, so we find it by inspecting the module.
135+ componentize-py generates ``byte_stream()`` (for ``stream<u8>``) and, for
136+ each distinct ``future`` type the world uses, a constructor with an
137+ auto-generated name. We need two of those futures, told apart by name:
138+
139+ - the **trailers** future ``future<result<option<fields>, error-code>>``,
140+ used when building a response, and
141+ - the **unit** future ``future<result<_, error-code>>``, which is the
142+ ``res`` argument to ``request.consume-body`` (and ``response.consume-body``).
139143 """
140144 global _world_helpers
141145 if _world_helpers is not None :
@@ -145,27 +149,50 @@ def _get_world_helpers():
145149
146150 byte_stream_fn = getattr (world , "byte_stream" , None )
147151
152+ # wasi:http/types has exactly two `error-code` futures: the trailers future
153+ # `result<option<fields>, error-code>` (named with "fields") and the unit
154+ # future `result<_, error-code>` (everything else). Tell them apart by the
155+ # presence of "fields" rather than relying on the exact "unit" spelling.
148156 trailers_future_fn = None
157+ unit_future_fn = None
149158 for name in dir (world ):
150- if name .endswith ("_future" ) and "fields" in name and "error_code" in name :
151- trailers_future_fn = getattr (world , name )
152- break
153-
154- _world_helpers = (byte_stream_fn , trailers_future_fn )
159+ if not (name .endswith ("_future" ) and "error_code" in name ):
160+ continue
161+ if "fields" in name :
162+ trailers_future_fn = trailers_future_fn or getattr (world , name )
163+ else :
164+ unit_future_fn = unit_future_fn or getattr (world , name )
165+
166+ _world_helpers = (byte_stream_fn , trailers_future_fn , unit_future_fn )
155167 return _world_helpers
156168
157169
158170def _make_trailers_future ():
159171 """Create a resolved trailers future indicating no trailers and no error."""
160172 from componentize_py_types import Ok
161- _ , trailers_future_fn = _get_world_helpers ()
173+ _ , trailers_future_fn , _ = _get_world_helpers ()
162174 if trailers_future_fn is None :
163175 raise RuntimeError (
164176 "Could not find trailers-future constructor on the world module"
165177 )
166178 return trailers_future_fn (lambda : Ok (None ))[1 ]
167179
168180
181+ def _make_unit_future ():
182+ """Create a resolved ``future<result<_, error-code>>`` (unit Ok, no error).
183+
184+ This is the ``res`` argument required by ``request.consume-body`` — it lets
185+ the caller signal an error in handling the request; we always resolve it Ok.
186+ """
187+ from componentize_py_types import Ok
188+ _ , _ , unit_future_fn = _get_world_helpers ()
189+ if unit_future_fn is None :
190+ raise RuntimeError (
191+ "Could not find unit result-future constructor on the world module"
192+ )
193+ return unit_future_fn (lambda : Ok (None ))[1 ]
194+
195+
169196def _make_body_stream (body_bytes ):
170197 """Wrap ``bytes`` in a WASIp3 ``stream<u8>`` readable end.
171198
@@ -175,7 +202,7 @@ def _make_body_stream(body_bytes):
175202 if not body_bytes :
176203 return None
177204
178- byte_stream_fn , _ = _get_world_helpers ()
205+ byte_stream_fn , _ , _ = _get_world_helpers ()
179206 if byte_stream_fn is None :
180207 raise RuntimeError (
181208 "Could not find byte_stream constructor on the world module"
@@ -219,7 +246,19 @@ async def handle(self, request):
219246 for name , value in headers_resource .copy_all ()
220247 )
221248
222- body = b""
249+ # Consume the incoming request body. In WASIp3 the body is a
250+ # `stream<u8>`: `consume-body` is a static func that *moves* the
251+ # request (so method/uri/headers must already be read, as above) and
252+ # returns the readable stream end plus a trailers future. Drain it to
253+ # bytes, reading until the writer end is closed.
254+ body_rx = http_types .Request .consume_body (request , _make_unit_future ())[0 ]
255+ body_chunks = []
256+ with body_rx :
257+ while not body_rx .writer_dropped :
258+ chunk = await body_rx .read (64 * 1024 )
259+ if chunk :
260+ body_chunks .append (chunk )
261+ body = b"" .join (body_chunks )
223262
224263 try :
225264 result = _component_instance .handle_request (
0 commit comments