use black as code formatter

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

Wyświetl plik

@ -2,6 +2,7 @@ from wsgiref.simple_server import make_server
import falcon
class Home:
def on_get(self, req, resp):
resp.status = falcon.HTTP_200 # This is the default status
@ -12,11 +13,11 @@ class Home:
app = falcon.App()
home = Home()
app.add_route('/', home)
app.add_route("/", home)
if __name__ == '__main__':
with make_server('', 8000, app) as httpd:
print('Serving on port 8000...')
if __name__ == "__main__":
with make_server("", 8000, app) as httpd:
print("Serving on port 8000...")
# Serve until process is killed
httpd.serve_forever()

Wyświetl plik

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

Wyświetl plik

@ -3,21 +3,28 @@ import os
import multiprocessing
def run_app():
app = App()
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()
def create_fork():
n = os.fork()
# n greater than 0 means parent process
if not n > 0:
run_app()
# fork limiting the cpu count - 1
# for i in range(1, multiprocessing.cpu_count()):
# create_fork()
for i in range(1, multiprocessing.cpu_count()):
create_fork()
run_app() # run app on the main process too :)

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -2,6 +2,7 @@ from socketify import App, AppOptions, OpCode, CompressOptions
remaining_clients = 16
def ws_open(ws):
ws.subscribe("room")
global remaining_clients
@ -20,19 +21,27 @@ def ws_message(ws, message, opcode):
ws.publish("room", message, opcode)
ws.send(message, opcode)
def ws_close(ws, close, message):
global remaining_clients
remaining_clients = remaining_clients + 1
app = App()
app.ws("/*", {
'compression': CompressOptions.DISABLED,
'max_payload_length': 16 * 1024 * 1024,
'idle_timeout': 60,
'open': ws_open,
'message': ws_message,
'close': ws_close
})
app.ws(
"/*",
{
"compression": CompressOptions.DISABLED,
"max_payload_length": 16 * 1024 * 1024,
"idle_timeout": 60,
"open": ws_open,
"message": ws_message,
"close": ws_close,
},
)
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()

Wyświetl plik

@ -3,10 +3,12 @@ import asyncio
app = App()
async def delayed_hello(delay, res):
await asyncio.sleep(delay) # do something async
res.cork_end("Hello with delay!")
def home(res, req):
# request object only lives during the life time of this call
# 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
res.run_async(delayed_hello(delay, res))
async def json(res, req):
# request object only lives during the life time of this call
# get parameters, query, headers anything you need here before first await :)
@ -25,13 +28,20 @@ async def json(res, req):
res.cork_end({"message": "I'm delayed!", "user-agent": user_agent})
def not_found(res, req):
res.write_status(404).end("Not Found")
app.get("/", home)
app.get("/json", json)
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()

Wyświetl plik

@ -2,5 +2,7 @@ from socketify import App
app = App()
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()

Wyświetl plik

@ -7,33 +7,42 @@ backpressure = 1024
messages = 0
message_number = 0
def ws_open(ws):
print('A WebSocket got connected!')
print("A WebSocket got connected!")
# We begin our example by sending until we have backpressure
global message_number
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)
message_number = message_number + 1
messages = messages + 1
def ws_drain(ws):
# Continue sending when we have drained (some)
global message_number
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)
message_number = message_number + 1
messages = messages + 1
app = App()
app.ws("/*", {
'compression': CompressOptions.DISABLED,
'max_payload_length': 16 * 1024 * 1024,
'idle_timeout': 60,
'open': ws_open,
'drain': ws_drain
})
app.ws(
"/*",
{
"compression": CompressOptions.DISABLED,
"max_payload_length": 16 * 1024 * 1024,
"idle_timeout": 60,
"open": ws_open,
"drain": ws_drain,
},
)
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()

Wyświetl plik

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

Wyświetl plik

@ -2,5 +2,8 @@ from socketify import App
app = App()
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()

Wyświetl plik

@ -2,5 +2,8 @@ from socketify import App
app = App()
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()

Wyświetl plik

@ -2,5 +2,8 @@ from socketify import App
app = App()
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()

Wyświetl plik

@ -2,5 +2,8 @@ from socketify import App
app = App()
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()

Wyświetl plik

@ -3,13 +3,16 @@ import asyncio
app = App()
def xablau(res, req):
raise RuntimeError("Xablau!")
async def async_xablau(res, req):
await asyncio.sleep(1)
raise RuntimeError("Async Xablau!")
# this can be async no problems
def on_error(error, res, req):
# 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.end("Sorry we did something wrong")
app.get("/", xablau)
app.get("/async", async_xablau)
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()

Wyświetl plik

@ -7,6 +7,7 @@ from os import path
mimetypes.init()
async def home(res, req):
# 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
@ -14,14 +15,14 @@ async def home(res, req):
filename = "./public/media/flower.webm"
# read headers before the first await
if_modified_since = req.get_header('if-modified-since')
range_header = req.get_header('range')
if_modified_since = req.get_header("if-modified-since")
range_header = req.get_header("range")
bytes_range = None
start = 0
end = -1
# parse range header
if range_header:
bytes_range = range_header.replace("bytes=", '').split('-')
bytes_range = range_header.replace("bytes=", "").split("-")
start = int(bytes_range[0])
if bytes_range[1]:
end = int(bytes_range[1])
@ -29,26 +30,28 @@ async def home(res, req):
exists = path.exists(filename)
# not found
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
stats = os.stat(filename)
total_size = stats.st_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
if if_modified_since == last_modified:
return res.write_status(304).end_without_body()
# 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
(content_type, encoding) = mimetypes.guess_type(filename, strict=True)
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:
res.write_header(b'Content-Type', content_type)
res.write_header(b"Content-Type", content_type)
with open(filename, "rb") as fd:
# check range and support it
@ -65,8 +68,10 @@ async def home(res, req):
res.write_status(200)
# tells the browser that we support range
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"Accept-Ranges", b"bytes")
res.write_header(
b"Content-Range", "bytes %d-%d/%d" % (start, end, total_size)
)
pending_size = size
# keep sending until abort or done
while not res.aborted:
@ -82,7 +87,11 @@ async def home(res, req):
except Exception as error:
res.write_status(500).end("Internal Error")
app = App()
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()

Wyświetl plik

@ -2,18 +2,27 @@ from socketify import App
import os
import multiprocessing
def run_app():
app = App()
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()
def create_fork():
n = os.fork()
# n greater than 0 means parent process
if not n > 0:
run_app()
# fork limiting the cpu count - 1
for i in range(1, multiprocessing.cpu_count()):
create_fork()

Wyświetl plik

@ -2,14 +2,21 @@ from socketify import App, AppOptions, AppListenOptions
app = App()
def shutdown(res, req):
res.end("Good bye!")
app.close()
app.get("/", lambda res, req: res.end("Hello!"))
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()
print("App Closed!")

Wyświetl plik

@ -1,4 +1,3 @@
import dataclasses
import strawberry
import strawberry.utils.graphiql
@ -7,10 +6,12 @@ from socketify import App
from typing import List, Optional
from helpers.graphiql import graphiql_from
@strawberry.type
class User:
name: str
@strawberry.type
class Query:
@strawberry.field
@ -24,5 +25,8 @@ app.get("/", lambda res, req: res.end(strawberry.utils.graphiql.get_graphiql_htm
app.post("/", graphiql_from(Query))
# you can also pass an Mutation as second parameter
# 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()

Wyświetl plik

@ -1,4 +1,3 @@
import dataclasses
import strawberry
import strawberry.utils.graphiql
@ -6,10 +5,12 @@ import strawberry.utils.graphiql
from socketify import App
from typing import List, Optional
@strawberry.type
class User:
name: str
@strawberry.type
class Query:
@strawberry.field
@ -40,14 +41,20 @@ async def graphiql_post(res, req):
operation_name,
)
res.cork_end({
res.cork_end(
{
"data": (data.data),
**({"errors": data.errors} if data.errors else {}),
**({"extensions": data.extensions} if data.extensions else {})
})
**({"extensions": data.extensions} if data.extensions else {}),
}
)
app = App()
app.get("/", lambda res, req: res.end(strawberry.utils.graphiql.get_graphiql_html()))
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()

Wyświetl plik

@ -2,5 +2,8 @@ from socketify import App
app = App()
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()

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -8,14 +8,14 @@ mimetypes.init()
# In production we highly recomend to use CDN like CloudFlare or/and NGINX or similar for static files
async def sendfile(res, req, filename):
# read headers before the first await
if_modified_since = req.get_header('if-modified-since')
range_header = req.get_header('range')
if_modified_since = req.get_header("if-modified-since")
range_header = req.get_header("range")
bytes_range = None
start = 0
end = -1
# parse range header
if range_header:
bytes_range = range_header.replace("bytes=", '').split('-')
bytes_range = range_header.replace("bytes=", "").split("-")
start = int(bytes_range[0])
if bytes_range[1]:
end = int(bytes_range[1])
@ -23,26 +23,28 @@ async def sendfile(res, req, filename):
exists = path.exists(filename)
# not found
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
stats = os.stat(filename)
total_size = stats.st_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
if if_modified_since == last_modified:
return res.write_status(304).end_without_body()
# 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
(content_type, encoding) = mimetypes.guess_type(filename, strict=True)
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:
res.write_header(b'Content-Type', content_type)
res.write_header(b"Content-Type", content_type)
async with async_open(filename, "rb") as fd:
# check range and support it
@ -78,9 +80,10 @@ async def sendfile(res, req, filename):
except Exception as error:
res.write_status(500).end("Internal Error")
def in_directory(file, directory):
# make both absolute
directory = path.join(path.realpath(directory), '')
directory = path.join(path.realpath(directory), "")
file = path.realpath(file)
# 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
@ -100,6 +103,7 @@ def static_route(app, route, directory):
res.write_status(404).end_without_body()
return
res.run_async(sendfile(res, req, filename))
if route.startswith("/"):
route = route[1::]
app.get("%s/*" % route, route_handler)

Wyświetl plik

@ -8,14 +8,14 @@ mimetypes.init()
# In production we highly recomend to use CDN like CloudFlare or/and NGINX or similar for static files
async def sendfile(res, req, filename):
# read headers before the first await
if_modified_since = req.get_header('if-modified-since')
range_header = req.get_header('range')
if_modified_since = req.get_header("if-modified-since")
range_header = req.get_header("range")
bytes_range = None
start = 0
end = -1
# parse range header
if range_header:
bytes_range = range_header.replace("bytes=", '').split('-')
bytes_range = range_header.replace("bytes=", "").split("-")
start = int(bytes_range[0])
if bytes_range[1]:
end = int(bytes_range[1])
@ -23,26 +23,28 @@ async def sendfile(res, req, filename):
exists = await os.path.exists(filename)
# not found
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
stats = await os.stat(filename)
total_size = stats.st_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
if if_modified_since == last_modified:
return res.write_status(304).end_without_body()
# 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
(content_type, encoding) = mimetypes.guess_type(filename, strict=True)
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:
res.write_header(b'Content-Type', content_type)
res.write_header(b"Content-Type", content_type)
async with aiofiles.open(filename, "rb") as fd:
# check range and support it
@ -78,9 +80,10 @@ async def sendfile(res, req, filename):
except Exception as error:
res.write_status(500).end("Internal Error")
def in_directory(file, directory):
# make both absolute
directory = path.join(path.realpath(directory), '')
directory = path.join(path.realpath(directory), "")
file = path.realpath(file)
# 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
@ -100,6 +103,7 @@ def static_route(app, route, directory):
res.write_status(404).end_without_body()
return
res.run_async(sendfile(res, req, filename))
if route.startswith("/"):
route = route[1::]
app.get("%s/*" % route, route_handler)

Wyświetl plik

@ -6,9 +6,12 @@ from mako import exceptions
from jinja2 import Environment, FileSystemLoader
class Jinja2Template:
def __init__(self, searchpath, encoding='utf-8', followlinks=False):
self.env = Environment(loader=FileSystemLoader(searchpath, encoding, followlinks))
def __init__(self, searchpath, encoding="utf-8", followlinks=False):
self.env = Environment(
loader=FileSystemLoader(searchpath, encoding, followlinks)
)
# You can also add caching and logging strategy here if you want ;)
def render(self, templatename, **kwargs):
@ -18,6 +21,7 @@ class Jinja2Template:
except Exception as err:
return str(err)
class MakoTemplate:
def __init__(self, **options):
self.lookup = TemplateLookup(**options)

Wyświetl plik

@ -3,7 +3,9 @@ from .memory_cache import MemoryCache
# 2 LEVEL CACHE (Redis to share amoung worker, Memory to be much faster)
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.redis_conection = redis_conection
self.memory_expiration_time = memory_expiration_time

Wyświetl plik

@ -5,7 +5,7 @@ import asyncio
from helpers.twolevel_cache import TwoLevelCache
# 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)
# 2 LEVEL CACHE (Redis to share amoung workers, Memory to be much faster)
# cache in memory is 30s, cache in redis is 60s duration
@ -15,17 +15,23 @@ cache = TwoLevelCache(redis_conection, 30, 60)
# Model
###
async def get_pokemon(number):
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()
# cache only works with strings/bytes
# we will not change nothing here so no needs to parse json
return pokemon.encode("utf-8")
async def get_original_pokemons():
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
# we will not change nothing here so no needs to parse json
pokemons = await response.text()
@ -74,6 +80,7 @@ def list_pokemon(res, req):
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
###
@ -81,5 +88,8 @@ app = App()
app.get("/", list_original_pokemons)
app.get("/:number", list_pokemon)
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()

Wyświetl plik

@ -1,8 +1,17 @@
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.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()
# openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -passout pass:1234 -keyout ./misc/key.pem -out ./misc/cert.pem

Wyświetl plik

@ -2,5 +2,10 @@ from socketify import App, AppListenOptions
app = App()
app.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()

Wyświetl plik

@ -1,13 +1,15 @@
from socketify import App, middleware
async def get_user(authorization):
if authorization:
# you can do something async here
return { 'greeting': 'Hello, World' }
return {"greeting": "Hello, World"}
return 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:
res.write_status(403).end("not authorized")
# 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
return user
def another_middie(res, req, data=None):
# now we can mix sync and async and change the data here
if isinstance(data, dict):
gretting = data.get('greeting', '')
data['greeting'] = f"{gretting} from another middie ;)"
gretting = data.get("greeting", "")
data["greeting"] = f"{gretting} from another middie ;)"
return data
def home(res, req, user=None):
res.cork_end(user.get('greeting', None))
res.cork_end(user.get("greeting", None))
app = App()
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()
# You can also take a loop on MiddlewareRouter in middleware_router.py ;)

Wyświetl plik

@ -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
async def get_user(authorization):
if authorization:
# do actually something async here
return { 'greeting': 'Hello, World' }
return {"greeting": "Hello, World"}
return None
def auth(route):
# in async query string, arguments and headers are only valid until the first await
async def auth_middleware(res, req):
@ -18,7 +20,7 @@ def auth(route):
# get queries will preserve all queries inside req after await
queries = req.get_queries()
user = await get_user(headers.get('authorization', None))
user = await get_user(headers.get("authorization", None))
if user:
return route(res, req, user)
@ -30,13 +32,17 @@ def auth(route):
def home(res, req, user=None):
theme = req.get_query("theme_color")
theme = theme if theme else "light"
greeting = user.get('greeting', None)
greeting = user.get("greeting", None)
user_id = req.get_parameter(0)
res.cork_end(f"{greeting} <br/> theme: {theme} <br/> id: {user_id}")
app = App()
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()

Wyświetl plik

@ -4,11 +4,12 @@ from socketify import App, MiddlewareRouter, middleware
async def get_user(authorization):
if authorization:
# you can do something async here
return { 'greeting': 'Hello, World' }
return {"greeting": "Hello, World"}
return 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:
res.write_status(403).end("not authorized")
# 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
return user
def another_middie(res, req, data=None):
# now we can mix sync and async and change the data here
if isinstance(data, dict):
gretting = data.get('greeting', '')
data['greeting'] = f"{gretting} from another middie ;)"
gretting = data.get("greeting", "")
data["greeting"] = f"{gretting} from another middie ;)"
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()
@ -42,5 +44,8 @@ other_router = MiddlewareRouter(app, auth, another_middie)
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()

Wyświetl plik

@ -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
def middleware(*functions):
def middleware_route(res, req):
data = None
@ -15,13 +16,15 @@ def middleware(*functions):
return middleware_route
def get_user(authorization_header):
if authorization_header:
return { 'greeting': 'Hello, World' }
return {"greeting": "Hello, World"}
return None
def auth(res, req, data=None):
user = get_user(req.get_header('authorization'))
user = get_user(req.get_header("authorization"))
if not user:
res.write_status(403).end("not authorized")
return False
@ -29,10 +32,15 @@ def auth(res, req, data=None):
# returns extra data
return user
def home(res, req, user=None):
res.end(user.get('greeting', None))
res.end(user.get("greeting", None))
app = App()
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()

Wyświetl plik

@ -2,9 +2,11 @@ from socketify import App, AppOptions, AppListenOptions
app = App()
async def home(res, req):
res.end("Hello, World!")
def user(res, req):
try:
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)
req.set_yield(1)
def not_found(res, req):
res.write_status(404).end("Not Found")
app.get("/", home)
app.get("/user/:user_id", user)
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()

Wyświetl plik

@ -1,13 +1,18 @@
from socketify import App
def home(res, req):
res.write('<html><h1>')
res.write('Your proxied IP is: %s' % res.get_proxied_remote_address())
res.write('</h1><h1>')
res.write('Your IP as seen by the origin server is: %s' % res.get_remote_address())
res.end('</h1></html>')
res.write("<html><h1>")
res.write("Your proxied IP is: %s" % res.get_proxied_remote_address())
res.write("</h1><h1>")
res.write("Your IP as seen by the origin server is: %s" % res.get_remote_address())
res.end("</h1></html>")
app = App()
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()

Wyświetl plik

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

Wyświetl plik

@ -1,19 +1,25 @@
from socketify import App, AppOptions, AppListenOptions
import asyncio
from datetime import datetime
from datetime import timedelta
app = App()
def home(res, req):
res.end("Hello :)")
def anything(res, req):
res.end("Any route with method: %s" % req.get_method())
def cookies(res, req):
# cookies are writen after end
res.set_cookie("spaciari", "1234567890",{
res.set_cookie(
"spaciari",
"1234567890",
{
# expires
# path
# comment
@ -28,12 +34,15 @@ def cookies(res, req):
"httponly": True,
"samesite": "None",
"secure": True,
"expires": datetime.utcnow() + timedelta(minutes=30)
})
res.end("Your session_id cookie is: %s" % req.get_cookie('session_id'));
"expires": datetime.utcnow() + timedelta(minutes=30),
},
)
res.end("Your session_id cookie is: %s" % req.get_cookie("session_id"))
def useragent(res, req):
res.end("Your user agent is: %s" % req.get_header('user-agent'));
res.end("Your user agent is: %s" % req.get_header("user-agent"))
def user(res, req):
try:
@ -46,6 +55,7 @@ def user(res, req):
# invalid user tells to go, to the next route valid route (not found)
req.set_yield(1)
async def delayed_hello(delay, res):
await asyncio.sleep(delay) # do something async
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
res.run_async(delayed_hello(delay, res))
def json(res, req):
# if you pass an object will auto write an header with application/json
res.end({"message": "I'm an application/json!"})
async def sleepy_json(res, req):
# get parameters, query, headers anything you need here before first await :)
user_agent = req.get_header("user-agent")
@ -83,11 +95,13 @@ async def sleepy_json(res, req):
await asyncio.sleep(2) # do something async
res.cork_end({"message": "I'm delayed!", "user-agent": user_agent})
def custom_header(res, req):
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 ;)")
def send_in_parts(res, req):
# write and end accepts bytes and str or its try to dumps to an json
res.write("I can")
@ -95,16 +109,20 @@ def send_in_parts(res, req):
res.write("messages")
res.end(" in parts!")
def redirect(res, req):
# status code is optional default is 302
res.redirect("/redirected", 302)
def redirected(res, req):
res.end("You got redirected to here :D")
def not_found(res, req):
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.get("/", home)
app.any("/anything", anything)
@ -122,5 +140,10 @@ app.get("/redirected", redirected)
# Wildcard at last always :)
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()

Wyświetl plik

@ -40,11 +40,14 @@ async def home(res, req):
# sends the whole file with 304 and bytes range support
await sendfile(res, req, "./public/index.html")
app.get("/", home)
# serve all files in public folder under /* route (you can use any route like /assets)
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()

Wyświetl plik

@ -1,14 +1,22 @@
from socketify import App
# see helper/templates.py for plugin implementation
from helpers.templates import Jinja2Template
app = App()
app.template(Jinja2Template("./templates", encoding='utf-8', followlinks=False))
app.template(Jinja2Template("./templates", encoding="utf-8", followlinks=False))
def home(res, req):
res.render("jinja2_home.html", title="Hello", message="Hello, World")
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()

Wyświetl plik

@ -1,14 +1,26 @@
from socketify import App
# see helper/templates.py for plugin implementation
from helpers.templates import MakoTemplate
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):
res.render("mako_home.html", message="Hello, World")
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()

Wyświetl plik

@ -1,31 +1,43 @@
from socketify import App, AppOptions, OpCode, CompressOptions
def ws_open(ws):
print('A WebSocket got connected!')
print("A WebSocket got connected!")
ws.send("Hello World!", OpCode.TEXT)
def ws_message(ws, message, opcode):
print(message, opcode)
# Ok is false if backpressure was built up, wait for drain
ok = ws.send(message, opcode)
def ws_upgrade(res, req, socket_context):
key = req.get_header("sec-websocket-key")
protocol = req.get_header("sec-websocket-protocol")
extensions = req.get_header("sec-websocket-extensions")
res.upgrade(key, protocol, extensions, socket_context)
app = App()
app.ws("/*", {
'compression': CompressOptions.SHARED_COMPRESSOR,
'max_payload_length': 16 * 1024 * 1024,
'idle_timeout': 12,
'open': ws_open,
'message': ws_message,
'upgrade': ws_upgrade,
'drain': lambda ws: print('WebSocket backpressure: %s', ws.get_buffered_amount()),
'close': lambda ws, code, message: print('WebSocket closed')
})
app.ws(
"/*",
{
"compression": CompressOptions.SHARED_COMPRESSOR,
"max_payload_length": 16 * 1024 * 1024,
"idle_timeout": 12,
"open": ws_open,
"message": ws_message,
"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.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()

Wyświetl plik

@ -1,15 +1,18 @@
from socketify import App, AppOptions, OpCode, CompressOptions
import asyncio
def ws_open(ws):
print('A WebSocket got connected!')
print("A WebSocket got connected!")
ws.send("Hello World!", OpCode.TEXT)
def ws_message(ws, message, opcode):
print(message, opcode)
# Ok is false if backpressure was built up, wait for drain
ok = ws.send(message, opcode)
async def ws_upgrade(res, req, socket_context):
key = req.get_header("sec-websocket-key")
protocol = req.get_header("sec-websocket-protocol")
@ -17,17 +20,26 @@ async def ws_upgrade(res, req, socket_context):
await asyncio.sleep(2)
res.upgrade(key, protocol, extensions, socket_context)
app = App()
app.ws("/*", {
'compression': CompressOptions.SHARED_COMPRESSOR,
'max_payload_length': 16 * 1024 * 1024,
'idle_timeout': 12,
'open': ws_open,
'message': ws_message,
'upgrade': ws_upgrade,
'drain': lambda ws: print('WebSocket backpressure: %s', ws.get_buffered_amount()),
'close': lambda ws, code, message: print('WebSocket closed')
})
app.ws(
"/*",
{
"compression": CompressOptions.SHARED_COMPRESSOR,
"max_payload_length": 16 * 1024 * 1024,
"idle_timeout": 12,
"open": ws_open,
"message": ws_message,
"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.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()

Wyświetl plik

@ -4,16 +4,18 @@ from socketify import App
# We always recomend check res.aborted in async operations
###
def upload(res, req):
print(f"Posted to {req.get_url()}")
def on_data(res, chunk, 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.on_data(on_data)
async def upload_chunks(res, req):
print(f"Posted to {req.get_url()}")
# 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
res.cork_end("Thanks for the data!")
async def upload_json(res, req):
print(f"Posted to {req.get_url()}")
# 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
res.cork_end("Thanks for the data!")
async def upload_text(res, req):
print(f"Posted to {req.get_url()}")
# 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
res.cork_end("Thanks for the data!")
async def upload_urlencoded(res, req):
print(f"Posted to {req.get_url()}")
# 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}")
@ -84,5 +91,8 @@ app.post("/urlencoded", upload_urlencoded)
app.post("/multiple", upload_multiple)
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()

Wyświetl plik

@ -1,24 +1,35 @@
from socketify import App, AppOptions, OpCode, CompressOptions
def ws_open(ws):
print('A WebSocket got connected!')
print("A WebSocket got connected!")
ws.send("Hello World!", OpCode.TEXT)
def ws_message(ws, message, opcode):
print(message, opcode)
# Ok is false if backpressure was built up, wait for drain
ok = ws.send(message, opcode)
app = App()
app.ws("/*", {
'compression': CompressOptions.SHARED_COMPRESSOR,
'max_payload_length': 16 * 1024 * 1024,
'idle_timeout': 12,
'open': ws_open,
'message': ws_message,
'drain': lambda ws: print('WebSocket backpressure: %s', ws.get_buffered_amount()),
'close': lambda ws, code, message: print('WebSocket closed')
})
app.ws(
"/*",
{
"compression": CompressOptions.SHARED_COMPRESSOR,
"max_payload_length": 16 * 1024 * 1024,
"idle_timeout": 12,
"open": ws_open,
"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.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()

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -11,14 +11,14 @@ mimetypes.init()
# In production we highly recomend to use CDN like CloudFlare or/and NGINX or similar for static files
async def sendfile(res, req, filename):
# read headers before the first await
if_modified_since = req.get_header('if-modified-since')
range_header = req.get_header('range')
if_modified_since = req.get_header("if-modified-since")
range_header = req.get_header("range")
bytes_range = None
start = 0
end = -1
# parse range header
if range_header:
bytes_range = range_header.replace("bytes=", '').split('-')
bytes_range = range_header.replace("bytes=", "").split("-")
start = int(bytes_range[0])
if bytes_range[1]:
end = int(bytes_range[1])
@ -26,26 +26,28 @@ async def sendfile(res, req, filename):
exists = path.exists(filename)
# not found
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
stats = os.stat(filename)
total_size = stats.st_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
if if_modified_since == last_modified:
return res.write_status(304).end_without_body()
# 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
(content_type, encoding) = mimetypes.guess_type(filename, strict=True)
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:
res.write_header(b'Content-Type', content_type)
res.write_header(b"Content-Type", content_type)
with open(filename, "rb") as fd:
# check range and support it
@ -62,8 +64,10 @@ async def sendfile(res, req, filename):
res.write_status(200)
# tells the browser that we support range
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"Accept-Ranges", b"bytes")
res.write_header(
b"Content-Range", "bytes %d-%d/%d" % (start, end, total_size)
)
pending_size = size
# keep sending until abort or done
while not res.aborted:
@ -82,7 +86,7 @@ async def sendfile(res, req, filename):
def in_directory(file, directory):
# make both absolute
directory = path.join(path.realpath(directory), '')
directory = path.join(path.realpath(directory), "")
file = path.realpath(file)
# 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
@ -102,12 +106,12 @@ def static_route(app, route, directory):
res.write_status(404).end_without_body()
return
res.run_async(sendfile(res, req, filename))
if route.startswith("/"):
route = route[1::]
app.get("%s/*" % route, route_handler)
def middleware(*functions):
# 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):
@ -133,7 +137,7 @@ def middleware(*functions):
return middleware_route
class MiddlewareRouter():
class MiddlewareRouter:
def __init__(self, app, *middlewares):
self.app = app
self.middlewares = middlewares
@ -149,41 +153,49 @@ class MiddlewareRouter():
middies.append(handler)
self.app.post(path, middleware(*middies))
return self
def options(self, path, handler):
middies = list(self.middlewares)
middies.append(handler)
self.app.options(path, middleware(*middies))
return self
def delete(self, path, handler):
middies = list(self.middlewares)
middies.append(handler)
self.app.delete(path, middleware(*middies))
return self
def patch(self, path, handler):
middies = list(self.middlewares)
middies.append(handler)
self.app.patch(path, middleware(*middies))
return self
def put(self, path, handler):
middies = list(self.middlewares)
middies.append(handler)
self.app.put(path, middleware(*middies))
return self
def head(self, path, handler):
middies = list(self.middlewares)
middies.append(handler)
self.app.head(path, middleware(*middies))
return self
def connect(self, path, handler):
middies = list(self.middlewares)
middies.append(handler)
self.app.connect(path, middleware(*middies))
return self
def trace(self, path, handler):
middies = list(self.middlewares)
middies.append(handler)
self.app.trace(path, middleware(*middies))
return self
def any(self, path, handler):
middies = list(self.middlewares)
middies.append(handler)

Wyświetl plik

@ -1,4 +1,3 @@
import asyncio
import threading
import time
@ -13,7 +12,7 @@ def future_handler(future, loop, exception_handler, response):
future.result()
return None
except Exception as error:
if hasattr(exception_handler, '__call__'):
if hasattr(exception_handler, "__call__"):
exception_handler(loop, error, response)
else:
try:
@ -25,14 +24,17 @@ def future_handler(future, loop, exception_handler, response):
return None
return None
class Loop:
def __init__(self, exception_handler=None):
self.loop = asyncio.new_event_loop()
self.uv_loop = UVLoop()
if hasattr(exception_handler, '__call__'):
if hasattr(exception_handler, "__call__"):
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:
self.exception_handler = None
@ -43,7 +45,6 @@ class Loop:
def set_timeout(self, timeout, callback, user_data):
return self.uv_loop.create_timer(timeout, 0, callback, user_data)
def create_future(self):
return self.loop.create_future()
@ -70,9 +71,8 @@ class Loop:
self.loop.call_soon(self.loop.stop)
self.loop.run_forever()
def stop(self):
if(self.started):
if self.started:
self.timer.stop()
self.started = False
# unbind run_once
@ -95,7 +95,9 @@ class Loop:
future = asyncio.ensure_future(task, loop=self.loop)
# 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
self.run_once_asyncio()
@ -104,7 +106,6 @@ class Loop:
return future
# if sys.version_info >= (3, 11)
# with asyncio.Runner(loop_factory=uvloop.new_event_loop) as runner:
# runner.run(main())

Wyświetl plik

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

Wyświetl plik

@ -1,10 +1,10 @@
import cffi
import os
import platform
ffi = cffi.FFI()
ffi.cdef("""
ffi.cdef(
"""
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);
void socketify_check_destroy(socketify_timer* timer);
""")
"""
)
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)
@ffi.callback("void(void *)")
def socketify_generic_handler(data):
if not data == ffi.NULL:
@ -70,7 +80,10 @@ def socketify_generic_handler(data):
class UVCheck:
def __init__(self, loop, 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):
lib.socketify_check_destroy(self._ptr)
self._handler_data = None
@ -81,10 +94,18 @@ class UVCheck:
lib.socketify_check_destroy(self._ptr)
self._handler_data = None
class UVTimer:
def __init__(self, loop, timeout, repeat, 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):
lib.socketify_timer_destroy(self._ptr)
self._handler_data = None
@ -107,7 +128,9 @@ class UVLoop:
def on_prepare(self, 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):
return UVTimer(self._loop, timeout, repeat, handler, user_data)

Wyświetl plik

@ -15,15 +15,18 @@
from socketify import App, AppOptions, OpCode, CompressOptions
import asyncio
def ws_open(ws):
print('A WebSocket got connected!')
print("A WebSocket got connected!")
ws.send("Hello World!", OpCode.TEXT)
def ws_message(ws, message, opcode):
print(message, opcode)
# Ok is false if backpressure was built up, wait for drain
ok = ws.send(message, opcode)
async def ws_upgrade(res, req, socket_context):
key = req.get_header("sec-websocket-key")
protocol = req.get_header("sec-websocket-protocol")
@ -31,15 +34,22 @@ async def ws_upgrade(res, req, socket_context):
await asyncio.sleep(2)
res.upgrade(key, protocol, extensions, socket_context)
app = App()
app.ws("/*", {
'compression': CompressOptions.SHARED_COMPRESSOR,
'max_payload_length': 16 * 1024 * 1024,
'idle_timeout': 12,
'open': ws_open,
'message': ws_message,
'upgrade': ws_upgrade
})
app.ws(
"/*",
{
"compression": CompressOptions.SHARED_COMPRESSOR,
"max_payload_length": 16 * 1024 * 1024,
"idle_timeout": 12,
"open": ws_open,
"message": ws_message,
"upgrade": ws_upgrade,
},
)
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()