diff --git a/ChangeLog b/ChangeLog index 1ccb84a..59b1131 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ next ===== -* +* Added `japi_set_max_linebuf_size()` to enable parsing of large JSON commands 0.4.0 ===== diff --git a/include/creadline.h b/include/creadline.h index a380d0e..2dbf07a 100644 --- a/include/creadline.h +++ b/include/creadline.h @@ -35,8 +35,7 @@ #ifndef __CREADLINE_H__ #define __CREADLINE_H__ -/*! Override the maximum line size here (default: 64 MiB) */ -//#define CREADLINE_MAX_LINE_SIZE 10*1024*1024 +#include /*! Define creadline's block size. * @@ -79,12 +78,13 @@ typedef struct __creadline_buffer { * \param fd File descriptor * \param dst Pointer to a pointer to the read line * \param buffer creadline buffer for storing remaining characters + * \param max_linebuf_size Maximum line length to parse * * \returns -1 on error, * 0 on EOF or when a zero-length line was read (check dst), * length of the read line otherwise */ -int creadline_r(int fd, void **dst, creadline_buf_t *buffer); +int creadline_r(int fd, void **dst, creadline_buf_t *buffer, size_t max_linebuf_size); /*! * \brief Read a single line from a file descriptor. @@ -96,12 +96,13 @@ int creadline_r(int fd, void **dst, creadline_buf_t *buffer); * * \param fd File descriptor * \param dst Pointer to a pointer to the read line + * \param max_linebuf_size Maximum line length to parse * * \returns -1 on error, * 0 on EOF or when a zero-length line was read (check dst), * length of the read line otherwise */ -int creadline(int fd, void **dst); +int creadline(int fd, void **dst, size_t max_linebuf_size); #ifdef __cplusplus } diff --git a/include/japi.h b/include/japi.h index 4016106..f5be6f3 100644 --- a/include/japi.h +++ b/include/japi.h @@ -53,6 +53,7 @@ typedef struct __japi_context { void *userptr; /*!< Pointer to user data */ uint16_t num_clients; /*!< Number of connected clients */ uint16_t max_clients; /*!< Number of maximal allowed clients */ + size_t max_linebuf_size; pthread_mutex_t lock; /*!< Mutual access lock */ struct __japi_request *requests; /*!< Pointer to the JAPI request list */ struct __japi_pushsrv_context @@ -147,6 +148,21 @@ int japi_register_request(japi_context *ctx, const char *req_name, */ int japi_start_server(japi_context *ctx, const char *port); +/*! + * \brief Define the maximum line size a japi-command can have + * + * Change default value of 64MiB = 64*1024*1024. Some applications need more + * need more capacity to transfer data. The line parser will round down to powers of 2 times + * CREADLINE_BLOCK_SIZE (1*1024, 2*1024, 4*1024, 8*1024, ...). + * + * \param ctx JAPI context + * \param max_linebuf_size_user Maximum line length of Japi-Commands (>1024) + * + * \returns On success, zero is returned. On error, -1 for empty JAPI context and -2 on too small line length, is + * returned. + */ +int japi_set_max_linebuf_size(japi_context *ctx, size_t max_linebuf_size_user); + /*! * \brief Set the number of allowed clients * diff --git a/src/creadline.c b/src/creadline.c index 63b6826..e5c1bc0 100644 --- a/src/creadline.c +++ b/src/creadline.c @@ -40,17 +40,6 @@ #include "creadline.h" -/* Do not change the maximum line size here! Define - * CREADLINE_MAX_LINE_SIZE in the header file to - * overwrite the default value. */ -#ifndef CREADLINE_MAX_LINE_SIZE -/*! Internal definition of the maximum allowed line size. Can be overwritten by - * defining CREADLINE_MAX_LINE_SIZE. */ -#define __MAX_LINEBUF_SIZE__ 64*1024*1024 -#else -#define __MAX_LINEBUF_SIZE__ CREADLINE_MAX_LINE_SIZE -#endif - static int strnpos(const char *s, int c, size_t maxlen) { int pos; @@ -67,7 +56,7 @@ static int strnpos(const char *s, int c, size_t maxlen) return -1; } -int creadline_r(int fd, void **dst, creadline_buf_t *buffer) +int creadline_r(int fd, void **dst, creadline_buf_t *buffer, size_t max_linebuf_size) { char *linebuf; size_t linebuf_size; @@ -102,15 +91,15 @@ int creadline_r(int fd, void **dst, creadline_buf_t *buffer) /* Check if there is enough space left to call read again. If a new * read could write beyond the line buffer it's size is doubled as long - * as __MAX_LINEBUF_SIZE__ is not reached. */ + * as max_linebuf_size is not reached. */ if (linebuf_nbytes + CREADLINE_BLOCK_SIZE > linebuf_size) { char* new_linebuf = NULL; int new_linebuf_size = 2*linebuf_size; - if (new_linebuf_size > __MAX_LINEBUF_SIZE__) { - fprintf(stderr, "ERROR: Maximum line size of %i bytes exceeded!\n", __MAX_LINEBUF_SIZE__); + if (new_linebuf_size > max_linebuf_size) { + fprintf(stderr, "ERROR: Maximum line size of %li bytes exceeded!\n", max_linebuf_size); goto error_free; } @@ -191,7 +180,7 @@ int creadline_r(int fd, void **dst, creadline_buf_t *buffer) return -1; } -int creadline(int fd, void **dst) +int creadline(int fd, void **dst, size_t max_linebuf_size) { static int fd_last = -1; static creadline_buf_t buffer; @@ -202,6 +191,6 @@ int creadline(int fd, void **dst) fd_last = fd; } - return creadline_r(fd, dst, &buffer); + return creadline_r(fd, dst, &buffer, max_linebuf_size); } diff --git a/src/japi.c b/src/japi.c index d4437c2..e046b94 100644 --- a/src/japi.c +++ b/src/japi.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "japi.h" @@ -310,6 +311,7 @@ japi_context *japi_init(void *userptr) ctx->clients = NULL; ctx->num_clients = 0; ctx->max_clients = 0; + ctx->max_linebuf_size = 64 * 1024 * 1024; /* 64MiB maximum line length per default. */ ctx->include_args_in_response = false; ctx->shutdown = false; @@ -337,6 +339,24 @@ japi_context *japi_init(void *userptr) return ctx; } +int japi_set_max_linebuf_size(japi_context *ctx, size_t max_linebuf_size_user) +{ + /* Error handling */ + if (ctx == NULL) { + fprintf(stderr, "ERROR: JAPI context is NULL.\n"); + return -1; + } + + if (max_linebuf_size_user < CREADLINE_BLOCK_SIZE) { + fprintf(stderr, "ERROR: Minimal linesize is CREADLINE_BLOCK_SIZE of %u bytes\n", CREADLINE_BLOCK_SIZE); + return -2; + } + + ctx->max_linebuf_size = max_linebuf_size_user; + + return 0; +} + /* * Set maximal allowed number of clients. * 0 is interpreted as unlimited number of allowed clients. @@ -558,7 +578,7 @@ int japi_start_server(japi_context *ctx, const char *port) do { ret = creadline_r(client->socket, (void **)&request, - &(client->crl_buffer)); + &(client->crl_buffer),ctx->max_linebuf_size); if (ret > 0) { response = NULL; diff --git a/test/japi_test.cc b/test/japi_test.cc index 513d5b4..e11a3f1 100644 --- a/test/japi_test.cc +++ b/test/japi_test.cc @@ -30,6 +30,7 @@ extern "C" { #include "japi_pushsrv_intern.h" #include "japi_utils.h" #include "rw_n.h" +#include "creadline.h" } /* The handler for japi_register_request test */ @@ -183,6 +184,42 @@ TEST(JAPI, IncludeArgsWithResponse) japi_destroy(ctx); } +TEST(JAPI, SetMaxLinebufSize) +{ + /* Setup */ + japi_context *ctx = japi_init(NULL); + char *response = NULL; + const char *sval; + const char *request = "{'japi_request':'dummy_request_handler'}"; + json_object *jobj; + json_object *jdata; + int socket = 4; + japi_register_request(ctx, "dummy_request_handler", &dummy_request_handler); + + /* Configure context to include request arguments in response */ + EXPECT_EQ(japi_set_max_linebuf_size(NULL, CREADLINE_BLOCK_SIZE), -1); + + EXPECT_EQ(japi_set_max_linebuf_size(ctx, 0), -2); + + /* Test valid line size */ + EXPECT_EQ(japi_set_max_linebuf_size(ctx, CREADLINE_BLOCK_SIZE), 0); + + /* TODO: set_max_linebuf_size is actually used by creadline_r which is + * not invoked in japi_process_message(). A mock client socket with + * dummy data has to be created so creadline can read from this socket. + * */ + + /* On success, 0 returned. On error, -1 is returned */ + EXPECT_EQ(japi_process_message(ctx, request, &response, socket), 0); + jobj = json_tokener_parse(response); + json_object_object_get_ex(jobj, "data", &jdata); + EXPECT_EQ(japi_get_value_as_str(jdata, "value", &sval), 0); + EXPECT_STREQ("hello world", sval); + + /* Clean up */ + japi_destroy(ctx); +} + TEST(JAPI, Register) { japi_context *ctx;