From 18f4beb6ab0fedb347768f4092c95b82b5c96670 Mon Sep 17 00:00:00 2001 From: Jeroen van der Heijden Date: Mon, 13 Jan 2020 16:45:13 +0100 Subject: [PATCH] API support --- include/qpack/qpack.h | 2 +- include/qpjson/qpjson.h | 7 +- include/siri/api.h | 37 +++- itest/test_http_api.py | 35 +++- siridb.conf | 7 + src/qpjson/qpjson.c | 122 +++++++++-- src/siri/api.c | 449 +++++++++++++++++++++++++++++++++++----- src/siri/cfg/cfg.c | 9 + src/siri/db/insert.c | 9 +- src/siri/db/users.c | 2 - src/siri/health.c | 14 +- src/siri/net/pkg.c | 42 +++- 12 files changed, 646 insertions(+), 89 deletions(-) diff --git a/include/qpack/qpack.h b/include/qpack/qpack.h index bb5168b6..3c8e731f 100644 --- a/include/qpack/qpack.h +++ b/include/qpack/qpack.h @@ -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) diff --git a/include/qpjson/qpjson.h b/include/qpjson/qpjson.h index 629e05e0..dee2e915 100644 --- a/include/qpjson/qpjson.h +++ b/include/qpjson/qpjson.h @@ -10,12 +10,10 @@ 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_ */ diff --git a/include/siri/api.h b/include/siri/api.h index 09551e1f..2ea8ae0d 100644 --- a/include/siri/api.h +++ b/include/siri/api.h @@ -10,16 +10,42 @@ 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; }; diff --git a/itest/test_http_api.py b/itest/test_http_api.py index dfe40749..7f8f62b4 100644 --- a/itest/test_http_api.py +++ b/itest/test_http_api.py @@ -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) diff --git a/siridb.conf b/siridb.conf index a88cf5c1..4d08f8b4 100644 --- a/siridb.conf +++ b/siridb.conf @@ -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 diff --git a/src/qpjson/qpjson.c b/src/qpjson/qpjson.c index da854953..26d9ffd0 100644 --- a/src/qpjson/qpjson.c +++ b/src/qpjson/qpjson.c @@ -4,6 +4,7 @@ #include #include #include +#include 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; } diff --git a/src/siri/api.c b/src/siri/api.c index 58e9523e..d2944a07 100644 --- a/src/siri/api.c +++ b/src/siri/api.c @@ -1,65 +1,68 @@ /* * api.c */ +#include #include #include +#include #include #include #include #include +#include #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); } diff --git a/src/siri/cfg/cfg.c b/src/siri/cfg/cfg.c index 8fa1861d..3e243b07 100644 --- a/src/siri/cfg/cfg.c +++ b/src/siri/cfg/cfg.c @@ -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); diff --git a/src/siri/db/insert.c b/src/siri/db/insert.c index 198bcaa6..f2faf9e5 100644 --- a/src/siri/db/insert.c +++ b/src/siri/db/insert.c @@ -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) diff --git a/src/siri/db/users.c b/src/siri/db/users.c index 1cc8ca0e..2d4daa78 100644 --- a/src/siri/db/users.c +++ b/src/siri/db/users.c @@ -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; } diff --git a/src/siri/health.c b/src/siri/health.c index b32744dd..2a9c4eee 100644 --- a/src/siri/health.c +++ b/src/siri/health.c @@ -26,6 +26,13 @@ "\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 = diff --git a/src/siri/net/pkg.c b/src/siri/net/pkg.c index 103cb6d3..1d9aa421 100644 --- a/src/siri/net/pkg.c +++ b/src/siri/net/pkg.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -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; } -- 2.30.2