kopia lustrzana https://github.com/cirospaciari/socketify.py
rename
rodzic
17c1b5b46b
commit
b6f2572372
|
@ -0,0 +1 @@
|
||||||
|
__pycache__
|
12
README.md
12
README.md
|
@ -1,14 +1,14 @@
|
||||||
# uWebSockets.py
|
# socketify.py
|
||||||
Fast WebSocket and Http/Https server using CFFI with C API from [uNetworking/uWebSockets](https://github.com/uNetworking/uWebSockets)
|
Fast WebSocket and Http/Https server using CFFI with C API from [uNetworking/uWebSockets](https://github.com/uNetworking/uWebSockets)
|
||||||
|
|
||||||
> This project will adapt the full capi from uNetworking/uWebSockets but for now it's just this.
|
> This project will adapt the full capi from uNetworking/uWebSockets but for now it's just this.
|
||||||
|
|
||||||
### Overly simple hello world app
|
### Overly simple hello world app
|
||||||
```python
|
```python
|
||||||
from "uws" import App
|
from socketify import App
|
||||||
|
|
||||||
app = App()
|
app = App()
|
||||||
app.get("/", lambda res, req: res.end("Hello World uWS from Python!"))
|
app.get("/", lambda res, req: res.end("Hello World socketify from Python!"))
|
||||||
app.listen(3000, lambda socket, config: print("Listening on port http://localhost:%d now\n" % config.port))
|
app.listen(3000, lambda socket, config: print("Listening on port http://localhost:%d now\n" % config.port))
|
||||||
app.run()
|
app.run()
|
||||||
```
|
```
|
||||||
|
@ -16,7 +16,7 @@ app.run()
|
||||||
### pip (working progress)
|
### pip (working progress)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install git+https://github.com/cirospaciari/uWebSockets.py.git
|
pip install git+https://github.com/cirospaciari/socketify.py.git
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run
|
### Run
|
||||||
|
@ -26,10 +26,10 @@ pypy3 ./hello_world.py
|
||||||
|
|
||||||
### SSL version sample
|
### SSL version sample
|
||||||
``` python
|
``` python
|
||||||
from "uws" import App, AppOptions
|
from socketify import App, AppOptions
|
||||||
|
|
||||||
app = App(AppOptions(key_file_name="./misc/key.pem", cert_file_name="./misc/cert.pem", passphrase="1234"))
|
app = App(AppOptions(key_file_name="./misc/key.pem", cert_file_name="./misc/cert.pem", passphrase="1234"))
|
||||||
app.get("/", plaintext)
|
app.get("/", lambda res, req: res.end("Hello World socketify from Python!"))
|
||||||
app.listen(3000, lambda socket, config: print("Listening on port http://localhost:%d now\n" % config.port))
|
app.listen(3000, lambda socket, config: print("Listening on port http://localhost:%d now\n" % config.port))
|
||||||
app.run()
|
app.run()
|
||||||
```
|
```
|
|
@ -0,0 +1,57 @@
|
||||||
|
|
||||||
|
import cffi
|
||||||
|
import os
|
||||||
|
# from ctypes.util import find_library
|
||||||
|
# print("libuv1: %s" % find_library('uv'))
|
||||||
|
|
||||||
|
ffi = cffi.FFI()
|
||||||
|
ffi.cdef("""
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct uv_handle_t uv_handle_t;
|
||||||
|
typedef struct uv_timer_s uv_timer_t;
|
||||||
|
typedef void(*uv_timer_cb)(uv_timer_t* handle);
|
||||||
|
|
||||||
|
struct uv_timer_s {
|
||||||
|
uv_handle_t* next_closing; \
|
||||||
|
unsigned int flags;
|
||||||
|
uv_timer_cb timer_cb; \
|
||||||
|
void* heap_node[3]; \
|
||||||
|
uint64_t timeout; \
|
||||||
|
uint64_t repeat; \
|
||||||
|
uint64_t start_id;
|
||||||
|
};
|
||||||
|
typedef struct uv_poll_t uv_poll_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct uv_loop_t uv_loop_t;
|
||||||
|
|
||||||
|
typedef struct uv_os_sock_t uv_os_sock_t;
|
||||||
|
typedef struct uv_poll_cb uv_poll_cb;
|
||||||
|
|
||||||
|
typedef void (*uv_close_cb)(uv_handle_t* handle);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
UV_RUN_DEFAULT = 0,
|
||||||
|
UV_RUN_ONCE,
|
||||||
|
UV_RUN_NOWAIT
|
||||||
|
} uv_run_mode;
|
||||||
|
|
||||||
|
int uv_run(uv_loop_t*, uv_run_mode mode);
|
||||||
|
void uv_stop(uv_loop_t*);
|
||||||
|
int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd);
|
||||||
|
int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, uv_os_sock_t socket);
|
||||||
|
int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb);
|
||||||
|
int uv_poll_stop(uv_poll_t* handle);
|
||||||
|
void uv_close(uv_handle_t* handle, uv_close_cb close_cb);
|
||||||
|
uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle);
|
||||||
|
int uv_timer_init(uv_loop_t*, uv_timer_t* handle);
|
||||||
|
int uv_timer_start(uv_timer_t* handle,
|
||||||
|
uv_timer_cb cb,
|
||||||
|
uint64_t timeout,
|
||||||
|
uint64_t repeat);
|
||||||
|
int uv_timer_stop(uv_timer_t* handle);
|
||||||
|
uv_loop_t* uv_default_loop(void);
|
||||||
|
""")
|
||||||
|
|
||||||
|
lib = ffi.dlopen("uv")
|
|
@ -0,0 +1,20 @@
|
||||||
|
import uws
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
# Integrate with asyncio
|
||||||
|
# asyncio.set_event_loop(uws.Loop())
|
||||||
|
|
||||||
|
app = uws.App()
|
||||||
|
|
||||||
|
def getHandler(res, req):
|
||||||
|
res.end("Hello Python!")
|
||||||
|
|
||||||
|
app.get("/*", getHandler)
|
||||||
|
|
||||||
|
def listenHandler():
|
||||||
|
print("Listening to port 3000")
|
||||||
|
|
||||||
|
app.listen(3000, listenHandler)
|
||||||
|
app.run()
|
||||||
|
# Run asyncio event loop
|
||||||
|
# asyncio.get_event_loop().run_forever()
|
|
@ -0,0 +1,119 @@
|
||||||
|
from libuv import lib, ffi
|
||||||
|
import signal
|
||||||
|
import asyncio
|
||||||
|
import selectors
|
||||||
|
# loop = lib.uv_default_loop()
|
||||||
|
# print(loop)
|
||||||
|
# lib.uv_run(loop, lib.UV_RUN_ONCE)
|
||||||
|
|
||||||
|
|
||||||
|
@ffi.callback("void(uv_timer_t*)")
|
||||||
|
def selector_timer_handler(timer):
|
||||||
|
global selector
|
||||||
|
selector.tick = True
|
||||||
|
|
||||||
|
class Selector(selectors.BaseSelector):
|
||||||
|
def __init__(self):
|
||||||
|
self.tick = False
|
||||||
|
self.list = []
|
||||||
|
self.pools = dict()
|
||||||
|
|
||||||
|
self.loop = lib.uv_default_loop()
|
||||||
|
self.timer = ffi.new("uv_timer_t *")
|
||||||
|
lib.uv_timer_init(self.loop, self.timer)
|
||||||
|
signal.signal(signal.SIGINT, lambda sig,frame: self.sigint())
|
||||||
|
# super().__init__(self)
|
||||||
|
|
||||||
|
def sigint(self):
|
||||||
|
self.interrupted = True
|
||||||
|
pass
|
||||||
|
def register(self, fileobj, events, data=None):
|
||||||
|
fd = -1
|
||||||
|
if isinstance(fileobj, int):
|
||||||
|
fd = fileobj
|
||||||
|
else:
|
||||||
|
fd = fileobj.fileno()
|
||||||
|
|
||||||
|
pass
|
||||||
|
def unregister(self, fileobj):
|
||||||
|
fd = -1
|
||||||
|
if isinstance(fileobj, int):
|
||||||
|
fd = fileobj
|
||||||
|
else:
|
||||||
|
fd = fileobj.fileno()
|
||||||
|
|
||||||
|
try:
|
||||||
|
pool = self.pools[fd]
|
||||||
|
lib.uv_poll_stop(pool)
|
||||||
|
del self.pools[fd]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
None
|
||||||
|
def modify(self, fileobj, events, data=None):
|
||||||
|
# fd = -1
|
||||||
|
# if isinstance(fileobj, int):
|
||||||
|
# fd = fileobj
|
||||||
|
# else:
|
||||||
|
# fd = fileobj.fileno()
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# del self.pools[fd]
|
||||||
|
# except:
|
||||||
|
# pass
|
||||||
|
None
|
||||||
|
def select(timeout=None):
|
||||||
|
self.interrupted = False
|
||||||
|
|
||||||
|
if timeout != None:
|
||||||
|
lib.uv_timer_stop(self.timer)
|
||||||
|
lib.uv_timer_start(self.timer, selector_timer_handler, ffi.cast("uint64_t", int(timeout)), ffi.cast("uint64_t", 0))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if timeout != None and timeout <= 0:
|
||||||
|
lib.uv_run(self.loop, lib.UV_RUN_NOWAIT)
|
||||||
|
break
|
||||||
|
keep_going = int(lib.uv_run(self.loop, lib.UV_RUN_ONCE))
|
||||||
|
if not keep_going:
|
||||||
|
break
|
||||||
|
|
||||||
|
if self.interrupted:
|
||||||
|
raise KeyboardInterrupt
|
||||||
|
|
||||||
|
if self.tick:
|
||||||
|
self.tick = False
|
||||||
|
break
|
||||||
|
if len(self.list):
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
return slice(self.list, 0 , len(self.list))
|
||||||
|
def get_key(self, fileobj):
|
||||||
|
fd = -1
|
||||||
|
if isinstance(fileobj, int):
|
||||||
|
fd = fileobj
|
||||||
|
else:
|
||||||
|
fd = fileobj.fileno()
|
||||||
|
|
||||||
|
try:
|
||||||
|
pool = self.pools[fd]
|
||||||
|
return fd
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_map(self):
|
||||||
|
None
|
||||||
|
def close(self):
|
||||||
|
None
|
||||||
|
def tick(self):
|
||||||
|
self.tick = True
|
||||||
|
# def call_soon(self, *args, **kwargs):
|
||||||
|
# self.tick()
|
||||||
|
# return super().call_soon(*args, **kwargs)
|
||||||
|
# def call_at(self, *args, **kwargs):
|
||||||
|
# self.tick()
|
||||||
|
# return super().call_at(*args, **kwargs)
|
||||||
|
selector = Selector()
|
||||||
|
# loop = asyncio.SelectorEventLoop(selector)
|
||||||
|
# asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
|
print(selector.loop, selector.timer)
|
|
@ -1,9 +1,4 @@
|
||||||
import cffi
|
import cffi
|
||||||
import threading
|
|
||||||
from datetime import datetime
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
ffi = cffi.FFI()
|
ffi = cffi.FFI()
|
||||||
ffi.cdef("""
|
ffi.cdef("""
|
||||||
|
@ -56,6 +51,7 @@ struct us_listen_socket_t {
|
||||||
unsigned int socket_ext_size;
|
unsigned int socket_ext_size;
|
||||||
};
|
};
|
||||||
void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls);
|
void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls);
|
||||||
|
struct us_loop_t *uws_get_loop();
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
|
@ -107,8 +103,6 @@ typedef struct
|
||||||
int options;
|
int options;
|
||||||
} uws_app_listen_config_t;
|
} uws_app_listen_config_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct uws_app_s;
|
struct uws_app_s;
|
||||||
struct uws_req_s;
|
struct uws_req_s;
|
||||||
struct uws_res_s;
|
struct uws_res_s;
|
||||||
|
@ -500,41 +494,3 @@ class AppOptions:
|
||||||
self.ca_file_name = ca_file_name
|
self.ca_file_name = ca_file_name
|
||||||
self.ssl_ciphers = ssl_ciphers
|
self.ssl_ciphers = ssl_ciphers
|
||||||
self.ssl_prefer_low_memory_usage = ssl_prefer_low_memory_usage
|
self.ssl_prefer_low_memory_usage = ssl_prefer_low_memory_usage
|
||||||
|
|
||||||
|
|
||||||
current_http_date = datetime.utcnow().isoformat() + "Z"
|
|
||||||
stopped = False
|
|
||||||
def time_thread():
|
|
||||||
while not stopped:
|
|
||||||
global current_http_date
|
|
||||||
current_http_date = datetime.utcnow().isoformat() + "Z"
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
def plaintext(res, req):
|
|
||||||
res.write_header("Date", current_http_date)
|
|
||||||
res.write_header("Server", "uws.py")
|
|
||||||
res.write_header("Content-Type", "text/plain")
|
|
||||||
res.end("Hello, World!")
|
|
||||||
|
|
||||||
def run_app():
|
|
||||||
timing = threading.Thread(target=time_thread, args=())
|
|
||||||
timing.start()
|
|
||||||
app = App(AppOptions(key_file_name="./misc/key.pem", cert_file_name="./misc/cert.pem", passphrase="1234"))
|
|
||||||
app.get("/", plaintext)
|
|
||||||
# app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port)))
|
|
||||||
app.listen(AppListenOptions(port=3000, host="0.0.0.0"), lambda config: print("Listening on port http://%s:%d now\n" % (config.host, config.port)))
|
|
||||||
app.run()
|
|
||||||
|
|
||||||
def create_fork():
|
|
||||||
n = os.fork()
|
|
||||||
# n greater than 0 means parent process
|
|
||||||
if not n > 0:
|
|
||||||
run_app()
|
|
||||||
|
|
||||||
for index in range(1):
|
|
||||||
create_fork()
|
|
||||||
|
|
||||||
run_app()
|
|
||||||
#pip install git+https://github.com/inducer/pycuda.git (submodules are cloned recursively)
|
|
||||||
#https://stackoverflow.com/questions/1754966/how-can-i-run-a-makefile-in-setup-py
|
|
||||||
#https://packaging.python.org/en/latest/tutorials/packaging-projects/
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
import threading
|
||||||
|
from datetime import datetime
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import asyncio
|
||||||
|
#import uvloop
|
||||||
|
from json import dumps as json
|
||||||
|
from socketify import App, AppOptions, AppListenOptions
|
||||||
|
# from ujson import dumps as json
|
||||||
|
# from zzzjson import stringify as json #is too slow with CFFI
|
||||||
|
# from orjson import dumps
|
||||||
|
# def json(data):
|
||||||
|
# return dumps(data).decode("utf-8")
|
||||||
|
# from rapidjson import dumps as json
|
||||||
|
|
||||||
|
# import cysimdjson
|
||||||
|
# parser = cysimdjson.JSONParser()
|
||||||
|
|
||||||
|
|
||||||
|
# def parse(value):
|
||||||
|
# return parser.loads(value)
|
||||||
|
|
||||||
|
# def json(value):
|
||||||
|
# if isinstance(value, dict):
|
||||||
|
# for key in value:
|
||||||
|
# return "{\"%s\":\"%s\"}" % (key, value[key])
|
||||||
|
|
||||||
|
#only supports dict
|
||||||
|
# return None
|
||||||
|
|
||||||
|
|
||||||
|
current_http_date = datetime.utcnow().isoformat() + "Z"
|
||||||
|
stopped = False
|
||||||
|
def time_thread():
|
||||||
|
while not stopped:
|
||||||
|
global current_http_date
|
||||||
|
current_http_date = datetime.utcnow().isoformat() + "Z"
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def plaintext(res, req):
|
||||||
|
res.write_header("Date", current_http_date)
|
||||||
|
res.write_header("Server", "socketify")
|
||||||
|
res.write_header("Content-Type", "text/plain")
|
||||||
|
res.end("Hello, World!")
|
||||||
|
|
||||||
|
def applicationjson(res, req):
|
||||||
|
res.write_header("Date", current_http_date)
|
||||||
|
res.write_header("Server", "socketify")
|
||||||
|
res.write_header("Content-Type", "application/json")
|
||||||
|
res.end(json({"message":"Hello, World!"}))
|
||||||
|
|
||||||
|
|
||||||
|
def async_route(handler):
|
||||||
|
return lambda res,req: asyncio.run(handler(res, req))
|
||||||
|
|
||||||
|
async def plaintext_async(res, req):
|
||||||
|
# await asyncio.sleep(1)
|
||||||
|
res.write_header("Date", current_http_date)
|
||||||
|
res.write_header("Server", "socketify")
|
||||||
|
res.write_header("Content-Type", "text/plain")
|
||||||
|
res.end("Hello, World!")
|
||||||
|
|
||||||
|
async def test():
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
print("Hello!")
|
||||||
|
|
||||||
|
def run_app():
|
||||||
|
timing = threading.Thread(target=time_thread, args=())
|
||||||
|
timing.start()
|
||||||
|
app = App(AppOptions(key_file_name="./misc/key.pem", cert_file_name="./misc/cert.pem", passphrase="1234"))
|
||||||
|
app.get("/", plaintext)
|
||||||
|
# app.get("/json", applicationjson)
|
||||||
|
# app.get("/plaintext", plaintext)
|
||||||
|
# app.get("/", async_route(plaintext_async))
|
||||||
|
app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port)))
|
||||||
|
# app.listen(AppListenOptions(port=3000, host="0.0.0.0"), lambda config: print("Listening on port http://%s:%d now\n" % (config.host, config.port)))
|
||||||
|
|
||||||
|
# loop = uvloop.new_event_loop()
|
||||||
|
# asyncio.set_event_loop(loop)
|
||||||
|
# print(loop)
|
||||||
|
# asyncio.run(test())
|
||||||
|
app.run()
|
||||||
|
|
||||||
|
# app.run()
|
||||||
|
# asyncio.get_event_loop().run_forever()
|
||||||
|
def create_fork():
|
||||||
|
n = os.fork()
|
||||||
|
# n greater than 0 means parent process
|
||||||
|
if not n > 0:
|
||||||
|
run_app()
|
||||||
|
|
||||||
|
# for index in range(3):
|
||||||
|
# create_fork()
|
||||||
|
|
||||||
|
# async def main():
|
||||||
|
# print('Hello ...')
|
||||||
|
# await asyncio.sleep(1)
|
||||||
|
# print('... World!')
|
||||||
|
|
||||||
|
# asyncio.run(main())
|
||||||
|
# asyncio.get_event_loop().run_forever()
|
||||||
|
|
||||||
|
run_app()
|
||||||
|
|
||||||
|
|
||||||
|
# print(parse("{\"message\":\"Hello, World\"}")["message"])
|
||||||
|
# print(json({ "array": [1, 5.5, True, False, None, "{\"message\":\"Hello, World\"}"], "yes": True, "nop": False, "none": None, "int": 1, "float": 5.5, "e": 1.2123123123123124e+37, "string": "{\"message\":\"Hello, World\"}" }))
|
||||||
|
|
||||||
|
# print(json(AppOptions(key_file_name="./misc/key.pem", cert_file_name="./misc/cert.pem", passphrase="1234").__dict__))
|
||||||
|
|
||||||
|
#pip install git+https://github.com/inducer/pycuda.git (submodules are cloned recursively)
|
||||||
|
#https://stackoverflow.com/questions/1754966/how-can-i-run-a-makefile-in-setup-py
|
||||||
|
#https://packaging.python.org/en/latest/tutorials/packaging-projects/
|
||||||
|
#pypy3 -m pip install uvloop (not working with pypy)
|
||||||
|
#apt install pypy3-dev
|
||||||
|
#pypy3 -m pip install ujson (its slow D=)
|
||||||
|
#pypy3 -m pip install orjson (dont support pypy)
|
||||||
|
#pypy3 -m pip install cysimdjson (uses simdjson) is parse only
|
||||||
|
#pypy3 -m pip install rapidjson (not working with pypy)
|
||||||
|
#https://github.com/MagicStack/uvloop/issues/380
|
||||||
|
#https://foss.heptapod.net/pypy/pypy/-/issues/3740
|
Ładowanie…
Reference in New Issue