From 3d3d705b985eda8499146e1533e0ea7862438961 Mon Sep 17 00:00:00 2001 From: Joris Vink Date: Mon, 30 Jan 2017 20:47:24 +0100 Subject: [PATCH] flesh out the python stuff a bit more. --- examples/python/README.md | 4 ++ examples/python/conf/python.conf | 16 ++++-- examples/python/src/index.py | 84 +++++++++++++++++++++++++------ examples/python/src/python.c | 35 ++++++++----- examples/python/src/websockets.py | 44 +++++++++++++++- 5 files changed, 153 insertions(+), 30 deletions(-) diff --git a/examples/python/README.md b/examples/python/README.md index 3e93dba..c0bb834 100644 --- a/examples/python/README.md +++ b/examples/python/README.md @@ -1,5 +1,9 @@ Kore python module example. +This application requires kore to be built with PYTHON=1. + +It mixes native code (dso) with python code. + Run: ``` $ kore run diff --git a/examples/python/conf/python.conf b/examples/python/conf/python.conf index 0695ac8..e4e286f 100644 --- a/examples/python/conf/python.conf +++ b/examples/python/conf/python.conf @@ -1,10 +1,11 @@ # python configuration load ./python.so onload + +# import both python modules. python_import src/index.py onload python_import src/websockets.py -#bind 127.0.0.1 8888 c_on_connect bind 127.0.0.1 8888 tls_dhparam dh2048.pem @@ -25,21 +26,30 @@ domain * { certfile cert/server.crt certkey cert/server.key + # Mix page handlers between native and python. static / page static /c cpage static /b minimal static /json json_parse static /state state_test static /ws ws_connect + + # Use the builtin asset_serve_* to serve frontend HTML. static /wspage asset_serve_frontend_html static /auth page auth - params get / { + # + # On the native page handler, use a python validator. + # + params get /c { validate id v_p_id } - params get /c { + # + # On the python page handler, use a native validator. + # + params get / { validate id v_id } } diff --git a/examples/python/src/index.py b/examples/python/src/index.py index 0746f2d..5dc07b2 100644 --- a/examples/python/src/index.py +++ b/examples/python/src/index.py @@ -1,16 +1,50 @@ -# Simplistic kore example +# +# Copyright (c) 2017 Joris Vink +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This is a simple python module that can be loaded into Kore. +# It demonstrates some basic abilities to deal with HTTP requests. + +# Pull in the kore stuff. import kore + +# Pull in python JSON parsing. import json +# +# A validator that the configuration for this application uses to determine +# if a request fulfills the requirements to pass an authentication block. +# +# See the configuration for more. +# def python_auth(req, data): -# print("python auth called %s" % data) + kore.log(kore.LOG_NOTICE, "python auth called %s" % data) return kore.RESULT_OK +# +# Define a validator that kore can use via the configuration to validate +# something before allowing access to it. +# def python_validator(req, data): - print("python validator called %s" % data) + kore.log(kore.LOG_NOTICE, "python validator called %s" % data) return kore.RESULT_OK +# +# This function is called when our python module is loaded/unloaded. +# The action param is kore.MODULE_LOAD or kore.MODULE_UNLOAD respectively. +# def onload(action): kore.log(kore.LOG_INFO, "FOOBAR python onload called with %d" % action) return kore.RESULT_OK @@ -21,22 +55,33 @@ def kore_onload(): def kore_preload(): print("kore_preload called") +# +# Test page handler that displays some debug information as well as +# fetches the "xframe" header from the request and logs it if present. +# +# If the request is a POST then we read the body up to 1024 bytes in +# one go and display the result and bytes read in the log. +# +# If it's a GET request attempts to find the "id" argument and presents +# it to the user. +# def page(req): - print("%s path is %s - host is %s" % (req, req.path, req.host)) - print("connection is %s" % req.connection) + kore.log(kore.LOG_INFO, + "%s path is %s - host is %s" % (req, req.path, req.host)) + kore.log(kore.LOG_INFO, "connection is %s" % req.connection) xframe = req.request_header("xframe") if xframe != None: - print("xframe header present %s" % xframe) + kore.log(kore.LOG_INFO, "xframe header present: '%s'" % xframe) if req.method == kore.METHOD_POST: try: length, body = req.body_read(1024) - print("POST and got %d bytes! (%s)" % + kore.log(kore.LOG_INFO, "POST and got %d bytes! (%s)" % (length, body.decode("utf-8"))) except RuntimeError as r: - print("oops runtime error %s" % r) + kore.log(kore.LOG_INFO, "oops runtime error %s" % r) req.response(500, b'') except: - print("oops other error") + kore.log(kore.LOG_INFO, "oops other error") req.response(500, b'') else: req.response_header("content-type", "text/plain") @@ -45,35 +90,43 @@ def page(req): req.populate_get() id = req.argument("id") if id != None: - print("got id of %s" % id) + kore.log(kore.LOG_INFO, "got id of %s" % id) req.response_header("content-type", "text/plain") req.response(200, "hello 1234".encode("utf-8")) return kore.RESULT_OK +# +# Handler that parses the incoming body as JSON and dumps out some things. +# def json_parse(req): if req.method != kore.METHOD_PUT: req.response(400, b'') return kore.RESULT_OK data = json.loads(req.body) - print("loaded json %s" % data) + kore.log(kore.LOG_INFO, "loaded json %s" % data) if data["hello"] == 123: - print("hello is 123!") + kore.log(kore.LOG_INFO, "hello is 123!") req.response(200, "ok".encode("utf-8")) return kore.RESULT_OK +# +# Handler that stores some python state in req.state that it reuses +# once the handler is called again by the event loop (after having +# returned RESULT_RETRY to the event loop). +# def state_test(req): # If we don't have a state this is the first time we're called. if req.state is None: - print("state_test: first time") + kore.log(kore.LOG_INFO, "state_test: first time") req.state = "hello world" # Tell Kore to call us again next event loop. return kore.RESULT_RETRY # We have been called before. - print("state_test: second time, with %s" % req.state) + kore.log(kore.LOG_INFO, "state_test: second time, with %s" % req.state) req.response(200, req.state.encode("utf-8")) # We *MUST* reset state back to None before returning RESULT_OK @@ -81,6 +134,9 @@ def state_test(req): return kore.RESULT_OK +# +# Small handler, returns 200 OK. +# def minimal(req): req.response(200, b'') return kore.RESULT_OK diff --git a/examples/python/src/python.c b/examples/python/src/python.c index 52325b8..9a3a097 100644 --- a/examples/python/src/python.c +++ b/examples/python/src/python.c @@ -1,28 +1,41 @@ +/* + * Copyright (c) 2017 Joris Vink + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + #include #include +/* + * Just some examples of things that can be mixed with python modules. + */ + int onload(int); int cpage(struct http_request *); -void c_on_connect(struct connection *); int c_validator(struct http_request *, void *); int c_validator(struct http_request *req, void *data) { - printf("c_validator called!\n"); + kore_log(LOG_NOTICE, "c_validator(): called!"); return (KORE_RESULT_OK); } -void -c_on_connect(struct connection *c) -{ - printf("c_on_connect!\n"); -} - int onload(int action) { - printf("C onload called!\n"); + kore_log(LOG_NOTICE, "onload called from native"); return (KORE_RESULT_OK); } @@ -30,9 +43,7 @@ int cpage(struct http_request *req) { http_populate_get(req); - - //printf("cpage called\n"); - http_response(req, 200, NULL, 0); + http_response(req, 200, "native", 6); return (KORE_RESULT_OK); } diff --git a/examples/python/src/websockets.py b/examples/python/src/websockets.py index c5e9894..65592d7 100644 --- a/examples/python/src/websockets.py +++ b/examples/python/src/websockets.py @@ -1,16 +1,58 @@ -# using kore websockets via python. +# +# Copyright (c) 2017 Joris Vink +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +# Using kore websockets via python. import kore +# +# Our connection callback, gets called for each new websocket connection. +# def onconnect(c): kore.log(kore.LOG_INFO, "%s: py connected" % c) +# +# Each websocket arriving on a connection triggers this function. +# +# It receives the connection object, the opcode (TEXT/BINARY) and the +# actual data received. +# +# In this example we use the websocket_broadcast() method from kore to +# simply relay the message to all other connection clients. +# def onmessage(c, op, data): kore.websocket_broadcast(c, op, data, kore.WEBSOCKET_BROADCAST_GLOBAL) +# +# Called for every connection that goes byebye. +# def ondisconnect(c): kore.log(kore.LOG_INFO, "%s: py disconnecting" % c) +# +# The /ws connection handler. It establishes the websocket connection +# after a request was made for it. +# +# Note that the websocket_handshake() method for the request takes 3 +# parameters which are the connection callback, message callback and +# disconnect callback. +# +# These are given as strings to Kore which will then resolve them +# in all modules which means you can give native callbacks here as well. +# def ws_connect(req): try: req.websocket_handshake("onconnect", "onmessage", "ondisconnect")