use black as code formatter

pull/39/head
Ciro 2022-11-16 16:28:46 -03:00
rodzic aa11d8bd58
commit 021dda7d2a
53 zmienionych plików z 1824 dodań i 1085 usunięć

Wyświetl plik

@ -2,6 +2,7 @@ from wsgiref.simple_server import make_server
import falcon import falcon
class Home: class Home:
def on_get(self, req, resp): def on_get(self, req, resp):
resp.status = falcon.HTTP_200 # This is the default status resp.status = falcon.HTTP_200 # This is the default status
@ -12,18 +13,18 @@ class Home:
app = falcon.App() app = falcon.App()
home = Home() home = Home()
app.add_route('/', home) app.add_route("/", home)
if __name__ == '__main__': if __name__ == "__main__":
with make_server('', 8000, app) as httpd: with make_server("", 8000, app) as httpd:
print('Serving on port 8000...') print("Serving on port 8000...")
# Serve until process is killed # Serve until process is killed
httpd.serve_forever() httpd.serve_forever()
#pypy3 -m gunicorn falcon_plaintext:app -w 4 --worker-class=gevent #recomended for pypy3 # pypy3 -m gunicorn falcon_plaintext:app -w 4 --worker-class=gevent #recomended for pypy3
#python3 -m gunicorn falcon_plaintext:app -w 4 #without Cython # python3 -m gunicorn falcon_plaintext:app -w 4 #without Cython
#pypy3 -m gunicorn falcon_plaintext:app -w 4 #without gevent # pypy3 -m gunicorn falcon_plaintext:app -w 4 #without gevent
#python3 -m gunicorn falcon_plaintext:app -w 4 --worker-class="egg:meinheld#gunicorn_worker" #with Cython # python3 -m gunicorn falcon_plaintext:app -w 4 --worker-class="egg:meinheld#gunicorn_worker" #with Cython
#meinheld is buggy -> greenlet.c:566:10: error: no member named 'use_tracing' in 'struct _ts' # meinheld is buggy -> greenlet.c:566:10: error: no member named 'use_tracing' in 'struct _ts'
#so using pip3 install git+https://github.com/idot/meinheld.git@2bfe452d6608c92688d92337c87b1dd6448f4ccb # so using pip3 install git+https://github.com/idot/meinheld.git@2bfe452d6608c92688d92337c87b1dd6448f4ccb

Wyświetl plik

@ -2,10 +2,12 @@ from robyn import Robyn
app = Robyn(__file__) app = Robyn(__file__)
@app.get("/") @app.get("/")
async def h(request): async def h(request):
return "Hello, world!" return "Hello, world!"
app.start(port=8000) app.start(port=8000)
# python3 ./robyn_plaintext.py --processes 4 --log-level CRITICAL # python3 ./robyn_plaintext.py --processes 4 --log-level CRITICAL

Wyświetl plik

@ -3,21 +3,28 @@ import os
import multiprocessing import multiprocessing
def run_app(): def run_app():
app = App() app = App()
app.get("/", lambda res, req: res.end("Hello, World!")) app.get("/", lambda res, req: res.end("Hello, World!"))
app.listen(8000, lambda config: print("PID %d Listening on port http://localhost:%d now\n" % (os.getpid(), config.port))) app.listen(
8000,
lambda config: print(
"PID %d Listening on port http://localhost:%d now\n"
% (os.getpid(), config.port)
),
)
app.run() app.run()
def create_fork(): def create_fork():
n = os.fork() n = os.fork()
# n greater than 0 means parent process # n greater than 0 means parent process
if not n > 0: if not n > 0:
run_app() run_app()
# fork limiting the cpu count - 1 # fork limiting the cpu count - 1
# for i in range(1, multiprocessing.cpu_count()): for i in range(1, multiprocessing.cpu_count()):
# create_fork() create_fork()
run_app() # run app on the main process too :) run_app() # run app on the main process too :)

Wyświetl plik

@ -1,17 +1,22 @@
async def app(scope, receive, send): async def app(scope, receive, send):
assert scope['type'] == 'http' assert scope["type"] == "http"
await send({ await send(
'type': 'http.response.start', {
'status': 200, "type": "http.response.start",
'headers': [ "status": 200,
[b'content-type', b'text/plain'], "headers": [
[b"content-type", b"text/plain"],
], ],
}) }
await send({ )
'type': 'http.response.body', await send(
'body': b'Hello, world!', {
}) "type": "http.response.body",
"body": b"Hello, world!",
}
)
#python3 -m gunicorn uvicorn_guvicorn_plaintext:app -w 1 -k uvicorn.workers.UvicornWorker
#pypy3 -m gunicorn uvicorn_guvicorn_plaintext:app -w 1 -k uvicorn.workers.UvicornWorker # python3 -m gunicorn uvicorn_guvicorn_plaintext:app -w 1 -k uvicorn.workers.UvicornWorker
# pypy3 -m gunicorn uvicorn_guvicorn_plaintext:app -w 1 -k uvicorn.workers.UvicornWorker

Wyświetl plik

@ -5,6 +5,7 @@ import asyncio
clients = set([]) clients = set([])
remaining_clients = 16 remaining_clients = 16
async def broadcast(message): async def broadcast(message):
# some clients got disconnected if we tried to to all async :/ # some clients got disconnected if we tried to to all async :/
# tasks = [ws.send_text(message) for ws in client] # tasks = [ws.send_text(message) for ws in client]
@ -14,7 +15,6 @@ async def broadcast(message):
class SomeResource: class SomeResource:
async def on_get(self, req): async def on_get(self, req):
pass pass
@ -36,10 +36,7 @@ class SomeResource:
remaining_clients = remaining_clients + 1 remaining_clients = remaining_clients + 1
app = falcon.asgi.App() app = falcon.asgi.App()
app.add_route('/', SomeResource()) app.add_route("/", SomeResource())
# python3 -m gunicorn falcon_server:app -b 127.0.0.1:4001 -w 1 -k uvicorn.workers.UvicornWorker # python3 -m gunicorn falcon_server:app -b 127.0.0.1:4001 -w 1 -k uvicorn.workers.UvicornWorker
# pypy3 -m gunicorn falcon_server:app -b 127.0.0.1:4001 -w 1 -k uvicorn.workers.UvicornH11Worker # pypy3 -m gunicorn falcon_server:app -b 127.0.0.1:4001 -w 1 -k uvicorn.workers.UvicornH11Worker

Wyświetl plik

@ -2,6 +2,7 @@ from socketify import App, AppOptions, OpCode, CompressOptions
remaining_clients = 16 remaining_clients = 16
def ws_open(ws): def ws_open(ws):
ws.subscribe("room") ws.subscribe("room")
global remaining_clients global remaining_clients
@ -11,28 +12,36 @@ def ws_open(ws):
print('Starting benchmark by sending "ready" message') print('Starting benchmark by sending "ready" message')
ws.publish("room", "ready", OpCode.TEXT) ws.publish("room", "ready", OpCode.TEXT)
#publish will send to everyone except it self so send to it self too # publish will send to everyone except it self so send to it self too
ws.send("ready", OpCode.TEXT) ws.send("ready", OpCode.TEXT)
def ws_message(ws, message, opcode): def ws_message(ws, message, opcode):
#publish will send to everyone except it self so send to it self too # publish will send to everyone except it self so send to it self too
ws.publish("room", message, opcode) ws.publish("room", message, opcode)
ws.send(message, opcode) ws.send(message, opcode)
def ws_close(ws, close, message): def ws_close(ws, close, message):
global remaining_clients global remaining_clients
remaining_clients = remaining_clients + 1 remaining_clients = remaining_clients + 1
app = App() app = App()
app.ws("/*", { app.ws(
'compression': CompressOptions.DISABLED, "/*",
'max_payload_length': 16 * 1024 * 1024, {
'idle_timeout': 60, "compression": CompressOptions.DISABLED,
'open': ws_open, "max_payload_length": 16 * 1024 * 1024,
'message': ws_message, "idle_timeout": 60,
'close': ws_close "open": ws_open,
}) "message": ws_message,
app.any("/", lambda res,req: res.end("Nothing to see here!'")) "close": ws_close,
app.listen(4001, lambda config: print("Listening on port http://localhost:%d now\n" % (config.port))) },
)
app.any("/", lambda res, req: res.end("Nothing to see here!'"))
app.listen(
4001,
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
)
app.run() app.run()

Wyświetl plik

@ -3,35 +3,45 @@ import asyncio
app = App() app = App()
async def delayed_hello(delay, res): async def delayed_hello(delay, res):
await asyncio.sleep(delay) #do something async await asyncio.sleep(delay) # do something async
res.cork_end("Hello with delay!") res.cork_end("Hello with delay!")
def home(res, req): def home(res, req):
#request object only lives during the life time of this call # request object only lives during the life time of this call
#get parameters, query, headers anything you need here # get parameters, query, headers anything you need here
delay = req.get_query("delay") delay = req.get_query("delay")
delay = 0 if delay == None else float(delay) delay = 0 if delay == None else float(delay)
#tell response to run this in the event loop # tell response to run this in the event loop
#abort handler is grabed here, so responses only will be send if res.aborted == False # abort handler is grabed here, so responses only will be send if res.aborted == False
res.run_async(delayed_hello(delay, res)) res.run_async(delayed_hello(delay, res))
async def json(res, req):
#request object only lives during the life time of this call
#get parameters, query, headers anything you need here before first await :)
user_agent = req.get_header("user-agent")
#req maybe will not be available in direct attached async functions after await
await asyncio.sleep(2) #do something async
res.cork_end({ "message": "I'm delayed!", "user-agent": user_agent}) async def json(res, req):
# request object only lives during the life time of this call
# get parameters, query, headers anything you need here before first await :)
user_agent = req.get_header("user-agent")
# req maybe will not be available in direct attached async functions after await
await asyncio.sleep(2) # do something async
res.cork_end({"message": "I'm delayed!", "user-agent": user_agent})
def not_found(res, req): def not_found(res, req):
res.write_status(404).end("Not Found") res.write_status(404).end("Not Found")
app.get("/", home) app.get("/", home)
app.get("/json", json) app.get("/json", json)
app.any("/*", not_found) app.any("/*", not_found)
app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port))) app.listen(
3000,
lambda config: print(
"Listening on port http://localhost:%s now\n" % str(config.port)
),
)
app.run() app.run()

Wyświetl plik

@ -2,5 +2,7 @@ from socketify import App
app = App() app = App()
app.get("/", lambda res, req: res.end("Hello World socketify from Python!")) app.get("/", lambda res, req: res.end("Hello World socketify from Python!"))
app.listen(0, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
0, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)
)
app.run() app.run()

Wyświetl plik

@ -1,39 +1,48 @@
from socketify import App, AppOptions, OpCode, CompressOptions from socketify import App, AppOptions, OpCode, CompressOptions
#Number between ok and not ok # Number between ok and not ok
backpressure = 1024 backpressure = 1024
# Used for statistics # Used for statistics
messages = 0 messages = 0
message_number = 0 message_number = 0
def ws_open(ws): def ws_open(ws):
print('A WebSocket got connected!') print("A WebSocket got connected!")
# We begin our example by sending until we have backpressure # We begin our example by sending until we have backpressure
global message_number global message_number
global messages global messages
while (ws.get_buffered_amount() < backpressure): while ws.get_buffered_amount() < backpressure:
ws.send("This is a message, let's call it %i" % message_number) ws.send("This is a message, let's call it %i" % message_number)
message_number = message_number + 1 message_number = message_number + 1
messages = messages + 1 messages = messages + 1
def ws_drain(ws): def ws_drain(ws):
# Continue sending when we have drained (some) # Continue sending when we have drained (some)
global message_number global message_number
global messages global messages
while (ws.get_buffered_amount() < backpressure): while ws.get_buffered_amount() < backpressure:
ws.send("This is a message, let's call it %i" % message_number) ws.send("This is a message, let's call it %i" % message_number)
message_number = message_number + 1 message_number = message_number + 1
messages = messages + 1 messages = messages + 1
app = App() app = App()
app.ws("/*", { app.ws(
'compression': CompressOptions.DISABLED, "/*",
'max_payload_length': 16 * 1024 * 1024, {
'idle_timeout': 60, "compression": CompressOptions.DISABLED,
'open': ws_open, "max_payload_length": 16 * 1024 * 1024,
'drain': ws_drain "idle_timeout": 60,
}) "open": ws_open,
app.any("/", lambda res,req: res.end("Nothing to see here!")) "drain": ws_drain,
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % (config.port))) },
)
app.any("/", lambda res, req: res.end("Nothing to see here!"))
app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
)
app.run() app.run()

Wyświetl plik

@ -1,26 +1,35 @@
from socketify import App, AppOptions, OpCode, CompressOptions from socketify import App, AppOptions, OpCode, CompressOptions
def ws_open(ws): def ws_open(ws):
print('A WebSocket got connected!') print("A WebSocket got connected!")
#Let this client listen to topic "broadcast" # Let this client listen to topic "broadcast"
ws.subscribe('broadcast') ws.subscribe("broadcast")
def ws_message(ws, message, opcode): def ws_message(ws, message, opcode):
#Ok is false if backpressure was built up, wait for drain # Ok is false if backpressure was built up, wait for drain
ok = ws.send(message, opcode) ok = ws.send(message, opcode)
#Broadcast this message # Broadcast this message
ws.publish('broadcast', message, opcode) ws.publish("broadcast", message, opcode)
app = App() app = App()
app.ws("/*", { app.ws(
'compression': CompressOptions.SHARED_COMPRESSOR, "/*",
'max_payload_length': 16 * 1024 * 1024, {
'idle_timeout': 12, "compression": CompressOptions.SHARED_COMPRESSOR,
'open': ws_open, "max_payload_length": 16 * 1024 * 1024,
'message': ws_message, "idle_timeout": 12,
"open": ws_open,
"message": ws_message,
# The library guarantees proper unsubscription at close # The library guarantees proper unsubscription at close
'close': lambda ws, code, message: print('WebSocket closed') "close": lambda ws, code, message: print("WebSocket closed"),
}) },
app.any("/", lambda res,req: res.end("Nothing to see here!")) )
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % (config.port))) app.any("/", lambda res, req: res.end("Nothing to see here!"))
app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
)
app.run() app.run()

Wyświetl plik

@ -2,5 +2,8 @@ from socketify import App
app = App() app = App()
app.get("/", lambda res, req: res.end("Hello World socketify from Python!")) app.get("/", lambda res, req: res.end("Hello World socketify from Python!"))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -2,5 +2,8 @@ from socketify import App
app = App() app = App()
app.get("/", lambda res, req: res.end("Hello World socketify from Python!")) app.get("/", lambda res, req: res.end("Hello World socketify from Python!"))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -2,5 +2,8 @@ from socketify import App
app = App() app = App()
app.get("/", lambda res, req: res.end("Hello World socketify from Python!")) app.get("/", lambda res, req: res.end("Hello World socketify from Python!"))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -2,5 +2,8 @@ from socketify import App
app = App() app = App()
app.get("/", lambda res, req: res.end("Hello World socketify from Python!")) app.get("/", lambda res, req: res.end("Hello World socketify from Python!"))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -3,27 +3,36 @@ import asyncio
app = App() app = App()
def xablau(res, req): def xablau(res, req):
raise RuntimeError("Xablau!") raise RuntimeError("Xablau!")
async def async_xablau(res, req): async def async_xablau(res, req):
await asyncio.sleep(1) await asyncio.sleep(1)
raise RuntimeError("Async Xablau!") raise RuntimeError("Async Xablau!")
#this can be async no problems
# this can be async no problems
def on_error(error, res, req): def on_error(error, res, req):
#here you can log properly the error and do a pretty response to your clients # here you can log properly the error and do a pretty response to your clients
print("Somethind goes %s" % str(error)) print("Somethind goes %s" % str(error))
#response and request can be None if the error is in an async function # response and request can be None if the error is in an async function
if res != None: if res != None:
#if response exists try to send something # if response exists try to send something
res.write_status(500) res.write_status(500)
res.end("Sorry we did something wrong") res.end("Sorry we did something wrong")
app.get("/", xablau) app.get("/", xablau)
app.get("/async", async_xablau) app.get("/async", async_xablau)
app.set_error_handler(on_error) app.set_error_handler(on_error)
app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port))) app.listen(
3000,
lambda config: print(
"Listening on port http://localhost:%s now\n" % str(config.port)
),
)
app.run() app.run()

Wyświetl plik

@ -7,51 +7,54 @@ from os import path
mimetypes.init() mimetypes.init()
async def home(res, req): async def home(res, req):
#this is just an implementation example see static_files.py example for use of sendfile and app.static usage # this is just an implementation example see static_files.py example for use of sendfile and app.static usage
#there is an static_aiofile.py helper and static.aiofiles helper using async implementation of this # there is an static_aiofile.py helper and static.aiofiles helper using async implementation of this
#asyncio with IO is really slow so, we will implement "aiofile" using libuv inside socketify in future # asyncio with IO is really slow so, we will implement "aiofile" using libuv inside socketify in future
filename = "./public/media/flower.webm" filename = "./public/media/flower.webm"
#read headers before the first await # read headers before the first await
if_modified_since = req.get_header('if-modified-since') if_modified_since = req.get_header("if-modified-since")
range_header = req.get_header('range') range_header = req.get_header("range")
bytes_range = None bytes_range = None
start = 0 start = 0
end = -1 end = -1
#parse range header # parse range header
if range_header: if range_header:
bytes_range = range_header.replace("bytes=", '').split('-') bytes_range = range_header.replace("bytes=", "").split("-")
start = int(bytes_range[0]) start = int(bytes_range[0])
if bytes_range[1]: if bytes_range[1]:
end = int(bytes_range[1]) end = int(bytes_range[1])
try: try:
exists = path.exists(filename) exists = path.exists(filename)
#not found # not found
if not exists: if not exists:
return res.write_status(404).end(b'Not Found') return res.write_status(404).end(b"Not Found")
#get size and last modified date # get size and last modified date
stats = os.stat(filename) stats = os.stat(filename)
total_size = stats.st_size total_size = stats.st_size
size = total_size size = total_size
last_modified = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(stats.st_mtime)) last_modified = time.strftime(
"%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)
)
#check if modified since is provided # check if modified since is provided
if if_modified_since == last_modified: if if_modified_since == last_modified:
return res.write_status(304).end_without_body() return res.write_status(304).end_without_body()
#tells the broswer the last modified date # tells the broswer the last modified date
res.write_header(b'Last-Modified', last_modified) res.write_header(b"Last-Modified", last_modified)
#add content type # add content type
(content_type, encoding) = mimetypes.guess_type(filename, strict=True) (content_type, encoding) = mimetypes.guess_type(filename, strict=True)
if content_type and encoding: if content_type and encoding:
res.write_header(b'Content-Type', '%s; %s' % (content_type, encoding)) res.write_header(b"Content-Type", "%s; %s" % (content_type, encoding))
elif content_type: elif content_type:
res.write_header(b'Content-Type', content_type) res.write_header(b"Content-Type", content_type)
with open(filename, "rb") as fd: with open(filename, "rb") as fd:
#check range and support it # check range and support it
if start > 0 or not end == -1: if start > 0 or not end == -1:
if end < 0 or end >= size: if end < 0 or end >= size:
end = size - 1 end = size - 1
@ -64,25 +67,31 @@ async def home(res, req):
end = size - 1 end = size - 1
res.write_status(200) res.write_status(200)
#tells the browser that we support range # tells the browser that we support range
res.write_header(b'Accept-Ranges', b'bytes') res.write_header(b"Accept-Ranges", b"bytes")
res.write_header(b'Content-Range', 'bytes %d-%d/%d' % (start, end, total_size)) res.write_header(
b"Content-Range", "bytes %d-%d/%d" % (start, end, total_size)
)
pending_size = size pending_size = size
#keep sending until abort or done # keep sending until abort or done
while not res.aborted: while not res.aborted:
chunk_size = 16384 #16kb chunks chunk_size = 16384 # 16kb chunks
if chunk_size > pending_size: if chunk_size > pending_size:
chunk_size = pending_size chunk_size = pending_size
buffer = fd.read(chunk_size) buffer = fd.read(chunk_size)
pending_size = pending_size - chunk_size pending_size = pending_size - chunk_size
(ok, done) = await res.send_chunk(buffer, size) (ok, done) = await res.send_chunk(buffer, size)
if not ok or done: #if cannot send probably aborted if not ok or done: # if cannot send probably aborted
break break
except Exception as error: except Exception as error:
res.write_status(500).end("Internal Error") res.write_status(500).end("Internal Error")
app = App() app = App()
app.get("/", home) app.get("/", home)
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -2,18 +2,27 @@ from socketify import App
import os import os
import multiprocessing import multiprocessing
def run_app(): def run_app():
app = App() app = App()
app.get("/", lambda res, req: res.end("Hello, World!")) app.get("/", lambda res, req: res.end("Hello, World!"))
app.listen(3000, lambda config: print("PID %d Listening on port http://localhost:%d now\n" % (os.getpid(), config.port))) app.listen(
3000,
lambda config: print(
"PID %d Listening on port http://localhost:%d now\n"
% (os.getpid(), config.port)
),
)
app.run() app.run()
def create_fork(): def create_fork():
n = os.fork() n = os.fork()
# n greater than 0 means parent process # n greater than 0 means parent process
if not n > 0: if not n > 0:
run_app() run_app()
# fork limiting the cpu count - 1 # fork limiting the cpu count - 1
for i in range(1, multiprocessing.cpu_count()): for i in range(1, multiprocessing.cpu_count()):
create_fork() create_fork()

Wyświetl plik

@ -2,14 +2,21 @@ from socketify import App, AppOptions, AppListenOptions
app = App() app = App()
def shutdown(res, req): def shutdown(res, req):
res.end("Good bye!") res.end("Good bye!")
app.close() app.close()
app.get("/", lambda res, req: res.end("Hello!")) app.get("/", lambda res, req: res.end("Hello!"))
app.get("/shutdown", shutdown) app.get("/shutdown", shutdown)
app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port))) app.listen(
3000,
lambda config: print(
"Listening on port http://localhost:%s now\n" % str(config.port)
),
)
app.run() app.run()
print("App Closed!") print("App Closed!")

Wyświetl plik

@ -1,4 +1,3 @@
import dataclasses import dataclasses
import strawberry import strawberry
import strawberry.utils.graphiql import strawberry.utils.graphiql
@ -7,10 +6,12 @@ from socketify import App
from typing import List, Optional from typing import List, Optional
from helpers.graphiql import graphiql_from from helpers.graphiql import graphiql_from
@strawberry.type @strawberry.type
class User: class User:
name: str name: str
@strawberry.type @strawberry.type
class Query: class Query:
@strawberry.field @strawberry.field
@ -24,5 +25,8 @@ app.get("/", lambda res, req: res.end(strawberry.utils.graphiql.get_graphiql_htm
app.post("/", graphiql_from(Query)) app.post("/", graphiql_from(Query))
# you can also pass an Mutation as second parameter # you can also pass an Mutation as second parameter
# app.post("/", graphiql_from(Query, Mutation)) # app.post("/", graphiql_from(Query, Mutation))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -1,4 +1,3 @@
import dataclasses import dataclasses
import strawberry import strawberry
import strawberry.utils.graphiql import strawberry.utils.graphiql
@ -6,10 +5,12 @@ import strawberry.utils.graphiql
from socketify import App from socketify import App
from typing import List, Optional from typing import List, Optional
@strawberry.type @strawberry.type
class User: class User:
name: str name: str
@strawberry.type @strawberry.type
class Query: class Query:
@strawberry.field @strawberry.field
@ -40,14 +41,20 @@ async def graphiql_post(res, req):
operation_name, operation_name,
) )
res.cork_end({ res.cork_end(
"data": ( data.data ), {
"data": (data.data),
**({"errors": data.errors} if data.errors else {}), **({"errors": data.errors} if data.errors else {}),
**({"extensions": data.extensions} if data.extensions else {}) **({"extensions": data.extensions} if data.extensions else {}),
}) }
)
app = App() app = App()
app.get("/", lambda res, req: res.end(strawberry.utils.graphiql.get_graphiql_html())) app.get("/", lambda res, req: res.end(strawberry.utils.graphiql.get_graphiql_html()))
app.post("/", graphiql_post) app.post("/", graphiql_post)
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -2,5 +2,8 @@ from socketify import App
app = App() app = App()
app.get("/", lambda res, req: res.end("Hello World!")) app.get("/", lambda res, req: res.end("Hello World!"))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -1,6 +1,7 @@
import strawberry import strawberry
import strawberry.utils.graphiql import strawberry.utils.graphiql
def graphiql_from(Query, Mutation=None): def graphiql_from(Query, Mutation=None):
if Mutation: if Mutation:
schema = strawberry.Schema(query=Query, mutation=Mutation) schema = strawberry.Schema(query=Query, mutation=Mutation)
@ -12,7 +13,7 @@ def graphiql_from(Query, Mutation=None):
context_value = { context_value = {
"query": req.get_queries(), "query": req.get_queries(),
"headers": req.get_headers(), "headers": req.get_headers(),
"params": req.get_parameters() "params": req.get_parameters(),
} }
# get all incomming data and parses as json # get all incomming data and parses as json
@ -31,9 +32,12 @@ def graphiql_from(Query, Mutation=None):
operation_name, operation_name,
) )
res.cork_end({ res.cork_end(
"data": ( data.data ), {
"data": (data.data),
**({"errors": data.errors} if data.errors else {}), **({"errors": data.errors} if data.errors else {}),
**({"extensions": data.extensions} if data.extensions else {}) **({"extensions": data.extensions} if data.extensions else {}),
}) }
)
return post return post

Wyświetl plik

@ -1,12 +1,15 @@
import datetime import datetime
class MemoryCacheItem: class MemoryCacheItem:
def __init__(self, expires, value): def __init__(self, expires, value):
self.expires = datetime.datetime.utcnow().timestamp() + expires self.expires = datetime.datetime.utcnow().timestamp() + expires
self.value = value self.value = value
def is_expired(self): def is_expired(self):
return datetime.datetime.utcnow().timestamp() > self.expires return datetime.datetime.utcnow().timestamp() > self.expires
class MemoryCache: class MemoryCache:
def __init__(self): def __init__(self):
self.cache = {} self.cache = {}

Wyświetl plik

@ -7,45 +7,47 @@ from os import path
mimetypes.init() mimetypes.init()
# In production we highly recomend to use CDN like CloudFlare or/and NGINX or similar for static files # In production we highly recomend to use CDN like CloudFlare or/and NGINX or similar for static files
async def sendfile(res, req, filename): async def sendfile(res, req, filename):
#read headers before the first await # read headers before the first await
if_modified_since = req.get_header('if-modified-since') if_modified_since = req.get_header("if-modified-since")
range_header = req.get_header('range') range_header = req.get_header("range")
bytes_range = None bytes_range = None
start = 0 start = 0
end = -1 end = -1
#parse range header # parse range header
if range_header: if range_header:
bytes_range = range_header.replace("bytes=", '').split('-') bytes_range = range_header.replace("bytes=", "").split("-")
start = int(bytes_range[0]) start = int(bytes_range[0])
if bytes_range[1]: if bytes_range[1]:
end = int(bytes_range[1]) end = int(bytes_range[1])
try: try:
exists = path.exists(filename) exists = path.exists(filename)
#not found # not found
if not exists: if not exists:
return res.write_status(404).end(b'Not Found') return res.write_status(404).end(b"Not Found")
#get size and last modified date # get size and last modified date
stats = os.stat(filename) stats = os.stat(filename)
total_size = stats.st_size total_size = stats.st_size
size = total_size size = total_size
last_modified = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(stats.st_mtime)) last_modified = time.strftime(
"%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)
)
#check if modified since is provided # check if modified since is provided
if if_modified_since == last_modified: if if_modified_since == last_modified:
return res.write_status(304).end_without_body() return res.write_status(304).end_without_body()
#tells the broswer the last modified date # tells the broswer the last modified date
res.write_header(b'Last-Modified', last_modified) res.write_header(b"Last-Modified", last_modified)
#add content type # add content type
(content_type, encoding) = mimetypes.guess_type(filename, strict=True) (content_type, encoding) = mimetypes.guess_type(filename, strict=True)
if content_type and encoding: if content_type and encoding:
res.write_header(b'Content-Type', '%s; %s' % (content_type, encoding)) res.write_header(b"Content-Type", "%s; %s" % (content_type, encoding))
elif content_type: elif content_type:
res.write_header(b'Content-Type', content_type) res.write_header(b"Content-Type", content_type)
async with async_open(filename, "rb") as fd: async with async_open(filename, "rb") as fd:
#check range and support it # check range and support it
if start > 0 or not end == -1: if start > 0 or not end == -1:
if end < 0 or end >= size: if end < 0 or end >= size:
end = size - 1 end = size - 1
@ -58,32 +60,33 @@ async def sendfile(res, req, filename):
end = size - 1 end = size - 1
res.write_status(200) res.write_status(200)
#tells the browser that we support range # tells the browser that we support range
#TODO: FIX BYTE RANGE IN ASYNC # TODO: FIX BYTE RANGE IN ASYNC
# res.write_header(b'Accept-Ranges', b'bytes') # res.write_header(b'Accept-Ranges', b'bytes')
# res.write_header(b'Content-Range', 'bytes %d-%d/%d' % (start, end, total_size)) # res.write_header(b'Content-Range', 'bytes %d-%d/%d' % (start, end, total_size))
pending_size = size pending_size = size
#keep sending until abort or done # keep sending until abort or done
while not res.aborted: while not res.aborted:
chunk_size = 16384 #16kb chunks chunk_size = 16384 # 16kb chunks
if chunk_size > pending_size: if chunk_size > pending_size:
chunk_size = pending_size chunk_size = pending_size
buffer = await fd.read(chunk_size) buffer = await fd.read(chunk_size)
pending_size = pending_size - chunk_size pending_size = pending_size - chunk_size
(ok, done) = await res.send_chunk(buffer, size) (ok, done) = await res.send_chunk(buffer, size)
if not ok or done: #if cannot send probably aborted if not ok or done: # if cannot send probably aborted
break break
except Exception as error: except Exception as error:
res.write_status(500).end("Internal Error") res.write_status(500).end("Internal Error")
def in_directory(file, directory): def in_directory(file, directory):
#make both absolute # make both absolute
directory = path.join(path.realpath(directory), '') directory = path.join(path.realpath(directory), "")
file = path.realpath(file) file = path.realpath(file)
#return true, if the common prefix of both is equal to directory # return true, if the common prefix of both is equal to directory
#e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b # e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b
return path.commonprefix([file, directory]) == directory return path.commonprefix([file, directory]) == directory
@ -91,7 +94,7 @@ def static_route(app, route, directory):
def route_handler(res, req): def route_handler(res, req):
url = req.get_url() url = req.get_url()
res.grab_aborted_handler() res.grab_aborted_handler()
url = url[len(route)::] url = url[len(route) : :]
if url.startswith("/"): if url.startswith("/"):
url = url[1::] url = url[1::]
filename = path.join(path.realpath(directory), url) filename = path.join(path.realpath(directory), url)
@ -100,6 +103,7 @@ def static_route(app, route, directory):
res.write_status(404).end_without_body() res.write_status(404).end_without_body()
return return
res.run_async(sendfile(res, req, filename)) res.run_async(sendfile(res, req, filename))
if route.startswith("/"): if route.startswith("/"):
route = route[1::] route = route[1::]
app.get("%s/*" % route, route_handler) app.get("%s/*" % route, route_handler)

Wyświetl plik

@ -7,45 +7,47 @@ from os import path
mimetypes.init() mimetypes.init()
# In production we highly recomend to use CDN like CloudFlare or/and NGINX or similar for static files # In production we highly recomend to use CDN like CloudFlare or/and NGINX or similar for static files
async def sendfile(res, req, filename): async def sendfile(res, req, filename):
#read headers before the first await # read headers before the first await
if_modified_since = req.get_header('if-modified-since') if_modified_since = req.get_header("if-modified-since")
range_header = req.get_header('range') range_header = req.get_header("range")
bytes_range = None bytes_range = None
start = 0 start = 0
end = -1 end = -1
#parse range header # parse range header
if range_header: if range_header:
bytes_range = range_header.replace("bytes=", '').split('-') bytes_range = range_header.replace("bytes=", "").split("-")
start = int(bytes_range[0]) start = int(bytes_range[0])
if bytes_range[1]: if bytes_range[1]:
end = int(bytes_range[1]) end = int(bytes_range[1])
try: try:
exists = await os.path.exists(filename) exists = await os.path.exists(filename)
#not found # not found
if not exists: if not exists:
return res.write_status(404).end(b'Not Found') return res.write_status(404).end(b"Not Found")
#get size and last modified date # get size and last modified date
stats = await os.stat(filename) stats = await os.stat(filename)
total_size = stats.st_size total_size = stats.st_size
size = total_size size = total_size
last_modified = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(stats.st_mtime)) last_modified = time.strftime(
"%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)
)
#check if modified since is provided # check if modified since is provided
if if_modified_since == last_modified: if if_modified_since == last_modified:
return res.write_status(304).end_without_body() return res.write_status(304).end_without_body()
#tells the broswer the last modified date # tells the broswer the last modified date
res.write_header(b'Last-Modified', last_modified) res.write_header(b"Last-Modified", last_modified)
#add content type # add content type
(content_type, encoding) = mimetypes.guess_type(filename, strict=True) (content_type, encoding) = mimetypes.guess_type(filename, strict=True)
if content_type and encoding: if content_type and encoding:
res.write_header(b'Content-Type', '%s; %s' % (content_type, encoding)) res.write_header(b"Content-Type", "%s; %s" % (content_type, encoding))
elif content_type: elif content_type:
res.write_header(b'Content-Type', content_type) res.write_header(b"Content-Type", content_type)
async with aiofiles.open(filename, "rb") as fd: async with aiofiles.open(filename, "rb") as fd:
#check range and support it # check range and support it
if start > 0 or not end == -1: if start > 0 or not end == -1:
if end < 0 or end >= size: if end < 0 or end >= size:
end = size - 1 end = size - 1
@ -58,32 +60,33 @@ async def sendfile(res, req, filename):
end = size - 1 end = size - 1
res.write_status(200) res.write_status(200)
#tells the browser that we support range # tells the browser that we support range
#TODO: FIX BYTE RANGE IN ASYNC # TODO: FIX BYTE RANGE IN ASYNC
# res.write_header(b'Accept-Ranges', b'bytes') # res.write_header(b'Accept-Ranges', b'bytes')
# res.write_header(b'Content-Range', 'bytes %d-%d/%d' % (start, end, total_size)) # res.write_header(b'Content-Range', 'bytes %d-%d/%d' % (start, end, total_size))
pending_size = size pending_size = size
#keep sending until abort or done # keep sending until abort or done
while not res.aborted: while not res.aborted:
chunk_size = 16384 #16kb chunks chunk_size = 16384 # 16kb chunks
if chunk_size > pending_size: if chunk_size > pending_size:
chunk_size = pending_size chunk_size = pending_size
buffer = await fd.read(chunk_size) buffer = await fd.read(chunk_size)
pending_size = pending_size - chunk_size pending_size = pending_size - chunk_size
(ok, done) = await res.send_chunk(buffer, size) (ok, done) = await res.send_chunk(buffer, size)
if not ok or done: #if cannot send probably aborted if not ok or done: # if cannot send probably aborted
break break
except Exception as error: except Exception as error:
res.write_status(500).end("Internal Error") res.write_status(500).end("Internal Error")
def in_directory(file, directory): def in_directory(file, directory):
#make both absolute # make both absolute
directory = path.join(path.realpath(directory), '') directory = path.join(path.realpath(directory), "")
file = path.realpath(file) file = path.realpath(file)
#return true, if the common prefix of both is equal to directory # return true, if the common prefix of both is equal to directory
#e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b # e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b
return path.commonprefix([file, directory]) == directory return path.commonprefix([file, directory]) == directory
@ -91,7 +94,7 @@ def static_route(app, route, directory):
def route_handler(res, req): def route_handler(res, req):
url = req.get_url() url = req.get_url()
res.grab_aborted_handler() res.grab_aborted_handler()
url = url[len(route)::] url = url[len(route) : :]
if url.startswith("/"): if url.startswith("/"):
url = url[1::] url = url[1::]
filename = path.join(path.realpath(directory), url) filename = path.join(path.realpath(directory), url)
@ -100,6 +103,7 @@ def static_route(app, route, directory):
res.write_status(404).end_without_body() res.write_status(404).end_without_body()
return return
res.run_async(sendfile(res, req, filename)) res.run_async(sendfile(res, req, filename))
if route.startswith("/"): if route.startswith("/"):
route = route[1::] route = route[1::]
app.get("%s/*" % route, route_handler) app.get("%s/*" % route, route_handler)

Wyświetl plik

@ -1,4 +1,4 @@
#Simple example of mako and jinja2 template plugin for socketify.py # Simple example of mako and jinja2 template plugin for socketify.py
from mako.template import Template from mako.template import Template
from mako.lookup import TemplateLookup from mako.lookup import TemplateLookup
from mako import exceptions from mako import exceptions
@ -6,11 +6,14 @@ from mako import exceptions
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
class Jinja2Template:
def __init__(self, searchpath, encoding='utf-8', followlinks=False):
self.env = Environment(loader=FileSystemLoader(searchpath, encoding, followlinks))
#You can also add caching and logging strategy here if you want ;) class Jinja2Template:
def __init__(self, searchpath, encoding="utf-8", followlinks=False):
self.env = Environment(
loader=FileSystemLoader(searchpath, encoding, followlinks)
)
# You can also add caching and logging strategy here if you want ;)
def render(self, templatename, **kwargs): def render(self, templatename, **kwargs):
try: try:
template = self.env.get_template(templatename) template = self.env.get_template(templatename)
@ -18,11 +21,12 @@ class Jinja2Template:
except Exception as err: except Exception as err:
return str(err) return str(err)
class MakoTemplate: class MakoTemplate:
def __init__(self, **options): def __init__(self, **options):
self.lookup = TemplateLookup(**options) self.lookup = TemplateLookup(**options)
#You can also add caching and logging strategy here if you want ;) # You can also add caching and logging strategy here if you want ;)
def render(self, templatename, **kwargs): def render(self, templatename, **kwargs):
try: try:
template = self.lookup.get_template(templatename) template = self.lookup.get_template(templatename)

Wyświetl plik

@ -3,16 +3,18 @@ from .memory_cache import MemoryCache
# 2 LEVEL CACHE (Redis to share amoung worker, Memory to be much faster) # 2 LEVEL CACHE (Redis to share amoung worker, Memory to be much faster)
class TwoLevelCache: class TwoLevelCache:
def __init__(self, redis_conection, memory_expiration_time=3, redis_expiration_time=10): def __init__(
self, redis_conection, memory_expiration_time=3, redis_expiration_time=10
):
self.memory_cache = MemoryCache() self.memory_cache = MemoryCache()
self.redis_conection = redis_conection self.redis_conection = redis_conection
self.memory_expiration_time = memory_expiration_time self.memory_expiration_time = memory_expiration_time
self.redis_expiration_time = redis_expiration_time self.redis_expiration_time = redis_expiration_time
#set cache to redis and memory # set cache to redis and memory
def set(self, key, data): def set(self, key, data):
try: try:
#never cache invalid data # never cache invalid data
if data == None: if data == None:
return False return False
self.redis_conection.setex(key, self.redis_expiration_time, data) self.redis_conection.setex(key, self.redis_expiration_time, data)
@ -27,26 +29,26 @@ class TwoLevelCache:
value = self.memory_cache.get(key) value = self.memory_cache.get(key)
if value != None: if value != None:
return value return value
#no memory cache so, got to redis # no memory cache so, got to redis
value = self.redis_conection.get(key) value = self.redis_conection.get(key)
if value != None: if value != None:
#refresh memory cache to speed up # refresh memory cache to speed up
self.memory_cache.setex(key, self.memory_expiration_time, data) self.memory_cache.setex(key, self.memory_expiration_time, data)
return value return value
except Exception as err: except Exception as err:
return None return None
#if more than 1 worker/request try to do this request, only one will call the Model and the others will get from cache # if more than 1 worker/request try to do this request, only one will call the Model and the others will get from cache
async def run_once(self, key, timeout, executor, *args): async def run_once(self, key, timeout, executor, *args):
result = None result = None
try: try:
lock = self.redis_conection.lock(f"lock-{key}", blocking_timeout=timeout) lock = self.redis_conection.lock(f"lock-{key}", blocking_timeout=timeout)
#wait lock (some request is yeat not finish) # wait lock (some request is yeat not finish)
while lock.locked(): while lock.locked():
await asyncio.sleep(0) await asyncio.sleep(0)
try: try:
lock.acquire(blocking=False) lock.acquire(blocking=False)
#always check cache first # always check cache first
cached = self.get(key) cached = self.get(key)
if cached != None: if cached != None:
return cached return cached
@ -59,10 +61,10 @@ class TwoLevelCache:
finally: finally:
lock.release() lock.release()
except Exception as err: except Exception as err:
#cannot even create or release the lock # cannot even create or release the lock
pass pass
finally: finally:
#if result is None, try cache one last time # if result is None, try cache one last time
if result == None: if result == None:
cache = self.get(key) cache = self.get(key)
if cache != None: if cache != None:

Wyświetl plik

@ -4,8 +4,8 @@ import aiohttp
import asyncio import asyncio
from helpers.twolevel_cache import TwoLevelCache from helpers.twolevel_cache import TwoLevelCache
#create redis poll + connections # create redis poll + connections
redis_pool = redis.ConnectionPool(host='localhost', port=6379, db=0) redis_pool = redis.ConnectionPool(host="localhost", port=6379, db=0)
redis_conection = redis.Redis(connection_pool=redis_pool) redis_conection = redis.Redis(connection_pool=redis_pool)
# 2 LEVEL CACHE (Redis to share amoung workers, Memory to be much faster) # 2 LEVEL CACHE (Redis to share amoung workers, Memory to be much faster)
# cache in memory is 30s, cache in redis is 60s duration # cache in memory is 30s, cache in redis is 60s duration
@ -15,19 +15,25 @@ cache = TwoLevelCache(redis_conection, 30, 60)
# Model # Model
### ###
async def get_pokemon(number): async def get_pokemon(number):
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(f'https://pokeapi.co/api/v2/pokemon/{number}') as response: async with session.get(
f"https://pokeapi.co/api/v2/pokemon/{number}"
) as response:
pokemon = await response.text() pokemon = await response.text()
#cache only works with strings/bytes # cache only works with strings/bytes
#we will not change nothing here so no needs to parse json # we will not change nothing here so no needs to parse json
return pokemon.encode("utf-8") return pokemon.encode("utf-8")
async def get_original_pokemons(): async def get_original_pokemons():
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(f'https://pokeapi.co/api/v2/pokemon?limit=151') as response: async with session.get(
#cache only works with strings/bytes f"https://pokeapi.co/api/v2/pokemon?limit=151"
#we will not change nothing here so no needs to parse json ) as response:
# cache only works with strings/bytes
# we will not change nothing here so no needs to parse json
pokemons = await response.text() pokemons = await response.text()
return pokemons.encode("utf-8") return pokemons.encode("utf-8")
@ -37,12 +43,12 @@ async def get_original_pokemons():
### ###
def list_original_pokemons(res, req): def list_original_pokemons(res, req):
#check cache for faster response # check cache for faster response
value = cache.get("original_pokemons") value = cache.get("original_pokemons")
if value != None: if value != None:
return res.end(value) return res.end(value)
#get asynchronous from Model # get asynchronous from Model
async def get_originals(): async def get_originals():
value = await cache.run_once("original_pokemons", 5, get_original_pokemons) value = await cache.run_once("original_pokemons", 5, get_original_pokemons)
res.cork_end(value) res.cork_end(value)
@ -52,28 +58,29 @@ def list_original_pokemons(res, req):
def list_pokemon(res, req): def list_pokemon(res, req):
#get needed parameters # get needed parameters
try: try:
number = int(req.get_parameter(0)) number = int(req.get_parameter(0))
except: except:
#invalid number # invalid number
return req.set_yield(1) return req.set_yield(1)
#check cache for faster response # check cache for faster response
cache_key = f"pokemon-{number}" cache_key = f"pokemon-{number}"
value = cache.get(cache_key) value = cache.get(cache_key)
if value != None: if value != None:
return res.end(value) return res.end(value)
#get asynchronous from Model # get asynchronous from Model
async def find_pokemon(number, res): async def find_pokemon(number, res):
#sync with redis lock to run only once # sync with redis lock to run only once
#if more than 1 worker/request try to do this request, only one will call the Model and the others will get from cache # if more than 1 worker/request try to do this request, only one will call the Model and the others will get from cache
value = await cache.run_once(cache_key, 5, get_pokemon, number) value = await cache.run_once(cache_key, 5, get_pokemon, number)
res.cork_end(value) res.cork_end(value)
res.run_async(find_pokemon(number, res)) res.run_async(find_pokemon(number, res))
### ###
# Here i decided to use an sync first and async only if needs, but you can use async directly see ./async.py # Here i decided to use an sync first and async only if needs, but you can use async directly see ./async.py
### ###
@ -81,5 +88,8 @@ app = App()
app.get("/", list_original_pokemons) app.get("/", list_original_pokemons)
app.get("/:number", list_pokemon) app.get("/:number", list_pokemon)
app.any("/*", lambda res, _: res.write_status(404).end("Not Found")) app.any("/*", lambda res, _: res.write_status(404).end("Not Found"))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -1,8 +1,17 @@
from socketify 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("/", lambda res, req: res.end("Hello World socketify from Python!")) app.get("/", lambda res, req: res.end("Hello World socketify from Python!"))
app.listen(3000, lambda config: print("Listening on port https://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port https://localhost:%d now\n" % config.port),
)
app.run() app.run()
#openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -passout pass:1234 -keyout ./misc/key.pem -out ./misc/cert.pem # openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -passout pass:1234 -keyout ./misc/key.pem -out ./misc/cert.pem

Wyświetl plik

@ -2,5 +2,10 @@ from socketify import App, AppListenOptions
app = App() app = App()
app.get("/", lambda res, req: res.end("Hello World socketify from Python!")) app.get("/", lambda res, req: res.end("Hello World socketify from Python!"))
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.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() app.run()

Wyświetl plik

@ -1,34 +1,42 @@
from socketify import App, middleware from socketify import App, middleware
async def get_user(authorization): async def get_user(authorization):
if authorization: if authorization:
#you can do something async here # you can do something async here
return { 'greeting': 'Hello, World' } return {"greeting": "Hello, World"}
return None return None
async def auth(res, req, data=None): async def auth(res, req, data=None):
user = await get_user(req.get_header('authorization')) user = await get_user(req.get_header("authorization"))
if not user: if not user:
res.write_status(403).end("not authorized") res.write_status(403).end("not authorized")
#returning Falsy in middlewares just stop the execution of the next middleware # returning Falsy in middlewares just stop the execution of the next middleware
return False return False
#returns extra data # returns extra data
return user return user
def another_middie(res, req, data=None): def another_middie(res, req, data=None):
#now we can mix sync and async and change the data here # now we can mix sync and async and change the data here
if isinstance(data, dict): if isinstance(data, dict):
gretting = data.get('greeting', '') gretting = data.get("greeting", "")
data['greeting'] = f"{gretting} from another middie ;)" data["greeting"] = f"{gretting} from another middie ;)"
return data return data
def home(res, req, user=None): def home(res, req, user=None):
res.cork_end(user.get('greeting', None)) res.cork_end(user.get("greeting", None))
app = App() app = App()
app.get("/", middleware(auth, another_middie, home)) app.get("/", middleware(auth, another_middie, home))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()
#You can also take a loop on MiddlewareRouter in middleware_router.py ;) # You can also take a loop on MiddlewareRouter in middleware_router.py ;)

Wyświetl plik

@ -1,24 +1,26 @@
from socketify import App from socketify import App
#this is just an example of implementation you can just import using from socketify import middleware for an more complete version # this is just an example of implementation you can just import using from socketify import middleware for an more complete version
async def get_user(authorization): async def get_user(authorization):
if authorization: if authorization:
#do actually something async here # do actually something async here
return { 'greeting': 'Hello, World' } return {"greeting": "Hello, World"}
return None return None
def auth(route): def auth(route):
#in async query string, arguments and headers are only valid until the first await # in async query string, arguments and headers are only valid until the first await
async def auth_middleware(res, req): async def auth_middleware(res, req):
#get_headers will preserve headers (and cookies) inside req, after await # get_headers will preserve headers (and cookies) inside req, after await
headers = req.get_headers() headers = req.get_headers()
#get_parameters will preserve all params inside req after await # get_parameters will preserve all params inside req after await
params = req.get_parameters() params = req.get_parameters()
#get queries will preserve all queries inside req after await # get queries will preserve all queries inside req after await
queries = req.get_queries() queries = req.get_queries()
user = await get_user(headers.get('authorization', None)) user = await get_user(headers.get("authorization", None))
if user: if user:
return route(res, req, user) return route(res, req, user)
@ -30,14 +32,18 @@ def auth(route):
def home(res, req, user=None): def home(res, req, user=None):
theme = req.get_query("theme_color") theme = req.get_query("theme_color")
theme = theme if theme else "light" theme = theme if theme else "light"
greeting = user.get('greeting', None) greeting = user.get("greeting", None)
user_id = req.get_parameter(0) user_id = req.get_parameter(0)
res.cork_end(f"{greeting} <br/> theme: {theme} <br/> id: {user_id}") res.cork_end(f"{greeting} <br/> theme: {theme} <br/> id: {user_id}")
app = App() app = App()
app.get("/user/:id", auth(home)) app.get("/user/:id", auth(home))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()
#curl --location --request GET 'http://localhost:3000/user/10?theme_color=dark' --header 'Authorization: Bearer 23456789' # curl --location --request GET 'http://localhost:3000/user/10?theme_color=dark' --header 'Authorization: Bearer 23456789'

Wyświetl plik

@ -3,44 +3,49 @@ from socketify import App, MiddlewareRouter, middleware
async def get_user(authorization): async def get_user(authorization):
if authorization: if authorization:
#you can do something async here # you can do something async here
return { 'greeting': 'Hello, World' } return {"greeting": "Hello, World"}
return None return None
async def auth(res, req, data=None): async def auth(res, req, data=None):
user = await get_user(req.get_header('authorization')) user = await get_user(req.get_header("authorization"))
if not user: if not user:
res.write_status(403).end("not authorized") res.write_status(403).end("not authorized")
#returning Falsy in middlewares just stop the execution of the next middleware # returning Falsy in middlewares just stop the execution of the next middleware
return False return False
#returns extra data # returns extra data
return user return user
def another_middie(res, req, data=None): def another_middie(res, req, data=None):
#now we can mix sync and async and change the data here # now we can mix sync and async and change the data here
if isinstance(data, dict): if isinstance(data, dict):
gretting = data.get('greeting', '') gretting = data.get("greeting", "")
data['greeting'] = f"{gretting} from another middie ;)" data["greeting"] = f"{gretting} from another middie ;)"
return data return data
def home(res, req, user=None):
res.cork_end(user.get('greeting', None))
def home(res, req, user=None):
res.cork_end(user.get("greeting", None))
app = App() app = App()
#you can use an Middleware router to add middlewares to every route you set # you can use an Middleware router to add middlewares to every route you set
auth_router = MiddlewareRouter(app, auth) auth_router = MiddlewareRouter(app, auth)
auth_router.get("/", home) auth_router.get("/", home)
#you can also mix middleware() with MiddlewareRouter # you can also mix middleware() with MiddlewareRouter
auth_router.get("/another", middleware(another_middie, home)) auth_router.get("/another", middleware(another_middie, home))
#you can also pass multiple middlewares on the MiddlewareRouter # you can also pass multiple middlewares on the MiddlewareRouter
other_router = MiddlewareRouter(app, auth, another_middie) other_router = MiddlewareRouter(app, auth, another_middie)
other_router.get("/another_way", home) other_router.get("/another_way", home)
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -1,38 +1,46 @@
from socketify import App from socketify import App
#this is just an example of implementation you can just import using from socketify import middleware for an more complete version # this is just an example of implementation you can just import using from socketify import middleware for an more complete version
def middleware(*functions): def middleware(*functions):
def middleware_route(res, req): def middleware_route(res, req):
data = None data = None
#cicle to all middlewares # cicle to all middlewares
for function in functions: for function in functions:
#call middlewares # call middlewares
data = function(res, req, data) data = function(res, req, data)
#stops if returns Falsy # stops if returns Falsy
if not data: if not data:
break break
return middleware_route return middleware_route
def get_user(authorization_header): def get_user(authorization_header):
if authorization_header: if authorization_header:
return { 'greeting': 'Hello, World' } return {"greeting": "Hello, World"}
return None return None
def auth(res, req, data=None): def auth(res, req, data=None):
user = get_user(req.get_header('authorization')) user = get_user(req.get_header("authorization"))
if not user: if not user:
res.write_status(403).end("not authorized") res.write_status(403).end("not authorized")
return False return False
#returns extra data # returns extra data
return user return user
def home(res, req, user=None): def home(res, req, user=None):
res.end(user.get('greeting', None)) res.end(user.get("greeting", None))
app = App() app = App()
app.get("/", middleware(auth, home)) app.get("/", middleware(auth, home))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -2,23 +2,32 @@ from socketify import App, AppOptions, AppListenOptions
app = App() app = App()
async def home(res, req): async def home(res, req):
res.end("Hello, World!") res.end("Hello, World!")
def user(res, req): def user(res, req):
try: try:
if int(req.get_parameter(0)) == 1: if int(req.get_parameter(0)) == 1:
return res.end("Hello user 1!") return res.end("Hello user 1!")
finally: finally:
#invalid user tells to go, to the next route valid route (not found) # invalid user tells to go, to the next route valid route (not found)
req.set_yield(1) req.set_yield(1)
def not_found(res, req): def not_found(res, req):
res.write_status(404).end("Not Found") res.write_status(404).end("Not Found")
app.get("/", home) app.get("/", home)
app.get("/user/:user_id", user) app.get("/user/:user_id", user)
app.any("/*", not_found) app.any("/*", not_found)
app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port))) app.listen(
3000,
lambda config: print(
"Listening on port http://localhost:%s now\n" % str(config.port)
),
)
app.run() app.run()

Wyświetl plik

@ -1,13 +1,18 @@
from socketify import App from socketify import App
def home(res, req): def home(res, req):
res.write('<html><h1>') res.write("<html><h1>")
res.write('Your proxied IP is: %s' % res.get_proxied_remote_address()) res.write("Your proxied IP is: %s" % res.get_proxied_remote_address())
res.write('</h1><h1>') res.write("</h1><h1>")
res.write('Your IP as seen by the origin server is: %s' % res.get_remote_address()) res.write("Your IP as seen by the origin server is: %s" % res.get_remote_address())
res.end('</h1></html>') res.end("</h1></html>")
app = App() app = App()
app.get("/*", home) app.get("/*", home)
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -4,4 +4,4 @@ aiofile
redis redis
strawberry-graphql strawberry-graphql
mako mako
git+https://github.com/cirospaciari/socketify.py.git@main#socketify --global-option="build_ext" git+https://github.com/cirospaciari/socketify.py.git@main#socketify

Wyświetl plik

@ -1,19 +1,25 @@
from socketify import App, AppOptions, AppListenOptions from socketify import App, AppOptions, AppListenOptions
import asyncio import asyncio
from datetime import datetime from datetime import datetime
from datetime import timedelta from datetime import timedelta
app = App() app = App()
def home(res, req): def home(res, req):
res.end("Hello :)") res.end("Hello :)")
def anything(res, req): def anything(res, req):
res.end("Any route with method: %s" % req.get_method()) res.end("Any route with method: %s" % req.get_method())
def cookies(res, req): def cookies(res, req):
#cookies are writen after end # cookies are writen after end
res.set_cookie("spaciari", "1234567890",{ res.set_cookie(
"spaciari",
"1234567890",
{
# expires # expires
# path # path
# comment # comment
@ -28,12 +34,15 @@ def cookies(res, req):
"httponly": True, "httponly": True,
"samesite": "None", "samesite": "None",
"secure": True, "secure": True,
"expires": datetime.utcnow() + timedelta(minutes=30) "expires": datetime.utcnow() + timedelta(minutes=30),
}) },
res.end("Your session_id cookie is: %s" % req.get_cookie('session_id')); )
res.end("Your session_id cookie is: %s" % req.get_cookie("session_id"))
def useragent(res, req):
res.end("Your user agent is: %s" % req.get_header("user-agent"))
def useragent(res,req):
res.end("Your user agent is: %s" % req.get_header('user-agent'));
def user(res, req): def user(res, req):
try: try:
@ -46,65 +55,74 @@ def user(res, req):
# invalid user tells to go, to the next route valid route (not found) # invalid user tells to go, to the next route valid route (not found)
req.set_yield(1) req.set_yield(1)
async def delayed_hello(delay, res): async def delayed_hello(delay, res):
await asyncio.sleep(delay) #do something async await asyncio.sleep(delay) # do something async
res.cork_end("Hello sorry for the delay!") res.cork_end("Hello sorry for the delay!")
# cork_end is a less verbose way of writing # cork_end is a less verbose way of writing
# res.cork(lambda res: res.end("Hello sorry for the delay!")) # res.cork(lambda res: res.end("Hello sorry for the delay!"))
def delayed(res, req): def delayed(res, req):
#request object only lives during the life time of this call # request object only lives during the life time of this call
#get parameters, query, headers anything you need here # get parameters, query, headers anything you need here
delay = req.get_query("delay") delay = req.get_query("delay")
delay = 1 if delay == None else float(delay) delay = 1 if delay == None else float(delay)
#get queries returns an dict with all query string # get queries returns an dict with all query string
# queries = req.get_queries() # queries = req.get_queries()
#tell response to run this in the event loop # tell response to run this in the event loop
#abort handler is grabed here, so responses only will be send if res.aborted == False # abort handler is grabed here, so responses only will be send if res.aborted == False
res.run_async(delayed_hello(delay, res)) res.run_async(delayed_hello(delay, res))
def json(res, req): def json(res, req):
#if you pass an object will auto write an header with application/json # if you pass an object will auto write an header with application/json
res.end({ "message": "I'm an application/json!"}) res.end({"message": "I'm an application/json!"})
async def sleepy_json(res, req): async def sleepy_json(res, req):
#get parameters, query, headers anything you need here before first await :) # get parameters, query, headers anything you need here before first await :)
user_agent = req.get_header("user-agent") user_agent = req.get_header("user-agent")
#print all headers # print all headers
req.for_each_header(lambda key,value: print("Header %s: %s" % (key, value))) req.for_each_header(lambda key, value: print("Header %s: %s" % (key, value)))
#or if you want get all headers in an dict # or if you want get all headers in an dict
print("All headers", req.get_headers()) print("All headers", req.get_headers())
#req maybe will not be available in direct attached async functions after await # req maybe will not be available in direct attached async functions after await
#but if you dont care about req info you can do it # but if you dont care about req info you can do it
await asyncio.sleep(2) #do something async await asyncio.sleep(2) # do something async
res.cork_end({ "message": "I'm delayed!", "user-agent": user_agent}) res.cork_end({"message": "I'm delayed!", "user-agent": user_agent})
def custom_header(res, req): def custom_header(res, req):
res.write_header("Content-Type", "application/octet-stream") res.write_header("Content-Type", "application/octet-stream")
res.write_header("Content-Disposition", "attachment; filename=\"message.txt\"") res.write_header("Content-Disposition", 'attachment; filename="message.txt"')
res.end("Downloaded this ;)") res.end("Downloaded this ;)")
def send_in_parts(res, req): def send_in_parts(res, req):
#write and end accepts bytes and str or its try to dumps to an json # write and end accepts bytes and str or its try to dumps to an json
res.write("I can") res.write("I can")
res.write(" send ") res.write(" send ")
res.write("messages") res.write("messages")
res.end(" in parts!") res.end(" in parts!")
def redirect(res, req): def redirect(res, req):
#status code is optional default is 302 # status code is optional default is 302
res.redirect("/redirected", 302) res.redirect("/redirected", 302)
def redirected(res, req): def redirected(res, req):
res.end("You got redirected to here :D") res.end("You got redirected to here :D")
def not_found(res, req): def not_found(res, req):
res.write_status(404).end("Not Found") res.write_status(404).end("Not Found")
# app.any, app.get, app.put, app.post, app.head, app.options, app.delete, app.patch, app.connect and app.trace are available # app.any, app.get, app.put, app.post, app.head, app.options, app.delete, app.patch, app.connect and app.trace are available
app.get("/", home) app.get("/", home)
app.any("/anything", anything) app.any("/anything", anything)
@ -122,5 +140,10 @@ app.get("/redirected", redirected)
# Wildcard at last always :) # Wildcard at last always :)
app.any("/*", not_found) app.any("/*", not_found)
app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port))) app.listen(
3000,
lambda config: print(
"Listening on port http://localhost:%s now\n" % str(config.port)
),
)
app.run() app.run()

Wyświetl plik

@ -35,16 +35,19 @@ from socketify import App, sendfile
app = App() app = App()
#send home page index.html # send home page index.html
async def home(res, req): async def home(res, req):
#sends the whole file with 304 and bytes range support # sends the whole file with 304 and bytes range support
await sendfile(res, req, "./public/index.html") await sendfile(res, req, "./public/index.html")
app.get("/", home) app.get("/", home)
#serve all files in public folder under /* route (you can use any route like /assets) # serve all files in public folder under /* route (you can use any route like /assets)
app.static("/", "./public") app.static("/", "./public")
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -1,14 +1,22 @@
from socketify import App from socketify import App
#see helper/templates.py for plugin implementation
# see helper/templates.py for plugin implementation
from helpers.templates import Jinja2Template from helpers.templates import Jinja2Template
app = App() app = App()
app.template(Jinja2Template("./templates", encoding='utf-8', followlinks=False)) app.template(Jinja2Template("./templates", encoding="utf-8", followlinks=False))
def home(res, req): def home(res, req):
res.render("jinja2_home.html", title="Hello", message="Hello, World") res.render("jinja2_home.html", title="Hello", message="Hello, World")
app.get("/", home) app.get("/", home)
app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port))) app.listen(
3000,
lambda config: print(
"Listening on port http://localhost:%s now\n" % str(config.port)
),
)
app.run() app.run()

Wyświetl plik

@ -1,14 +1,26 @@
from socketify import App from socketify import App
#see helper/templates.py for plugin implementation
# see helper/templates.py for plugin implementation
from helpers.templates import MakoTemplate from helpers.templates import MakoTemplate
app = App() app = App()
app.template(MakoTemplate(directories=['./templates'], output_encoding='utf-8', encoding_errors='replace')) app.template(
MakoTemplate(
directories=["./templates"], output_encoding="utf-8", encoding_errors="replace"
)
)
def home(res, req): def home(res, req):
res.render("mako_home.html", message="Hello, World") res.render("mako_home.html", message="Hello, World")
app.get("/", home) app.get("/", home)
app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port))) app.listen(
3000,
lambda config: print(
"Listening on port http://localhost:%s now\n" % str(config.port)
),
)
app.run() app.run()

Wyświetl plik

@ -1,31 +1,43 @@
from socketify import App, AppOptions, OpCode, CompressOptions from socketify import App, AppOptions, OpCode, CompressOptions
def ws_open(ws): def ws_open(ws):
print('A WebSocket got connected!') print("A WebSocket got connected!")
ws.send("Hello World!", OpCode.TEXT) ws.send("Hello World!", OpCode.TEXT)
def ws_message(ws, message, opcode): def ws_message(ws, message, opcode):
print(message, opcode) print(message, opcode)
#Ok is false if backpressure was built up, wait for drain # Ok is false if backpressure was built up, wait for drain
ok = ws.send(message, opcode) ok = ws.send(message, opcode)
def ws_upgrade(res, req, socket_context): def ws_upgrade(res, req, socket_context):
key = req.get_header("sec-websocket-key") key = req.get_header("sec-websocket-key")
protocol = req.get_header("sec-websocket-protocol") protocol = req.get_header("sec-websocket-protocol")
extensions = req.get_header("sec-websocket-extensions") extensions = req.get_header("sec-websocket-extensions")
res.upgrade(key, protocol, extensions, socket_context) res.upgrade(key, protocol, extensions, socket_context)
app = App() app = App()
app.ws("/*", { app.ws(
'compression': CompressOptions.SHARED_COMPRESSOR, "/*",
'max_payload_length': 16 * 1024 * 1024, {
'idle_timeout': 12, "compression": CompressOptions.SHARED_COMPRESSOR,
'open': ws_open, "max_payload_length": 16 * 1024 * 1024,
'message': ws_message, "idle_timeout": 12,
'upgrade': ws_upgrade, "open": ws_open,
'drain': lambda ws: print('WebSocket backpressure: %s', ws.get_buffered_amount()), "message": ws_message,
'close': lambda ws, code, message: print('WebSocket closed') "upgrade": ws_upgrade,
}) "drain": lambda ws: print(
app.any("/", lambda res,req: res.end("Nothing to see here!")) "WebSocket backpressure: %s", ws.get_buffered_amount()
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % (config.port))) ),
"close": lambda ws, code, message: print("WebSocket closed"),
},
)
app.any("/", lambda res, req: res.end("Nothing to see here!"))
app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
)
app.run() app.run()

Wyświetl plik

@ -1,15 +1,18 @@
from socketify import App, AppOptions, OpCode, CompressOptions from socketify import App, AppOptions, OpCode, CompressOptions
import asyncio import asyncio
def ws_open(ws): def ws_open(ws):
print('A WebSocket got connected!') print("A WebSocket got connected!")
ws.send("Hello World!", OpCode.TEXT) ws.send("Hello World!", OpCode.TEXT)
def ws_message(ws, message, opcode): def ws_message(ws, message, opcode):
print(message, opcode) print(message, opcode)
#Ok is false if backpressure was built up, wait for drain # Ok is false if backpressure was built up, wait for drain
ok = ws.send(message, opcode) ok = ws.send(message, opcode)
async def ws_upgrade(res, req, socket_context): async def ws_upgrade(res, req, socket_context):
key = req.get_header("sec-websocket-key") key = req.get_header("sec-websocket-key")
protocol = req.get_header("sec-websocket-protocol") protocol = req.get_header("sec-websocket-protocol")
@ -17,17 +20,26 @@ async def ws_upgrade(res, req, socket_context):
await asyncio.sleep(2) await asyncio.sleep(2)
res.upgrade(key, protocol, extensions, socket_context) res.upgrade(key, protocol, extensions, socket_context)
app = App() app = App()
app.ws("/*", { app.ws(
'compression': CompressOptions.SHARED_COMPRESSOR, "/*",
'max_payload_length': 16 * 1024 * 1024, {
'idle_timeout': 12, "compression": CompressOptions.SHARED_COMPRESSOR,
'open': ws_open, "max_payload_length": 16 * 1024 * 1024,
'message': ws_message, "idle_timeout": 12,
'upgrade': ws_upgrade, "open": ws_open,
'drain': lambda ws: print('WebSocket backpressure: %s', ws.get_buffered_amount()), "message": ws_message,
'close': lambda ws, code, message: print('WebSocket closed') "upgrade": ws_upgrade,
}) "drain": lambda ws: print(
app.any("/", lambda res,req: res.end("Nothing to see here!")) "WebSocket backpressure: %s", ws.get_buffered_amount()
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % (config.port))) ),
"close": lambda ws, code, message: print("WebSocket closed"),
},
)
app.any("/", lambda res, req: res.end("Nothing to see here!"))
app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
)
app.run() app.run()

Wyświetl plik

@ -4,64 +4,71 @@ from socketify import App
# We always recomend check res.aborted in async operations # We always recomend check res.aborted in async operations
### ###
def upload(res, req): def upload(res, req):
print(f"Posted to {req.get_url()}") print(f"Posted to {req.get_url()}")
def on_data(res, chunk, is_end): def on_data(res, chunk, is_end):
print(f"Got chunk of data with length {len(chunk)}, is_end: {is_end}") print(f"Got chunk of data with length {len(chunk)}, is_end: {is_end}")
if (is_end): if is_end:
res.cork_end("Thanks for the data!") res.cork_end("Thanks for the data!")
res.on_data(on_data) res.on_data(on_data)
async def upload_chunks(res, req): async def upload_chunks(res, req):
print(f"Posted to {req.get_url()}") print(f"Posted to {req.get_url()}")
#await all the data, returns received chunks if fail (most likely fail is aborted requests) # await all the data, returns received chunks if fail (most likely fail is aborted requests)
data = await res.get_data() data = await res.get_data()
print(f"Got {len(data)} chunks of data!") print(f"Got {len(data)} chunks of data!")
for chunk in data: for chunk in data:
print(f"Got chunk of data with length {len(chunk)}") print(f"Got chunk of data with length {len(chunk)}")
#We respond when we are done # We respond when we are done
res.cork_end("Thanks for the data!") res.cork_end("Thanks for the data!")
async def upload_json(res, req): async def upload_json(res, req):
print(f"Posted to {req.get_url()}") print(f"Posted to {req.get_url()}")
#await all the data and parses as json, returns None if fail # await all the data and parses as json, returns None if fail
people = await res.get_json() people = await res.get_json()
if isinstance(people, list) and isinstance(people[0], dict): if isinstance(people, list) and isinstance(people[0], dict):
print(f"First person is named: {people[0]['name']}") print(f"First person is named: {people[0]['name']}")
#We respond when we are done # We respond when we are done
res.cork_end("Thanks for the data!") res.cork_end("Thanks for the data!")
async def upload_text(res, req): async def upload_text(res, req):
print(f"Posted to {req.get_url()}") print(f"Posted to {req.get_url()}")
#await all the data and decode as text, returns None if fail # await all the data and decode as text, returns None if fail
text = await res.get_text() #first parameter is the encoding (default utf-8) text = await res.get_text() # first parameter is the encoding (default utf-8)
print(f"Your text is ${text}") print(f"Your text is ${text}")
#We respond when we are done # We respond when we are done
res.cork_end("Thanks for the data!") res.cork_end("Thanks for the data!")
async def upload_urlencoded(res, req): async def upload_urlencoded(res, req):
print(f"Posted to {req.get_url()}") print(f"Posted to {req.get_url()}")
#await all the data and decode as application/x-www-form-urlencoded, returns None if fails # await all the data and decode as application/x-www-form-urlencoded, returns None if fails
form = await res.get_form_urlencoded() #first parameter is the encoding (default utf-8) form = (
await res.get_form_urlencoded()
) # first parameter is the encoding (default utf-8)
print(f"Your form is ${form}") print(f"Your form is ${form}")
#We respond when we are done # We respond when we are done
res.cork_end("Thanks for the data!") res.cork_end("Thanks for the data!")
async def upload_multiple(res, req): async def upload_multiple(res, req):
print(f"Posted to {req.get_url()}") print(f"Posted to {req.get_url()}")
content_type = req.get_header("content-type") content_type = req.get_header("content-type")
#we can check the Content-Type to accept multiple formats # we can check the Content-Type to accept multiple formats
if content_type == "application/json": if content_type == "application/json":
data = await res.get_json() data = await res.get_json()
elif content_type == "application/x-www-form-urlencoded": elif content_type == "application/x-www-form-urlencoded":
@ -71,7 +78,7 @@ async def upload_multiple(res, req):
print(f"Your data is ${data}") print(f"Your data is ${data}")
#We respond when we are done # We respond when we are done
res.cork_end("Thanks for the data!") res.cork_end("Thanks for the data!")
@ -83,6 +90,9 @@ app.post("/text", upload_text)
app.post("/urlencoded", upload_urlencoded) app.post("/urlencoded", upload_urlencoded)
app.post("/multiple", upload_multiple) app.post("/multiple", upload_multiple)
app.any("/*", lambda res,_: res.write_status(404).end("Not Found")) app.any("/*", lambda res, _: res.write_status(404).end("Not Found"))
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port)) app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run() app.run()

Wyświetl plik

@ -1,24 +1,35 @@
from socketify import App, AppOptions, OpCode, CompressOptions from socketify import App, AppOptions, OpCode, CompressOptions
def ws_open(ws): def ws_open(ws):
print('A WebSocket got connected!') print("A WebSocket got connected!")
ws.send("Hello World!", OpCode.TEXT) ws.send("Hello World!", OpCode.TEXT)
def ws_message(ws, message, opcode): def ws_message(ws, message, opcode):
print(message, opcode) print(message, opcode)
#Ok is false if backpressure was built up, wait for drain # Ok is false if backpressure was built up, wait for drain
ok = ws.send(message, opcode) ok = ws.send(message, opcode)
app = App() app = App()
app.ws("/*", { app.ws(
'compression': CompressOptions.SHARED_COMPRESSOR, "/*",
'max_payload_length': 16 * 1024 * 1024, {
'idle_timeout': 12, "compression": CompressOptions.SHARED_COMPRESSOR,
'open': ws_open, "max_payload_length": 16 * 1024 * 1024,
'message': ws_message, "idle_timeout": 12,
'drain': lambda ws: print('WebSocket backpressure: %s', ws.get_buffered_amount()), "open": ws_open,
'close': lambda ws, code, message: print('WebSocket closed') "message": ws_message,
}) "drain": lambda ws: print(
app.any("/", lambda res,req: res.end("Nothing to see here!'")) "WebSocket backpressure: %s", ws.get_buffered_amount()
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % (config.port))) ),
"close": lambda ws, code, message: print("WebSocket closed"),
},
)
app.any("/", lambda res, req: res.end("Nothing to see here!'"))
app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
)
app.run() app.run()

Wyświetl plik

@ -2,12 +2,13 @@ import sys
vi = sys.version_info vi = sys.version_info
if vi < (3, 7): if vi < (3, 7):
raise RuntimeError('socketify requires Python 3.7 or greater') raise RuntimeError("socketify requires Python 3.7 or greater")
# if sys.platform in ('win32', 'cygwin', 'cli'): # if sys.platform in ('win32', 'cygwin', 'cli'):
# raise RuntimeError('socketify does not support Windows at the moment') # raise RuntimeError('socketify does not support Windows at the moment')
import setuptools import setuptools
# from setuptools.command.sdist import sdist # from setuptools.command.sdist import sdist
# from setuptools.command.build_ext import build_ext # from setuptools.command.build_ext import build_ext
@ -28,8 +29,6 @@ import setuptools
# NATIVE_LIB_OUTPUT = str(_ROOT / "src" / "socketify" / "libsocketify.so") # NATIVE_LIB_OUTPUT = str(_ROOT / "src" / "socketify" / "libsocketify.so")
# class Prepare(sdist): # class Prepare(sdist):
# def run(self): # def run(self):
# super().run() # super().run()
@ -60,7 +59,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
setuptools.setup( setuptools.setup(
name="socketify", name="socketify",
version="0.0.1", version="0.0.1",
platforms=['macOS', 'POSIX'], platforms=["macOS", "POSIX"],
author="Ciro Spaciari", author="Ciro Spaciari",
author_email="ciro.spaciari@gmail.com", author_email="ciro.spaciari@gmail.com",
description="Fast WebSocket and Http/Https server", description="Fast WebSocket and Http/Https server",
@ -77,11 +76,20 @@ setuptools.setup(
], ],
packages=["socketify"], packages=["socketify"],
package_dir={"": "src"}, package_dir={"": "src"},
package_data={"": ['./*.so', './uWebSockets/*','./uWebSockets/*/*','./uWebSockets/*/*/*', './native/*','./native/*/*','./native/*/*/*']}, package_data={
"": [
"./*.so",
"./uWebSockets/*",
"./uWebSockets/*/*",
"./uWebSockets/*/*/*",
"./native/*",
"./native/*/*",
"./native/*/*/*",
]
},
python_requires=">=3.7", python_requires=">=3.7",
install_requires=["cffi>=1.0.0", "setuptools>=58.1.0"], install_requires=["cffi>=1.0.0", "setuptools>=58.1.0"],
has_ext_modules=lambda: True, has_ext_modules=lambda: True,
cmdclass={}, #cmdclass={'sdist': Prepare, 'build_ext': Makefile}, cmdclass={}, # cmdclass={'sdist': Prepare, 'build_ext': Makefile},
include_package_data=True,
include_package_data=True
) )

Wyświetl plik

@ -1,2 +1,9 @@
from .socketify import App, AppOptions, AppListenOptions, OpCode, SendStatus, CompressOptions from .socketify import (
App,
AppOptions,
AppListenOptions,
OpCode,
SendStatus,
CompressOptions,
)
from .helpers import sendfile, middleware, MiddlewareRouter from .helpers import sendfile, middleware, MiddlewareRouter

Wyświetl plik

@ -10,45 +10,47 @@ mimetypes.init()
# This is an sync version without any dependencies is normally much faster in CPython and PyPy3 # This is an sync version without any dependencies is normally much faster in CPython and PyPy3
# In production we highly recomend to use CDN like CloudFlare or/and NGINX or similar for static files # In production we highly recomend to use CDN like CloudFlare or/and NGINX or similar for static files
async def sendfile(res, req, filename): async def sendfile(res, req, filename):
#read headers before the first await # read headers before the first await
if_modified_since = req.get_header('if-modified-since') if_modified_since = req.get_header("if-modified-since")
range_header = req.get_header('range') range_header = req.get_header("range")
bytes_range = None bytes_range = None
start = 0 start = 0
end = -1 end = -1
#parse range header # parse range header
if range_header: if range_header:
bytes_range = range_header.replace("bytes=", '').split('-') bytes_range = range_header.replace("bytes=", "").split("-")
start = int(bytes_range[0]) start = int(bytes_range[0])
if bytes_range[1]: if bytes_range[1]:
end = int(bytes_range[1]) end = int(bytes_range[1])
try: try:
exists = path.exists(filename) exists = path.exists(filename)
#not found # not found
if not exists: if not exists:
return res.write_status(404).end(b'Not Found') return res.write_status(404).end(b"Not Found")
#get size and last modified date # get size and last modified date
stats = os.stat(filename) stats = os.stat(filename)
total_size = stats.st_size total_size = stats.st_size
size = total_size size = total_size
last_modified = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(stats.st_mtime)) last_modified = time.strftime(
"%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)
)
#check if modified since is provided # check if modified since is provided
if if_modified_since == last_modified: if if_modified_since == last_modified:
return res.write_status(304).end_without_body() return res.write_status(304).end_without_body()
#tells the broswer the last modified date # tells the broswer the last modified date
res.write_header(b'Last-Modified', last_modified) res.write_header(b"Last-Modified", last_modified)
#add content type # add content type
(content_type, encoding) = mimetypes.guess_type(filename, strict=True) (content_type, encoding) = mimetypes.guess_type(filename, strict=True)
if content_type and encoding: if content_type and encoding:
res.write_header(b'Content-Type', '%s; %s' % (content_type, encoding)) res.write_header(b"Content-Type", "%s; %s" % (content_type, encoding))
elif content_type: elif content_type:
res.write_header(b'Content-Type', content_type) res.write_header(b"Content-Type", content_type)
with open(filename, "rb") as fd: with open(filename, "rb") as fd:
#check range and support it # check range and support it
if start > 0 or not end == -1: if start > 0 or not end == -1:
if end < 0 or end >= size: if end < 0 or end >= size:
end = size - 1 end = size - 1
@ -61,19 +63,21 @@ async def sendfile(res, req, filename):
end = size - 1 end = size - 1
res.write_status(200) res.write_status(200)
#tells the browser that we support range # tells the browser that we support range
res.write_header(b'Accept-Ranges', b'bytes') res.write_header(b"Accept-Ranges", b"bytes")
res.write_header(b'Content-Range', 'bytes %d-%d/%d' % (start, end, total_size)) res.write_header(
b"Content-Range", "bytes %d-%d/%d" % (start, end, total_size)
)
pending_size = size pending_size = size
#keep sending until abort or done # keep sending until abort or done
while not res.aborted: while not res.aborted:
chunk_size = 16384 #16kb chunks chunk_size = 16384 # 16kb chunks
if chunk_size > pending_size: if chunk_size > pending_size:
chunk_size = pending_size chunk_size = pending_size
buffer = fd.read(chunk_size) buffer = fd.read(chunk_size)
pending_size = pending_size - chunk_size pending_size = pending_size - chunk_size
(ok, done) = await res.send_chunk(buffer, size) (ok, done) = await res.send_chunk(buffer, size)
if not ok or done: #if cannot send probably aborted if not ok or done: # if cannot send probably aborted
break break
except Exception as error: except Exception as error:
@ -81,11 +85,11 @@ async def sendfile(res, req, filename):
def in_directory(file, directory): def in_directory(file, directory):
#make both absolute # make both absolute
directory = path.join(path.realpath(directory), '') directory = path.join(path.realpath(directory), "")
file = path.realpath(file) file = path.realpath(file)
#return true, if the common prefix of both is equal to directory # return true, if the common prefix of both is equal to directory
#e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b # e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b
return path.commonprefix([file, directory]) == directory return path.commonprefix([file, directory]) == directory
@ -93,7 +97,7 @@ def static_route(app, route, directory):
def route_handler(res, req): def route_handler(res, req):
url = req.get_url() url = req.get_url()
res.grab_aborted_handler() res.grab_aborted_handler()
url = url[len(route)::] url = url[len(route) : :]
if url.startswith("/"): if url.startswith("/"):
url = url[1::] url = url[1::]
filename = path.join(path.realpath(directory), url) filename = path.join(path.realpath(directory), url)
@ -102,30 +106,30 @@ def static_route(app, route, directory):
res.write_status(404).end_without_body() res.write_status(404).end_without_body()
return return
res.run_async(sendfile(res, req, filename)) res.run_async(sendfile(res, req, filename))
if route.startswith("/"): if route.startswith("/"):
route = route[1::] route = route[1::]
app.get("%s/*" % route, route_handler) app.get("%s/*" % route, route_handler)
def middleware(*functions): def middleware(*functions):
#we use Optional data=None at the end so you can use and middleware inside a middleware # we use Optional data=None at the end so you can use and middleware inside a middleware
async def middleware_route(res, req, data=None): async def middleware_route(res, req, data=None):
some_async_as_run = False some_async_as_run = False
#cicle to all middlewares # cicle to all middlewares
for function in functions: for function in functions:
#detect if is coroutine or not # detect if is coroutine or not
if inspect.iscoroutinefunction(function): if inspect.iscoroutinefunction(function):
#in async query string, arguments and headers are only valid until the first await # in async query string, arguments and headers are only valid until the first await
if not some_async_as_run: if not some_async_as_run:
#preserve queries, headers, parameters, url, full_url and method # preserve queries, headers, parameters, url, full_url and method
req.preserve() req.preserve()
some_async_as_run = True some_async_as_run = True
data = await function(res, req, data) data = await function(res, req, data)
else: else:
#call middlewares # call middlewares
data = function(res, req, data) data = function(res, req, data)
#stops if returns Falsy # stops if returns Falsy
if not data: if not data:
break break
return data return data
@ -133,7 +137,7 @@ def middleware(*functions):
return middleware_route return middleware_route
class MiddlewareRouter(): class MiddlewareRouter:
def __init__(self, app, *middlewares): def __init__(self, app, *middlewares):
self.app = app self.app = app
self.middlewares = middlewares self.middlewares = middlewares
@ -149,41 +153,49 @@ class MiddlewareRouter():
middies.append(handler) middies.append(handler)
self.app.post(path, middleware(*middies)) self.app.post(path, middleware(*middies))
return self return self
def options(self, path, handler): def options(self, path, handler):
middies = list(self.middlewares) middies = list(self.middlewares)
middies.append(handler) middies.append(handler)
self.app.options(path, middleware(*middies)) self.app.options(path, middleware(*middies))
return self return self
def delete(self, path, handler): def delete(self, path, handler):
middies = list(self.middlewares) middies = list(self.middlewares)
middies.append(handler) middies.append(handler)
self.app.delete(path, middleware(*middies)) self.app.delete(path, middleware(*middies))
return self return self
def patch(self, path, handler): def patch(self, path, handler):
middies = list(self.middlewares) middies = list(self.middlewares)
middies.append(handler) middies.append(handler)
self.app.patch(path, middleware(*middies)) self.app.patch(path, middleware(*middies))
return self return self
def put(self, path, handler): def put(self, path, handler):
middies = list(self.middlewares) middies = list(self.middlewares)
middies.append(handler) middies.append(handler)
self.app.put(path, middleware(*middies)) self.app.put(path, middleware(*middies))
return self return self
def head(self, path, handler): def head(self, path, handler):
middies = list(self.middlewares) middies = list(self.middlewares)
middies.append(handler) middies.append(handler)
self.app.head(path, middleware(*middies)) self.app.head(path, middleware(*middies))
return self return self
def connect(self, path, handler): def connect(self, path, handler):
middies = list(self.middlewares) middies = list(self.middlewares)
middies.append(handler) middies.append(handler)
self.app.connect(path, middleware(*middies)) self.app.connect(path, middleware(*middies))
return self return self
def trace(self, path, handler): def trace(self, path, handler):
middies = list(self.middlewares) middies = list(self.middlewares)
middies.append(handler) middies.append(handler)
self.app.trace(path, middleware(*middies)) self.app.trace(path, middleware(*middies))
return self return self
def any(self, path, handler): def any(self, path, handler):
middies = list(self.middlewares) middies = list(self.middlewares)
middies.append(handler) middies.append(handler)

Wyświetl plik

@ -1,4 +1,3 @@
import asyncio import asyncio
import threading import threading
import time import time
@ -13,11 +12,11 @@ def future_handler(future, loop, exception_handler, response):
future.result() future.result()
return None return None
except Exception as error: except Exception as error:
if hasattr(exception_handler, '__call__'): if hasattr(exception_handler, "__call__"):
exception_handler(loop, error, response) exception_handler(loop, error, response)
else: else:
try: try:
#just log in console the error to call attention # just log in console the error to call attention
print("Uncaught Exception: %s" % str(error)) print("Uncaught Exception: %s" % str(error))
if response != None: if response != None:
response.write_status(500).end("Internal Error") response.write_status(500).end("Internal Error")
@ -25,14 +24,17 @@ def future_handler(future, loop, exception_handler, response):
return None return None
return None return None
class Loop: class Loop:
def __init__(self, exception_handler=None): def __init__(self, exception_handler=None):
self.loop = asyncio.new_event_loop() self.loop = asyncio.new_event_loop()
self.uv_loop = UVLoop() self.uv_loop = UVLoop()
if hasattr(exception_handler, '__call__'): if hasattr(exception_handler, "__call__"):
self.exception_handler = exception_handler self.exception_handler = exception_handler
self.loop.set_exception_handler(lambda loop, context: exception_handler(loop, context, None)) self.loop.set_exception_handler(
lambda loop, context: exception_handler(loop, context, None)
)
else: else:
self.exception_handler = None self.exception_handler = None
@ -43,18 +45,17 @@ class Loop:
def set_timeout(self, timeout, callback, user_data): def set_timeout(self, timeout, callback, user_data):
return self.uv_loop.create_timer(timeout, 0, callback, user_data) return self.uv_loop.create_timer(timeout, 0, callback, user_data)
def create_future(self): def create_future(self):
return self.loop.create_future() return self.loop.create_future()
def start(self): def start(self):
self.started = True self.started = True
#run asyncio once per tick # run asyncio once per tick
def tick(loop): def tick(loop):
#run once asyncio # run once asyncio
loop.run_once_asyncio() loop.run_once_asyncio()
#use check for calling asyncio once per tick # use check for calling asyncio once per tick
self.timer = self.uv_loop.create_timer(0, 1, tick, self) self.timer = self.uv_loop.create_timer(0, 1, tick, self)
# self.timer = self.uv_loop.create_check(tick, self) # self.timer = self.uv_loop.create_check(tick, self)
@ -66,17 +67,16 @@ class Loop:
def run_once_asyncio(self): def run_once_asyncio(self):
# with suppress(asyncio.CancelledError): # with suppress(asyncio.CancelledError):
#run only one step # run only one step
self.loop.call_soon(self.loop.stop) self.loop.call_soon(self.loop.stop)
self.loop.run_forever() self.loop.run_forever()
def stop(self): def stop(self):
if(self.started): if self.started:
self.timer.stop() self.timer.stop()
self.started = False self.started = False
#unbind run_once # unbind run_once
#if is still running stops # if is still running stops
if self.loop.is_running(): if self.loop.is_running():
self.loop.stop() self.loop.stop()
@ -86,25 +86,26 @@ class Loop:
# Run loop until tasks done # Run loop until tasks done
self.loop.run_until_complete(asyncio.gather(*pending)) self.loop.run_until_complete(asyncio.gather(*pending))
#Exposes native loop for uWS # Exposes native loop for uWS
def get_native_loop(self): def get_native_loop(self):
return self.uv_loop.get_native_loop() return self.uv_loop.get_native_loop()
def run_async(self, task, response=None): def run_async(self, task, response=None):
#with run_once # with run_once
future = asyncio.ensure_future(task, loop=self.loop) future = asyncio.ensure_future(task, loop=self.loop)
#with threads # with threads
future.add_done_callback(lambda f: future_handler(f, self.loop, self.exception_handler, response)) future.add_done_callback(
#force asyncio run once to enable req in async functions before first await lambda f: future_handler(f, self.loop, self.exception_handler, response)
)
# force asyncio run once to enable req in async functions before first await
self.run_once_asyncio() self.run_once_asyncio()
#if response != None: #set auto cork # if response != None: #set auto cork
# response.needs_cork = True # response.needs_cork = True
return future return future
# if sys.version_info >= (3, 11) # if sys.version_info >= (3, 11)
# with asyncio.Runner(loop_factory=uvloop.new_event_loop) as runner: # with asyncio.Runner(loop_factory=uvloop.new_event_loop) as runner:
# runner.run(main()) # runner.run(main())
@ -113,7 +114,7 @@ class Loop:
# asyncio.run(main()) # asyncio.run(main())
#see ./native/uv_selector.txt # see ./native/uv_selector.txt
# will only work on linux and macos # will only work on linux and macos
# class UVSelector(asyncio.SelectorEventLoop): # class UVSelector(asyncio.SelectorEventLoop):
# def register(self, fileobj, events, data=None): # def register(self, fileobj, events, data=None):

Wyświetl plik

@ -1,65 +1,65 @@
status_codes = { status_codes = {
100 : b'100 Continue', 100: b"100 Continue",
101 : b'101 Switching Protocols', 101: b"101 Switching Protocols",
102 : b'102 Processing', 102: b"102 Processing",
103 : b'103 Early Hints', 103: b"103 Early Hints",
200 : b'200 OK', 200: b"200 OK",
201 : b'201 Created', 201: b"201 Created",
202 : b'202 Accepted', 202: b"202 Accepted",
203 : b'203 Non-Authoritative Information', 203: b"203 Non-Authoritative Information",
204 : b'204 No Content', 204: b"204 No Content",
205 : b'205 Reset Content', 205: b"205 Reset Content",
206 : b'206 Partial Content', 206: b"206 Partial Content",
207 : b'207 Multi-Status', 207: b"207 Multi-Status",
208 : b'208 Already Reported', 208: b"208 Already Reported",
226 : b'226 IM Used (HTTP Delta encoding)', 226: b"226 IM Used (HTTP Delta encoding)",
300 : b'300 Multiple Choices', 300: b"300 Multiple Choices",
301 : b'301 Moved Permanently', 301: b"301 Moved Permanently",
302 : b'302 Found', 302: b"302 Found",
303 : b'303 See Other', 303: b"303 See Other",
304 : b'304 Not Modified', 304: b"304 Not Modified",
305 : b'305 Use Proxy Deprecated', 305: b"305 Use Proxy Deprecated",
306 : b'306 unused', 306: b"306 unused",
307 : b'307 Temporary Redirect', 307: b"307 Temporary Redirect",
308 : b'308 Permanent Redirect', 308: b"308 Permanent Redirect",
400 : b'400 Bad Request', 400: b"400 Bad Request",
401 : b'401 Unauthorized', 401: b"401 Unauthorized",
402 : b'402 Payment Required Experimental', 402: b"402 Payment Required Experimental",
403 : b'403 Forbidden', 403: b"403 Forbidden",
404 : b'404 Not Found', 404: b"404 Not Found",
405 : b'405 Method Not Allowed', 405: b"405 Method Not Allowed",
406 : b'406 Not Acceptable', 406: b"406 Not Acceptable",
407 : b'407 Proxy Authentication Required', 407: b"407 Proxy Authentication Required",
408 : b'408 Request Timeout', 408: b"408 Request Timeout",
409 : b'409 Conflict', 409: b"409 Conflict",
410 : b'410 Gone', 410: b"410 Gone",
411 : b'411 Length Required', 411: b"411 Length Required",
412 : b'412 Precondition Failed', 412: b"412 Precondition Failed",
413 : b'413 Payload Too Large', 413: b"413 Payload Too Large",
414 : b'414 URI Too Long', 414: b"414 URI Too Long",
415 : b'415 Unsupported Media Type', 415: b"415 Unsupported Media Type",
416 : b'416 Range Not Satisfiable', 416: b"416 Range Not Satisfiable",
417 : b'417 Expectation Failed', 417: b"417 Expectation Failed",
418 : b'418 I\'m a teapot', 418: b"418 I'm a teapot",
421 : b'421 Misdirected Request', 421: b"421 Misdirected Request",
422 : b'422 Unprocessable Entity', 422: b"422 Unprocessable Entity",
423 : b'423 Locked', 423: b"423 Locked",
424 : b'424 Failed Dependency', 424: b"424 Failed Dependency",
425 : b'425 Too Early Experimental', 425: b"425 Too Early Experimental",
426 : b'426 Upgrade Required', 426: b"426 Upgrade Required",
428 : b'428 Precondition Required', 428: b"428 Precondition Required",
429 : b'429 Too Many Requests', 429: b"429 Too Many Requests",
431 : b'431 Request Header Fields Too Large', 431: b"431 Request Header Fields Too Large",
451 : b'451 Unavailable For Legal Reasons', 451: b"451 Unavailable For Legal Reasons",
500 : b'500 Internal Server Error', 500: b"500 Internal Server Error",
501 : b'501 Not Implemented', 501: b"501 Not Implemented",
502 : b'502 Bad Gateway', 502: b"502 Bad Gateway",
503 : b'503 Service Unavailable', 503: b"503 Service Unavailable",
504 : b'504 Gateway Timeout', 504: b"504 Gateway Timeout",
505 : b'505 HTTP Version Not Supported', 505: b"505 HTTP Version Not Supported",
506 : b'506 Variant Also Negotiates', 506: b"506 Variant Also Negotiates",
507 : b'507 Insufficient Storage', 507: b"507 Insufficient Storage",
508 : b'508 Loop Detected', 508: b"508 Loop Detected",
510 : b'510 Not Extended', 510: b"510 Not Extended",
511 : b'511 Network Authentication Required' 511: b"511 Network Authentication Required",
} }

Wyświetl plik

@ -1,10 +1,10 @@
import cffi import cffi
import os import os
import platform import platform
ffi = cffi.FFI() ffi = cffi.FFI()
ffi.cdef(""" ffi.cdef(
"""
typedef void (*socketify_prepare_handler)(void* user_data); typedef void (*socketify_prepare_handler)(void* user_data);
@ -54,12 +54,22 @@ void socketify_timer_set_repeat(socketify_timer* timer, uint64_t repeat);
socketify_timer* socketify_create_check(socketify_loop* loop, socketify_timer_handler handler, void* user_data); socketify_timer* socketify_create_check(socketify_loop* loop, socketify_timer_handler handler, void* user_data);
void socketify_check_destroy(socketify_timer* timer); void socketify_check_destroy(socketify_timer* timer);
""") """
)
library_extension = "dll" if platform.system().lower() == "windows" else "so" library_extension = "dll" if platform.system().lower() == "windows" else "so"
library_path = os.path.join(os.path.dirname(__file__), "libsocketify_%s_%s.%s" % (platform.system().lower(), "arm64" if "arm" in platform.processor().lower() else "amd64", library_extension)) library_path = os.path.join(
os.path.dirname(__file__),
"libsocketify_%s_%s.%s"
% (
platform.system().lower(),
"arm64" if "arm" in platform.processor().lower() else "amd64",
library_extension,
),
)
lib = ffi.dlopen(library_path) lib = ffi.dlopen(library_path)
@ffi.callback("void(void *)") @ffi.callback("void(void *)")
def socketify_generic_handler(data): def socketify_generic_handler(data):
if not data == ffi.NULL: if not data == ffi.NULL:
@ -70,7 +80,10 @@ def socketify_generic_handler(data):
class UVCheck: class UVCheck:
def __init__(self, loop, handler, user_data): def __init__(self, loop, handler, user_data):
self._handler_data = ffi.new_handle((handler, user_data)) self._handler_data = ffi.new_handle((handler, user_data))
self._ptr = lib.socketify_create_check(loop, socketify_generic_handler, self._handler_data) self._ptr = lib.socketify_create_check(
loop, socketify_generic_handler, self._handler_data
)
def stop(self): def stop(self):
lib.socketify_check_destroy(self._ptr) lib.socketify_check_destroy(self._ptr)
self._handler_data = None self._handler_data = None
@ -81,10 +94,18 @@ class UVCheck:
lib.socketify_check_destroy(self._ptr) lib.socketify_check_destroy(self._ptr)
self._handler_data = None self._handler_data = None
class UVTimer: class UVTimer:
def __init__(self, loop, timeout, repeat, handler, user_data): def __init__(self, loop, timeout, repeat, handler, user_data):
self._handler_data = ffi.new_handle((handler, user_data)) self._handler_data = ffi.new_handle((handler, user_data))
self._ptr = lib.socketify_create_timer(loop, ffi.cast("uint64_t", timeout), ffi.cast("uint64_t", repeat), socketify_generic_handler, self._handler_data) self._ptr = lib.socketify_create_timer(
loop,
ffi.cast("uint64_t", timeout),
ffi.cast("uint64_t", repeat),
socketify_generic_handler,
self._handler_data,
)
def stop(self): def stop(self):
lib.socketify_timer_destroy(self._ptr) lib.socketify_timer_destroy(self._ptr)
self._handler_data = None self._handler_data = None
@ -107,7 +128,9 @@ class UVLoop:
def on_prepare(self, handler, user_data): def on_prepare(self, handler, user_data):
self._handler_data = ffi.new_handle((handler, user_data)) self._handler_data = ffi.new_handle((handler, user_data))
lib.socketify_on_prepare(self._loop, socketify_generic_handler, self._handler_data) lib.socketify_on_prepare(
self._loop, socketify_generic_handler, self._handler_data
)
def create_timer(self, timeout, repeat, handler, user_data): def create_timer(self, timeout, repeat, handler, user_data):
return UVTimer(self._loop, timeout, repeat, handler, user_data) return UVTimer(self._loop, timeout, repeat, handler, user_data)

Wyświetl plik

@ -15,15 +15,18 @@
from socketify import App, AppOptions, OpCode, CompressOptions from socketify import App, AppOptions, OpCode, CompressOptions
import asyncio import asyncio
def ws_open(ws): def ws_open(ws):
print('A WebSocket got connected!') print("A WebSocket got connected!")
ws.send("Hello World!", OpCode.TEXT) ws.send("Hello World!", OpCode.TEXT)
def ws_message(ws, message, opcode): def ws_message(ws, message, opcode):
print(message, opcode) print(message, opcode)
#Ok is false if backpressure was built up, wait for drain # Ok is false if backpressure was built up, wait for drain
ok = ws.send(message, opcode) ok = ws.send(message, opcode)
async def ws_upgrade(res, req, socket_context): async def ws_upgrade(res, req, socket_context):
key = req.get_header("sec-websocket-key") key = req.get_header("sec-websocket-key")
protocol = req.get_header("sec-websocket-protocol") protocol = req.get_header("sec-websocket-protocol")
@ -31,15 +34,22 @@ async def ws_upgrade(res, req, socket_context):
await asyncio.sleep(2) await asyncio.sleep(2)
res.upgrade(key, protocol, extensions, socket_context) res.upgrade(key, protocol, extensions, socket_context)
app = App() app = App()
app.ws("/*", { app.ws(
'compression': CompressOptions.SHARED_COMPRESSOR, "/*",
'max_payload_length': 16 * 1024 * 1024, {
'idle_timeout': 12, "compression": CompressOptions.SHARED_COMPRESSOR,
'open': ws_open, "max_payload_length": 16 * 1024 * 1024,
'message': ws_message, "idle_timeout": 12,
'upgrade': ws_upgrade "open": ws_open,
}) "message": ws_message,
app.any("/", lambda res,req: res.end("Nothing to see here!")) "upgrade": ws_upgrade,
app.listen(3000, lambda config: print("Listening on port http://localhost:%d now\n" % (config.port))) },
)
app.any("/", lambda res, req: res.end("Nothing to see here!"))
app.listen(
3000,
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
)
app.run() app.run()