diff --git a/main.py b/main.py index 4bc2b02..a20ba91 100644 --- a/main.py +++ b/main.py @@ -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() diff --git a/misc/crt.pem b/misc/crt.pem new file mode 100644 index 0000000..0c20258 --- /dev/null +++ b/misc/crt.pem @@ -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----- diff --git a/misc/key.pem b/misc/key.pem new file mode 100644 index 0000000..a04a55a --- /dev/null +++ b/misc/key.pem @@ -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-----