kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit 93fa404470cd0ea12015471a6c035bdd61225d08
parent ab592d2a594c0b0b65ada770bc83931b069167b4
Author: Joris Vink <joris@coders.se>
Date:   Sun,  3 Aug 2014 17:45:23 +0200

Move contrib/examples into examples

Diffstat:
contrib/examples/generic/assets/index.html | 15---------------
contrib/examples/generic/assets/intro.jpg | 0
contrib/examples/generic/assets/params.html | 33---------------------------------
contrib/examples/generic/assets/private.html | 16----------------
contrib/examples/generic/assets/private_test.html | 15---------------
contrib/examples/generic/assets/style.css | 16----------------
contrib/examples/generic/assets/upload.html | 22----------------------
contrib/examples/generic/conf/generic.conf | 49-------------------------------------------------
contrib/examples/generic/src/example.c | 361-------------------------------------------------------------------------------
contrib/examples/pgsql/conf/pgsql.conf | 17-----------------
contrib/examples/pgsql/src/pgsql.c | 73-------------------------------------------------------------------------
contrib/examples/tasks/README | 3---
contrib/examples/tasks/conf/tasks.conf | 27---------------------------
contrib/examples/tasks/src/tasks.c | 225-------------------------------------------------------------------------------
examples/examples/generic/assets/index.html | 15+++++++++++++++
examples/examples/generic/assets/intro.jpg | 0
examples/examples/generic/assets/params.html | 33+++++++++++++++++++++++++++++++++
examples/examples/generic/assets/private.html | 16++++++++++++++++
examples/examples/generic/assets/private_test.html | 15+++++++++++++++
examples/examples/generic/assets/style.css | 16++++++++++++++++
examples/examples/generic/assets/upload.html | 22++++++++++++++++++++++
examples/examples/generic/conf/generic.conf | 49+++++++++++++++++++++++++++++++++++++++++++++++++
examples/examples/generic/src/example.c | 361+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
examples/examples/pgsql/conf/pgsql.conf | 17+++++++++++++++++
examples/examples/pgsql/src/pgsql.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
examples/examples/tasks/README | 3+++
examples/examples/tasks/conf/tasks.conf | 27+++++++++++++++++++++++++++
examples/examples/tasks/src/tasks.c | 225+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
28 files changed, 872 insertions(+), 872 deletions(-)

diff --git a/contrib/examples/generic/assets/index.html b/contrib/examples/generic/assets/index.html @@ -1,15 +0,0 @@ -<!DOCTYPE> -<html> -<head> - <link rel="stylesheet" href="/css/style.css" type="text/css"> - <title>Your KORE module worked!</title> -</head> - -<body> - -<div class="content"> - <p>Your first Kore module worked.</p> -</div> - -</body> -</html> diff --git a/contrib/examples/generic/assets/intro.jpg b/contrib/examples/generic/assets/intro.jpg Binary files differ. diff --git a/contrib/examples/generic/assets/params.html b/contrib/examples/generic/assets/params.html @@ -1,33 +0,0 @@ -<!DOCTYPE> -<html> -<head> - <link rel="stylesheet" href="/css/style.css" type="text/css"> - <title>Kore params test</title> -</head> - -<body style="overflow: auto"> - -<div class="content" style="font-size: 12px; font-weight: normal"> - <p>You can pass one GET parameter (arg1), any other GET parameter will - be filtered out</p> - <p>Only two out of the three input fields will be visible to Kore.</p> - <p>The first field accepts the input "test"</p> - <p>The second field accepts anything like /test/[a-z]*</p> - <p>The third field will be removed by Kore, as it is not in the params - block configured for this page.</p> - <form method="POST"> - <input type="input" name="test1" value="$test1$"> - <input type="input" name="test2" value="$test2$"> - <input type="input" name="test3" value="$test3$"> - <input type="submit"> - </form> - - <p style="font-size: 12px; font-weight: normal">GET param arg1: $arg1$</p> - <p style="font-size: 12px; font-weight: normal">GET param arg2: $arg2$</p> - <p style="font-size: 12px; font-weight: normal">test1: $test1$</p> - <p style="font-size: 12px; font-weight: normal">test2: $test2$</p> - <p style="font-size: 12px; font-weight: normal">test3: $test3$</p> -</div> - -</body> -</html> diff --git a/contrib/examples/generic/assets/private.html b/contrib/examples/generic/assets/private.html @@ -1,16 +0,0 @@ -<!DOCTYPE> -<html> -<head> - <link rel="stylesheet" href="/css/style.css" type="text/css"> - <title>Kore Authentication tests</title> -</head> - -<body> - -<div class="content"> - <p style="font-size: small">The cookie session_id should now be set.</p> - <p style="font-size: small">You can continue to <a href="/private/test">view page handler in auth block</a></p> -</div> - -</body> -</html> diff --git a/contrib/examples/generic/assets/private_test.html b/contrib/examples/generic/assets/private_test.html @@ -1,15 +0,0 @@ -<!DOCTYPE> -<html> -<head> - <link rel="stylesheet" href="/css/style.css" type="text/css"> - <title>Kore Authentication tests</title> -</head> - -<body> - -<div class="content"> - <p style="font-size: small">If you see this, the authentication worked. This page should redirect back to /private once you remove your session_id cookie.</p> -</div> - -</body> -</html> diff --git a/contrib/examples/generic/assets/style.css b/contrib/examples/generic/assets/style.css @@ -1,16 +0,0 @@ -body { - width: 100%; - margin: 0px; - color: #000; - overflow: hidden; - background-color: #fff; -} - -.content { - width: 800px; - margin-left: auto; - margin-right: auto; - margin-top: 100px; - font-size: 60px; - text-align: center; -} diff --git a/contrib/examples/generic/assets/upload.html b/contrib/examples/generic/assets/upload.html @@ -1,22 +0,0 @@ -<!DOCTYPE> -<html> -<head> - <link rel="stylesheet" href="/css/style.css" type="text/css"> - <title>Kore upload test</title> -</head> - -<body style="overflow: auto"> - -<div class="content"> - <form method="POST" enctype="multipart/form-data"> - <input type="input" name="firstname"> - <input type="file" name="file"> - <input type="submit" value="upload"> - </form> - - <p style="font-size: 12px; font-weight: normal">$upload$</p> - <p style="font-size: 12px; font-weight: normal">$firstname$</p> -</div> - -</body> -</html> diff --git a/contrib/examples/generic/conf/generic.conf b/contrib/examples/generic/conf/generic.conf @@ -1,49 +0,0 @@ -# Placeholder configuration - -bind 127.0.0.1 8888 -pidfile kore.pid - -load ./generic.so example_load - -validator v_example function v_example_func -validator v_regex regex ^/test/[a-z]*$ -validator v_number regex ^[0-9]*$ -validator v_session function v_session_validate - -ssl_no_compression - -authentication auth_example { - authentication_type cookie - authentication_value session_id - authentication_validator v_session - authentication_uri /private -} - -domain 127.0.0.1 { - certfile cert/server.crt - certkey cert/server.key - accesslog kore_access.log - - static /css/style.css serve_style_css - static / serve_index - static /intro.jpg serve_intro - static /b64test serve_b64test - static /spdy-reset serve_spdyreset - static /upload serve_file_upload - static /lock-test serve_lock_test - static /validator serve_validator - static /params-test serve_params_test - static /private serve_private - - static /private/test serve_private_test auth_example - - params post /params-test { - validate test1 v_example - validate test2 v_regex - } - - params get /params-test { - validate arg1 v_example - validate id v_number - } -} diff --git a/contrib/examples/generic/src/example.c b/contrib/examples/generic/src/example.c @@ -1,361 +0,0 @@ -/* - * Copyright (c) 2013 Joris Vink <joris@coders.se> - * - * 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 <kore/kore.h> -#include <kore/http.h> - -#include "assets.h" - -void example_load(int); - -int serve_style_css(struct http_request *); -int serve_index(struct http_request *); -int serve_intro(struct http_request *); -int serve_b64test(struct http_request *); -int serve_spdyreset(struct http_request *); -int serve_file_upload(struct http_request *); -int serve_lock_test(struct http_request *); -int serve_validator(struct http_request *); -int serve_params_test(struct http_request *); -int serve_private(struct http_request *); -int serve_private_test(struct http_request *); - -void my_callback(void); -int v_example_func(struct http_request *, char *); -int v_session_validate(struct http_request *, char *); -void test_base64(u_int8_t *, u_int32_t, struct kore_buf *); - -char *b64tests[] = { - "1234567890", - "One two three four five", - "Man", - "any carnal pleasure.", - "any carnal pleasure", - "any carnal pleas", - "I am a nobody, nobody is perfect, therefor I am.", - NULL -}; - -void -example_load(int state) -{ - switch (state) { - case KORE_MODULE_LOAD: - kore_log(LOG_NOTICE, "module loading"); - break; - case KORE_MODULE_UNLOAD: - kore_log(LOG_NOTICE, "module unloading"); - break; - default: - kore_log(LOG_NOTICE, "state %d unknown!", state); - break; - } -} - -int -serve_style_css(struct http_request *req) -{ - char *date; - time_t tstamp; - - tstamp = 0; - if (http_request_header_get(req, "if-modified-since", &date)) { - tstamp = kore_date_to_time(date); - kore_mem_free(date); - - kore_debug("header was present with %ld", tstamp); - } - - if (tstamp != 0 && tstamp <= asset_mtime_style_css) { - http_response(req, 304, NULL, 0); - } else { - date = kore_time_to_date(asset_mtime_style_css); - if (date != NULL) - http_response_header_add(req, "last-modified", date); - - http_response_header_add(req, "content-type", "text/css"); - http_response(req, 200, asset_style_css, asset_len_style_css); - } - - return (KORE_RESULT_OK); -} - -int -serve_index(struct http_request *req) -{ - http_response_header_add(req, "content-type", "text/html"); - http_response(req, 200, asset_index_html, asset_len_index_html); - - return (KORE_RESULT_OK); -} - -int -serve_intro(struct http_request *req) -{ - http_response_header_add(req, "content-type", "image/jpg"); - http_response(req, 200, asset_intro_jpg, asset_len_intro_jpg); - - return (KORE_RESULT_OK); -} - -int -serve_b64test(struct http_request *req) -{ - int i; - u_int32_t len; - struct kore_buf *res; - u_int8_t *data; - - res = kore_buf_create(1024); - for (i = 0; b64tests[i] != NULL; i++) - test_base64((u_int8_t *)b64tests[i], strlen(b64tests[i]), res); - - data = kore_buf_release(res, &len); - - http_response_header_add(req, "content-type", "text/plain"); - http_response(req, 200, data, len); - kore_mem_free(data); - - return (KORE_RESULT_OK); -} - -int -serve_spdyreset(struct http_request *req) -{ - spdy_session_teardown(req->owner, SPDY_SESSION_ERROR_OK); - return (KORE_RESULT_OK); -} - -int -serve_file_upload(struct http_request *req) -{ - int r; - u_int8_t *d; - struct kore_buf *b; - u_int32_t len; - char *name, buf[BUFSIZ]; - - b = kore_buf_create(asset_len_upload_html); - kore_buf_append(b, asset_upload_html, asset_len_upload_html); - - if (req->method == HTTP_METHOD_POST) { - http_populate_multipart_form(req, &r); - if (http_argument_get_string("firstname", &name, &len)) { - kore_buf_replace_string(b, "$firstname$", name, len); - } else { - kore_buf_replace_string(b, "$firstname$", NULL, 0); - } - - if (http_file_lookup(req, "file", &name, &d, &len)) { - snprintf(buf, sizeof(buf), "%s is %d bytes", name, len); - kore_buf_replace_string(b, - "$upload$", buf, strlen(buf)); - } else { - kore_buf_replace_string(b, "$upload$", NULL, 0); - } - } else { - kore_buf_replace_string(b, "$upload$", NULL, 0); - kore_buf_replace_string(b, "$firstname$", NULL, 0); - } - - d = kore_buf_release(b, &len); - - http_response_header_add(req, "content-type", "text/html"); - http_response(req, 200, d, len); - kore_mem_free(d); - - return (KORE_RESULT_OK); -} - -int -serve_lock_test(struct http_request *req) -{ - kore_log(LOG_NOTICE, "lock-test called on worker %d", worker->id); - kore_worker_acceptlock_release(); - - http_response(req, 200, "OK", 2); - return (KORE_RESULT_OK); -} - -void -test_base64(u_int8_t *src, u_int32_t slen, struct kore_buf *res) -{ - char *in; - u_int32_t len; - u_int8_t *out; - - kore_buf_appendf(res, "test '%s'\n", src); - - if (!kore_base64_encode(src, slen, &in)) { - kore_buf_appendf(res, "encoding '%s' failed\n", src); - } else { - kore_buf_appendf(res, "encoded: '%s'\n", in); - - if (!kore_base64_decode(in, &out, &len)) { - kore_buf_appendf(res, "decoding failed\n"); - } else { - kore_buf_appendf(res, "decoded: "); - kore_buf_append(res, out, len); - kore_buf_appendf(res, "\n"); - kore_mem_free(out); - } - - kore_mem_free(in); - } - - kore_buf_appendf(res, "\n"); -} - -int -serve_validator(struct http_request *req) -{ - if (kore_validator_run(NULL, "v_example", "test")) - kore_log(LOG_NOTICE, "v_example ok (expected)"); - else - kore_log(LOG_NOTICE, "v_example failed"); - - if (kore_validator_run(NULL, "v_regex", "/test/123")) - kore_log(LOG_NOTICE, "regex #1 ok"); - else - kore_log(LOG_NOTICE, "regex #1 failed (expected)"); - - if (kore_validator_run(NULL, "v_regex", "/test/joris")) - kore_log(LOG_NOTICE, "regex #2 ok (expected)"); - else - kore_log(LOG_NOTICE, "regex #2 failed"); - - http_response(req, 200, "OK", 2); - - return (KORE_RESULT_OK); -} - -int -serve_params_test(struct http_request *req) -{ - struct kore_buf *b; - u_int8_t *d; - u_int32_t len; - int r, i; - char *test, name[10]; - - http_populate_arguments(req); - - b = kore_buf_create(asset_len_params_html); - kore_buf_append(b, asset_params_html, asset_len_params_html); - - /* - * The GET parameters will be filtered out on POST. - */ - if (http_argument_get_string("arg1", &test, &len)) { - kore_buf_replace_string(b, "$arg1$", test, len); - } else { - kore_buf_replace_string(b, "$arg1$", NULL, 0); - } - - if (http_argument_get_string("arg2", &test, &len)) { - kore_buf_replace_string(b, "$arg2$", test, len); - } else { - kore_buf_replace_string(b, "$arg2$", NULL, 0); - } - - if (req->method == HTTP_METHOD_GET) { - kore_buf_replace_string(b, "$test1$", NULL, 0); - kore_buf_replace_string(b, "$test2$", NULL, 0); - kore_buf_replace_string(b, "$test3$", NULL, 0); - - if (http_argument_get_uint16("id", &r)) - kore_log(LOG_NOTICE, "id: %d", r); - else - kore_log(LOG_NOTICE, "No id set"); - - http_response_header_add(req, "content-type", "text/html"); - d = kore_buf_release(b, &len); - http_response(req, 200, d, len); - kore_mem_free(d); - - return (KORE_RESULT_OK); - } - - for (i = 1; i < 4; i++) { - snprintf(name, sizeof(name), "test%d", i); - if (http_argument_get_string(name, &test, &len)) { - snprintf(name, sizeof(name), "$test%d$", i); - kore_buf_replace_string(b, name, test, len); - } else { - snprintf(name, sizeof(name), "$test%d$", i); - kore_buf_replace_string(b, name, NULL, 0); - } - } - - http_response_header_add(req, "content-type", "text/html"); - d = kore_buf_release(b, &len); - http_response(req, 200, d, len); - kore_mem_free(d); - - return (KORE_RESULT_OK); -} - -int -serve_private(struct http_request *req) -{ - http_response_header_add(req, "content-type", "text/html"); - http_response_header_add(req, "set-cookie", "session_id=test123"); - http_response(req, 200, asset_private_html, asset_len_private_html); - - return (KORE_RESULT_OK); -} - -int -serve_private_test(struct http_request *req) -{ - http_response_header_add(req, "content-type", "text/html"); - - http_response(req, 200, asset_private_test_html, - asset_len_private_test_html); - - return (KORE_RESULT_OK); -} - -void -my_callback(void) -{ - if (worker != NULL) - kore_log(LOG_NOTICE, "running on worker %d", worker->id); - else - kore_log(LOG_NOTICE, "running from parent"); -} - -int -v_example_func(struct http_request *req, char *data) -{ - kore_log(LOG_NOTICE, "v_example_func called"); - - if (!strcmp(data, "test")) - return (KORE_RESULT_OK); - - return (KORE_RESULT_ERROR); -} - -int -v_session_validate(struct http_request *req, char *data) -{ - kore_log(LOG_NOTICE, "v_session_validate: %s", data); - - if (!strcmp(data, "test123")) - return (KORE_RESULT_OK); - - return (KORE_RESULT_ERROR); -} diff --git a/contrib/examples/pgsql/conf/pgsql.conf b/contrib/examples/pgsql/conf/pgsql.conf @@ -1,17 +0,0 @@ -# Kore pgsql_test configuration - -bind 127.0.0.1 8888 -pidfile kore.pid - -ssl_no_compression -load ./pgsql.so pgsql_load - -pgsql_conn_max 5 - -domain localhost { - certfile cert/server.crt - certkey cert/server.key - - accesslog kore_access.log - static / serve_pgsql_test -} diff --git a/contrib/examples/pgsql/src/pgsql.c b/contrib/examples/pgsql/src/pgsql.c @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2014 Joris Vink <joris@coders.se> - * - * 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 <kore/kore.h> -#include <kore/http.h> -#include <kore/pgsql.h> - -void pgsql_load(int); -int serve_pgsql_test(struct http_request *); - -void -pgsql_load(int state) -{ - switch (state) { - case KORE_MODULE_LOAD: - pgsql_conn_string = "Your connection string"; - break; - default: - break; - } -} - -int -serve_pgsql_test(struct http_request *req) -{ - int r, i; - char *col1, *col2; - - KORE_PGSQL(req, "SELECT * FROM test", 0, { - if (req->pgsql[0]->state == KORE_PGSQL_STATE_ERROR) { - kore_pgsql_logerror(req->pgsql[0]); - http_response(req, 500, "fail\n", 5); - return (KORE_RESULT_OK); - } - - r = kore_pgsql_ntuples(req->pgsql[0]); - for (i = 0; i < r; i++) { - col1 = kore_pgsql_getvalue(req->pgsql[0], i, 0); - col2 = kore_pgsql_getvalue(req->pgsql[0], i, 1); - - kore_log(LOG_NOTICE, "%s and %s", col1, col2); - } - }); - - KORE_PGSQL(req, "SELECT * FROM foobar", 1, { - if (req->pgsql[1]->state != KORE_PGSQL_STATE_ERROR) { - kore_log(LOG_NOTICE, "expected error, got %d", - req->pgsql[1]->state); - http_response(req, 500, "fail2\n", 6); - return (KORE_RESULT_OK); - } else { - kore_pgsql_logerror(req->pgsql[1]); - } - }); - - /* Query successfully completed */ - http_response(req, 200, "ok\n", 3); - - return (KORE_RESULT_OK); -} diff --git a/contrib/examples/tasks/README b/contrib/examples/tasks/README @@ -1,3 +0,0 @@ -Compile this example using: - -# env KORE_LDFLAGS="-I/path/to/libcurl -lcurl" diff --git a/contrib/examples/tasks/conf/tasks.conf b/contrib/examples/tasks/conf/tasks.conf @@ -1,27 +0,0 @@ -# Kore config for tasks example - -bind 127.0.0.1 8888 -pidfile kore.pid - -load ./tasks.so - -ssl_no_compression - -validator v_user regex ^[a-z]*$ - -domain 127.0.0.1 { - certfile cert/server.crt - certkey cert/server.key - accesslog kore_access.log - - static / page_handler - static /post_back post_back - - params get / { - validate user v_user - } - - params post /post_back { - validate user v_user - } -} diff --git a/contrib/examples/tasks/src/tasks.c b/contrib/examples/tasks/src/tasks.c @@ -1,225 +0,0 @@ -/* - * Copyright (c) 2014 Joris Vink <joris@coders.se> - * - * 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. - */ - -/* - * In this example, we use the background tasks available in Kore - * to fire off a POST to our /post_back page handler containing - * the user argument that was passed to us in our GET request to /. - * - * This illustrates how Kore its background tasks in effect work and - * how to operate on the channel in order to pass data back and forth. - * - * You need libcurl installed for this to build (including headers) - * - * Compile using build.sh, afterwards start using: - * # kore -nc module.conf (depending on where kore is installed) - * - * Test using: - * # curl -k https://127.0.0.1:4443/?user=foobar - * - * If the result echo'd back matches what you specified, its all green. - */ - -#include <curl/curl.h> - -#include <kore/kore.h> -#include <kore/http.h> -#include <kore/tasks.h> - -int run_curl(struct kore_task *); -int post_back(struct http_request *); -int page_handler(struct http_request *); -size_t curl_write_cb(char *, size_t, size_t, void *); - -int -page_handler(struct http_request *req) -{ - u_int32_t len; - char *user, result[64]; - - /* - * Lets check if a task has been created yet, this is important - * as we only want to fire this off once and we will be called - * again once it has been created. - */ - if (req->task == NULL) { - /* Grab the user argument */ - http_populate_arguments(req); - if (!http_argument_get_string("user", &user, &len)) { - http_response(req, 500, "ERROR\n", 6); - return (KORE_RESULT_OK); - } - - /* - * Create a new task that will execute the run_curl() - * function and bind it to our request. - * - * Binding a task to a request means Kore will reschedule - * the page handler for that request to refire after the - * task has completed or when it writes on the task channel. - */ - kore_task_create(&req->task, run_curl); - kore_task_bind_request(req->task, req); - - /* - * Start the task and write the user we received in our - * GET request to its channel. - */ - kore_task_run(req->task); - kore_task_channel_write(req->task, user, len); - - /* - * Tell Kore to retry us later. - */ - return (KORE_RESULT_RETRY); - } - - /* - * Our page handler is scheduled to be called when either the - * task finishes or has written data onto the channel. - * - * In order to distuingish between the two we can inspect the - * state of the task. - */ - if (kore_task_state(req->task) != KORE_TASK_STATE_FINISHED) { - http_request_sleep(req); - return (KORE_RESULT_RETRY); - } - - /* - * Task is finished, check the result. - */ - if (kore_task_result(req->task) != KORE_RESULT_OK) { - kore_task_destroy(req->task); - http_response(req, 500, NULL, 0); - return (KORE_RESULT_OK); - } - - /* - * Lets read what our task has written to the channel. - * - * kore_task_channel_read() will return the amount of bytes - * that it received for that read. If the returned bytes is - * larger then the buffer you passed this is a sign of truncation - * and should be treated carefully. - */ - len = kore_task_channel_read(req->task, result, sizeof(result)); - if (len > sizeof(result)) { - http_response(req, 500, NULL, 0); - } else { - http_response(req, 200, result, len); - } - - /* We good, destroy the task. */ - kore_task_destroy(req->task); - - return (KORE_RESULT_OK); -} - -int -post_back(struct http_request *req) -{ - u_int32_t len; - char *user; - - if (req->method != HTTP_METHOD_POST) { - http_response(req, 500, NULL, 0); - return (KORE_RESULT_OK); - } - - http_populate_arguments(req); - if (!http_argument_get_string("user", &user, &len)) { - http_response(req, 500, NULL, 0); - return (KORE_RESULT_OK); - } - - /* Simply echo the supplied user argument back. */ - http_response(req, 200, user, len); - - return (KORE_RESULT_OK); -} - -/* - * This is the function that is executed by our task which is created - * in the page_handler() callback. - * - * It sets up a CURL POST request to /post_back passing along the - * user argument which it receives from its channel from page_handler(). - */ -int -run_curl(struct kore_task *t) -{ - int l; - struct kore_buf *b; - u_int32_t len; - CURLcode res; - u_int8_t *data; - CURL *curl; - char user[64], fields[128]; - - /* - * Read the channel in order to obtain the user argument - * that was written to it by page_handler(). - */ - len = kore_task_channel_read(t, user, sizeof(user)); - if (len > sizeof(user)) - return (KORE_RESULT_ERROR); - - l = snprintf(fields, sizeof(fields), "user=%.*s", len, user); - if (l == -1 || (size_t)l >= sizeof(fields)) - return (KORE_RESULT_ERROR); - - if ((curl = curl_easy_init()) == NULL) - return (KORE_RESULT_ERROR); - - b = kore_buf_create(128); - - /* Do CURL magic. */ - curl_easy_setopt(curl, CURLOPT_POST, 1); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, b); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, fields); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb); - curl_easy_setopt(curl, CURLOPT_URL, "https://127.0.0.1:8888/post_back"); - - res = curl_easy_perform(curl); - if (res != CURLE_OK) { - kore_buf_free(b); - curl_easy_cleanup(curl); - return (KORE_RESULT_ERROR); - } - - /* - * Grab the response from the CURL request and write the - * result back to the task channel. - */ - data = kore_buf_release(b, &len); - kore_task_channel_write(t, data, len); - kore_mem_free(data); - - return (KORE_RESULT_OK); -} - -size_t -curl_write_cb(char *ptr, size_t size, size_t nmemb, void *udata) -{ - struct kore_buf *b = udata; - - kore_buf_append(b, ptr, size * nmemb); - return (size * nmemb); -} diff --git a/examples/examples/generic/assets/index.html b/examples/examples/generic/assets/index.html @@ -0,0 +1,15 @@ +<!DOCTYPE> +<html> +<head> + <link rel="stylesheet" href="/css/style.css" type="text/css"> + <title>Your KORE module worked!</title> +</head> + +<body> + +<div class="content"> + <p>Your first Kore module worked.</p> +</div> + +</body> +</html> diff --git a/examples/examples/generic/assets/intro.jpg b/examples/examples/generic/assets/intro.jpg Binary files differ. diff --git a/examples/examples/generic/assets/params.html b/examples/examples/generic/assets/params.html @@ -0,0 +1,33 @@ +<!DOCTYPE> +<html> +<head> + <link rel="stylesheet" href="/css/style.css" type="text/css"> + <title>Kore params test</title> +</head> + +<body style="overflow: auto"> + +<div class="content" style="font-size: 12px; font-weight: normal"> + <p>You can pass one GET parameter (arg1), any other GET parameter will + be filtered out</p> + <p>Only two out of the three input fields will be visible to Kore.</p> + <p>The first field accepts the input "test"</p> + <p>The second field accepts anything like /test/[a-z]*</p> + <p>The third field will be removed by Kore, as it is not in the params + block configured for this page.</p> + <form method="POST"> + <input type="input" name="test1" value="$test1$"> + <input type="input" name="test2" value="$test2$"> + <input type="input" name="test3" value="$test3$"> + <input type="submit"> + </form> + + <p style="font-size: 12px; font-weight: normal">GET param arg1: $arg1$</p> + <p style="font-size: 12px; font-weight: normal">GET param arg2: $arg2$</p> + <p style="font-size: 12px; font-weight: normal">test1: $test1$</p> + <p style="font-size: 12px; font-weight: normal">test2: $test2$</p> + <p style="font-size: 12px; font-weight: normal">test3: $test3$</p> +</div> + +</body> +</html> diff --git a/examples/examples/generic/assets/private.html b/examples/examples/generic/assets/private.html @@ -0,0 +1,16 @@ +<!DOCTYPE> +<html> +<head> + <link rel="stylesheet" href="/css/style.css" type="text/css"> + <title>Kore Authentication tests</title> +</head> + +<body> + +<div class="content"> + <p style="font-size: small">The cookie session_id should now be set.</p> + <p style="font-size: small">You can continue to <a href="/private/test">view page handler in auth block</a></p> +</div> + +</body> +</html> diff --git a/examples/examples/generic/assets/private_test.html b/examples/examples/generic/assets/private_test.html @@ -0,0 +1,15 @@ +<!DOCTYPE> +<html> +<head> + <link rel="stylesheet" href="/css/style.css" type="text/css"> + <title>Kore Authentication tests</title> +</head> + +<body> + +<div class="content"> + <p style="font-size: small">If you see this, the authentication worked. This page should redirect back to /private once you remove your session_id cookie.</p> +</div> + +</body> +</html> diff --git a/examples/examples/generic/assets/style.css b/examples/examples/generic/assets/style.css @@ -0,0 +1,16 @@ +body { + width: 100%; + margin: 0px; + color: #000; + overflow: hidden; + background-color: #fff; +} + +.content { + width: 800px; + margin-left: auto; + margin-right: auto; + margin-top: 100px; + font-size: 60px; + text-align: center; +} diff --git a/examples/examples/generic/assets/upload.html b/examples/examples/generic/assets/upload.html @@ -0,0 +1,22 @@ +<!DOCTYPE> +<html> +<head> + <link rel="stylesheet" href="/css/style.css" type="text/css"> + <title>Kore upload test</title> +</head> + +<body style="overflow: auto"> + +<div class="content"> + <form method="POST" enctype="multipart/form-data"> + <input type="input" name="firstname"> + <input type="file" name="file"> + <input type="submit" value="upload"> + </form> + + <p style="font-size: 12px; font-weight: normal">$upload$</p> + <p style="font-size: 12px; font-weight: normal">$firstname$</p> +</div> + +</body> +</html> diff --git a/examples/examples/generic/conf/generic.conf b/examples/examples/generic/conf/generic.conf @@ -0,0 +1,49 @@ +# Placeholder configuration + +bind 127.0.0.1 8888 +pidfile kore.pid + +load ./generic.so example_load + +validator v_example function v_example_func +validator v_regex regex ^/test/[a-z]*$ +validator v_number regex ^[0-9]*$ +validator v_session function v_session_validate + +ssl_no_compression + +authentication auth_example { + authentication_type cookie + authentication_value session_id + authentication_validator v_session + authentication_uri /private +} + +domain 127.0.0.1 { + certfile cert/server.crt + certkey cert/server.key + accesslog kore_access.log + + static /css/style.css serve_style_css + static / serve_index + static /intro.jpg serve_intro + static /b64test serve_b64test + static /spdy-reset serve_spdyreset + static /upload serve_file_upload + static /lock-test serve_lock_test + static /validator serve_validator + static /params-test serve_params_test + static /private serve_private + + static /private/test serve_private_test auth_example + + params post /params-test { + validate test1 v_example + validate test2 v_regex + } + + params get /params-test { + validate arg1 v_example + validate id v_number + } +} diff --git a/examples/examples/generic/src/example.c b/examples/examples/generic/src/example.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2013 Joris Vink <joris@coders.se> + * + * 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 <kore/kore.h> +#include <kore/http.h> + +#include "assets.h" + +void example_load(int); + +int serve_style_css(struct http_request *); +int serve_index(struct http_request *); +int serve_intro(struct http_request *); +int serve_b64test(struct http_request *); +int serve_spdyreset(struct http_request *); +int serve_file_upload(struct http_request *); +int serve_lock_test(struct http_request *); +int serve_validator(struct http_request *); +int serve_params_test(struct http_request *); +int serve_private(struct http_request *); +int serve_private_test(struct http_request *); + +void my_callback(void); +int v_example_func(struct http_request *, char *); +int v_session_validate(struct http_request *, char *); +void test_base64(u_int8_t *, u_int32_t, struct kore_buf *); + +char *b64tests[] = { + "1234567890", + "One two three four five", + "Man", + "any carnal pleasure.", + "any carnal pleasure", + "any carnal pleas", + "I am a nobody, nobody is perfect, therefor I am.", + NULL +}; + +void +example_load(int state) +{ + switch (state) { + case KORE_MODULE_LOAD: + kore_log(LOG_NOTICE, "module loading"); + break; + case KORE_MODULE_UNLOAD: + kore_log(LOG_NOTICE, "module unloading"); + break; + default: + kore_log(LOG_NOTICE, "state %d unknown!", state); + break; + } +} + +int +serve_style_css(struct http_request *req) +{ + char *date; + time_t tstamp; + + tstamp = 0; + if (http_request_header_get(req, "if-modified-since", &date)) { + tstamp = kore_date_to_time(date); + kore_mem_free(date); + + kore_debug("header was present with %ld", tstamp); + } + + if (tstamp != 0 && tstamp <= asset_mtime_style_css) { + http_response(req, 304, NULL, 0); + } else { + date = kore_time_to_date(asset_mtime_style_css); + if (date != NULL) + http_response_header_add(req, "last-modified", date); + + http_response_header_add(req, "content-type", "text/css"); + http_response(req, 200, asset_style_css, asset_len_style_css); + } + + return (KORE_RESULT_OK); +} + +int +serve_index(struct http_request *req) +{ + http_response_header_add(req, "content-type", "text/html"); + http_response(req, 200, asset_index_html, asset_len_index_html); + + return (KORE_RESULT_OK); +} + +int +serve_intro(struct http_request *req) +{ + http_response_header_add(req, "content-type", "image/jpg"); + http_response(req, 200, asset_intro_jpg, asset_len_intro_jpg); + + return (KORE_RESULT_OK); +} + +int +serve_b64test(struct http_request *req) +{ + int i; + u_int32_t len; + struct kore_buf *res; + u_int8_t *data; + + res = kore_buf_create(1024); + for (i = 0; b64tests[i] != NULL; i++) + test_base64((u_int8_t *)b64tests[i], strlen(b64tests[i]), res); + + data = kore_buf_release(res, &len); + + http_response_header_add(req, "content-type", "text/plain"); + http_response(req, 200, data, len); + kore_mem_free(data); + + return (KORE_RESULT_OK); +} + +int +serve_spdyreset(struct http_request *req) +{ + spdy_session_teardown(req->owner, SPDY_SESSION_ERROR_OK); + return (KORE_RESULT_OK); +} + +int +serve_file_upload(struct http_request *req) +{ + int r; + u_int8_t *d; + struct kore_buf *b; + u_int32_t len; + char *name, buf[BUFSIZ]; + + b = kore_buf_create(asset_len_upload_html); + kore_buf_append(b, asset_upload_html, asset_len_upload_html); + + if (req->method == HTTP_METHOD_POST) { + http_populate_multipart_form(req, &r); + if (http_argument_get_string("firstname", &name, &len)) { + kore_buf_replace_string(b, "$firstname$", name, len); + } else { + kore_buf_replace_string(b, "$firstname$", NULL, 0); + } + + if (http_file_lookup(req, "file", &name, &d, &len)) { + snprintf(buf, sizeof(buf), "%s is %d bytes", name, len); + kore_buf_replace_string(b, + "$upload$", buf, strlen(buf)); + } else { + kore_buf_replace_string(b, "$upload$", NULL, 0); + } + } else { + kore_buf_replace_string(b, "$upload$", NULL, 0); + kore_buf_replace_string(b, "$firstname$", NULL, 0); + } + + d = kore_buf_release(b, &len); + + http_response_header_add(req, "content-type", "text/html"); + http_response(req, 200, d, len); + kore_mem_free(d); + + return (KORE_RESULT_OK); +} + +int +serve_lock_test(struct http_request *req) +{ + kore_log(LOG_NOTICE, "lock-test called on worker %d", worker->id); + kore_worker_acceptlock_release(); + + http_response(req, 200, "OK", 2); + return (KORE_RESULT_OK); +} + +void +test_base64(u_int8_t *src, u_int32_t slen, struct kore_buf *res) +{ + char *in; + u_int32_t len; + u_int8_t *out; + + kore_buf_appendf(res, "test '%s'\n", src); + + if (!kore_base64_encode(src, slen, &in)) { + kore_buf_appendf(res, "encoding '%s' failed\n", src); + } else { + kore_buf_appendf(res, "encoded: '%s'\n", in); + + if (!kore_base64_decode(in, &out, &len)) { + kore_buf_appendf(res, "decoding failed\n"); + } else { + kore_buf_appendf(res, "decoded: "); + kore_buf_append(res, out, len); + kore_buf_appendf(res, "\n"); + kore_mem_free(out); + } + + kore_mem_free(in); + } + + kore_buf_appendf(res, "\n"); +} + +int +serve_validator(struct http_request *req) +{ + if (kore_validator_run(NULL, "v_example", "test")) + kore_log(LOG_NOTICE, "v_example ok (expected)"); + else + kore_log(LOG_NOTICE, "v_example failed"); + + if (kore_validator_run(NULL, "v_regex", "/test/123")) + kore_log(LOG_NOTICE, "regex #1 ok"); + else + kore_log(LOG_NOTICE, "regex #1 failed (expected)"); + + if (kore_validator_run(NULL, "v_regex", "/test/joris")) + kore_log(LOG_NOTICE, "regex #2 ok (expected)"); + else + kore_log(LOG_NOTICE, "regex #2 failed"); + + http_response(req, 200, "OK", 2); + + return (KORE_RESULT_OK); +} + +int +serve_params_test(struct http_request *req) +{ + struct kore_buf *b; + u_int8_t *d; + u_int32_t len; + int r, i; + char *test, name[10]; + + http_populate_arguments(req); + + b = kore_buf_create(asset_len_params_html); + kore_buf_append(b, asset_params_html, asset_len_params_html); + + /* + * The GET parameters will be filtered out on POST. + */ + if (http_argument_get_string("arg1", &test, &len)) { + kore_buf_replace_string(b, "$arg1$", test, len); + } else { + kore_buf_replace_string(b, "$arg1$", NULL, 0); + } + + if (http_argument_get_string("arg2", &test, &len)) { + kore_buf_replace_string(b, "$arg2$", test, len); + } else { + kore_buf_replace_string(b, "$arg2$", NULL, 0); + } + + if (req->method == HTTP_METHOD_GET) { + kore_buf_replace_string(b, "$test1$", NULL, 0); + kore_buf_replace_string(b, "$test2$", NULL, 0); + kore_buf_replace_string(b, "$test3$", NULL, 0); + + if (http_argument_get_uint16("id", &r)) + kore_log(LOG_NOTICE, "id: %d", r); + else + kore_log(LOG_NOTICE, "No id set"); + + http_response_header_add(req, "content-type", "text/html"); + d = kore_buf_release(b, &len); + http_response(req, 200, d, len); + kore_mem_free(d); + + return (KORE_RESULT_OK); + } + + for (i = 1; i < 4; i++) { + snprintf(name, sizeof(name), "test%d", i); + if (http_argument_get_string(name, &test, &len)) { + snprintf(name, sizeof(name), "$test%d$", i); + kore_buf_replace_string(b, name, test, len); + } else { + snprintf(name, sizeof(name), "$test%d$", i); + kore_buf_replace_string(b, name, NULL, 0); + } + } + + http_response_header_add(req, "content-type", "text/html"); + d = kore_buf_release(b, &len); + http_response(req, 200, d, len); + kore_mem_free(d); + + return (KORE_RESULT_OK); +} + +int +serve_private(struct http_request *req) +{ + http_response_header_add(req, "content-type", "text/html"); + http_response_header_add(req, "set-cookie", "session_id=test123"); + http_response(req, 200, asset_private_html, asset_len_private_html); + + return (KORE_RESULT_OK); +} + +int +serve_private_test(struct http_request *req) +{ + http_response_header_add(req, "content-type", "text/html"); + + http_response(req, 200, asset_private_test_html, + asset_len_private_test_html); + + return (KORE_RESULT_OK); +} + +void +my_callback(void) +{ + if (worker != NULL) + kore_log(LOG_NOTICE, "running on worker %d", worker->id); + else + kore_log(LOG_NOTICE, "running from parent"); +} + +int +v_example_func(struct http_request *req, char *data) +{ + kore_log(LOG_NOTICE, "v_example_func called"); + + if (!strcmp(data, "test")) + return (KORE_RESULT_OK); + + return (KORE_RESULT_ERROR); +} + +int +v_session_validate(struct http_request *req, char *data) +{ + kore_log(LOG_NOTICE, "v_session_validate: %s", data); + + if (!strcmp(data, "test123")) + return (KORE_RESULT_OK); + + return (KORE_RESULT_ERROR); +} diff --git a/examples/examples/pgsql/conf/pgsql.conf b/examples/examples/pgsql/conf/pgsql.conf @@ -0,0 +1,17 @@ +# Kore pgsql_test configuration + +bind 127.0.0.1 8888 +pidfile kore.pid + +ssl_no_compression +load ./pgsql.so pgsql_load + +pgsql_conn_max 5 + +domain localhost { + certfile cert/server.crt + certkey cert/server.key + + accesslog kore_access.log + static / serve_pgsql_test +} diff --git a/examples/examples/pgsql/src/pgsql.c b/examples/examples/pgsql/src/pgsql.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014 Joris Vink <joris@coders.se> + * + * 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 <kore/kore.h> +#include <kore/http.h> +#include <kore/pgsql.h> + +void pgsql_load(int); +int serve_pgsql_test(struct http_request *); + +void +pgsql_load(int state) +{ + switch (state) { + case KORE_MODULE_LOAD: + pgsql_conn_string = "Your connection string"; + break; + default: + break; + } +} + +int +serve_pgsql_test(struct http_request *req) +{ + int r, i; + char *col1, *col2; + + KORE_PGSQL(req, "SELECT * FROM test", 0, { + if (req->pgsql[0]->state == KORE_PGSQL_STATE_ERROR) { + kore_pgsql_logerror(req->pgsql[0]); + http_response(req, 500, "fail\n", 5); + return (KORE_RESULT_OK); + } + + r = kore_pgsql_ntuples(req->pgsql[0]); + for (i = 0; i < r; i++) { + col1 = kore_pgsql_getvalue(req->pgsql[0], i, 0); + col2 = kore_pgsql_getvalue(req->pgsql[0], i, 1); + + kore_log(LOG_NOTICE, "%s and %s", col1, col2); + } + }); + + KORE_PGSQL(req, "SELECT * FROM foobar", 1, { + if (req->pgsql[1]->state != KORE_PGSQL_STATE_ERROR) { + kore_log(LOG_NOTICE, "expected error, got %d", + req->pgsql[1]->state); + http_response(req, 500, "fail2\n", 6); + return (KORE_RESULT_OK); + } else { + kore_pgsql_logerror(req->pgsql[1]); + } + }); + + /* Query successfully completed */ + http_response(req, 200, "ok\n", 3); + + return (KORE_RESULT_OK); +} diff --git a/examples/examples/tasks/README b/examples/examples/tasks/README @@ -0,0 +1,3 @@ +Compile this example using: + +# env KORE_LDFLAGS="-I/path/to/libcurl -lcurl" diff --git a/examples/examples/tasks/conf/tasks.conf b/examples/examples/tasks/conf/tasks.conf @@ -0,0 +1,27 @@ +# Kore config for tasks example + +bind 127.0.0.1 8888 +pidfile kore.pid + +load ./tasks.so + +ssl_no_compression + +validator v_user regex ^[a-z]*$ + +domain 127.0.0.1 { + certfile cert/server.crt + certkey cert/server.key + accesslog kore_access.log + + static / page_handler + static /post_back post_back + + params get / { + validate user v_user + } + + params post /post_back { + validate user v_user + } +} diff --git a/examples/examples/tasks/src/tasks.c b/examples/examples/tasks/src/tasks.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2014 Joris Vink <joris@coders.se> + * + * 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. + */ + +/* + * In this example, we use the background tasks available in Kore + * to fire off a POST to our /post_back page handler containing + * the user argument that was passed to us in our GET request to /. + * + * This illustrates how Kore its background tasks in effect work and + * how to operate on the channel in order to pass data back and forth. + * + * You need libcurl installed for this to build (including headers) + * + * Compile using build.sh, afterwards start using: + * # kore -nc module.conf (depending on where kore is installed) + * + * Test using: + * # curl -k https://127.0.0.1:4443/?user=foobar + * + * If the result echo'd back matches what you specified, its all green. + */ + +#include <curl/curl.h> + +#include <kore/kore.h> +#include <kore/http.h> +#include <kore/tasks.h> + +int run_curl(struct kore_task *); +int post_back(struct http_request *); +int page_handler(struct http_request *); +size_t curl_write_cb(char *, size_t, size_t, void *); + +int +page_handler(struct http_request *req) +{ + u_int32_t len; + char *user, result[64]; + + /* + * Lets check if a task has been created yet, this is important + * as we only want to fire this off once and we will be called + * again once it has been created. + */ + if (req->task == NULL) { + /* Grab the user argument */ + http_populate_arguments(req); + if (!http_argument_get_string("user", &user, &len)) { + http_response(req, 500, "ERROR\n", 6); + return (KORE_RESULT_OK); + } + + /* + * Create a new task that will execute the run_curl() + * function and bind it to our request. + * + * Binding a task to a request means Kore will reschedule + * the page handler for that request to refire after the + * task has completed or when it writes on the task channel. + */ + kore_task_create(&req->task, run_curl); + kore_task_bind_request(req->task, req); + + /* + * Start the task and write the user we received in our + * GET request to its channel. + */ + kore_task_run(req->task); + kore_task_channel_write(req->task, user, len); + + /* + * Tell Kore to retry us later. + */ + return (KORE_RESULT_RETRY); + } + + /* + * Our page handler is scheduled to be called when either the + * task finishes or has written data onto the channel. + * + * In order to distuingish between the two we can inspect the + * state of the task. + */ + if (kore_task_state(req->task) != KORE_TASK_STATE_FINISHED) { + http_request_sleep(req); + return (KORE_RESULT_RETRY); + } + + /* + * Task is finished, check the result. + */ + if (kore_task_result(req->task) != KORE_RESULT_OK) { + kore_task_destroy(req->task); + http_response(req, 500, NULL, 0); + return (KORE_RESULT_OK); + } + + /* + * Lets read what our task has written to the channel. + * + * kore_task_channel_read() will return the amount of bytes + * that it received for that read. If the returned bytes is + * larger then the buffer you passed this is a sign of truncation + * and should be treated carefully. + */ + len = kore_task_channel_read(req->task, result, sizeof(result)); + if (len > sizeof(result)) { + http_response(req, 500, NULL, 0); + } else { + http_response(req, 200, result, len); + } + + /* We good, destroy the task. */ + kore_task_destroy(req->task); + + return (KORE_RESULT_OK); +} + +int +post_back(struct http_request *req) +{ + u_int32_t len; + char *user; + + if (req->method != HTTP_METHOD_POST) { + http_response(req, 500, NULL, 0); + return (KORE_RESULT_OK); + } + + http_populate_arguments(req); + if (!http_argument_get_string("user", &user, &len)) { + http_response(req, 500, NULL, 0); + return (KORE_RESULT_OK); + } + + /* Simply echo the supplied user argument back. */ + http_response(req, 200, user, len); + + return (KORE_RESULT_OK); +} + +/* + * This is the function that is executed by our task which is created + * in the page_handler() callback. + * + * It sets up a CURL POST request to /post_back passing along the + * user argument which it receives from its channel from page_handler(). + */ +int +run_curl(struct kore_task *t) +{ + int l; + struct kore_buf *b; + u_int32_t len; + CURLcode res; + u_int8_t *data; + CURL *curl; + char user[64], fields[128]; + + /* + * Read the channel in order to obtain the user argument + * that was written to it by page_handler(). + */ + len = kore_task_channel_read(t, user, sizeof(user)); + if (len > sizeof(user)) + return (KORE_RESULT_ERROR); + + l = snprintf(fields, sizeof(fields), "user=%.*s", len, user); + if (l == -1 || (size_t)l >= sizeof(fields)) + return (KORE_RESULT_ERROR); + + if ((curl = curl_easy_init()) == NULL) + return (KORE_RESULT_ERROR); + + b = kore_buf_create(128); + + /* Do CURL magic. */ + curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, b); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, fields); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb); + curl_easy_setopt(curl, CURLOPT_URL, "https://127.0.0.1:8888/post_back"); + + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + kore_buf_free(b); + curl_easy_cleanup(curl); + return (KORE_RESULT_ERROR); + } + + /* + * Grab the response from the CURL request and write the + * result back to the task channel. + */ + data = kore_buf_release(b, &len); + kore_task_channel_write(t, data, len); + kore_mem_free(data); + + return (KORE_RESULT_OK); +} + +size_t +curl_write_cb(char *ptr, size_t size, size_t nmemb, void *udata) +{ + struct kore_buf *b = udata; + + kore_buf_append(b, ptr, size * nmemb); + return (size * nmemb); +}