Skip to content

Commit 6ae34c3

Browse files
committed
fixes, json serializer, wip extensions
1 parent 2f8a0ca commit 6ae34c3

17 files changed

+1152
-723
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@
3434
- Max Backpressure, Max Timeout, Max Payload and Idle Timeout Support
3535
- Automatic Ping / Pong Support
3636
- Per Socket Data
37-
- Middlewares
38-
- Templates Support (examples with [`Mako`](https://github.com/cirospaciari/socketify.py/tree/main/examples/template_mako.py) and [`Jinja2`](https://github.com/cirospaciari/socketify.py/tree/main/examples/template_jinja2.py))
39-
- ASGI Server with pub/sub extension for Falcon
40-
- WSGI Server
37+
- [`Middlewares`](https://docs.socketify.dev/middlewares.html)
38+
- [`Templates`](https://docs.socketify.dev/templates.html) Support (examples with [`Mako`](https://github.com/cirospaciari/socketify.py/tree/main/examples/template_mako.py) and [`Jinja2`](https://github.com/cirospaciari/socketify.py/tree/main/examples/template_jinja2.py))
39+
- [`ASGI Server`](https://docs.socketify.dev/cli.html)
40+
- [`WSGI Server`](https://docs.socketify.dev/cli.html)
41+
- [`Plugins/Extensions`](https://docs.socketify.dev/extensions.html)
4142

4243
## :mag_right: Upcoming Features
4344
- In-Memory Cache Tools

bench/asgi_wsgi/falcon-asgi.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ async def on_get(self, req, resp):
88
resp.content_type = falcon.MEDIA_TEXT # Default is JSON, so override
99
resp.text = "Hello, World!"
1010
async def on_post(self, req, resp):
11-
# curl -d '{"key1":"value1", "key2":"value2"}' -H "Content-Type: application/json" -X POST http://localhost:8000/
12-
raw_data = await req.stream.read()
13-
print("data", raw_data)
11+
# curl -d '{"name":"test"}' -H "Content-Type: application/json" -X POST http://localhost:8000/
12+
json = await req.media
1413
resp.status = falcon.HTTP_200 # This is the default status
1514
resp.content_type = falcon.MEDIA_TEXT # Default is JSON, so override
16-
resp.text = raw_data
15+
resp.text = json.get("name", "")
1716

1817

1918

docs/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@ With no precedents websocket performance and an really fast HTTP server that can
3535
- [GraphiQL](graphiql.md)
3636
- [WebSockets and Backpressure](websockets-backpressure.md)
3737
- [SSL](ssl.md)
38-
- [CLI Reference](cli.md)
38+
- [Plugins / Extensions](extensions.md)
39+
- [CLI, ASGI and WSGI](cli.md)
3940
- [API Reference](api.md)

docs/_sidebar.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@
1313
- [GraphiQL](graphiql.md)
1414
- [WebSockets and Backpressure](websockets-backpressure.md)
1515
- [SSL](ssl.md)
16-
- [CLI Reference](cli.md)
16+
- [Plugins / Extensions](extensions.md)
17+
- [CLI, ASGI and WSGI](cli.md)
1718
- [API Reference](api.md)

docs/api.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
class App:
55
def __init__(self, options=None):
66
def template(self, template_engine):
7+
def json_serializer(self, json_serializer):
78
def static(self, route, directory):
89
def get(self, path, handler):
910
def post(self, path, handler):
@@ -34,7 +35,7 @@ class App:
3435
## AppResponse
3536
```python
3637
class AppResponse:
37-
def __init__(self, response, loop, ssl, render=None):
38+
def __init__(self, response, app):
3839
def cork(self, callback):
3940
def set_cookie(self, name, value, options={}):
4041
def run_async(self, task):
@@ -81,7 +82,7 @@ class AppResponse:
8182
## AppRequest
8283
```python
8384
class AppRequest:
84-
def __init__(self, request):
85+
def __init__(self, request, app):
8586
def get_cookie(self, name):
8687
def get_url(self):
8788
def get_full_url(self):
@@ -123,7 +124,7 @@ class AppOptions:
123124
```python
124125

125126
class WebSocket:
126-
def __init__(self, websocket, ssl, loop):
127+
def __init__(self, websocket, app):
127128

128129
# uuid for socket data, used to free data after socket closes
129130
def get_user_data_uuid(self):

docs/basics.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,22 @@ def route_handler(res, req):
182182
res.run_async(sendfile(res, req, "my_text"))
183183
```
184184

185+
186+
## Using ujson, orjson or any custom JSON serializer
187+
socketify by default uses built in `json` module with have great performance on PyPy, but if you wanna to use another module instead of the default you can just register using `app.json_serializer(module)`
188+
189+
```python
190+
from socketify import App
191+
import ujson
192+
app = App()
193+
194+
# set json serializer to ujson
195+
# json serializer must have dumps and loads functions
196+
app.json_serializer(ujson)
197+
198+
app.get("/", lambda res, req: res.end({"Hello":"World!"}))
199+
```
200+
185201
## Raw socket pointer
186202

187203
If for some reason you need the raw socket pointer you can use `res.get_native_handle()` and will get an CFFI handler.

docs/extensions.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
2+
# Plugins / Extensions
3+
4+
You can add more functionality to request, response, and websocket objects, for this you can use `app.register(extension)` to register an extension.
5+
Be aware that using extensions can have a performance impact and using it with `request_response_factory_max_items`, `websocket_factory_max_items`
6+
or the equivalent on CLI `--req-res-factory-maxitems`, `--ws-factory-maxitems` will reduce this performance impact.
7+
8+
Extensions must follow the signature `def extension(request, response, ws)`, request, response, and ws objects contain `method` decorator that binds a method to an instance,
9+
and also a `property(name: str, default_value: any = None)` that dynamic adds an property to the instance.
10+
11+
```python
12+
from socketify import App, OpCode
13+
14+
app = App()
15+
16+
def extension(request, response, ws):
17+
@request.method
18+
async def get_user(self):
19+
token = self.get_header("token")
20+
return { "name": "Test" } if token else { "name", "Anonymous" }
21+
22+
@response.method
23+
def msgpack(self, value: any):
24+
self.write_header(b'Content-Type', b'application/msgpack')
25+
data = msgpack.packb(value, default=encode_datetime, use_bin_type=True)
26+
return self.end(data)
27+
28+
@ws.method
29+
def send_pm(self, to_username: str, message: str):
30+
user_data = self.get_user_data()
31+
pm_topic = f"pm-{to_username}+{user_data.username}"
32+
33+
# if topic exists just send the message
34+
if app.num_subscribers(pm_topic) > 0:
35+
# send private message
36+
return self.publish(pm_topic, message, OpCode.TEXT)
37+
38+
# if the topic not exists create it and signal the user
39+
# subscribe to the conversation
40+
self.subscribe(pm_topic)
41+
# signal user that you want to talk and create an pm room
42+
# all users must subscribe to signal-{username}
43+
self.publish(f"signal-{to_username}", {
44+
"type": "pm",
45+
"username": user_data.username,
46+
"message": message
47+
}, OpCode.TEXT)
48+
# this property can be used on extension methods and/or middlewares
49+
request.property("cart", [])
50+
51+
# extensions must be registered before routes
52+
app.register(extension)
53+
```
54+
55+
### Next [CLI, ASGI and WSGI](cli.md)

docs/graphiql.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
## GraphiQL Support
2-
In /src/examples/helper/graphiql.py we implemented an helper for using graphiQL with strawberry.
2+
In [`/src/examples/helper/graphiql.py`](https://github.com/cirospaciari/socketify.py/blob/main/examples/graphiql.py) we implemented an helper for using graphiQL with strawberry.
33

44
### Usage
55
```python

docs/websockets-backpressure.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,4 @@ You probably want shared compressor if dealing with larger JSON messages, or 4kb
4949

5050
idle_timeout is roughly the amount of seconds that may pass between messages. Being idle for more than this, and the connection is severed. This means you should make your clients send small ping messages every now and then, to keep the connection alive. The server will automatically send pings in case it needs to.
5151

52-
### Next [SSL](ssl.md)
52+
### Next [Plugins / Extensions](extensions.md)

examples/custom_json_serializer.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from socketify import App
2+
import ujson
3+
4+
app = App()
5+
6+
7+
# set json serializer to ujson
8+
# json serializer must have dumps and loads functions
9+
app.json_serializer(ujson)
10+
11+
app.get("/", lambda res, req: res.end({"Hello":"World!"}))
12+
app.listen(
13+
3000,
14+
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
15+
)
16+
app.run()

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "socketify"
7-
version = "0.0.3"
7+
version = "0.0.4"
88
authors = [
99
{ name="Ciro Spaciari", email="[email protected]" },
1010
]

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858

5959
setuptools.setup(
6060
name="socketify",
61-
version="0.0.3",
61+
version="0.0.4",
6262
platforms=["any"],
6363
author="Ciro Spaciari",
6464
author_email="[email protected]",

src/socketify/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
OpCode,
66
SendStatus,
77
CompressOptions,
8-
Loop
8+
Loop,
9+
AppExtension
910
)
1011
from .asgi import (
1112
ASGI

src/socketify/asgi.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,6 @@ def uws_asgi_corked_response_start_handler(res, user_data):
374374
lib.socketify_res_write_int_status(ssl, res, int(status))
375375
for name, value in headers:
376376
write_header(ssl, res, name, value)
377-
write_header(ssl, res, b"Server", b"socketify.py")
378377

379378

380379
@ffi.callback("void(uws_res_t*, void*)")
@@ -384,15 +383,13 @@ def uws_asgi_corked_accept_handler(res, user_data):
384383
lib.socketify_res_write_int_status(ssl, res, int(status))
385384
for name, value in headers:
386385
write_header(ssl, res, name, value)
387-
write_header(ssl, res, b"Server", b"socketify.py")
388386

389387

390388
@ffi.callback("void(uws_res_t*, void*)")
391389
def uws_asgi_corked_ws_accept_handler(res, user_data):
392390
(ssl, headers) = ffi.from_handle(user_data)
393391
for name, value in headers:
394392
write_header(ssl, res, name, value)
395-
write_header(ssl, res, b"Server", b"socketify.py")
396393

397394

398395
@ffi.callback("void(uws_res_t*, void*)")

0 commit comments

Comments
 (0)