kopia lustrzana https://github.com/cirospaciari/socketify.py
use black as code formatter
rodzic
aa11d8bd58
commit
021dda7d2a
|
@ -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,11 +13,11 @@ 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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 :)
|
|
@ -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
|
# 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
|
# pypy3 -m gunicorn uvicorn_guvicorn_plaintext:app -w 1 -k uvicorn.workers.UvicornWorker
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
@ -20,19 +21,27 @@ def ws_message(ws, message, opcode):
|
||||||
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,
|
||||||
|
"close": ws_close,
|
||||||
|
},
|
||||||
|
)
|
||||||
app.any("/", lambda res, req: res.end("Nothing to see here!'"))
|
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.listen(
|
||||||
|
4001,
|
||||||
|
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
|
||||||
|
)
|
||||||
app.run()
|
app.run()
|
|
@ -3,10 +3,12 @@ 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
|
||||||
|
@ -16,6 +18,7 @@ def home(res, req):
|
||||||
# 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):
|
async def json(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 before first await :)
|
# get parameters, query, headers anything you need here before first await :)
|
||||||
|
@ -25,13 +28,20 @@ async def json(res, req):
|
||||||
|
|
||||||
res.cork_end({"message": "I'm delayed!", "user-agent": user_agent})
|
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()
|
|
@ -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()
|
|
@ -7,33 +7,42 @@ backpressure = 1024
|
||||||
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,
|
||||||
|
"drain": ws_drain,
|
||||||
|
},
|
||||||
|
)
|
||||||
app.any("/", lambda res, req: res.end("Nothing to see here!"))
|
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.listen(
|
||||||
|
3000,
|
||||||
|
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
|
||||||
|
)
|
||||||
app.run()
|
app.run()
|
|
@ -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.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.listen(
|
||||||
|
3000,
|
||||||
|
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
|
||||||
|
)
|
||||||
app.run()
|
app.run()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -3,13 +3,16 @@ 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
|
||||||
|
@ -20,10 +23,16 @@ def on_error(error, res, req):
|
||||||
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()
|
|
@ -7,6 +7,7 @@ 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
|
||||||
|
@ -14,14 +15,14 @@ async def home(res, req):
|
||||||
|
|
||||||
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])
|
||||||
|
@ -29,26 +30,28 @@ async def home(res, req):
|
||||||
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
|
||||||
|
@ -65,8 +68,10 @@ async def home(res, req):
|
||||||
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:
|
||||||
|
@ -82,7 +87,11 @@ async def home(res, req):
|
||||||
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()
|
|
@ -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()
|
||||||
|
|
|
@ -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!")
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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
|
|
@ -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 = {}
|
||||||
|
|
|
@ -8,14 +8,14 @@ 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])
|
||||||
|
@ -23,26 +23,28 @@ async def sendfile(res, req, filename):
|
||||||
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
|
||||||
|
@ -78,9 +80,10 @@ async def sendfile(res, req, filename):
|
||||||
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
|
||||||
|
@ -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)
|
|
@ -8,14 +8,14 @@ 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])
|
||||||
|
@ -23,26 +23,28 @@ async def sendfile(res, req, filename):
|
||||||
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
|
||||||
|
@ -78,9 +80,10 @@ async def sendfile(res, req, filename):
|
||||||
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
|
||||||
|
@ -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)
|
|
@ -6,9 +6,12 @@ from mako import exceptions
|
||||||
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
|
||||||
|
|
||||||
class Jinja2Template:
|
class Jinja2Template:
|
||||||
def __init__(self, searchpath, encoding='utf-8', followlinks=False):
|
def __init__(self, searchpath, encoding="utf-8", followlinks=False):
|
||||||
self.env = Environment(loader=FileSystemLoader(searchpath, encoding, followlinks))
|
self.env = Environment(
|
||||||
|
loader=FileSystemLoader(searchpath, encoding, followlinks)
|
||||||
|
)
|
||||||
|
|
||||||
# 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):
|
||||||
|
@ -18,6 +21,7 @@ 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)
|
||||||
|
|
|
@ -3,7 +3,9 @@ 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
|
||||||
|
|
|
@ -5,7 +5,7 @@ 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,17 +15,23 @@ 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(
|
||||||
|
f"https://pokeapi.co/api/v2/pokemon?limit=151"
|
||||||
|
) as response:
|
||||||
# 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
|
||||||
pokemons = await response.text()
|
pokemons = await response.text()
|
||||||
|
@ -74,6 +80,7 @@ def list_pokemon(res, req):
|
||||||
|
|
||||||
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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
|
@ -1,13 +1,15 @@
|
||||||
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
|
||||||
|
@ -16,19 +18,25 @@ async def auth(res, req, data=None):
|
||||||
# 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 ;)
|
|
@ -2,12 +2,14 @@ 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):
|
||||||
|
@ -18,7 +20,7 @@ def auth(route):
|
||||||
# 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,13 +32,17 @@ 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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,12 @@ 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
|
||||||
|
@ -17,16 +18,17 @@ async def auth(res, req, data=None):
|
||||||
# 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()
|
||||||
|
@ -42,5 +44,8 @@ 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()
|
|
@ -2,6 +2,7 @@ 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
|
||||||
|
@ -15,13 +16,15 @@ def middleware(*functions):
|
||||||
|
|
||||||
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
|
||||||
|
@ -29,10 +32,15 @@ def auth(res, req, data=None):
|
||||||
# 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()
|
|
@ -2,9 +2,11 @@ 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:
|
||||||
|
@ -13,12 +15,19 @@ 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)
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
@ -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()
|
|
@ -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
|
|
@ -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):
|
def useragent(res, req):
|
||||||
res.end("Your user agent is: %s" % req.get_header('user-agent'));
|
res.end("Your user agent is: %s" % req.get_header("user-agent"))
|
||||||
|
|
||||||
|
|
||||||
def user(res, req):
|
def user(res, req):
|
||||||
try:
|
try:
|
||||||
|
@ -46,6 +55,7 @@ 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!")
|
||||||
|
@ -66,10 +76,12 @@ def delayed(res, req):
|
||||||
# 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")
|
||||||
|
@ -83,11 +95,13 @@ async def sleepy_json(res, req):
|
||||||
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")
|
||||||
|
@ -95,16 +109,20 @@ def send_in_parts(res, req):
|
||||||
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()
|
|
@ -40,11 +40,14 @@ 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()
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
@ -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()
|
|
@ -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(
|
||||||
|
"WebSocket backpressure: %s", ws.get_buffered_amount()
|
||||||
|
),
|
||||||
|
"close": lambda ws, code, message: print("WebSocket closed"),
|
||||||
|
},
|
||||||
|
)
|
||||||
app.any("/", lambda res, req: res.end("Nothing to see here!"))
|
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.listen(
|
||||||
|
3000,
|
||||||
|
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
|
||||||
|
)
|
||||||
app.run()
|
app.run()
|
|
@ -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(
|
||||||
|
"WebSocket backpressure: %s", ws.get_buffered_amount()
|
||||||
|
),
|
||||||
|
"close": lambda ws, code, message: print("WebSocket closed"),
|
||||||
|
},
|
||||||
|
)
|
||||||
app.any("/", lambda res, req: res.end("Nothing to see here!"))
|
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.listen(
|
||||||
|
3000,
|
||||||
|
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
|
||||||
|
)
|
||||||
app.run()
|
app.run()
|
|
@ -4,16 +4,18 @@ 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)
|
||||||
|
@ -26,6 +28,7 @@ async def upload_chunks(res, req):
|
||||||
# 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
|
||||||
|
@ -37,6 +40,7 @@ async def upload_json(res, req):
|
||||||
# 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
|
||||||
|
@ -47,10 +51,13 @@ async def upload_text(res, req):
|
||||||
# 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}")
|
||||||
|
|
||||||
|
@ -84,5 +91,8 @@ 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()
|
|
@ -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(
|
||||||
|
"WebSocket backpressure: %s", ws.get_buffered_amount()
|
||||||
|
),
|
||||||
|
"close": lambda ws, code, message: print("WebSocket closed"),
|
||||||
|
},
|
||||||
|
)
|
||||||
app.any("/", lambda res, req: res.end("Nothing to see here!'"))
|
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.listen(
|
||||||
|
3000,
|
||||||
|
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
|
||||||
|
)
|
||||||
app.run()
|
app.run()
|
22
setup.py
22
setup.py
|
@ -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
|
|
||||||
)
|
)
|
|
@ -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
|
|
@ -11,14 +11,14 @@ 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])
|
||||||
|
@ -26,26 +26,28 @@ async def sendfile(res, req, filename):
|
||||||
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
|
||||||
|
@ -62,8 +64,10 @@ async def sendfile(res, req, filename):
|
||||||
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:
|
||||||
|
@ -82,7 +86,7 @@ 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
|
||||||
|
@ -102,12 +106,12 @@ 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):
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
@ -13,7 +12,7 @@ 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:
|
||||||
|
@ -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,7 +45,6 @@ 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()
|
||||||
|
|
||||||
|
@ -70,9 +71,8 @@ class Loop:
|
||||||
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
|
||||||
|
@ -95,7 +95,9 @@ class Loop:
|
||||||
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(
|
||||||
|
lambda f: future_handler(f, self.loop, self.exception_handler, response)
|
||||||
|
)
|
||||||
# force asyncio run once to enable req in async functions before first await
|
# force asyncio run once to enable req in async functions before first await
|
||||||
self.run_once_asyncio()
|
self.run_once_asyncio()
|
||||||
|
|
||||||
|
@ -104,7 +106,6 @@ class Loop:
|
||||||
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())
|
||||||
|
|
Plik diff jest za duży
Load Diff
|
@ -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",
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
|
30
src/tests.py
30
src/tests.py
|
@ -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,
|
||||||
|
"upgrade": ws_upgrade,
|
||||||
|
},
|
||||||
|
)
|
||||||
app.any("/", lambda res, req: res.end("Nothing to see here!"))
|
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.listen(
|
||||||
|
3000,
|
||||||
|
lambda config: print("Listening on port http://localhost:%d now\n" % (config.port)),
|
||||||
|
)
|
||||||
app.run()
|
app.run()
|
Ładowanie…
Reference in New Issue