Skip to content

Instantly share code, notes, and snippets.

@co3k
Created September 11, 2010 19:53
Show Gist options
  • Save co3k/575491 to your computer and use it in GitHub Desktop.
Save co3k/575491 to your computer and use it in GitHub Desktop.
/*
INSTALL::
$ apxs2 -c mod_doyagawa.c -ltokyocabinet -ljansson
$ libtool --mode=install cp mod_doyagawa.la $PWD
Restful API Interface::
Get Feed: GET http://example.com/doya
Get Entry: GET http://example.com/doya/{id}
Post Entry: POST http://example.com/doya
Put Entry: [Not supported]
Delete Entry: [Not supported]
JSON Format::
[
{ "x" : 20, "y" : 10, "opacity" : 1.0, "size" : 10, "string" : "doya" },
]
TODO:
* Getting entry outputs multiple formated doya
* Image
* Tumbneil
* So I think find image generator (supported embbedding font)
*/
#include <tcutil.h>
#include <tctdb.h>
#include <jansson.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
static char *doyagawa_get_post_data(request_rec *r)
{
apr_bucket_brigade *bb;
int is_eos = 0;
char *body = "";
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
do
{
apr_bucket *bucket;
apr_status_t rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket = APR_BUCKET_NEXT(bucket))
{
if (APR_BUCKET_IS_EOS(bucket))
{
is_eos = 1;
break;
}
if (bucket->length != 0)
{
const char *data;
apr_size_t len;
rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
body = apr_pstrcat(r->pool, body, data, NULL);
break;
}
}
apr_brigade_cleanup(bb);
}
while (!is_eos);
return body;
}
static int doyagawa_get_doyas(request_rec *r, TCTDB *db)
{
TCXSTR *result;
const char *rbuf;
int i, rsiz;
TDBQRY *q;
TCLIST *s;
TCMAP *cols;
result = tcxstrnew();
tcxstrcat2(result, "[");
q = tctdbqrynew(db);
tctdbqrysetorder(q, "created_at", TDBQOSTRDESC);
tctdbqrysetlimit(q, 10, 0);
s = tctdbqrysearch(q);
for (i = 0; i < tclistnum(s); i++) {
if (i > 0)
{
tcxstrcat2(result, ",");
}
rbuf = tclistval(s, i, &rsiz);
cols = tctdbget(db, rbuf, rsiz);
if (cols) {
tcxstrcat2(result, "[");
tcxstrcat2(result, rbuf);
tcxstrcat2(result, ",");
tcxstrcat2(result, tcmapget2(cols, "json"));
tcxstrcat2(result, "]");
}
tcmapdel(cols);
}
tcxstrcat2(result, "]");
ap_rputs((char *)tcxstrptr(result), r);
tcxstrdel(result);
tclistdel(s);
tctdbqrydel(q);
return 1;
}
static int doyagawa_get_doya(request_rec *r, TCTDB *db, const char *pk)
{
TCXSTR *result;
int i, res;
TCMAP *cols;
res = 1;
result = tcxstrnew();
cols = tctdbget(db, pk, strlen(pk));
if (cols)
{
tcxstrcat2(result, "[");
tcxstrcat2(result, pk);
tcxstrcat2(result, ",");
tcxstrcat2(result, tcmapget2(cols, "json"));
tcxstrcat2(result, "]");
}
tcmapdel(cols);
ap_rputs((char *)tcxstrptr(result), r);
tcxstrdel(result);
return res;
}
static int doyagawa_post_doya(request_rec *r, TCTDB *db)
{
int result = 1, pksiz, i;
char pkbuf[256], ut[256];
const char *rbuf, *str;
TCMAP *cols;
json_t *root;
json_error_t error;
root = json_loads(doyagawa_get_post_data(r), &error);
if (!root || !json_is_array(root))
{
return 0;
}
for (i = 0; i < json_array_size(root); i++)
{
json_t *obj;
obj = json_array_get(root, i);
if (!json_is_object(obj))
{
return 0;
}
const char *key;
json_t *value;
void *iter = json_object_iter(obj);
while (iter)
{
key = json_object_iter_key(iter);
if (0 != strcmp("x", key) && 0 != strcmp("y", key)
&& 0 != strcmp("opacity", key) && 0 != strcmp("string", key)
&& 0 != strcmp("size", key))
{
json_object_del(obj, key);
}
iter = json_object_iter_next(obj, iter);
}
}
str = json_dumps(root, JSON_ENSURE_ASCII);
pksiz = sprintf(pkbuf, "%ld", (long)tctdbgenuid(db));
sprintf(ut, "%d", (int)time(NULL));
cols = tcmapnew3("json", str, "created_at", ut, NULL);
if (tctdbput(db, pkbuf, pksiz, cols))
{
ap_rputs(str, r);
}
else
{
result = 0;
}
tcmapdel(cols);
return result;
}
static void doyagawa_apply_allowed_method_list(ap_method_list_t *l)
{
/* initialize default list */
ap_clear_method_list(l);
ap_method_list_add(l, "GET");
ap_method_list_add(l, "POST");
ap_method_list_add(l, "HEAD");
}
static int doyagawa_handler(request_rec *r)
{
TCTDB *db;
const char *pk;
int result;
if (strcmp(r->handler, "doyagawa"))
{
return DECLINED;
}
r->content_type = "application/json";
db = tctdbnew();
result = OK;
if (!tctdbopen(db, "/tmp/example.tct", TDBOWRITER | TDBOCREAT))
{
return HTTP_INTERNAL_SERVER_ERROR;
}
if (!r->header_only)
{
switch (r->method_number)
{
case M_GET:
if (tcregexmatch(r->uri, "^.*/doya/?$"))
{
if (!doyagawa_get_doyas(r, db))
{
result = HTTP_INTERNAL_SERVER_ERROR;
}
}
else if (pk = tcregexreplace(r->uri, "^.*/doya/([0-9]+)/?$", "\\1"))
{
if (0 == strcmp(pk, r->uri))
{
result = HTTP_NOT_FOUND;
}
else if (!doyagawa_get_doya(r, db, pk))
{
result = HTTP_NOT_FOUND;
}
}
else
{
result = HTTP_NOT_FOUND;
}
break;
case M_POST:
if (!doyagawa_post_doya(r, db))
{
result = HTTP_INTERNAL_SERVER_ERROR;
}
break;
default:
doyagawa_apply_allowed_method_list(r->allowed_methods);
result = HTTP_METHOD_NOT_ALLOWED;
}
}
if (!tctdbclose(db))
{
result = HTTP_INTERNAL_SERVER_ERROR;
}
tctdbdel(db);
return result;
}
static void doyagawa_register_hooks(apr_pool_t *p)
{
ap_hook_handler(doyagawa_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA doyagawa_module =
{
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
NULL, /* table of config file commands */
doyagawa_register_hooks /* register hooks */
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment