Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hasssanezzz/PyMicroHTTP
A minimal speedrun project, HTTP server from scratch.
https://github.com/hasssanezzz/PyMicroHTTP
Last synced: about 1 month ago
JSON representation
A minimal speedrun project, HTTP server from scratch.
- Host: GitHub
- URL: https://github.com/hasssanezzz/PyMicroHTTP
- Owner: hasssanezzz
- License: other
- Created: 2024-08-11T03:53:27.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2024-08-14T13:43:17.000Z (5 months ago)
- Last Synced: 2024-12-06T18:09:42.907Z (about 1 month ago)
- Language: Python
- Homepage: https://pypi.org/project/pymicrohttp
- Size: 30.3 KB
- Stars: 29
- Watchers: 1
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- awesome-egypt-opensource - hasssanezzz/PyMicroHTTP - a lightweight, flexible HTTP framework built from scratch in Python. It provides a simple way to create HTTP services without heavy external dependencies, making it ideal for learning purposes or small projects. (Projects / Web Frameworks)
README
# PyMicroHTTP
PyMicroHTTP is a lightweight, flexible HTTP framework built from scratch in Python. It provides a simple way to create HTTP services without heavy external dependencies, making it ideal for learning purposes or small projects.
__NOTE: this is a toy project and not production ready.__
## Content Table
### PyMicroHTTP
* **[PyMicroHTTP](#pymicrohttp)**
* What is PyMicroHTTP? (Introduction)
* **[Features](#features)** (List of functionalities)
* **[Installation](#installation)** (How to install)
* **[Quick Start](#quick-start)** (Getting started example)
* Running the example
* **[Routing](#routing)** (Defining routes)
* Basic Routing Syntax
* **[Path Parameters](#path-parameters)** (Accessing dynamic parts of the URL)
* **[Query Parameters](#query-parameters)** (Accessing data after the '?' in the URL)
* **[Request Object](#request-object)** (Understanding the request data)
* Available properties in the request object
* Accessing headers, path parameters, query parameters, body and verb
* **[Response Handling](#response-handling)** (Sending data back to the client)
* Returning dictionaries (converted to JSON)
* Returning strings
* Returning custom status codes and headers
* **[Middleware](#middleware)** (Adding custom logic before request handling)
* Creating middleware functions
* **[Before all](#before-all)** (Running middleware before every request)
* **[Middleware chaining](#middleware-chaining)**
* **[Running the Server](#running-the-server)** (Starting the server)
* **[Contributing](#contributing)** (How to contribute to the project)## Features
- Built on raw TCP sockets
- Routing with HTTP verb and path matching
- Middleware support with easy chaining
- JSON response handling
- Zero external dependencies## Installation
You can install the package via pip:
```
$ pip install pymicrohttp
```## Quick Start
Here's a simple example to get you started:
```python
from pymicrohttp.server import Servers = Server()
@s.register('GET /hello')
def hello(request):
return {"message": "Hello, World!"}@s.register('GET /hello/:name')
def hello_name(request):
name = request['params'].get('name')
return {"message": f"Hello, {name}!"}if __name__ == "__main__":
s.start_server(port=8080)
```Run this script, and you'll have a server running on `http://localhost:8080`. Access it with:
```
curl http://localhost:8080/hello
```## Routing
Routes are defined using the `@s.register` decorator:
```python
@s.register('GET /ping')
def ping_handler(request):
return "pong"
```Following this syntax:
```
VERB /
```
### Supported verbs:
- `*`: to match any verb
- GET
- POST
- PUT
- PATCH
- DELETE
- HEAD
- OPTIONSWith a signle space separating between the verb and the request path.
Example:
```python
@s.register('POST /login')
def login_handler(request):
try:
body = json.loads(request['body'])
if 'username' not in body or 'password' not in body:
# do somthing
except:
return { 'error': 'invalid data' }
```### Path parameters
You can declare dynamic path params using a colon, for example:
```
GET /users/:group/:channel
```
To read these params you can access them via the request object:
```py
@s.register('GET /users/:group/:channel')
def handler(request):
...
group = request['params']['group']
channel = request['params']['channel']
...
```### Query parameters
You can read query parameters via the request obejct:
```py
@s.register('GET /products')
def handler(request):
...
name = request['query'].get('name', '')
category = request['query'].get('category', 'shoes')
...
```
Note that it is better to use `.get(key, default_value)` because query params are optional and may not exist, and accessing them without the `.get()` method may result in key errors.## Request Object
The request object is a dict containing these key and value:
```
{
'verb': ...
'path': ...
'body': ...
'headers': ... # { 'key': 'value' }
'params': ... # { 'key': 'value' }
'query': ... # { 'key': 'value' }
}
```You can access it via the handler:
```py
@s.register('* /ping')
def ping_handler(request):
# accessing request headers
if 'double' in request['headers']:
return "pong-pong"
return "pong"
```Examples:
1. Accessing headers:
```py
# say hello
s.register('GET /hello/:name')
def hello(request):
name = request['params']['name']
return "Hello " + name
```1. Accessing dynamic path params:
```py
# say hello `n` times
s.register('GET /hello/:name/:n')
def hello(request):
name, n = request['params']['name'], request['params']['n']
return "Hello " * int(n) + name
```1. Accessing query params:
```py
# say hello `n` times
# read n from query params
# with default value of 3
s.register('GET /hello/:name')
def hello(request):
name = request['params']['name']
n = request['query'].get('n', 3)
return "Hello " * n + name
```## Response Handling
The framework supports different types of responses:
1. Dictionary (automatically converted to JSON):
```python
return {"key": "value"}
```2. String:
```python
return "Hello, World!"
```3. Tuple for custom status codes and headers:
```python
return "Not Found", 404
# or
return "Created", 201, {"Location": "/resource/1"}
```## Middleware
Middleware functions can be used to add functionality to your routes:```python
def log_middleware(next):
def handler(request):
print(f"Request: {request['verb']} {request['path']}")
return next(request)
return handler@s.register('GET /logged', log_middleware)
def logged_route(request):
return {"message": "This is a logged route"}
```### Before all
If you want to run a middleware before every single request you can use the `s.beforeAll()` decorator:
```py
@s.beforeAll()
def logger(next):
def handler(request):
verb, path = request['verb'], request['path']
print(f'{datetime.datetime.now()} {verb} {path}')
return next(request)
return handler
```### Middleware chaining
You can chain multiple middlwares together
```py
def log_middleware(next):
def handler(request):
# do your logging logic here
return next(request)
return handlerdef auth_middleware(next):
def handler(request):
# do your auth logic here
return next(request)
return handler@s.register('GET /protected', [log_middleware, auth_middleware])
def protected_route(request):
return {"message": "This is a protected route"}
```## Running the Server
To run the server:
```python
if __name__ == "__main__":
s = Server()
# Register your routes here
s.start_server(port=8080)
```## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.