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 8e9c3da764c0ff9bfd28cbba3d051ef9901d8fdb
parent 0e11edefb59437fd78480af37dc338c72c5ac9d4
Author: Joris Vink <joris@coders.se>
Date:   Fri, 19 Sep 2014 12:32:49 +0200

Add a new "simple query" layer to our pgsql api.

This simple query allows you to ditch rolling your own
state machine for handling async pgsql states and instead
asks you to provide 3 functions:
	- init
	- results
	- done

You can see the different in complexity in the pgsql example,
which now contains a pgsql_simple.c holding the same asynchronous
query as in pgsql.c but using the simple pgsql api.

You can of course still roll your own in case you want more control.

Diffstat:
examples/pgsql/conf/pgsql.conf | 1+
examples/pgsql/src/pgsql_simple.c | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
includes/pgsql.h | 34++++++++++++++++++++++------------
src/pgsql.c | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 243 insertions(+), 12 deletions(-)

diff --git a/examples/pgsql/conf/pgsql.conf b/examples/pgsql/conf/pgsql.conf @@ -9,4 +9,5 @@ domain 127.0.0.1 { certfile cert/server.crt certkey cert/server.key static / page + static /simple page_simple } diff --git a/examples/pgsql/src/pgsql_simple.c b/examples/pgsql/src/pgsql_simple.c @@ -0,0 +1,97 @@ +/* + * 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. + */ + +/* + * Same as pgsql.c except using the more simple form. + * + * The simple form of the pgsql API hides the wait state machine + * from you so you only have to implement a few functions to get + * queries up and running asynchronously. + */ + +#include <kore/kore.h> +#include <kore/http.h> +#include <kore/pgsql.h> + +int page_simple(struct http_request *); + +int page_simple_init(struct http_request *, struct kore_pgsql_simple *); +void page_simple_done(struct http_request *, struct kore_pgsql_simple *); +void page_simple_result(struct http_request *, struct kore_pgsql_simple *); + +/* + * Set our callbacks for initialization, result and completion. + * At least init and done MUST be set. + */ +static struct kore_pgsql_simple simple_query = { + .init = page_simple_init, + .done = page_simple_done, + .result = page_simple_result +}; + +int +page_simple(struct http_request *req) +{ + return (kore_pgsql_run(req, &simple_query)); +} + +/* + * Initialization so we can parse arguments, set states, set our query, ... + * + * Return KORE_RESULT_OK if we can proceed or KORE_RESULT_ERROR in case + * you want the state machine to just stop. + * + * Note that if you return KORE_RESULT_ERROR you must call http_response() + * before doing so if you want to relay an error to your client. + */ +int +page_simple_init(struct http_request *req, struct kore_pgsql_simple *simple) +{ + simple->query = "SELECT * FROM coders"; + + return (KORE_RESULT_OK); +} + +/* + * Called when you get a result from your query. + */ +void +page_simple_result(struct http_request *req, struct kore_pgsql_simple *simple) +{ + char *name; + int i, rows; + + rows = kore_pgsql_ntuples(&simple->sql); + for (i = 0; i < rows; i++) { + name = kore_pgsql_getvalue(&simple->sql, i, 0); + kore_log(LOG_NOTICE, "name: '%s'", name); + } +} + +/* + * When we get here req->status will reflect if something went wrong, + * if so then status will be HTTP_STATUS_INTERNAL_ERROR. + * + * Any pgsql errors will already have been logged. + */ +void +page_simple_done(struct http_request *req, struct kore_pgsql_simple *simple) +{ + if (req->status != HTTP_STATUS_INTERNAL_ERROR) + req->status = HTTP_STATUS_OK; + + http_response(req, req->status, NULL, 0); +} diff --git a/includes/pgsql.h b/includes/pgsql.h @@ -37,21 +37,31 @@ struct kore_pgsql { LIST_ENTRY(kore_pgsql) rlist; }; +struct kore_pgsql_simple { + char *query; + void *udata; + struct kore_pgsql sql; + + int (*init)(struct http_request *, struct kore_pgsql_simple *); + void (*done)(struct http_request *, struct kore_pgsql_simple *); + void (*result)(struct http_request *, struct kore_pgsql_simple *); +}; + extern u_int16_t pgsql_conn_max; extern char *pgsql_conn_string; -void kore_pgsql_init(void); -void kore_pgsql_handle(void *, int); -void kore_pgsql_cleanup(struct kore_pgsql *); -void kore_pgsql_continue(struct http_request *, - struct kore_pgsql *); -int kore_pgsql_async(struct kore_pgsql *, - struct http_request *, const char *); - -int kore_pgsql_ntuples(struct kore_pgsql *); -void kore_pgsql_logerror(struct kore_pgsql *); -void kore_pgsql_queue_remove(struct http_request *); -char *kore_pgsql_getvalue(struct kore_pgsql *, int, int); +void kore_pgsql_init(void); +void kore_pgsql_handle(void *, int); +void kore_pgsql_cleanup(struct kore_pgsql *); +void kore_pgsql_continue(struct http_request *, struct kore_pgsql *); +int kore_pgsql_run(struct http_request *, struct kore_pgsql_simple *); +int kore_pgsql_async(struct kore_pgsql *, + struct http_request *, const char *); + +int kore_pgsql_ntuples(struct kore_pgsql *); +void kore_pgsql_logerror(struct kore_pgsql *); +void kore_pgsql_queue_remove(struct http_request *); +char *kore_pgsql_getvalue(struct kore_pgsql *, int, int); #define KORE_PGSQL_STATE_INIT 1 #define KORE_PGSQL_STATE_WAIT 2 diff --git a/src/pgsql.c b/src/pgsql.c @@ -50,6 +50,26 @@ static void pgsql_read_result(struct kore_pgsql *, int); static void pgsql_queue_add(struct http_request *); static void pgsql_queue_wakeup(void); +static int pgsql_simple_state_init(struct http_request *); +static int pgsql_simple_state_query(struct http_request *); +static int pgsql_simple_state_wait(struct http_request *); +static int pgsql_simple_state_result(struct http_request *); +static int pgsql_simple_state_done(struct http_request *); + +#define PGSQL_SIMPLE_STATE_INIT 0 +#define PGSQL_SIMPLE_STATE_QUERY 1 +#define PGSQL_SIMPLE_STATE_WAIT 2 +#define PGSQL_SIMPLE_STATE_RESULT 3 +#define PGSQL_SIMPLE_STATE_DONE 4 + +static struct http_state pgsql_states[] = { + { "PGSQL_SIMPLE_STATE_INIT", pgsql_simple_state_init }, + { "PGSQL_SIMPLE_STATE_QUERY", pgsql_simple_state_query }, + { "PGSQL_SIMPLE_STATE_WAIT", pgsql_simple_state_wait }, + { "PGSQL_SIMPLE_STATE_RESULT", pgsql_simple_state_result }, + { "PGSQL_SIMPLE_STATE_DONE", pgsql_simple_state_done } +}; + static struct kore_pool pgsql_job_pool; static struct kore_pool pgsql_wait_pool; static TAILQ_HEAD(, pgsql_conn) pgsql_conn_free; @@ -125,6 +145,13 @@ kore_pgsql_async(struct kore_pgsql *pgsql, struct http_request *req, return (KORE_RESULT_OK); } +int +kore_pgsql_run(struct http_request *req, struct kore_pgsql_simple *query) +{ + req->hdlr_extra = query; + return (http_state_run(pgsql_states, sizeof(pgsql_states), req)); +} + void kore_pgsql_handle(void *c, int err) { @@ -405,3 +432,99 @@ pgsql_read_result(struct kore_pgsql *pgsql, int async) break; } } + +static int +pgsql_simple_state_init(struct http_request *req) +{ + struct kore_pgsql_simple *simple = req->hdlr_extra; + + if (simple->init == NULL || simple->done == NULL) + fatal("pgsql_simple_state_init: missing callbacks"); + + simple->query = NULL; + simple->udata = NULL; + simple->sql.state = 0; + + if (simple->init(req, simple) != KORE_RESULT_OK) { + req->hdlr_extra = NULL; + return (HTTP_STATE_COMPLETE); + } + + req->fsm_state = PGSQL_SIMPLE_STATE_QUERY; + return (HTTP_STATE_CONTINUE); +} + +static int +pgsql_simple_state_query(struct http_request *req) +{ + struct kore_pgsql_simple *simple = req->hdlr_extra; + + if (simple->query == NULL) + fatal("No string set after pgsql_state_init()"); + + req->fsm_state = PGSQL_SIMPLE_STATE_WAIT; + + if (!kore_pgsql_async(&simple->sql, req, simple->query)) { + if (simple->sql.state == KORE_PGSQL_STATE_INIT) { + req->fsm_state = PGSQL_SIMPLE_STATE_QUERY; + return (HTTP_STATE_RETRY); + } + + return (HTTP_STATE_CONTINUE); + } + + return (HTTP_STATE_CONTINUE); +} + +static int +pgsql_simple_state_wait(struct http_request *req) +{ + struct kore_pgsql_simple *simple = req->hdlr_extra; + + switch (simple->sql.state) { + case KORE_PGSQL_STATE_WAIT: + return (HTTP_STATE_RETRY); + case KORE_PGSQL_STATE_COMPLETE: + req->fsm_state = PGSQL_SIMPLE_STATE_DONE; + break; + case KORE_PGSQL_STATE_ERROR: + req->status = HTTP_STATUS_INTERNAL_ERROR; + req->fsm_state = PGSQL_SIMPLE_STATE_DONE; + kore_pgsql_logerror(&simple->sql); + break; + case KORE_PGSQL_STATE_RESULT: + req->fsm_state = PGSQL_SIMPLE_STATE_RESULT; + break; + default: + kore_pgsql_continue(req, &simple->sql); + break; + } + + return (HTTP_STATE_CONTINUE); +} + +static int +pgsql_simple_state_result(struct http_request *req) +{ + struct kore_pgsql_simple *simple = req->hdlr_extra; + + if (simple->result) + simple->result(req, simple); + + req->fsm_state = PGSQL_SIMPLE_STATE_DONE; + return (HTTP_STATE_CONTINUE); +} + +static int +pgsql_simple_state_done(struct http_request *req) +{ + struct kore_pgsql_simple *simple = req->hdlr_extra; + + req->hdlr_extra = NULL; + simple->done(req, simple); + + if (simple->sql.state != 0) + kore_pgsql_cleanup(&simple->sql); + + return (HTTP_STATE_COMPLETE); +}