0
# HTTP Services
1
2
HTTP server functionality with REST support, request/response abstractions, routing, and connector patterns. Provides the foundation for building HTTP APIs and web services with the Minos framework.
3
4
## Capabilities
5
6
### HTTP Port and Service Management
7
8
Port classes for managing HTTP service lifecycle with automatic setup and teardown.
9
10
```python { .api }
11
class HttpPort:
12
connector: HttpConnector
13
@staticmethod
14
def _get_connector(connector: Optional[HttpConnector] = None, http_connector: Optional[HttpConnector] = None, **kwargs) -> HttpConnector: ...
15
async def _start(self) -> None: ...
16
async def _stop(self, err: Exception = None) -> None: ...
17
18
class RestService:
19
"""Deprecated - use HttpPort instead"""
20
```
21
22
**Usage Examples:**
23
24
```python
25
from minos.networks import HttpPort
26
from minos.common import Config
27
28
# Create HTTP port from configuration
29
config = Config("config.yml")
30
http_port = HttpPort._from_config(config)
31
32
# Start the HTTP server
33
await http_port.start()
34
35
# Server runs and handles requests...
36
37
# Stop the HTTP server
38
await http_port.stop()
39
```
40
41
### HTTP Adapters
42
43
Adapters that manage HTTP routers and provide route aggregation.
44
45
```python { .api }
46
class HttpAdapter:
47
def __init__(self, routers: list[HttpRouter]): ...
48
routes: dict[HttpEnrouteDecorator, Callable]
49
routers: list[HttpRouter]
50
@classmethod
51
def _from_config(cls, config: Config, **kwargs) -> HttpAdapter: ...
52
@staticmethod
53
def _routers_from_config(config: Config, **kwargs) -> list[HttpRouter]: ...
54
```
55
56
**Usage Examples:**
57
58
```python
59
from minos.networks import HttpAdapter, RestHttpRouter
60
61
# Create routers
62
router = RestHttpRouter._from_config(config)
63
64
# Create adapter with routers
65
adapter = HttpAdapter(routers=[router])
66
67
# Access aggregated routes
68
routes = adapter.routes
69
for decorator, callback in routes.items():
70
print(f"Route: {decorator.path} {decorator.method}")
71
```
72
73
### HTTP Connectors
74
75
Abstract base classes for HTTP server implementations that bridge framework-specific HTTP servers with Minos request/response system.
76
77
```python { .api }
78
from typing import TypeVar, Generic, Callable, Awaitable
79
80
RawRequest = TypeVar("RawRequest")
81
RawResponse = TypeVar("RawResponse")
82
83
class HttpConnector(Generic[RawRequest, RawResponse]):
84
def __init__(self, adapter: HttpAdapter, host: Optional[str] = None, port: Optional[int] = None, max_connections: int = 5): ...
85
host: str # default: "0.0.0.0"
86
port: int # default: 8080
87
routes: dict[HttpEnrouteDecorator, Callable]
88
adapter: HttpAdapter
89
@classmethod
90
def _from_config(cls, config: Config, **kwargs) -> HttpConnector: ...
91
async def start(self) -> None: ...
92
async def stop(self) -> None: ...
93
def mount_routes(self) -> None: ...
94
def mount_route(self, path: str, method: str, callback: Callable[[Request], Optional[Response]]) -> None: ...
95
def adapt_callback(self, callback: Callable[[Request], Union[Optional[Response], Awaitable[Optional[Response]]]]) -> Callable[[RawRequest], Awaitable[RawResponse]]: ...
96
97
# Abstract methods - implement in subclasses
98
async def _start(self) -> None: ...
99
async def _stop(self) -> None: ...
100
def _mount_route(self, path: str, method: str, adapted_callback: Callable) -> None: ...
101
async def _build_request(self, request: RawRequest) -> Request: ...
102
async def _build_response(self, response: Optional[Response]) -> RawResponse: ...
103
async def _build_error_response(self, message: str, status: int) -> RawResponse: ...
104
```
105
106
**Usage Examples:**
107
108
```python
109
# Custom connector implementation
110
class FastAPIConnector(HttpConnector[FastAPIRequest, FastAPIResponse]):
111
async def _start(self) -> None:
112
# Start FastAPI server
113
pass
114
115
async def _stop(self) -> None:
116
# Stop FastAPI server
117
pass
118
119
def _mount_route(self, path: str, method: str, adapted_callback: Callable) -> None:
120
# Mount route on FastAPI app
121
pass
122
123
async def _build_request(self, request: FastAPIRequest) -> Request:
124
# Convert FastAPI request to Minos Request
125
pass
126
127
async def _build_response(self, response: Optional[Response]) -> FastAPIResponse:
128
# Convert Minos Response to FastAPI response
129
pass
130
131
# Using the connector
132
connector = FastAPIConnector(adapter=adapter, host="localhost", port=8080)
133
await connector.start()
134
```
135
136
### HTTP Request Handling
137
138
HTTP-specific request classes with URL parameters, query parameters, and header support.
139
140
```python { .api }
141
class HttpRequest:
142
user: Optional[UUID]
143
headers: dict[str, str]
144
content_type: str
145
has_url_params: bool
146
has_query_params: bool
147
async def url_params(self, type_: Optional[Union[type, str]] = None, **kwargs) -> Any: ...
148
async def query_params(self, type_: Optional[Union[type, str]] = None, **kwargs) -> Any: ...
149
150
# Inherited from Request
151
has_content: bool
152
has_params: bool
153
async def content(self, **kwargs) -> Any: ...
154
async def params(self, **kwargs) -> dict[str, Any]: ...
155
```
156
157
**Usage Examples:**
158
159
```python
160
@enroute.rest.query("/users/{user_id}", method="GET")
161
async def get_user(request: HttpRequest) -> Response:
162
# Get URL parameters
163
url_params = await request.url_params()
164
user_id = url_params["user_id"]
165
166
# Get query parameters
167
query_params = await request.query_params()
168
include_posts = query_params.get("include_posts", False)
169
170
# Access headers
171
auth_header = request.headers.get("Authorization")
172
173
# Get request body (if any)
174
if request.has_content:
175
body = await request.content()
176
177
return Response({"user_id": user_id, "include_posts": include_posts})
178
179
@enroute.rest.command("/users/{user_id}", method="PUT")
180
async def update_user(request: HttpRequest) -> Response:
181
# URL parameters from path
182
url_params = await request.url_params()
183
user_id = url_params["user_id"]
184
185
# Request body content
186
user_data = await request.content()
187
188
# Update user logic
189
updated_user = {"id": user_id, **user_data}
190
191
return Response(updated_user)
192
```
193
194
### HTTP Response Handling
195
196
HTTP-specific response classes with content type management and status codes.
197
198
```python { .api }
199
class HttpResponse:
200
def __init__(self, *args, content_type: str = "application/json", **kwargs): ...
201
content_type: str
202
status: int
203
has_content: bool
204
async def content(self, **kwargs) -> Any: ...
205
@classmethod
206
def from_response(cls, response: Optional[Response]) -> HttpResponse: ...
207
208
class HttpResponseException:
209
status: int # default: 400
210
```
211
212
**Usage Examples:**
213
214
```python
215
@enroute.rest.query("/users/{user_id}", method="GET")
216
async def get_user(request: HttpRequest) -> HttpResponse:
217
url_params = await request.url_params()
218
user_id = url_params["user_id"]
219
220
# Return JSON response (default)
221
return HttpResponse({"id": user_id, "name": "John Doe"})
222
223
@enroute.rest.query("/users/{user_id}/avatar", method="GET")
224
async def get_user_avatar(request: HttpRequest) -> HttpResponse:
225
# Return image response
226
avatar_data = get_avatar_bytes()
227
return HttpResponse(
228
avatar_data,
229
content_type="image/png",
230
status=200
231
)
232
233
@enroute.rest.command("/users", method="POST")
234
async def create_user(request: HttpRequest) -> HttpResponse:
235
try:
236
user_data = await request.content()
237
if not user_data.get("email"):
238
raise HttpResponseException(status=400)
239
240
# Create user logic
241
new_user = {"id": "123", **user_data}
242
return HttpResponse(new_user, status=201)
243
244
except ValueError:
245
raise HttpResponseException(status=400)
246
```
247
248
### Router Integration
249
250
Integration with the routing system for HTTP request handling.
251
252
```python { .api }
253
class RestHttpRouter:
254
routes: dict[HttpEnrouteDecorator, Callable]
255
def _filter_routes(self, routes: dict[EnrouteDecorator, Callable]) -> dict[EnrouteDecorator, Callable]: ...
256
```
257
258
**Usage Examples:**
259
260
```python
261
from minos.networks import RestHttpRouter, enroute
262
263
class UserController:
264
@enroute.rest.query("/users", method="GET")
265
async def list_users(self, request: HttpRequest) -> HttpResponse:
266
return HttpResponse({"users": []})
267
268
@enroute.rest.command("/users", method="POST")
269
async def create_user(self, request: HttpRequest) -> HttpResponse:
270
user_data = await request.content()
271
return HttpResponse({"id": "123", **user_data}, status=201)
272
273
# Create router and filter routes
274
config = Config("config.yml")
275
router = RestHttpRouter._from_config(config)
276
277
# Router automatically discovers and filters HTTP routes
278
http_routes = router.routes
279
```
280
281
## Advanced Usage
282
283
### Complete HTTP Service Setup
284
285
```python
286
from minos.networks import HttpPort, enroute, HttpRequest, HttpResponse
287
from minos.common import Config
288
289
class UserAPI:
290
@enroute.rest.query("/users", method="GET")
291
async def list_users(self, request: HttpRequest) -> HttpResponse:
292
query_params = await request.query_params()
293
page = query_params.get("page", 1)
294
limit = query_params.get("limit", 10)
295
296
# Fetch users with pagination
297
users = fetch_users(page=page, limit=limit)
298
return HttpResponse({"users": users, "page": page})
299
300
@enroute.rest.command("/users", method="POST")
301
async def create_user(self, request: HttpRequest) -> HttpResponse:
302
user_data = await request.content()
303
304
# Validate required fields
305
if not user_data.get("email"):
306
return HttpResponse({"error": "Email required"}, status=400)
307
308
# Create user
309
new_user = create_user(user_data)
310
return HttpResponse(new_user, status=201)
311
312
@enroute.rest.query("/users/{user_id}", method="GET")
313
async def get_user(self, request: HttpRequest) -> HttpResponse:
314
url_params = await request.url_params()
315
user_id = url_params["user_id"]
316
317
user = get_user_by_id(user_id)
318
if not user:
319
return HttpResponse({"error": "User not found"}, status=404)
320
321
return HttpResponse(user)
322
323
@enroute.rest.command("/users/{user_id}", method="PUT")
324
async def update_user(self, request: HttpRequest) -> HttpResponse:
325
url_params = await request.url_params()
326
user_id = url_params["user_id"]
327
user_data = await request.content()
328
329
updated_user = update_user(user_id, user_data)
330
return HttpResponse(updated_user)
331
332
@enroute.rest.command("/users/{user_id}", method="DELETE")
333
async def delete_user(self, request: HttpRequest) -> HttpResponse:
334
url_params = await request.url_params()
335
user_id = url_params["user_id"]
336
337
delete_user(user_id)
338
return HttpResponse(status=204)
339
340
# Setup HTTP service
341
config = Config("config.yml")
342
http_port = HttpPort._from_config(config)
343
344
# Start HTTP server
345
await http_port.start()
346
print(f"HTTP server running on {http_port.connector.host}:{http_port.connector.port}")
347
348
# Server handles requests automatically based on decorators
349
# Stop when done
350
await http_port.stop()
351
```
352
353
### Custom Content Types and Response Handling
354
355
```python
356
@enroute.rest.query("/users/{user_id}/report", method="GET")
357
async def generate_user_report(request: HttpRequest) -> HttpResponse:
358
url_params = await request.url_params()
359
user_id = url_params["user_id"]
360
361
# Check accept header for response format
362
accept = request.headers.get("Accept", "application/json")
363
364
user_data = get_user_data(user_id)
365
366
if "application/pdf" in accept:
367
pdf_data = generate_pdf_report(user_data)
368
return HttpResponse(
369
pdf_data,
370
content_type="application/pdf",
371
status=200
372
)
373
elif "text/csv" in accept:
374
csv_data = generate_csv_report(user_data)
375
return HttpResponse(
376
csv_data,
377
content_type="text/csv",
378
status=200
379
)
380
else:
381
# Default JSON response
382
return HttpResponse(user_data)
383
```
384
385
### Error Handling and Middleware
386
387
```python
388
from minos.networks import HttpResponseException
389
390
@enroute.rest.command("/users/{user_id}/validate", method="POST")
391
async def validate_user(request: HttpRequest) -> HttpResponse:
392
try:
393
# Check authentication
394
auth_header = request.headers.get("Authorization")
395
if not auth_header:
396
raise HttpResponseException(status=401)
397
398
# Get and validate input
399
url_params = await request.url_params()
400
user_id = url_params["user_id"]
401
validation_data = await request.content()
402
403
if not validation_data:
404
raise HttpResponseException(status=400)
405
406
# Perform validation
407
result = validate_user_data(user_id, validation_data)
408
return HttpResponse({"valid": result})
409
410
except ValueError as e:
411
return HttpResponse({"error": str(e)}, status=400)
412
except PermissionError:
413
return HttpResponse({"error": "Forbidden"}, status=403)
414
except Exception as e:
415
return HttpResponse({"error": "Internal server error"}, status=500)
416
```