API support
authorJeroen van der Heijden <jeroen@transceptor.technology>
Mon, 13 Jan 2020 15:45:13 +0000 (16:45 +0100)
committerJeroen van der Heijden <jeroen@transceptor.technology>
Mon, 13 Jan 2020 15:45:13 +0000 (16:45 +0100)
12 files changed:
include/qpack/qpack.h
include/qpjson/qpjson.h
include/siri/api.h
itest/test_http_api.py
siridb.conf
src/qpjson/qpjson.c
src/siri/api.c
src/siri/cfg/cfg.c
src/siri/db/insert.c
src/siri/db/users.c
src/siri/health.c
src/siri/net/pkg.c

index bb5168b682d1af1d3d62a1e03118ba6cf1fccf04..3c8e731f1a9a93767d2291891de1c28f35e45545 100644 (file)
@@ -133,7 +133,7 @@ void qp_print(unsigned char * pt, size_t len);
 
 /* Shortcut to print an unpacker object */
 #define qp_unpacker_print(unpacker) \
-    qp_print(unpacker->pt, unpacker->end - unpacker->pt)
+    qp_print((unpacker)->pt, (unpacker)->end - (unpacker)->pt)
 
 /* Test functions */
 static inline int qp_is_array(qp_types_t tp)
index 629e05e090cf9d67d4120620ed10be1a23a04d7f..dee2e915141942caee4cb408ca0230236ffa1960 100644 (file)
 
 enum
 {
-    /* flags map to the API flags */
-    QPJSON_FLAG_BEAUTIFY        =1<<2,
-    QPJSON_FLAG_VALIDATE_UTF8   =1<<3,
+    QPJSON_FLAG_BEAUTIFY        =1<<0,
+    QPJSON_FLAG_VALIDATE_UTF8   =1<<1,
 };
 
-
 yajl_gen_status qpjson_qp_to_json(
         void * src,
         size_t src_n,
@@ -29,5 +27,4 @@ yajl_status qpjson_json_to_qp(
         char ** dst,
         size_t * dst_n);
 
-
 #endif  /* QPJSON_H_ */
index 09551e1f2cb13a31e18495b8bc8d474a22e54099..2ea8ae0d92aa4e5fe923506a7d1732498a754325 100644 (file)
 
 typedef enum
 {
-    SIRIDB_API_CT_TEXT,
-    SIRIDB_API_CT_JSON,
-} siridb_api_content_t;
+    SIRI_API_CT_TEXT,
+    SIRI_API_CT_JSON,
+    SIRI_API_CT_QPACK,
+} siri_api_content_t;
+
+typedef enum
+{
+    SIRI_API_RT_NONE,
+    SIRI_API_RT_QUERY,
+    SIRI_API_RT_INSERT,
+} siri_api_req_t;
+
+typedef enum
+{
+    E200_OK,
+    E400_BAD_REQUEST,
+    E401_UNAUTHORIZED,
+    E403_FORBIDDEN,
+    E404_NOT_FOUND,
+    E405_METHOD_NOT_ALLOWED,
+    E415_UNSUPPORTED_MEDIA_TYPE,
+    E422_UNPROCESSABLE_ENTITY,
+    E500_INTERNAL_SERVER_ERROR,
+    E503_SERVICE_UNAVAILABLE
+} siri_api_header_t;
 
 typedef struct siri_api_request_s siri_api_request_t;
 
 typedef int (*on_state_cb_t)(siri_api_request_t * ar, const char * at, size_t n);
 
 int siri_api_init(void);
-int siri_api_send(siri_api_request_t * ar, unsigned char * src, size_t n);
+int siri_api_send(
+        siri_api_request_t * ar,
+        siri_api_header_t ht,
+        unsigned char * src,
+        size_t n);
 
 struct siri_api_request_s
 {
@@ -32,7 +58,8 @@ struct siri_api_request_s
     size_t len;
     size_t size;
     uv_stream_t * stream;
-    siridb_api_content_t content_type;
+    siri_api_content_t content_type;
+    siri_api_req_t request_type;
     http_parser parser;
     uv_write_t req;
 };
index dfe40749a7149849c11868be4a658fb0d4ce2b54..7f8f62b4958b5cc05e49e158068944a3ee271ac2 100644 (file)
@@ -1,8 +1,11 @@
 import requests
 import json
+from testing import gen_points
+
+TIME_PRECISION = 's'
 
 data = {
-    'q': 'select * from *'
+    'q': 'select * from "aggr"',
 }
 
 x = requests.post(
@@ -12,5 +15,33 @@ x = requests.post(
     headers={'Content-Type': 'application/json'}
 )
 
+print(x.status_code)
+
+if x.status_code == 200:
+    print(x.json())
+else:
+    print(x.text)
+
+series_float = gen_points(
+    tp=float, n=10000, time_precision=TIME_PRECISION, ts_gap='5m')
+
+series_int = gen_points(
+    tp=int, n=10000, time_precision=TIME_PRECISION, ts_gap='5m')
+
+data = {
+    'my_float': series_float,
+    'my_int': series_int
+}
+
+x = requests.post(
+    f'http://localhost:9020/insert/dbtest',
+    data=json.dumps(data),
+    auth=('iris', 'siri'),
+    headers={'Content-Type': 'application/json'}
+)
 
-print(x.content)
\ No newline at end of file
+print(x.status_code)
+if x.status_code == 200:
+    print(x.json())
+else:
+    print(x.text)
index a88cf5c11c9dcf7079f990c65f8d1c94af2d44b5..4d08f8b4171cca84454455098802b3ba44619066 100644 (file)
@@ -95,3 +95,10 @@ pipe_client_name = siridb_client.sock
 #
 #http_status_port = 8080
 http_status_port = 0
+
+#
+# When the HTTP API port is not set (or 0), the API service will not start. 
+# Otherwise the HTTP POST requests can be user to insert or query data points.
+#
+#http_api_port = 0
+http_api_port = 9020
\ No newline at end of file
index da854953e47ae6cb04c1da69d06298d9c51eb736..26d9ffd04e925f132ee13c4af1d54fe1b789080e 100644 (file)
@@ -4,6 +4,7 @@
 #include <assert.h>
 #include <qpjson/qpjson.h>
 #include <qpack/qpack.h>
+#include <logger/logger.h>
 
 static yajl_gen_status qp__to_json(yajl_gen g, qp_unpacker_t * up, qp_obj_t * obj)
 {
@@ -113,7 +114,6 @@ static yajl_gen_status qp__to_json(yajl_gen g, qp_unpacker_t * up, qp_obj_t * ob
     return yajl_gen_in_error_state;
 }
 
-
 yajl_gen_status qpjson_qp_to_json(
         void * src,
         size_t src_n,
@@ -126,7 +126,13 @@ yajl_gen_status qpjson_qp_to_json(
     yajl_gen g;
     yajl_status stat;
 
-    assert (src_n);
+    if (src_n == 0)
+    {
+        *dst_n = 0;
+        *dst = NULL;
+        return yajl_gen_status_ok;
+    }
+
     qp_unpacker_init(&up, src, src_n);
 
     g = yajl_gen_alloc(NULL);
@@ -155,6 +161,91 @@ yajl_gen_status qpjson_qp_to_json(
     return stat;
 }
 
+static int reformat_null(void * ctx)
+{
+    qp_packer_t * pk = (qp_packer_t *) ctx;
+    return 0 == qp_add_null(pk);
+}
+
+static int reformat_boolean(void * ctx, int boolean)
+{
+    qp_packer_t * pk = (qp_packer_t *) ctx;
+    return 0 == (boolean ? qp_add_true(pk) : qp_add_false(pk));
+}
+
+static int reformat_integer(void * ctx, long long i)
+{
+    qp_packer_t * pk = (qp_packer_t *) ctx;
+    return 0 == qp_add_int64(pk, i);
+}
+
+static int reformat_double(void * ctx, double d)
+{
+    qp_packer_t * pk = (qp_packer_t *) ctx;
+    return 0 == qp_add_double(pk, d);
+}
+
+static int reformat_string(void * ctx, const unsigned char * s, size_t n)
+{
+    qp_packer_t * pk = (qp_packer_t *) ctx;
+    return 0 == qp_add_raw(pk, s, n);
+}
+
+static int reformat_map_key(void * ctx, const unsigned char * s, size_t n)
+{
+    qp_packer_t * pk = (qp_packer_t *) ctx;
+    return 0 == qp_add_raw(pk, s, n);
+}
+
+static int reformat_start_map(void * ctx)
+{
+    qp_packer_t * pk = (qp_packer_t *) ctx;
+    return 0 == qp_add_type(pk, QP_MAP_OPEN);
+}
+
+
+static int reformat_end_map(void * ctx)
+{
+    qp_packer_t * pk = (qp_packer_t *) ctx;
+    return 0 == qp_add_type(pk, QP_MAP_CLOSE);
+}
+
+static int reformat_start_array(void * ctx)
+{
+    qp_packer_t * pk = (qp_packer_t *) ctx;
+    return 0 == qp_add_type(pk, QP_ARRAY_OPEN);
+}
+
+static int reformat_end_array(void * ctx)
+{
+    qp_packer_t * pk = (qp_packer_t *) ctx;
+    return 0 == qp_add_type(pk, QP_ARRAY_CLOSE);
+}
+
+static void take_buffer(
+        qp_packer_t * pk,
+        char ** dst,
+        size_t * dst_n)
+{
+    *dst = (char *) pk->buffer;
+    *dst_n = pk->len;
+    pk->buffer = NULL;
+    pk->buffer_size = 0;
+}
+
+static yajl_callbacks qpjson__callbacks = {
+    reformat_null,
+    reformat_boolean,
+    reformat_integer,
+    reformat_double,
+    NULL,
+    reformat_string,
+    reformat_start_map,
+    reformat_map_key,
+    reformat_end_map,
+    reformat_start_array,
+    reformat_end_array
+};
 
 yajl_status qpjson_json_to_qp(
         const void * src,
@@ -164,19 +255,24 @@ yajl_status qpjson_json_to_qp(
 {
     yajl_handle hand;
     yajl_status stat = yajl_status_error;
-    qp_packer_t * packer = qp_packer_new(src_n);
-    if (!packer)
+    qp_packer_t * pk = qp_packer_new(src_n);
+
+    if (pk == NULL)
         return stat;
 
-//    hand = yajl_alloc(&callbacks, NULL, c);
-//    if (!hand)
-//        goto fail1;
-//
-//    stat = yajl_parse(hand, src, src_n);
-//    if (stat == yajl_status_ok)
-//        take_buffer(&buffer, dst, dst_n);
-//    yajl_free(hand);
 
-    qp_packer_free(packer);
+    hand = yajl_alloc(&qpjson__callbacks, NULL, pk);
+    if (!hand)
+        goto fail1;
+
+    stat = yajl_parse(hand, src, src_n);
+
+    if (stat == yajl_status_ok)
+        take_buffer(pk, dst, dst_n);
+
+    yajl_free(hand);
+
+fail1:
+    qp_packer_free(pk);
     return stat;
 }
index 58e9523ebfd28f40ff6b919065be79349ff40aaa..d2944a07a9f6769e1c695decdb3a0e1be446f2a4 100644 (file)
@@ -1,65 +1,68 @@
 /*
  * api.c
  */
+#include <string.h>
 #include <siri/api.h>
 #include <assert.h>
+#include <math.h>
 #include <siri/siri.h>
 #include <siri/db/users.h>
 #include <qpjson/qpjson.h>
 #include <siri/db/query.h>
+#include <siri/db/insert.h>
 
 #define API__HEADER_MAX_SZ 256
-#define CONTENT_TYPE_JSON "application/json"
 
 static uv_tcp_t api__uv_server;
 static http_parser_settings api__settings;
 
+typedef struct
+{
+    char * query;
+    size_t query_n;
+    double factor;
+} api__query_t;
+
 #define API__ICMP_WITH(__s, __n, __w) \
-    __n == strlen(__w) && strncasecmp(__s, __w, __n) == 0
+    (__n == strlen(__w) && strncasecmp(__s, __w, __n) == 0)
 
-static const char api__content_type[2][20] = {
+static const char api__content_type[3][20] = {
         "text/plain",
         "application/json",
+        "application/qpack",
 };
 
-static const char api__html_header[8][32] = {
+static const char api__html_header[10][32] = {
         "200 OK",
         "400 Bad Request",
         "401 Unauthorized",
         "403 Forbidden",
         "404 Not Found",
+        "405 Method Not Allowed",
         "415 Unsupported Media Type",
+        "422 Unprocessable Entity",
         "500 Internal Server Error",
         "503 Service Unavailable",
 };
 
-static const char api__default_body[8][30] = {
+static const char api__default_body[10][30] = {
         "OK\r\n",
         "BAD REQUEST\r\n",
         "UNAUTHORIZED\r\n",
         "FORBIDDEN\r\n",
         "NOT FOUND\r\n",
+        "METHOD NOT ALLOWED\r\n",
         "UNSUPPORTED MEDIA TYPE\r\n",
+        "UNPROCESSABLE ENTITY\r\n",
         "INTERNAL SERVER ERROR\r\n",
         "SERVICE UNAVAILABLE\r\n",
 };
 
-typedef enum
-{
-    E200_OK,
-    E400_BAD_REQUEST,
-    E401_UNAUTHORIZED,
-    E403_FORBIDDEN,
-    E404_NOT_FOUND,
-    E415_UNSUPPORTED_MEDIA_TYPE,
-    E500_INTERNAL_SERVER_ERROR,
-    E503_SERVICE_UNAVAILABLE
-} api__header_t;
 
 inline static int api__header(
         char * ptr,
-        const api__header_t ht,
-        const siridb_api_content_t ct,
+        const siri_api_header_t ht,
+        const siri_api_content_t ct,
         size_t content_length)
 {
     int len = sprintf(
@@ -74,7 +77,7 @@ inline static int api__header(
     return len;
 }
 
-static inline _Bool api__starts_with(
+static inline _Bool api__istarts_with(
         const char ** str,
         size_t * strn,
         const char * with,
@@ -89,6 +92,21 @@ static inline _Bool api__starts_with(
     return true;
 }
 
+static inline _Bool api__starts_with(
+        const char ** str,
+        size_t * strn,
+        const char * with,
+        size_t withn)
+{
+    if (*strn < withn || strncmp(*str, with, withn))
+    {
+        return false;
+    }
+    *str += withn;
+    *strn -= withn;
+    return true;
+}
+
 static void api__alloc_cb(
         uv_handle_t * UNUSED_handle __attribute__((unused)),
         size_t UNUSED_sugsz __attribute__((unused)),
@@ -147,24 +165,45 @@ static int api__headers_complete_cb(http_parser * parser)
     assert (!ar->buf);
 
     ar->buf = malloc(parser->content_length);
-    if (ar->len)
+    if (ar->buf)
     {
         ar->len = parser->content_length;
     }
     return 0;
 }
 
+static void api__get_siridb(siri_api_request_t * ar, const char * at, size_t n)
+{
+    const char * pt = at;
+    size_t nn = 0;
+
+    while (n && *pt != '?')
+    {
+        ++pt;
+        --n;
+        ++nn;
+    }
+
+    ar->siridb = siridb_getn(siri.siridb_list, at, nn);
+    if (ar->siridb)
+    {
+        siridb_incref(ar->siridb);
+    }
+}
+
 static int api__url_cb(http_parser * parser, const char * at, size_t n)
 {
     siri_api_request_t * ar = parser->data;
 
     if (api__starts_with(&at, &n, "/query/", strlen("/query/")))
     {
-        ar->siridb = siridb_getn(siri.siridb_list, at, n);
-        if (ar->siridb)
-        {
-            siridb_incref(ar->siridb);
-        }
+        ar->request_type = SIRI_API_RT_QUERY;
+        api__get_siridb(ar, at, n);
+    }
+    else if (api__starts_with(&at, &n, "/insert/", strlen("/insert/")))
+    {
+        ar->request_type = SIRI_API_RT_INSERT;
+        api__get_siridb(ar, at, n);
     }
 
     return 0;
@@ -228,15 +267,17 @@ static void api__connection_cb(uv_stream_t * server, int status)
 
 static int api__on_content_type(siri_api_request_t * ar, const char * at, size_t n)
 {
-    if (API__ICMP_WITH(at, n, CONTENT_TYPE_JSON))
+    if (API__ICMP_WITH(at, n, api__content_type[SIRI_API_CT_JSON]) ||
+        API__ICMP_WITH(at, n, "text/json"))
     {
-        ar->content_type = SIRIDB_API_CT_JSON;
+        ar->content_type = SIRI_API_CT_JSON;
         return 0;
     }
 
-    if (API__ICMP_WITH(at, n, "text/json"))
+    if (API__ICMP_WITH(at, n, api__content_type[SIRI_API_CT_QPACK]) ||
+        API__ICMP_WITH(at, n, "application/x-qpack"))
     {
-        ar->content_type = SIRIDB_API_CT_JSON;
+        ar->content_type = SIRI_API_CT_QPACK;
         return 0;
     }
 
@@ -247,15 +288,17 @@ static int api__on_content_type(siri_api_request_t * ar, const char * at, size_t
 
 static int api__on_authorization(siri_api_request_t * ar, const char * at, size_t n)
 {
-    if (api__starts_with(&at, &n, "token ", strlen("token ")))
+    if (api__istarts_with(&at, &n, "bearer ", strlen("bearer ")))
     {
         log_debug("token authorization is not supported yet");
     }
 
-    if (api__starts_with(&at, &n, "basic ", strlen("basic ")))
+    if (api__istarts_with(&at, &n, "basic ", strlen("basic ")))
     {
         siridb_user_t * user;
-        user = siridb_users_get_user_from_basic(ar->siridb, at, n);
+        user = ar->siridb
+                ? siridb_users_get_user_from_basic(ar->siridb, at, n)
+                : NULL;
 
         if (user)
         {
@@ -311,7 +354,9 @@ static void api__write_cb(uv_write_t * req, int status)
     sirinet_stream_decref((siri_api_request_t *) req->handle->data);
 }
 
-static int api__plain_response(siri_api_request_t * ar, const api__header_t ht)
+static int api__plain_response(
+        siri_api_request_t * ar,
+        const siri_api_header_t ht)
 {
     const char * body = api__default_body[ht];
     char header[API__HEADER_MAX_SZ];
@@ -319,7 +364,7 @@ static int api__plain_response(siri_api_request_t * ar, const api__header_t ht)
     int header_size;
 
     body_size = strlen(body);
-    header_size = api__header(header, ht, SIRIDB_API_CT_TEXT, body_size);
+    header_size = api__header(header, ht, SIRI_API_CT_TEXT, body_size);
 
     if (header_size > 0)
     {
@@ -334,22 +379,176 @@ static int api__plain_response(siri_api_request_t * ar, const api__header_t ht)
     return -1;
 }
 
-static int api__query(siri_api_request_t * ar)
+static int api__query(siri_api_request_t * ar, api__query_t * q)
 {
-    const char q[100] = "select * from 'aggr'";
+    sirinet_stream_incref(ar);
 
     siridb_query_run(
                     0,
                     (sirinet_stream_t *) ar,
-                    q, strlen(q),
-                    0.0,
+                    q->query,
+                    q->query_n,
+                    q->factor,
                     SIRIDB_QUERY_FLAG_MASTER);
     return 0;
 }
 
-static int api__message_complete_cb(http_parser * parser)
+static int api__read_factor(
+        siridb_timep_t precision,
+        qp_unpacker_t * up,
+        api__query_t * q)
 {
-    siri_api_request_t * ar = parser->data;
+    qp_obj_t obj;
+
+    if (!qp_is_raw(qp_next(up, &obj)))
+        return -1;
+
+     if (obj.len == 1 && obj.via.raw[0] == 's')
+     {
+         q->factor = pow(1000.0, SIRIDB_TIME_SECONDS - precision);
+         return 0;
+     }
+
+     if (obj.len == 2 && obj.via.raw[1] == 's')
+     {
+         switch (obj.via.raw[0])
+         {
+         case 'm':
+             q->factor = pow(1000.0, SIRIDB_TIME_MILLISECONDS - precision);
+             return 0;
+         case 'u':
+             q->factor = pow(1000.0, SIRIDB_TIME_MICROSECONDS - precision);
+             return 0;
+         case 'n':
+             q->factor = pow(1000.0, SIRIDB_TIME_NANOSECONDS - precision);
+             return 0;
+         }
+     }
+     return -1;
+}
+
+static int api__query_from_qp(api__query_t * q, siri_api_request_t * ar)
+{
+    qp_obj_t obj;
+    qp_unpacker_t up;
+    qp_unpacker_init(&up, (unsigned char *) ar->buf, ar->len);
+
+    q->factor = 0.0;
+    q->query = NULL;
+
+    if (!qp_is_map(qp_next(&up, &obj)))
+    {
+        return api__plain_response(ar, E400_BAD_REQUEST);
+    }
+
+    while (qp_next(&up, &obj) && !qp_is_close(obj.tp))
+    {
+        if (!qp_is_raw(obj.tp) || obj.len != 1)
+        {
+            return api__plain_response(ar, E400_BAD_REQUEST);
+        }
+
+        switch(*obj.via.str)
+        {
+        case 't':
+            if (api__read_factor(ar->siridb->time->precision, &up, q))
+            {
+                return api__plain_response(ar, E400_BAD_REQUEST);
+            }
+            break;
+        case 'q':
+            if (!qp_is_raw(qp_next(&up, &obj)))
+            {
+                return api__plain_response(ar, E400_BAD_REQUEST);
+            }
+            q->query = obj.via.str;
+            q->query_n = obj.len;
+            break;
+        default:
+            return api__plain_response(ar, E400_BAD_REQUEST);
+        }
+    }
+
+    return q->query == NULL
+            ? api__plain_response(ar, E400_BAD_REQUEST)
+            : api__query(ar, q);
+}
+
+static int api__insert_from_qp(siri_api_request_t * ar)
+{
+    qp_unpacker_t unpacker;
+    qp_unpacker_init(&unpacker, (unsigned char *) ar->buf, ar->len);
+
+    siridb_insert_t * insert = siridb_insert_new(
+            ar->siridb,
+            0,
+            (sirinet_stream_t *) ar);
+
+    if (insert == NULL)
+    {
+        return api__plain_response(ar, E500_INTERNAL_SERVER_ERROR);
+    }
+
+    ssize_t rc = siridb_insert_assign_pools(
+            ar->siridb,
+            &unpacker,
+            insert->packer);
+
+    switch ((siridb_insert_err_t) rc)
+    {
+    case ERR_EXPECTING_ARRAY:
+    case ERR_EXPECTING_SERIES_NAME:
+    case ERR_EXPECTING_MAP_OR_ARRAY:
+    case ERR_EXPECTING_INTEGER_TS:
+    case ERR_TIMESTAMP_OUT_OF_RANGE:
+    case ERR_UNSUPPORTED_VALUE:
+    case ERR_EXPECTING_AT_LEAST_ONE_POINT:
+    case ERR_EXPECTING_NAME_AND_POINTS:
+    case ERR_INCOMPATIBLE_SERVER_VERSION:
+    case ERR_MEM_ALLOC:
+        {
+            /* something went wrong, get correct err message */
+            const char * err_msg = siridb_insert_err_msg(rc);
+
+            log_error("Insert error: '%s' at position %lu",
+                    err_msg, unpacker.pt -  (unsigned char *) ar->buf);
+
+            /* create and send package */
+            sirinet_pkg_t * package = sirinet_pkg_err(
+                    0,
+                    strlen(err_msg),
+                    CPROTO_ERR_INSERT,
+                    err_msg);
+
+            if (package != NULL)
+            {
+                /* ignore result code, signal can be raised */
+                sirinet_pkg_send((sirinet_stream_t *) ar, package);
+            }
+        }
+
+        /* error, free insert */
+        siridb_insert_free(insert);
+        break;
+
+    default:
+        if (siridb_insert_points_to_pools(insert, (size_t) rc))
+        {
+            siridb_insert_free(insert);  /* signal is raised */
+        }
+        else
+        {
+            /* extra increment for the insert task */
+            sirinet_stream_incref(ar);
+        }
+        break;
+    }
+    return 0;
+}
+
+static int api__insert_cb(siri_api_request_t * ar)
+{
+    assert (ar->request_type == SIRI_API_RT_INSERT);
 
     if (!ar->siridb)
         return api__plain_response(ar, E404_NOT_FOUND);
@@ -357,13 +556,89 @@ static int api__message_complete_cb(http_parser * parser)
     if (!ar->origin)
         return api__plain_response(ar, E401_UNAUTHORIZED);
 
+
+    if (!(((siridb_user_t *) ar->origin)->access_bit & SIRIDB_ACCESS_INSERT))
+        return api__plain_response(ar, E403_FORBIDDEN);
+
+    if ((
+            ar->siridb->server->flags != SERVER_FLAG_RUNNING &&
+            ar->siridb->server->flags != SERVER_FLAG_RUNNING + SERVER_FLAG_REINDEXING
+        ) ||
+        !siridb_pools_accessible(ar->siridb))
+        return api__plain_response(ar, E503_SERVICE_UNAVAILABLE);
+
     switch (ar->content_type)
     {
-    case SIRIDB_API_CT_TEXT:
+    case SIRI_API_CT_TEXT:
         /* Or, shall we allow text and return we some sort of CSV format? */
-        return api__plain_response(ar, E400_BAD_REQUEST);
-    case SIRIDB_API_CT_JSON:
-        return api__query(ar);
+        break;
+    case SIRI_API_CT_JSON:
+    {
+        char * dst;
+        size_t dst_n;
+        if (qpjson_json_to_qp(ar->buf, ar->len, &dst, &dst_n))
+            return api__plain_response(ar, E400_BAD_REQUEST);
+        free(ar->buf);
+        ar->buf = dst;
+        ar->len = dst_n;
+    }
+    /* fall through */
+    case SIRI_API_CT_QPACK:
+        return api__insert_from_qp(ar);
+    }
+
+    return api__plain_response(ar, E415_UNSUPPORTED_MEDIA_TYPE);
+}
+
+static int api__query_cb(siri_api_request_t * ar)
+{
+    api__query_t q;
+    assert (ar->request_type == SIRI_API_RT_QUERY);
+
+    if (!ar->siridb)
+        return api__plain_response(ar, E404_NOT_FOUND);
+
+    if (!ar->origin)
+        return api__plain_response(ar, E401_UNAUTHORIZED);
+
+    switch (ar->content_type)
+    {
+    case SIRI_API_CT_TEXT:
+        /* Or, shall we allow text and return we some sort of CSV format? */
+        break;
+    case SIRI_API_CT_JSON:
+    {
+        char * dst;
+        size_t dst_n;
+        if (qpjson_json_to_qp(ar->buf, ar->len, &dst, &dst_n))
+            return api__plain_response(ar, E400_BAD_REQUEST);
+        free(ar->buf);
+        ar->buf = dst;
+        ar->len = dst_n;
+    }
+    /* fall through */
+    case SIRI_API_CT_QPACK:
+        return api__query_from_qp(&q, ar);
+    }
+
+    return api__plain_response(ar, E415_UNSUPPORTED_MEDIA_TYPE);
+}
+
+static int api__message_complete_cb(http_parser * parser)
+{
+    siri_api_request_t * ar = parser->data;
+
+    if (parser->method != HTTP_POST)
+        return api__plain_response(ar, E405_METHOD_NOT_ALLOWED);
+
+    switch(ar->request_type)
+    {
+    case SIRI_API_RT_NONE:
+        return api__plain_response(ar, E404_NOT_FOUND);
+    case SIRI_API_RT_QUERY:
+        return api__query_cb(ar);
+    case SIRI_API_RT_INSERT:
+        return api__insert_cb(ar);
     }
 
     return api__plain_response(ar, E500_INTERNAL_SERVER_ERROR);
@@ -387,12 +662,16 @@ static void api__write_free_cb(uv_write_t * req, int status)
     api__write_cb(req, status);
 }
 
-static int api__close_resp(siri_api_request_t * ar, void * data, size_t size)
+static int api__close_resp(
+        siri_api_request_t * ar,
+        const siri_api_header_t ht,
+        void * data,
+        size_t size)
 {
     char header[API__HEADER_MAX_SZ];
     int header_size = 0;
 
-    header_size = api__header(header, E200_OK, ar->content_type, size);
+    header_size = api__header(header, ht, ar->content_type, size);
 
     uv_buf_t uvbufs[2] = {
             uv_buf_init(header, (unsigned int) header_size),
@@ -447,10 +726,64 @@ int siri_api_init(void)
     return 0;
 }
 
-int siri_api_send(siri_api_request_t * ar, unsigned char * src, size_t n)
+#define API__DUP(__data, __n, __s) \
+    do { \
+        __n = strlen("{\"error_msg\":\""__s"\"}"); \
+        __data = strndup("{\"error_msg\":\""__s"\"}", __n); \
+        if (!__data) __n = 0; \
+    } while(0)
+
+static void api__yajl_parse_error(
+        char ** str,
+        size_t * size,
+        siri_api_header_t * ht,
+        yajl_gen_status stat)
+{
+    switch (stat)
+    {
+    case yajl_gen_status_ok:
+        return;
+    case yajl_gen_keys_must_be_strings:
+        *ht = E400_BAD_REQUEST;
+        API__DUP(*str, *size, "JSON keys must be strings");
+        return;
+    case yajl_max_depth_exceeded:
+        *ht = E400_BAD_REQUEST;
+        API__DUP(*str, *size, "JSON max depth exceeded");
+        return;
+    case yajl_gen_in_error_state:
+        *ht = E500_INTERNAL_SERVER_ERROR;
+        API__DUP(*str, *size, "JSON general error");
+        return;
+    case yajl_gen_generation_complete:
+        *ht = E500_INTERNAL_SERVER_ERROR;
+        API__DUP(*str, *size, "JSON completed");
+        return;
+    case yajl_gen_invalid_number:
+        *ht = E400_BAD_REQUEST;
+        API__DUP(*str, *size, "JSON invalid number");
+        return;
+    case yajl_gen_no_buf:
+        *ht = E500_INTERNAL_SERVER_ERROR;
+        API__DUP(*str, *size, "JSON no buffer has been set");
+        return;
+    case yajl_gen_invalid_string:
+        *ht = E400_BAD_REQUEST;
+        API__DUP(*str, *size, "JSON only accepts valid UTF8 strings");
+        return;
+    }
+    *ht = E500_INTERNAL_SERVER_ERROR;
+    API__DUP(*str, *size, "JSON unexpected error");
+}
+
+int siri_api_send(
+        siri_api_request_t * ar,
+        siri_api_header_t ht,
+        unsigned char * src,
+        size_t n)
 {
     unsigned char * data;
-    if (ar->content_type == SIRIDB_API_CT_JSON)
+    if (ar->content_type == SIRI_API_CT_JSON)
     {
         size_t tmp_sz;
         yajl_gen_status stat = qpjson_qp_to_json(
@@ -460,22 +793,24 @@ int siri_api_send(siri_api_request_t * ar, unsigned char * src, size_t n)
                 &tmp_sz,
                 0);
         if (stat)
-        {
-            // api__set_yajl_gen_status_error(&ar->e, stat);
-            // return ti_api_close_with_err(ar, &ar->e);
-            // TODO : return error
-            LOGC("HERE: %d", stat);
-        }
+            api__yajl_parse_error((char **) &data, &tmp_sz, &ht, stat);
+
         n = tmp_sz;
     }
     else
     {
+        assert (ar->content_type == SIRI_API_CT_QPACK);
         data = malloc(n);
         if (data == NULL)
         {
-            // TODO : return error
+            ht = E500_INTERNAL_SERVER_ERROR;
+            n = 0;
+        }
+        else
+        {
+            memcpy(data, src, n);
         }
-        memcpy(data, src, n);
     }
-    return api__close_resp(ar, data, n);
+
+    return api__close_resp(ar, ht, data, n);
 }
index 8fa1861d5b24823d33228a7af52c168c14c03380..3e243b074f19eb60bd1d30fe0ebfce54522aabc3 100644 (file)
@@ -123,6 +123,15 @@ void siri_cfg_init(siri_t * siri)
             &tmp);
     siri_cfg.http_status_port = (uint16_t) tmp;
 
+    tmp = siri_cfg.http_api_port;
+    SIRI_CFG_read_uint(
+            cfgparser,
+            "http_api_port",
+            0,
+            65535,
+            &tmp);
+    siri_cfg.http_status_port = (uint16_t) tmp;
+
     SIRI_CFG_read_default_db_path(cfgparser);
     SIRI_CFG_read_max_open_files(cfgparser);
     SIRI_CFG_read_ip_support(cfgparser);
index 198bcaa6fd8e9642292bc37a662bcf440b1267f8..f2faf9e549bfa8e7dceb1bed84300e2c0293e268 100644 (file)
@@ -1404,12 +1404,14 @@ static int INSERT_read_points(
 
     qp_add_type(packer, QP_ARRAY_OPEN);
 
-    if ((tp = qp_next(unpacker, NULL)) != QP_ARRAY2)
+    if ((tp = qp_next(unpacker, NULL)) != QP_ARRAY2 && tp != QP_ARRAY_OPEN)
     {
         return ERR_EXPECTING_AT_LEAST_ONE_POINT;
     }
 
-    for (; tp == QP_ARRAY2; (*count)++, tp = qp_next(unpacker, qp_obj))
+    for (;
+            tp == QP_ARRAY2 || tp == QP_ARRAY_OPEN;
+            (*count)++, tp = qp_next(unpacker, qp_obj))
     {
         qp_add_type(packer, QP_ARRAY2);
 
@@ -1446,6 +1448,9 @@ static int INSERT_read_points(
         default:
             return ERR_UNSUPPORTED_VALUE;
         }
+
+        if (tp == QP_ARRAY_OPEN && qp_next(unpacker, NULL) != QP_ARRAY_CLOSE)
+            break;
     }
 
     if (tp == QP_ARRAY_CLOSE)
index 1cc8ca0e7b853dacb91470f87a12d11f4c94930d..2d4daa788bb823830715fab422e36d538eef3f8a 100644 (file)
@@ -268,8 +268,6 @@ siridb_user_t * siridb_users_get_user_from_basic(
             if (++nn > end)
                 break;
 
-            LOGC("User: %s Pass: %s", b64, b64 + nn);
-
             user = siridb_users_get_user(siridb, b64, b64 + nn);
             break;
         }
index b32744dd738b02cf2159ecd3291354dfafc15e3b..2a9c4eeed092cdab19ed68776b127adab66dae43 100644 (file)
     "\r\n" \
     "NOT FOUND\n"
 
+#define MNA_RESPONSE \
+    "HTTP/1.1 405 Method Not Allowed\r\n" \
+    "Content-Type: text/plain\r\n" \
+    "Content-Length: 19\r\n" \
+    "\r\n" \
+    "METHOD NOT ALLOWED\n"
+
 #define SYNC_RESPONSE \
     "HTTP/1.1 200 OK\r\n" \
     "Content-Type: text/plain\r\n" \
@@ -51,6 +58,7 @@
 static uv_buf_t health__uv_ok_buf;
 static uv_buf_t health__uv_nok_buf;
 static uv_buf_t health__uv_nfound_buf;
+static uv_buf_t health__uv_mna_buf;
 static uv_buf_t health__uv_sync_buf;
 static uv_buf_t health__uv_reidx_buf;
 static uv_buf_t health__uv_bmode_buf;
@@ -203,7 +211,9 @@ static int health__message_complete_cb(http_parser * parser)
 {
     siri_health_request_t * web_request = parser->data;
 
-    if (!web_request->response)
+    if (parser->method != HTTP_GET)
+        web_request->response = &health__uv_mna_buf;
+    else if (!web_request->response)
         web_request->response = &health__uv_nfound_buf;
 
     (void) uv_write(
@@ -275,6 +285,8 @@ int siri_health_init(void)
             uv_buf_init(NOK_RESPONSE, strlen(NOK_RESPONSE));
     health__uv_nfound_buf =
             uv_buf_init(NFOUND_RESPONSE, strlen(NFOUND_RESPONSE));
+    health__uv_mna_buf =
+            uv_buf_init(MNA_RESPONSE, strlen(MNA_RESPONSE));
     health__uv_sync_buf =
             uv_buf_init(SYNC_RESPONSE, strlen(SYNC_RESPONSE));
     health__uv_reidx_buf =
index 103cb6d36483afdf48c1b3d1160fc018533874fa..1d9aa421ba4333e065c496c0256b742301a93c6f 100644 (file)
@@ -7,6 +7,7 @@
 #include <siri/api.h>
 #include <siri/net/pkg.h>
 #include <siri/net/clserver.h>
+#include <siri/net/protocol.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
@@ -127,6 +128,41 @@ sirinet_pkg_t * sirinet_pkg_err(
     return pkg;
 }
 
+static siri_api_header_t pkg__tp_as_ht(uint8_t tp)
+{
+    switch (tp)
+    {
+    /* success */
+    case CPROTO_RES_QUERY:
+    case CPROTO_RES_INSERT:
+    case CPROTO_RES_AUTH_SUCCESS:
+    case CPROTO_RES_ACK:
+    case CPROTO_RES_FILE:
+    case CPROTO_ACK_SERVICE:
+    case CPROTO_ACK_SERVICE_DATA:
+        return E200_OK;
+
+    case CPROTO_ERR_QUERY:
+    case CPROTO_ERR_INSERT:
+    case CPROTO_ERR_SERVICE:
+    case CPROTO_ERR_SERVICE_INVALID_REQUEST:
+        return E400_BAD_REQUEST;
+
+    case CPROTO_ERR_SERVER:
+    case CPROTO_ERR_POOL:
+        return E503_SERVICE_UNAVAILABLE;
+
+    case CPROTO_ERR_USER_ACCESS:
+    case CPROTO_ERR_NOT_AUTHENTICATED:
+        return E403_FORBIDDEN;
+
+    case CPROTO_ERR_AUTH_UNKNOWN_DB:
+    case CPROTO_ERR_AUTH_CREDENTIALS:
+        return E401_UNAUTHORIZED;
+    }
+    return E500_INTERNAL_SERVER_ERROR;
+}
+
 /*
  * Returns 0 if successful or -1 when an error has occurred.
  * (signal is raised in case of an error)
@@ -137,7 +173,11 @@ int sirinet_pkg_send(sirinet_stream_t * client, sirinet_pkg_t * pkg)
 {
     if (client->tp == STREAM_API_CLIENT)
     {
-        siri_api_send((siri_api_request_t *) client, pkg->data, pkg->len);
+        siri_api_send(
+                (siri_api_request_t *) client,
+                pkg__tp_as_ht(pkg->tp),
+                pkg->data,
+                pkg->len);
         free(pkg);
         return 0;
     }