Module spin_sdk.http
Module with helpers for wasi http
Sub-modules
spin_sdk.http.poll_loop
-
Defines a custom
asyncio
event loop backed bywasi:io/poll#poll
…
Functions
def send(request: Request) ‑> Response
-
Expand source code
def send(request: Request) -> Response: """Send an HTTP request and return a response or raise an error""" loop = PollLoop() asyncio.set_event_loop(loop) return loop.run_until_complete(send_async(request))
Send an HTTP request and return a response or raise an error
async def send_and_close(sink: Sink,
data: bytes)-
Expand source code
async def send_and_close(sink: Sink, data: bytes): await sink.send(data) sink.close()
async def send_async(request: Request) ‑> Response
-
Expand source code
async def send_async(request: Request) -> Response: match request.method: case "GET": method: Method = Method_Get() case "HEAD": method = Method_Head() case "POST": method = Method_Post() case "PUT": method = Method_Put() case "DELETE": method = Method_Delete() case "CONNECT": method = Method_Connect() case "OPTIONS": method = Method_Options() case "TRACE": method = Method_Trace() case "PATCH": method = Method_Patch() case _: method = Method_Other(request.method) url_parsed = parse.urlparse(request.uri) match url_parsed.scheme: case "http": scheme: Scheme = Scheme_Http() case "https": scheme = Scheme_Https() case "": scheme = Scheme_Http() case _: scheme = Scheme_Other(url_parsed.scheme) headers_dict = request.headers # Add a `content-length` header if the caller didn't include one, but did # specify a body: if headers_dict.get('content-length') is None: content_length = len(request.body) if request.body is not None else 0 # Make a copy rather than mutate in place, since the caller might not # expect us to mutate it: headers_dict = headers_dict.copy() headers_dict['content-length'] = str(content_length) headers = list(map( lambda pair: (pair[0], bytes(pair[1], "utf-8")), headers_dict.items() )) outgoing_request = OutgoingRequest(Fields.from_list(headers)) outgoing_request.set_method(method) outgoing_request.set_scheme(scheme) if url_parsed.netloc == '': if scheme == "http": authority = ":80" else: authority = ":443" else: authority = url_parsed.netloc outgoing_request.set_authority(authority) path_and_query = url_parsed.path if url_parsed.query: path_and_query += '?' + url_parsed.query outgoing_request.set_path_with_query(path_and_query) outgoing_body = request.body if request.body is not None else bytearray() sink = Sink(outgoing_request.body()) incoming_response: IncomingResponse = (await asyncio.gather( poll_loop.send(outgoing_request), send_and_close(sink, outgoing_body) ))[0] response_body = Stream(incoming_response.consume()) body = bytearray() while True: chunk = await response_body.next() if chunk is None: headers = incoming_response.headers() simple_response = Response( incoming_response.status(), dict(map( lambda pair: (pair[0], str(pair[1], "utf-8")), headers.entries() )), bytes(body) ) headers.__exit__(None, None, None) incoming_response.__exit__(None, None, None) return simple_response else: body += chunk
Classes
class IncomingHandler (*args, **kwargs)
-
Expand source code
class IncomingHandler(Base): """Simplified handler for incoming HTTP requests using blocking, buffered I/O.""" def handle_request(self, request: Request) -> Response: """Handle an incoming HTTP request and return a response or raise an error""" raise NotImplementedError def handle(self, request: IncomingRequest, response_out: ResponseOutparam): method = request.method() if isinstance(method, Method_Get): method_str = "GET" elif isinstance(method, Method_Head): method_str = "HEAD" elif isinstance(method, Method_Post): method_str = "POST" elif isinstance(method, Method_Put): method_str = "PUT" elif isinstance(method, Method_Delete): method_str = "DELETE" elif isinstance(method, Method_Connect): method_str = "CONNECT" elif isinstance(method, Method_Options): method_str = "OPTIONS" elif isinstance(method, Method_Trace): method_str = "TRACE" elif isinstance(method, Method_Patch): method_str = "PATCH" elif isinstance(method, Method_Other): method_str = method.value else: raise AssertionError request_body = request.consume() request_stream = request_body.stream() body = bytearray() while True: try: body += request_stream.blocking_read(16 * 1024) except Err as e: if isinstance(e.value, StreamError_Closed): request_stream.__exit__(None, None, None) IncomingBody.finish(request_body) break else: raise e request_uri = request.path_with_query() if request_uri is None: uri = "/" else: uri = request_uri try: simple_response = self.handle_request(Request( method_str, uri, dict(map(lambda pair: (pair[0], str(pair[1], "utf-8")), request.headers().entries())), bytes(body) )) except: traceback.print_exc() response = OutgoingResponse(Fields()) response.set_status_code(500) ResponseOutparam.set(response_out, Ok(response)) return if simple_response.headers.get('content-length') is None: content_length = len(simple_response.body) if simple_response.body is not None else 0 simple_response.headers['content-length'] = str(content_length) response = OutgoingResponse(Fields.from_list(list(map( lambda pair: (pair[0], bytes(pair[1], "utf-8")), simple_response.headers.items() )))) response_body = response.body() response.set_status_code(simple_response.status) ResponseOutparam.set(response_out, Ok(response)) response_stream = response_body.write() if simple_response.body is not None: MAX_BLOCKING_WRITE_SIZE = 4096 offset = 0 while offset < len(simple_response.body): count = min(len(simple_response.body) - offset, MAX_BLOCKING_WRITE_SIZE) response_stream.blocking_write_and_flush(simple_response.body[offset:offset+count]) offset += count response_stream.__exit__(None, None, None) OutgoingBody.finish(response_body, None)
Simplified handler for incoming HTTP requests using blocking, buffered I/O.
Ancestors
- IncomingHandler
- typing.Protocol
- typing.Generic
Methods
def handle_request(self,
request: Request) ‑> Response-
Expand source code
def handle_request(self, request: Request) -> Response: """Handle an incoming HTTP request and return a response or raise an error""" raise NotImplementedError
Handle an incoming HTTP request and return a response or raise an error
Inherited members
class Request (method: str, uri: str, headers: MutableMapping[str, str], body: bytes | None)
-
Expand source code
@dataclass class Request: """An HTTP request""" method: str uri: str headers: MutableMapping[str, str] body: Optional[bytes]
An HTTP request
Instance variables
var body : bytes | None
var headers : MutableMapping[str, str]
var method : str
var uri : str
class Response (status: int, headers: MutableMapping[str, str], body: bytes | None)
-
Expand source code
@dataclass class Response: """An HTTP response""" status: int headers: MutableMapping[str, str] body: Optional[bytes]
An HTTP response
Instance variables
var body : bytes | None
var headers : MutableMapping[str, str]
var status : int