added listen options and SSL support

pull/39/head
Ciro 2022-05-25 10:55:23 -03:00
rodzic 2bfb007ce4
commit 74f098bf50
3 zmienionych plików z 140 dodań i 36 usunięć

130
main.py
Wyświetl plik

@ -213,8 +213,16 @@ lib = ffi.dlopen("./libuwebsockets.so")
def uws_generic_method_handler(res, req, user_data):
if not user_data == ffi.NULL:
handler = ffi.from_handle(user_data)
response = UWSResponse(res, False)
request = UWSRequest(req)
response = AppResponse(res, False)
request = AppRequest(req)
handler(response, request)
@ffi.callback("void(uws_res_t *, uws_req_t *, void *)")
def uws_generic_ssl_method_handler(res, req, user_data):
if not user_data == ffi.NULL:
handler = ffi.from_handle(user_data)
response = AppResponse(res, True)
request = AppRequest(req)
handler(response, request)
@ffi.callback("void(struct us_listen_socket_t *, uws_app_listen_config_t, void *)")
@ -223,7 +231,7 @@ def uws_generic_listen_handler(listen_socket, config, user_data):
app = ffi.from_handle(user_data)
if hasattr(app, "_listen_handler") and hasattr(app._listen_handler, '__call__'):
app.socket = listen_socket
app._listen_handler(config)
app._listen_handler(None if config == ffi.NULL else AppListenOptions(port=int(config.port),host=None if config.host == ffi.NULL else ffi.string(config.host).decode("utf-8"), options=int(config.options)))
@ffi.callback("void(uws_res_t *, void*)")
def uws_generic_abord_handler(response, user_data):
@ -232,7 +240,7 @@ def uws_generic_abord_handler(response, user_data):
res.aborted = True
res.trigger_aborted()
class UWSRequest:
class AppRequest:
def __init__(self, request):
self.req = request
def get_url(self):
@ -279,7 +287,7 @@ class UWSRequest:
def is_ancient(self):
return bool(lib.uws_req_is_ancient(self.req))
class UWSResponse:
class AppResponse:
def __init__(self, response, is_ssl):
self.res = response
self.SSL = ffi.cast("int", 1 if is_ssl else 0)
@ -361,70 +369,96 @@ class UWSResponse:
# void uws_res_on_writable(int ssl, uws_res_t *res, bool (*handler)(uws_res_t *res, uintmax_t, void *opcional_data), void *user_data);
class UWSApp:
def __init__(self):
socket_options = ffi.new("struct us_socket_context_options_t *")
self.SSL = ffi.cast("int", 0)
self.app = lib.uws_create_app(self.SSL, socket_options[0])
class App:
def __init__(self, options=None):
socket_options_ptr = ffi.new("struct us_socket_context_options_t *")
socket_options = socket_options_ptr[0]
self.options = options
if options != None:
self.is_ssl = True
self.SSL = ffi.cast("int", 1)
socket_options.key_file_name = ffi.NULL if options.key_file_name == None else ffi.new("char[]", options.key_file_name.encode("utf-8"))
socket_options.key_file_name = ffi.NULL if options.key_file_name == None else ffi.new("char[]", options.key_file_name.encode("utf-8"))
socket_options.cert_file_name = ffi.NULL if options.cert_file_name == None else ffi.new("char[]", options.cert_file_name.encode("utf-8"))
socket_options.passphrase = ffi.NULL if options.passphrase == None else ffi.new("char[]", options.passphrase.encode("utf-8"))
socket_options.dh_params_file_name = ffi.NULL if options.dh_params_file_name == None else ffi.new("char[]", options.dh_params_file_name.encode("utf-8"))
socket_options.ca_file_name = ffi.NULL if options.ca_file_name == None else ffi.new("char[]", options.ca_file_name.encode("utf-8"))
socket_options.ssl_ciphers = ffi.NULL if options.ssl_ciphers == None else ffi.new("char[]", options.ssl_ciphers.encode("utf-8"))
socket_options.ssl_prefer_low_memory_usage = ffi.cast("int", options.ssl_prefer_low_memory_usage)
else:
self.is_ssl = False
self.SSL = ffi.cast("int", 0)
self.app = lib.uws_create_app(self.SSL, socket_options)
self._ptr = ffi.new_handle(self)
if bool(lib.uws_constructor_failed(self.SSL, self.app)):
raise RuntimeError("Failed to create connection") from exc
raise RuntimeError("Failed to create connection")
self.handlers = []
def get(self, path, handler):
user_data = ffi.new_handle(handler)
self.handlers.append(user_data) #Keep alive handler
lib.uws_app_get(self.SSL, self.app, path.encode("utf-8"), uws_generic_method_handler, user_data)
lib.uws_app_get(self.SSL, self.app, path.encode("utf-8"), uws_generic_ssl_method_handler if self.is_ssl else uws_generic_method_handler, user_data)
return self
def post(self, path, handler):
user_data = ffi.new_handle(handler)
self.handlers.append(user_data) #Keep alive handler
lib.uws_app_post(self.SSL, self.app, path.encode("utf-8"), uws_generic_method_handler, user_data)
lib.uws_app_post(self.SSL, self.app, path.encode("utf-8"), uws_generic_ssl_method_handler if self.is_ssl else uws_generic_method_handler, user_data)
return self
def options(self, path, handler):
user_data = ffi.new_handle(handler)
self.handlers.append(user_data) #Keep alive handler
lib.uws_app_options(self.SSL, self.app, path.encode("utf-8"), uws_generic_method_handler, user_data)
lib.uws_app_options(self.SSL, self.app, path.encode("utf-8"), uws_generic_ssl_method_handler if self.is_ssl else uws_generic_method_handler, user_data)
return self
def delete(self, path, handler):
user_data = ffi.new_handle(handler)
self.handlers.append(user_data) #Keep alive handler
lib.uws_app_delete(self.SSL, self.app, path.encode("utf-8"), uws_generic_method_handler, user_data)
lib.uws_app_delete(self.SSL, self.app, path.encode("utf-8"), uws_generic_ssl_method_handler if self.is_ssl else uws_generic_method_handler, user_data)
return self
def patch(self, path, handler):
user_data = ffi.new_handle(handler)
self.handlers.append(user_data) #Keep alive handler
lib.uws_app_patch(self.SSL, self.app, path.encode("utf-8"), uws_generic_method_handler, user_data)
lib.uws_app_patch(self.SSL, self.app, path.encode("utf-8"), uws_generic_ssl_method_handler if self.is_ssl else uws_generic_method_handler, user_data)
return self
def put(self, path, handler):
user_data = ffi.new_handle(handler)
self.handlers.append(user_data) #Keep alive handler
lib.uws_app_put(self.SSL, self.app, path.encode("utf-8"), uws_generic_method_handler, user_data)
lib.uws_app_put(self.SSL, self.app, path.encode("utf-8"), uws_generic_ssl_method_handler if self.is_ssl else uws_generic_method_handler, user_data)
return self
def head(self, path, handler):
user_data = ffi.new_handle(handler)
self.handlers.append(user_data) #Keep alive handler
lib.uws_app_head(self.SSL, self.app, path.encode("utf-8"), uws_generic_method_handler, user_data)
lib.uws_app_head(self.SSL, self.app, path.encode("utf-8"), uws_generic_ssl_method_handler if self.is_ssl else uws_generic_method_handler, user_data)
return self
def connect(self, path, handler):
user_data = ffi.new_handle(handler)
self.handlers.append(user_data) #Keep alive handler
lib.uws_app_connect(self.SSL, self.app, path.encode("utf-8"), uws_generic_method_handler, user_data)
lib.uws_app_connect(self.SSL, self.app, path.encode("utf-8"), uws_generic_ssl_method_handler if self.is_ssl else uws_generic_method_handler, user_data)
return self
def trace(self, path, handler):
user_data = ffi.new_handle(handler)
self.handlers.append(user_data) #Keep alive handler
lib.uws_app_trace(self.SSL, self.app, path.encode("utf-8"), uws_generic_method_handler, user_data)
lib.uws_app_trace(self.SSL, self.app, path.encode("utf-8"), uws_generic_ssl_method_handler if self.is_ssl else uws_generic_method_handler, user_data)
return self
def any(self, path, handler):
user_data = ffi.new_handle(handler)
self.handlers.append(user_data) #Keep alive handler
lib.uws_app_any(self.SSL, self.app, path.encode("utf-8"), uws_generic_method_handler, user_data)
lib.uws_app_any(self.SSL, self.app, path.encode("utf-8"), uws_generic_ssl_method_handler if self.is_ssl else uws_generic_method_handler, user_data)
return self
def listen(self, port, handler):
def listen(self, port_or_options, handler):
self._listen_handler = handler
lib.uws_app_listen(self.SSL, self.app, ffi.cast("int", port), uws_generic_listen_handler, self._ptr)
if isinstance(port_or_options, int):
lib.uws_app_listen(self.SSL, self.app, ffi.cast("int", port_or_options), uws_generic_listen_handler, self._ptr)
else:
native_options = ffi.new("uws_app_listen_config_t *")
options = native_options[0]
options.port = ffi.cast("int", port_or_options.port)
options.host = ffi.NULL if port_or_options.host == None else ffi.new("char[]", port_or_options.host.encode("utf-8"))
options.options = ffi.cast("int", port_or_options.options)
self.native_options_listen = native_options #Keep alive native_options
lib.uws_app_listen_with_config(self.SSL, self.app, options, uws_generic_listen_handler, self._ptr)
return self
def run(self):
@ -440,17 +474,40 @@ class UWSApp:
lib.uws_app_destroy(self.SSL, self.app)
# uws_app_t *app = uws_create_app(SSL, (struct us_socket_context_options_t){
# /* There are example certificates in uWebSockets.js repo */
# .key_file_name = "../misc/key.pem",
# .cert_file_name = "../misc/cert.pem",
# .passphrase = "1234"
# });
# uws_app_get(SSL, app, "/*", get_handler, NULL);
# uws_app_listen(SSL, app, 3000, listen_handler, NULL);
# uws_app_run(SSL, app);
class AppListenOptions:
def __init__(self, port=0, host=None, options=0):
if not isinstance(port, int): raise RuntimeError("port must be an int")
if host != None and not isinstance(host, str): raise RuntimeError("host must be an String or None")
if not isinstance(options, int): raise RuntimeError("options must be an int")
self.port = port
self.host = host
self.options = options
# typedef struct
# {
# int port;
# const char *host;
# int options;
# } uws_app_listen_config_t;
class AppOptions:
def __init__(self, key_file_name=None, cert_file_name=None, passphrase=None, dh_params_file_name=None, ca_file_name=None, ssl_ciphers=None, ssl_prefer_low_memory_usage=0):
if key_file_name != None and not isinstance(key_file_name, str): raise RuntimeError("key_file_name must be an String or None")
if cert_file_name != None and not isinstance(cert_file_name, str): raise RuntimeError("cert_file_name must be an String or None")
if passphrase != None and not isinstance(passphrase, str): raise RuntimeError("passphrase must be an String or None")
if dh_params_file_name != None and not isinstance(dh_params_file_name, str): raise RuntimeError("dh_params_file_name must be an String or None")
if ca_file_name != None and not isinstance(ca_file_name, str): raise RuntimeError("ca_file_name must be an String or None")
if ssl_ciphers != None and not isinstance(ssl_ciphers, str): raise RuntimeError("ssl_ciphers must be an String or None")
if not isinstance(ssl_prefer_low_memory_usage, int): raise RuntimeError("ssl_prefer_low_memory_usage must be an int")
self.key_file_name = key_file_name
self.cert_file_name = cert_file_name
self.passphrase = passphrase
self.dh_params_file_name = dh_params_file_name
self.ca_file_name = ca_file_name
self.ssl_ciphers = ssl_ciphers
self.ssl_prefer_low_memory_usage = ssl_prefer_low_memory_usage
# res.writeHeader("Date", current_http_date).writeHeader("Server", "uws.js").writeHeader("Content-Type", "text/plain").end('Hello, World!');
current_http_date = datetime.utcnow().isoformat() + "Z"
stopped = False
@ -469,9 +526,10 @@ def plaintext(res, req):
def run_app():
timing = threading.Thread(target=time_thread, args=())
timing.start()
app = UWSApp()
app = App(AppOptions(key_file_name="./misc/key.pem", cert_file_name="./misc/cert.pem", passphrase="1234"))
app.get("/", plaintext)
app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port)))
# app.listen(3000, lambda config: print("Listening on port http://localhost:%s now\n" % str(config.port)))
app.listen(AppListenOptions(port=3000, host="0.0.0.0"), lambda config: print("Listening on port http://%s:%d now\n" % (config.host, config.port)))
app.run()
def create_fork():
@ -480,7 +538,7 @@ def create_fork():
if not n > 0:
run_app()
for index in range(3):
for index in range(1):
create_fork()
run_app()

19
misc/crt.pem 100644
Wyświetl plik

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDETCCAfkCFFn0Tj7tkPnpmhSVj1rDLZ5FHEBTMA0GCSqGSIb3DQEBCwUAMEUx
FDASBgNVBAoMC3VOZXR3b3JraW5nMREwDwYDVQQKDAh1U29ja2V0czEaMBgGA1UE
AwwRc2VsZnNpZ25lZF9jbGllbnQwHhcNMjIwMTE3MTYzOTI4WhcNMjMwMTE3MTYz
OTI4WjBFMRQwEgYDVQQKDAt1TmV0d29ya2luZzERMA8GA1UECgwIdVNvY2tldHMx
GjAYBgNVBAMMEXNlbGZzaWduZWRfY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAv0YnxbShmV9IgmpURNmubYfuk7zi4szJMNAO4HATTO+4lEnE
EWEIWBt+3kbfp2dgp9YRk2HJzaXzYyGd4E2lcCFxsTDyEEbda2mNwpuq8jEUb2AM
prfL6lbmxRcKKL/4I4khfLy+f8vgbLnTS8TAPneDGa4wcHGmpIKDuC3ceS7KgcCy
wVhfYse7h5jWpw4/LVgEXBYwzi1XSDeQjbEWCx7kcB/Xcai2OdmS6iFqG0Dgx6Ka
t0qf2Afd0kw954UbvvP4SCAypLjD3OAEJGlpuSUq7z8OYFmnHYcQIJ29Fm368DXn
RAOuN0jUoc/HW03poWeuKZ+Vj5qUj8AAOXZ0jwIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQAi7n9u8/7IGtnI5hojyWXTxJH+jwMgCowU1AUiR5Ysr6FyEcprs51TDlRj
aQm1Lf+cHTLk8DGCT/tBT4PsoA9fgpKWBDkhh7HQG5WIMUyfLZVKWHCr8aNm1iuC
8EgCrp5CYEAPvmb7KQ3WzOQnlVgjQTLl6DcgjLBGi5w45Wk6eWc3YZml492WhjF4
fxUM5xjZ+sqzOFjpp0oSNeKRrBRMe7CBYSD3/ZejZLyxl/C9UFlxkK7PS+ja2CqN
Nnms2uiPkQIgg9UImH00W5hJoGwgvVONA+UTvFjGRj8a4GSnkLihVqy48Yiy7Bez
DuOG90JG17siujTQjx+njbVDv2zX
-----END CERTIFICATE-----

27
misc/key.pem 100644
Wyświetl plik

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAv0YnxbShmV9IgmpURNmubYfuk7zi4szJMNAO4HATTO+4lEnE
EWEIWBt+3kbfp2dgp9YRk2HJzaXzYyGd4E2lcCFxsTDyEEbda2mNwpuq8jEUb2AM
prfL6lbmxRcKKL/4I4khfLy+f8vgbLnTS8TAPneDGa4wcHGmpIKDuC3ceS7KgcCy
wVhfYse7h5jWpw4/LVgEXBYwzi1XSDeQjbEWCx7kcB/Xcai2OdmS6iFqG0Dgx6Ka
t0qf2Afd0kw954UbvvP4SCAypLjD3OAEJGlpuSUq7z8OYFmnHYcQIJ29Fm368DXn
RAOuN0jUoc/HW03poWeuKZ+Vj5qUj8AAOXZ0jwIDAQABAoIBAQC9sHONDIAevHIK
dCyyQzdLBL3D4lUYG4ODVzMJvdxGNo7U8PrzSUmfJ1WAVsVDHbCrgg7YHOine+aN
7y7E3fwt4d0AnsvQ/JZmCb4+u2ai3a2obpbdV/Vwp1IhL6Ixm4AYrcx6CizaTHR7
Hya/Q5Zr3NY1R5xeRze+enjq1QCLY/OXGEToejwJOBNBA2+Ai5sZfR8pGn0Mszb6
C3LBMtHx8rRDkeS+s3X2g/Sseg0aO4F9E1JwFd/+RZPzZtUB3LGbMwKe4b4s+kQn
zAjP2CMthleFs6Ki2s6z5OhCvjOUE+kYe6529+vxvmVm747keuJ9CcO6dFY92o6u
/xwmlPLBAoGBAPtL3D3sIYLzOfsZ+w2lEg6l6e5TO5lxfX1+fMyFLJ45hFaTTvR1
e0PQNahIrSrHTkhuYcicFgbh2iQTfzgnEfXVeWw2YO6fF1HUbhcdByB2+K9xT3Ap
SN2GagIn02Bgh1rOJ7VNGG+Ri/tXgoI0J7IIvp+et2VDLgWGXrGtm/NRAoGBAMLa
r36zXAqj7ISX2IDPJXI7cPuEfLJqx2wED9daxw2mDbNwGIxkP5QjEJN+iZjzaZqX
a6E6UCU09RGRwuHfy9AqCp9IiOXbi+dKo+IwjoHcWWGA3qv/wj/AlT5Hal0kiV+G
4OETAlSckxvFuZ64vrFyuHptGQL4Q5O7xTLjmzHfAoGBALRZGzUtlFdgq8n0OWLv
hugQVrT98xYKhx9becFmCkF70eg4TD/RWKewc/HURsMeyqXc4jyRGJXT3TRq8bCh
CZi+nif1VteqQZguttvLr2OzPoLa9UHvvyWM4+OsJV1TqZCXx5OsQs8/S5EUmstL
FvoEoJn51HDOJ+c7KhamG/ghAoGASkkG6N3GNEREUlR1dL4EP6WLsEfVJkvxFSwD
Qg3Yn0p0JLmSkktRtc8cba6rFIWP+CDMJp5NmbGz0GvqiSRB1m2AuTL1BfSKRLY+
/meWnMl9xd9UhOwviRCJlUGyuinIuYN5TjVqCQncR5U869bw1EOxMvNOusQdN0A5
sOn2668CgYEAjxc386wLww9CsZ9oMAWxh1v4GWEB+c3o6bEuKW7BppNYvaTftxGt
Eww9BgT7ZfORZjyCWtRxlLj+sUHXoZAnRoheTEVVKhkezcWq73SrR6Ij5SeYTuCG
yBK57WSlpRT24D/fR9tTTesQ1LUh/lBLG23KNbScIODStkaIlqO4HYQ=
-----END RSA PRIVATE KEY-----