|
@@ -19,25 +19,39 @@
|
|
|
|
|
|
#include <assert.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <ctype.h>
|
|
|
|
+#include <errno.h>
|
|
#include <h2o.h>
|
|
#include <h2o.h>
|
|
#include <mustache.h>
|
|
#include <mustache.h>
|
|
#include <stdbool.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdint.h>
|
|
|
|
+#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdlib.h>
|
|
#include <postgresql/libpq-fe.h>
|
|
#include <postgresql/libpq-fe.h>
|
|
|
|
+#include <string.h>
|
|
|
|
|
|
#include "database.h"
|
|
#include "database.h"
|
|
#include "error.h"
|
|
#include "error.h"
|
|
#include "fortune.h"
|
|
#include "fortune.h"
|
|
|
|
+#include "global_data.h"
|
|
#include "list.h"
|
|
#include "list.h"
|
|
#include "request_handler.h"
|
|
#include "request_handler.h"
|
|
#include "thread.h"
|
|
#include "thread.h"
|
|
#include "utility.h"
|
|
#include "utility.h"
|
|
|
|
|
|
|
|
+#define ID_FIELD_NAME "id"
|
|
|
|
+#define FORTUNE_TABLE_NAME "Fortune"
|
|
#define MAX_IOVEC 64
|
|
#define MAX_IOVEC 64
|
|
|
|
+#define MESSAGE_FIELD_NAME "message"
|
|
#define NEW_FORTUNE_ID "0"
|
|
#define NEW_FORTUNE_ID "0"
|
|
#define NEW_FORTUNE_MESSAGE "Additional fortune added at request time."
|
|
#define NEW_FORTUNE_MESSAGE "Additional fortune added at request time."
|
|
|
|
+#define TEMPLATE_PATH_SUFFIX "/fortunes.mustache"
|
|
|
|
+
|
|
|
|
+typedef struct {
|
|
|
|
+ list_t l;
|
|
|
|
+ h2o_iovec_t id;
|
|
|
|
+ h2o_iovec_t message;
|
|
|
|
+} fortune_t;
|
|
|
|
|
|
typedef struct {
|
|
typedef struct {
|
|
list_t l;
|
|
list_t l;
|
|
@@ -60,6 +74,11 @@ typedef struct {
|
|
h2o_generator_t generator;
|
|
h2o_generator_t generator;
|
|
} fortune_ctx_t;
|
|
} fortune_ctx_t;
|
|
|
|
|
|
|
|
+typedef struct {
|
|
|
|
+ FILE *input;
|
|
|
|
+ const char *name;
|
|
|
|
+} template_input_t;
|
|
|
|
+
|
|
static uintmax_t add_iovec(mustache_api_t *api,
|
|
static uintmax_t add_iovec(mustache_api_t *api,
|
|
void *userdata,
|
|
void *userdata,
|
|
const char *buffer,
|
|
const char *buffer,
|
|
@@ -68,6 +87,7 @@ static void cleanup_fortunes(fortune_ctx_t *fortune_ctx);
|
|
static void cleanup_request(void *data);
|
|
static void cleanup_request(void *data);
|
|
static int compare_fortunes(const list_t *x, const list_t *y);
|
|
static int compare_fortunes(const list_t *x, const list_t *y);
|
|
static void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req);
|
|
static void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req);
|
|
|
|
+static int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req);
|
|
static list_t *get_sorted_sublist(list_t *head);
|
|
static list_t *get_sorted_sublist(list_t *head);
|
|
static list_t *merge_lists(list_t *head1, list_t *head2);
|
|
static list_t *merge_lists(list_t *head1, list_t *head2);
|
|
static void on_fortune_error(db_query_param_t *param, const char *error_string);
|
|
static void on_fortune_error(db_query_param_t *param, const char *error_string);
|
|
@@ -79,7 +99,21 @@ static void on_fortune_timeout(db_query_param_t *param);
|
|
static uintmax_t on_fortune_variable(mustache_api_t *api,
|
|
static uintmax_t on_fortune_variable(mustache_api_t *api,
|
|
void *userdata,
|
|
void *userdata,
|
|
mustache_token_variable_t *token);
|
|
mustache_token_variable_t *token);
|
|
|
|
+static uintmax_t prerender_section(mustache_api_t *api,
|
|
|
|
+ void *userdata,
|
|
|
|
+ mustache_token_section_t *token);
|
|
|
|
+static uintmax_t prerender_variable(mustache_api_t *api,
|
|
|
|
+ void *userdata,
|
|
|
|
+ mustache_token_variable_t *token);
|
|
|
|
+static uintmax_t read_template(mustache_api_t *api,
|
|
|
|
+ void *userdata,
|
|
|
|
+ char *buffer,
|
|
|
|
+ uintmax_t buffer_size);
|
|
static list_t *sort_fortunes(list_t *head);
|
|
static list_t *sort_fortunes(list_t *head);
|
|
|
|
+static void template_error(mustache_api_t *api,
|
|
|
|
+ void *userdata,
|
|
|
|
+ uintmax_t lineno,
|
|
|
|
+ const char *error);
|
|
|
|
|
|
static uintmax_t add_iovec(mustache_api_t *api,
|
|
static uintmax_t add_iovec(mustache_api_t *api,
|
|
void *userdata,
|
|
void *userdata,
|
|
@@ -155,6 +189,46 @@ static void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req)
|
|
h2o_send(req, iovec_list->iov, iovec_list->iovcnt, state);
|
|
h2o_send(req, iovec_list->iov, iovec_list->iovcnt, state);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
|
|
|
|
+{
|
|
|
|
+ IGNORE_FUNCTION_PARAMETER(self);
|
|
|
|
+
|
|
|
|
+ thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
|
|
|
|
+ event_loop.h2o_ctx,
|
|
|
|
+ req->conn->ctx);
|
|
|
|
+ fortune_ctx_t * const fortune_ctx = calloc(1, sizeof(*fortune_ctx));
|
|
|
|
+
|
|
|
|
+ if (fortune_ctx) {
|
|
|
|
+ fortune_t * const fortune = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune));
|
|
|
|
+ fortune_ctx_t ** const p = h2o_mem_alloc_shared(&req->pool, sizeof(*p), cleanup_request);
|
|
|
|
+
|
|
|
|
+ *p = fortune_ctx;
|
|
|
|
+ memset(fortune, 0, sizeof(*fortune));
|
|
|
|
+ fortune->id.base = NEW_FORTUNE_ID;
|
|
|
|
+ fortune->id.len = sizeof(NEW_FORTUNE_ID) - 1;
|
|
|
|
+ fortune->message.base = NEW_FORTUNE_MESSAGE;
|
|
|
|
+ fortune->message.len = sizeof(NEW_FORTUNE_MESSAGE) - 1;
|
|
|
|
+ fortune_ctx->generator.proceed = complete_fortunes;
|
|
|
|
+ fortune_ctx->num_result = 1;
|
|
|
|
+ fortune_ctx->param.command = FORTUNE_TABLE_NAME;
|
|
|
|
+ fortune_ctx->param.on_error = on_fortune_error;
|
|
|
|
+ fortune_ctx->param.on_result = on_fortune_result;
|
|
|
|
+ fortune_ctx->param.on_timeout = on_fortune_timeout;
|
|
|
|
+ fortune_ctx->param.flags = IS_PREPARED;
|
|
|
|
+ fortune_ctx->req = req;
|
|
|
|
+ fortune_ctx->result = &fortune->l;
|
|
|
|
+
|
|
|
|
+ if (execute_query(ctx, &fortune_ctx->param)) {
|
|
|
|
+ fortune_ctx->cleanup = true;
|
|
|
|
+ send_service_unavailable_error(DB_REQ_ERROR, req);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ send_error(INTERNAL_SERVER_ERROR, REQ_ERROR, req);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static list_t *get_sorted_sublist(list_t *head)
|
|
static list_t *get_sorted_sublist(list_t *head)
|
|
{
|
|
{
|
|
list_t *tail = head;
|
|
list_t *tail = head;
|
|
@@ -263,7 +337,9 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
|
|
fortune_ctx->iovec_list_iter = iovec_list;
|
|
fortune_ctx->iovec_list_iter = iovec_list;
|
|
fortune_ctx->result = sort_fortunes(fortune_ctx->result);
|
|
fortune_ctx->result = sort_fortunes(fortune_ctx->result);
|
|
|
|
|
|
- if (mustache_render(&api, fortune_ctx, ctx->global_data->fortunes_template)) {
|
|
|
|
|
|
+ if (mustache_render(&api,
|
|
|
|
+ fortune_ctx,
|
|
|
|
+ ctx->global_data->request_handler_data.fortunes_template)) {
|
|
fortune_ctx->iovec_list = iovec_list->l.next;
|
|
fortune_ctx->iovec_list = iovec_list->l.next;
|
|
set_default_response_param(HTML, fortune_ctx->content_length, fortune_ctx->req);
|
|
set_default_response_param(HTML, fortune_ctx->content_length, fortune_ctx->req);
|
|
h2o_start_response(fortune_ctx->req, &fortune_ctx->generator);
|
|
h2o_start_response(fortune_ctx->req, &fortune_ctx->generator);
|
|
@@ -334,6 +410,59 @@ static uintmax_t on_fortune_variable(mustache_api_t *api,
|
|
return add_iovec(api, fortune_ctx, iovec->base, iovec->len);
|
|
return add_iovec(api, fortune_ctx, iovec->base, iovec->len);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static uintmax_t prerender_section(mustache_api_t *api,
|
|
|
|
+ void *userdata,
|
|
|
|
+ mustache_token_section_t *token)
|
|
|
|
+{
|
|
|
|
+ bool * const in_section = userdata;
|
|
|
|
+ uintmax_t ret = 0;
|
|
|
|
+
|
|
|
|
+ if (!*in_section && !strcmp(token->name, FORTUNE_TABLE_NAME)) {
|
|
|
|
+ *in_section = true;
|
|
|
|
+ ret = mustache_prerender(api, userdata, token->section);
|
|
|
|
+ *in_section = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static uintmax_t prerender_variable(mustache_api_t *api,
|
|
|
|
+ void *userdata,
|
|
|
|
+ mustache_token_variable_t *token)
|
|
|
|
+{
|
|
|
|
+ IGNORE_FUNCTION_PARAMETER(api);
|
|
|
|
+
|
|
|
|
+ bool * const in_section = userdata;
|
|
|
|
+ uintmax_t ret = 0;
|
|
|
|
+
|
|
|
|
+ if (*in_section) {
|
|
|
|
+ if (token->text_length == sizeof(ID_FIELD_NAME) - 1 &&
|
|
|
|
+ !memcmp(token->text, ID_FIELD_NAME, sizeof(ID_FIELD_NAME) - 1)) {
|
|
|
|
+ token->userdata = (void *) offsetof(fortune_t, id);
|
|
|
|
+ ret = 1;
|
|
|
|
+ }
|
|
|
|
+ else if (token->text_length == sizeof(MESSAGE_FIELD_NAME) - 1 &&
|
|
|
|
+ !memcmp(token->text, MESSAGE_FIELD_NAME, sizeof(MESSAGE_FIELD_NAME) - 1)) {
|
|
|
|
+ token->userdata = (void *) offsetof(fortune_t, message);
|
|
|
|
+ ret = 1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static uintmax_t read_template(mustache_api_t *api,
|
|
|
|
+ void *userdata,
|
|
|
|
+ char *buffer,
|
|
|
|
+ uintmax_t buffer_size)
|
|
|
|
+{
|
|
|
|
+ IGNORE_FUNCTION_PARAMETER(api);
|
|
|
|
+
|
|
|
|
+ const template_input_t * const template_input = userdata;
|
|
|
|
+
|
|
|
|
+ return fread(buffer, sizeof(*buffer), buffer_size, template_input->input);
|
|
|
|
+}
|
|
|
|
+
|
|
// merge sort
|
|
// merge sort
|
|
static list_t *sort_fortunes(list_t *head)
|
|
static list_t *sort_fortunes(list_t *head)
|
|
{
|
|
{
|
|
@@ -365,42 +494,65 @@ static list_t *sort_fortunes(list_t *head)
|
|
return head;
|
|
return head;
|
|
}
|
|
}
|
|
|
|
|
|
-int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
|
|
|
|
|
|
+static void template_error(mustache_api_t *api,
|
|
|
|
+ void *userdata,
|
|
|
|
+ uintmax_t lineno,
|
|
|
|
+ const char *error)
|
|
{
|
|
{
|
|
- IGNORE_FUNCTION_PARAMETER(self);
|
|
|
|
|
|
+ IGNORE_FUNCTION_PARAMETER(api);
|
|
|
|
|
|
- thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
|
|
|
|
- event_loop.h2o_ctx,
|
|
|
|
- req->conn->ctx);
|
|
|
|
- fortune_ctx_t * const fortune_ctx = calloc(1, sizeof(*fortune_ctx));
|
|
|
|
|
|
+ const template_input_t * const template_input = userdata;
|
|
|
|
|
|
- if (fortune_ctx) {
|
|
|
|
- fortune_t * const fortune = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune));
|
|
|
|
- fortune_ctx_t ** const p = h2o_mem_alloc_shared(&req->pool, sizeof(*p), cleanup_request);
|
|
|
|
|
|
+ print_error(template_input->name, lineno, "mustache_compile", error);
|
|
|
|
+}
|
|
|
|
|
|
- *p = fortune_ctx;
|
|
|
|
- memset(fortune, 0, sizeof(*fortune));
|
|
|
|
- fortune->id.base = NEW_FORTUNE_ID;
|
|
|
|
- fortune->id.len = sizeof(NEW_FORTUNE_ID) - 1;
|
|
|
|
- fortune->message.base = NEW_FORTUNE_MESSAGE;
|
|
|
|
- fortune->message.len = sizeof(NEW_FORTUNE_MESSAGE) - 1;
|
|
|
|
- fortune_ctx->generator.proceed = complete_fortunes;
|
|
|
|
- fortune_ctx->num_result = 1;
|
|
|
|
- fortune_ctx->param.command = FORTUNE_TABLE_NAME;
|
|
|
|
- fortune_ctx->param.on_error = on_fortune_error;
|
|
|
|
- fortune_ctx->param.on_result = on_fortune_result;
|
|
|
|
- fortune_ctx->param.on_timeout = on_fortune_timeout;
|
|
|
|
- fortune_ctx->param.flags = IS_PREPARED;
|
|
|
|
- fortune_ctx->req = req;
|
|
|
|
- fortune_ctx->result = &fortune->l;
|
|
|
|
|
|
+void cleanup_fortunes_handler(global_data_t *global_data)
|
|
|
|
+{
|
|
|
|
+ if (global_data->request_handler_data.fortunes_template) {
|
|
|
|
+ mustache_api_t api = {.freedata = NULL};
|
|
|
|
|
|
- if (execute_query(ctx, &fortune_ctx->param)) {
|
|
|
|
- fortune_ctx->cleanup = true;
|
|
|
|
- send_service_unavailable_error(DB_REQ_ERROR, req);
|
|
|
|
|
|
+ mustache_free(&api, global_data->request_handler_data.fortunes_template);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void initialize_fortunes_handler(const config_t *config,
|
|
|
|
+ global_data_t *global_data,
|
|
|
|
+ h2o_hostconf_t *hostconf,
|
|
|
|
+ h2o_access_log_filehandle_t *log_handle)
|
|
|
|
+{
|
|
|
|
+ mustache_template_t *template = NULL;
|
|
|
|
+ const size_t template_path_prefix_len = strlen(config->template_path);
|
|
|
|
+ char path[template_path_prefix_len + sizeof(TEMPLATE_PATH_SUFFIX)];
|
|
|
|
+
|
|
|
|
+ memcpy(path, config->template_path, template_path_prefix_len);
|
|
|
|
+ memcpy(path + template_path_prefix_len, TEMPLATE_PATH_SUFFIX, sizeof(TEMPLATE_PATH_SUFFIX));
|
|
|
|
+
|
|
|
|
+ template_input_t template_input = {.input = fopen(path, "rb"), .name = path};
|
|
|
|
+
|
|
|
|
+ if (template_input.input) {
|
|
|
|
+ mustache_api_t api = {.error = template_error,
|
|
|
|
+ .read = read_template,
|
|
|
|
+ .sectget = prerender_section,
|
|
|
|
+ .varget = prerender_variable};
|
|
|
|
+ bool in_section = false;
|
|
|
|
+
|
|
|
|
+ template = mustache_compile(&api, &template_input);
|
|
|
|
+
|
|
|
|
+ if (template && !mustache_prerender(&api, &in_section, template)) {
|
|
|
|
+ mustache_free(&api, template);
|
|
|
|
+ template = NULL;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ fclose(template_input.input);
|
|
}
|
|
}
|
|
else
|
|
else
|
|
- send_error(INTERNAL_SERVER_ERROR, REQ_ERROR, req);
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
|
|
+ STANDARD_ERROR("fopen");
|
|
|
|
+
|
|
|
|
+ if (template) {
|
|
|
|
+ global_data->request_handler_data.fortunes_template = template;
|
|
|
|
+ add_prepared_statement(FORTUNE_TABLE_NAME,
|
|
|
|
+ "SELECT * FROM " FORTUNE_TABLE_NAME ";",
|
|
|
|
+ &global_data->prepared_statements);
|
|
|
|
+ register_request_handler("/fortunes", fortunes, hostconf, log_handle);
|
|
|
|
+ }
|
|
}
|
|
}
|