mirror of https://git.kore.io/kore.git
233 lines
6.4 KiB
C
233 lines
6.4 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* This example demonstrates on how to use state machines and
|
|
* asynchronous pgsql queries. For a synchronous query example
|
|
* see the pgsql-sync/ example under the examples/ directory.
|
|
*
|
|
* While this example might seem overly complex for a simple pgsql
|
|
* query, there is a reason behind its complexity:
|
|
* Asynchronous pgsql queries mean that Kore will not block while
|
|
* executing the queries, giving a worker time to continue handling
|
|
* other events such as I/O or other http requests.
|
|
*
|
|
* The state machine framework present in Kore makes it trivial
|
|
* to get going into dropping from your page handler into the right
|
|
* state that you are currently in.
|
|
*
|
|
* The example connects to a local pgsql database (test) using a table
|
|
* called "coders" (which has 2 columns): name, surname.
|
|
*/
|
|
|
|
#include <kore/kore.h>
|
|
#include <kore/http.h>
|
|
#include <kore/pgsql.h>
|
|
|
|
#define REQ_STATE_INIT 0
|
|
#define REQ_STATE_QUERY 1
|
|
#define REQ_STATE_DB_WAIT 2
|
|
#define REQ_STATE_DB_READ 3
|
|
#define REQ_STATE_ERROR 4
|
|
#define REQ_STATE_DONE 5
|
|
|
|
int init(int);
|
|
int page(struct http_request *);
|
|
|
|
static int request_perform_init(struct http_request *);
|
|
static int request_perform_query(struct http_request *);
|
|
static int request_db_wait(struct http_request *);
|
|
static int request_db_read(struct http_request *);
|
|
static int request_error(struct http_request *);
|
|
static int request_done(struct http_request *);
|
|
|
|
struct http_state mystates[] = {
|
|
{ "REQ_STATE_INIT", request_perform_init },
|
|
{ "REQ_STATE_QUERY", request_perform_query },
|
|
{ "REQ_STATE_DB_WAIT", request_db_wait },
|
|
{ "REQ_STATE_DB_READ", request_db_read },
|
|
{ "REQ_STATE_ERROR", request_error },
|
|
{ "REQ_STATE_DONE", request_done },
|
|
};
|
|
|
|
#define mystates_size (sizeof(mystates) / sizeof(mystates[0]))
|
|
|
|
struct rstate {
|
|
int cnt;
|
|
struct kore_pgsql sql;
|
|
};
|
|
|
|
/* Called when our module is loaded (see config) */
|
|
int
|
|
init(int state)
|
|
{
|
|
/* Register our database. */
|
|
kore_pgsql_register("db", "host=/tmp dbname=test");
|
|
|
|
return (KORE_RESULT_OK);
|
|
}
|
|
|
|
/* Page handler entry point (see config) */
|
|
int
|
|
page(struct http_request *req)
|
|
{
|
|
/* Drop into our state machine. */
|
|
kore_log(LOG_NOTICE, "page start");
|
|
return (http_state_run(mystates, mystates_size, req));
|
|
}
|
|
|
|
/* Initialize our PGSQL data structure and prepare for an async query. */
|
|
int
|
|
request_perform_init(struct http_request *req)
|
|
{
|
|
struct rstate *state;
|
|
|
|
/* Setup our state context (if not yet set). */
|
|
if (req->hdlr_extra == NULL) {
|
|
state = kore_malloc(sizeof(*state));
|
|
req->hdlr_extra = state;
|
|
} else {
|
|
state = req->hdlr_extra;
|
|
}
|
|
|
|
/* Initialize our kore_pgsql data structure. */
|
|
if (!kore_pgsql_query_init(&state->sql, req, "db", KORE_PGSQL_ASYNC)) {
|
|
/* If the state was still INIT, we'll try again later. */
|
|
if (state->sql.state == KORE_PGSQL_STATE_INIT) {
|
|
req->fsm_state = REQ_STATE_INIT;
|
|
return (HTTP_STATE_RETRY);
|
|
}
|
|
|
|
kore_pgsql_logerror(&state->sql);
|
|
req->fsm_state = REQ_STATE_ERROR;
|
|
} else {
|
|
req->fsm_state = REQ_STATE_QUERY;
|
|
}
|
|
|
|
return (HTTP_STATE_CONTINUE);
|
|
}
|
|
|
|
/* After setting everything up we will execute our async query. */
|
|
int
|
|
request_perform_query(struct http_request *req)
|
|
{
|
|
struct rstate *state = req->hdlr_extra;
|
|
|
|
/* We want to move to read result after this. */
|
|
req->fsm_state = REQ_STATE_DB_WAIT;
|
|
|
|
/* Fire off the query. */
|
|
if (!kore_pgsql_query(&state->sql, "SELECT * FROM coders")) {
|
|
/*
|
|
* Let the state machine continue immediately since we
|
|
* have an error anyway.
|
|
*/
|
|
return (HTTP_STATE_CONTINUE);
|
|
}
|
|
|
|
/* Resume state machine later when the query results start coming in. */
|
|
return (HTTP_STATE_RETRY);
|
|
}
|
|
|
|
/*
|
|
* After firing off the query, we returned HTTP_STATE_RETRY (see above).
|
|
* When request_db_wait() finally is called by Kore we will have results
|
|
* from pgsql so we'll process them.
|
|
*/
|
|
int
|
|
request_db_wait(struct http_request *req)
|
|
{
|
|
struct rstate *state = req->hdlr_extra;
|
|
|
|
kore_log(LOG_NOTICE, "request_db_wait: %d", state->sql.state);
|
|
|
|
/*
|
|
* When we get here, our asynchronous pgsql query has
|
|
* given us something, check the state to figure out what.
|
|
*/
|
|
switch (state->sql.state) {
|
|
case KORE_PGSQL_STATE_WAIT:
|
|
return (HTTP_STATE_RETRY);
|
|
case KORE_PGSQL_STATE_COMPLETE:
|
|
req->fsm_state = REQ_STATE_DONE;
|
|
break;
|
|
case KORE_PGSQL_STATE_ERROR:
|
|
req->fsm_state = REQ_STATE_ERROR;
|
|
kore_pgsql_logerror(&state->sql);
|
|
break;
|
|
case KORE_PGSQL_STATE_RESULT:
|
|
req->fsm_state = REQ_STATE_DB_READ;
|
|
break;
|
|
default:
|
|
/* This MUST be present in order to advance the pgsql state */
|
|
kore_pgsql_continue(req, &state->sql);
|
|
break;
|
|
}
|
|
|
|
return (HTTP_STATE_CONTINUE);
|
|
}
|
|
|
|
/*
|
|
* Called when there's an actual result to be gotten. After we handle the
|
|
* entire result, we'll drop back into REQ_STATE_DB_WAIT (above) in order
|
|
* to continue until the pgsql API returns KORE_PGSQL_STATE_COMPLETE.
|
|
*/
|
|
int
|
|
request_db_read(struct http_request *req)
|
|
{
|
|
char *name;
|
|
int i, rows;
|
|
struct rstate *state = req->hdlr_extra;
|
|
|
|
/* We have sql data to read! */
|
|
rows = kore_pgsql_ntuples(&state->sql);
|
|
for (i = 0; i < rows; i++) {
|
|
name = kore_pgsql_getvalue(&state->sql, i, 0);
|
|
kore_log(LOG_NOTICE, "name: '%s'", name);
|
|
}
|
|
|
|
/* Continue processing our query results. */
|
|
kore_pgsql_continue(req, &state->sql);
|
|
|
|
/* Back to our DB waiting state. */
|
|
req->fsm_state = REQ_STATE_DB_WAIT;
|
|
return (HTTP_STATE_CONTINUE);
|
|
}
|
|
|
|
/* An error occurred. */
|
|
int
|
|
request_error(struct http_request *req)
|
|
{
|
|
struct rstate *state = req->hdlr_extra;
|
|
|
|
kore_pgsql_cleanup(&state->sql);
|
|
http_response(req, 500, NULL, 0);
|
|
|
|
return (HTTP_STATE_COMPLETE);
|
|
}
|
|
|
|
/* Request was completed successfully. */
|
|
int
|
|
request_done(struct http_request *req)
|
|
{
|
|
struct rstate *state = req->hdlr_extra;
|
|
|
|
kore_pgsql_cleanup(&state->sql);
|
|
http_response(req, 200, NULL, 0);
|
|
|
|
return (HTTP_STATE_COMPLETE);
|
|
}
|