Create basic request/response abstract models
This commit is contained in:
@@ -2,34 +2,18 @@ from __future__ import annotations
|
||||
|
||||
from turbosloth import SlothApp
|
||||
from turbosloth.exceptions import NotFoundException
|
||||
from turbosloth.types import Scope, Receive, Send
|
||||
from turbosloth.types import Scope, Receive, Send, BasicRequest, BasicResponse
|
||||
|
||||
app = SlothApp()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def index(scope: Scope, receive: Receive, send: Send):
|
||||
body = b"Hello, ASGI Router!"
|
||||
await send({
|
||||
"type": "http.response.start",
|
||||
"status": 200,
|
||||
"headers": [(b"content-type", b"text/plain")],
|
||||
})
|
||||
await send({
|
||||
"type": "http.response.body",
|
||||
"body": body,
|
||||
})
|
||||
async def index(req: BasicRequest) -> BasicResponse:
|
||||
return BasicResponse(200, {}, b'Hello, ASGI Router!')
|
||||
|
||||
|
||||
@app.get("/user/")
|
||||
async def get_user(scope: Scope, receive: Receive, send: Send):
|
||||
async def get_user(req: BasicRequest) -> BasicResponse:
|
||||
text = f"User ID: ".encode("utf-8")
|
||||
await send({
|
||||
"type": "http.response.start",
|
||||
"status": 200,
|
||||
"headers": [(b"content-type", b"text/plain")],
|
||||
})
|
||||
await send({
|
||||
"type": "http.response.body",
|
||||
"body": text,
|
||||
})
|
||||
|
||||
return BasicResponse(200, {}, b'Hello, User!')
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import json
|
||||
from typing import Optional, Callable, Awaitable, Protocol
|
||||
|
||||
from .exceptions import HTTPException
|
||||
from .router import Router
|
||||
from .types import Scope, Receive, Send, MethodType, HandlerType
|
||||
from .types import Scope, Receive, Send, MethodType, HandlerType, BasicRequest, BasicResponse
|
||||
|
||||
|
||||
class ASGIApp(Protocol):
|
||||
@@ -17,20 +18,27 @@ class HTTPApp(ASGIApp):
|
||||
method = scope['method']
|
||||
path = scope['path']
|
||||
|
||||
body = b''
|
||||
while True:
|
||||
event = await receive()
|
||||
body += event.get('body', b'')
|
||||
if not event.get('more_body', False):
|
||||
break
|
||||
scope['body'] = body
|
||||
|
||||
req = BasicRequest.from_scope(scope)
|
||||
|
||||
try:
|
||||
handler = self.router.match(method, path)
|
||||
await handler(scope, receive, send)
|
||||
resp = await handler(req)
|
||||
except HTTPException as e:
|
||||
await send({
|
||||
'type': 'http.response.start',
|
||||
'status': e.code,
|
||||
'headers': [(b'content-type', b'text/plain')],
|
||||
})
|
||||
await send({
|
||||
'type': 'http.response.body',
|
||||
'body': str(e).encode(),
|
||||
})
|
||||
return
|
||||
resp = BasicResponse(e.code, {'content-type': 'text/plain'}, str(e).encode())
|
||||
|
||||
await send(resp.into_start_message())
|
||||
await send({
|
||||
'type': 'http.response.body',
|
||||
'body': resp.body,
|
||||
})
|
||||
|
||||
|
||||
class WSApp(ASGIApp):
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
from typing import Callable, Awaitable, Dict, Literal
|
||||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
from dataclasses import dataclass
|
||||
from typing import Callable, Awaitable, Dict, Literal, Any, TypeVar
|
||||
|
||||
import urllib.parse
|
||||
|
||||
type Scope = Dict
|
||||
type Receive = Callable[[], Awaitable[Dict]]
|
||||
type Send = Callable[[Dict], Awaitable[None]]
|
||||
type HandlerType = Callable[[Scope, Receive, Send], Awaitable[None]]
|
||||
type HandlerType = Callable[[BasicRequest], Awaitable[BasicResponse]]
|
||||
|
||||
type MethodType = (
|
||||
Literal['GET'] |
|
||||
@@ -16,3 +22,47 @@ type MethodType = (
|
||||
Literal['CONNECT'] |
|
||||
Literal['OPTIONS'] |
|
||||
Literal['TRACE'])
|
||||
|
||||
K = TypeVar('K')
|
||||
V = TypeVar('V')
|
||||
|
||||
|
||||
@dataclass
|
||||
class BasicRequest:
|
||||
method: str
|
||||
path: str
|
||||
headers: dict[str, str]
|
||||
query: dict[str, list[str]]
|
||||
body: dict[str, Any]
|
||||
|
||||
@classmethod
|
||||
def from_scope(cls, scope: Scope) -> BasicRequest:
|
||||
path = scope['path']
|
||||
method = scope['method']
|
||||
headers = {}
|
||||
for key, value in scope.get('headers', []):
|
||||
headers[key.decode('latin1')] = value.decode('latin1')
|
||||
|
||||
query = urllib.parse.parse_qs(scope['query_string'].decode('latin1'))
|
||||
|
||||
body = scope['body']
|
||||
|
||||
return BasicRequest(method, path, headers, query, body)
|
||||
|
||||
|
||||
@dataclass
|
||||
class BasicResponse:
|
||||
code: int
|
||||
headers: dict[str, str]
|
||||
body: bytes
|
||||
|
||||
def into_start_message(self) -> dict:
|
||||
enc_headers = []
|
||||
for k, v in self.headers.items():
|
||||
enc_headers.append((k.encode('latin1'), v.encode('latin1')))
|
||||
|
||||
return {
|
||||
'type': 'http.response.start',
|
||||
'status': self.code,
|
||||
'headers': enc_headers
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user