diff --git a/src/socketify/socketify.py b/src/socketify/socketify.py index ca8e3f9..65bd9bb 100644 --- a/src/socketify/socketify.py +++ b/src/socketify/socketify.py @@ -56,6 +56,7 @@ struct us_listen_socket_t { unsigned int socket_ext_size; }; void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls); +int us_socket_local_port(int ssl, struct us_listen_socket_t *ls); struct us_loop_t *uws_get_loop(); typedef enum @@ -245,8 +246,11 @@ def uws_generic_ssl_method_handler(res, req, user_data): def uws_generic_listen_handler(listen_socket, config, user_data): if listen_socket == ffi.NULL: raise RuntimeError("Failed to listen on port %d" % int(config.port)) + + if not user_data == ffi.NULL: app = ffi.from_handle(user_data) + config.port = lib.us_socket_local_port(app.SSL, listen_socket) if hasattr(app, "_listen_handler") and hasattr(app._listen_handler, '__call__'): app.socket = listen_socket 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))) @@ -330,6 +334,7 @@ class AppResponse: elif isinstance(message, bytes): data = message else: + self.write_header("Content-Type", "application/json") data = json.dumps(message).encode("utf-8") lib.uws_res_end(self.SSL, self.res, data, len(data), 1 if end_connection else 0) return self diff --git a/tests/examples/async.py b/tests/examples/async.py index f1adcdd..5cba560 100644 --- a/tests/examples/async.py +++ b/tests/examples/async.py @@ -20,7 +20,6 @@ async def json(res, _): #req maybe will not be available in direct attached async functions #but if you dont care about req info you can do it await asyncio.sleep(2) #do something async - res.write_header("Content-Type", "application/json") res.end({ "message": "I'm delayed!"}) def not_found(res, req): diff --git a/tests/examples/automatic_port_selection.py b/tests/examples/automatic_port_selection.py new file mode 100644 index 0000000..4145920 --- /dev/null +++ b/tests/examples/automatic_port_selection.py @@ -0,0 +1,6 @@ +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.run() \ No newline at end of file diff --git a/tests/examples/error_handler.py b/tests/examples/error_handler.py index db82345..57843d6 100644 --- a/tests/examples/error_handler.py +++ b/tests/examples/error_handler.py @@ -12,10 +12,11 @@ async def async_xablau(res, req): #this can be async no problems def on_error(error, res, req): - #here you can log properly the error and doa pretty response to your clients + #here you can log properly the error and do a pretty response to your clients print("Somethind goes %s" % str(error)) #response and request can be None if the error is in an async function if res != None: + #if response exists try to send something res.write_status(500) res.end("Sorry we did something wrong") diff --git a/tests/examples/forks.py b/tests/examples/forks.py new file mode 100644 index 0000000..141af22 --- /dev/null +++ b/tests/examples/forks.py @@ -0,0 +1,21 @@ +from socketify import App +import os +import multiprocessing + +def run_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.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() + +run_app() # run app on the main process too :) \ No newline at end of file diff --git a/tests/examples/router_and_basics.py b/tests/examples/router_and_basics.py new file mode 100644 index 0000000..762971b --- /dev/null +++ b/tests/examples/router_and_basics.py @@ -0,0 +1,76 @@ + +from socketify import App, AppOptions, AppListenOptions +import asyncio + +app = App() + +def home(res, req): + res.end("Hello :)") + +def anything(res, req): + res.end("Any route with method: %s" % req.get_method()) + +def useragent(res,req): + res.end("Your user agent is: %s" % req.get_header('user-agent')); + +def user(res, req): + try: + if int(req.get_parameter(0)) == 1: + return res.end("Hello user with id 1!") + finally: + # 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.end("Hello sorry for the delay!") + +def delayed(res, req): + #request object only lives during the life time of this call + #get parameters, query, headers anything you need here + delay = req.get_query("delay") + delay = 1 if delay == None else float(delay) + #tell response to run this in the event loop + #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 maybe will not be available in direct attached async functions + #but if you dont care about req info you can do it + await asyncio.sleep(2) #do something async + res.end({ "message": "I'm delayed!"}) + +def custom_header(res, req): + res.write_header("Content-Type", "application/octet-stream") + 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") + res.write(" send ") + res.write("messages") + res.end(" in parts!") + +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) +app.get("/user/agent", useragent) +app.get("/user/:id", user) +app.get("/delayed", delayed) +app.get("/json", json) +app.get("/sleepy", sleepy_json) +app.get("/custom_header", custom_header) +app.get("/send_in_parts", send_in_parts) +# 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.run() \ No newline at end of file