jsonrpc: add JSON-RPC 2.0 library

Change-Id: I4f58792c3af1f85f55144717478f868ebe5b1700
Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
Daniel Verkamp 2016-05-19 13:29:37 -07:00
parent 4957d2642a
commit 376d117c90
14 changed files with 1397 additions and 2 deletions

88
include/spdk/jsonrpc.h Normal file
View File

@ -0,0 +1,88 @@
/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
* JSON-RPC 2.0 server implementation
*/
#ifndef SPDK_JSONRPC_H_
#define SPDK_JSONRPC_H_
#include "spdk/json.h"
#include <stdlib.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#define SPDK_JSONRPC_ERROR_PARSE_ERROR -32700
#define SPDK_JSONRPC_ERROR_INVALID_REQUEST -32600
#define SPDK_JSONRPC_ERROR_METHOD_NOT_FOUND -32601
#define SPDK_JSONRPC_ERROR_INVALID_PARAMS -32602
#define SPDK_JSONRPC_ERROR_INTERNAL_ERROR -32603
struct spdk_jsonrpc_server;
struct spdk_jsonrpc_server_conn;
/**
* User callback to handle a single JSON-RPC request.
*
* The user should respond by calling one of spdk_jsonrpc_begin_result() or
* spdk_jsonrpc_send_error_response().
*/
typedef void (*spdk_jsonrpc_handle_request_fn)(
struct spdk_jsonrpc_server_conn *conn,
const struct spdk_json_val *method,
const struct spdk_json_val *params,
const struct spdk_json_val *id);
struct spdk_jsonrpc_server *spdk_jsonrpc_server_listen(struct sockaddr *listen_addr,
socklen_t addrlen, spdk_jsonrpc_handle_request_fn handle_request);
int spdk_jsonrpc_server_poll(struct spdk_jsonrpc_server *server);
void spdk_jsonrpc_server_shutdown(struct spdk_jsonrpc_server *server);
struct spdk_json_write_ctx *spdk_jsonrpc_begin_result(struct spdk_jsonrpc_server_conn *conn,
const struct spdk_json_val *id);
void spdk_jsonrpc_end_result(struct spdk_jsonrpc_server_conn *conn, struct spdk_json_write_ctx *w);
void spdk_jsonrpc_send_error_response(struct spdk_jsonrpc_server_conn *conn,
const struct spdk_json_val *id, int error_code, const char *msg);
#endif

View File

@ -34,7 +34,7 @@
SPDK_ROOT_DIR := $(abspath $(CURDIR)/..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
DIRS-y += conf cunit json log memory trace util nvme ioat
DIRS-y += conf cunit json jsonrpc log memory trace util nvme ioat
.PHONY: all clean $(DIRS-y)

39
lib/jsonrpc/Makefile Normal file
View File

@ -0,0 +1,39 @@
#
# BSD LICENSE
#
# Copyright (c) Intel Corporation.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Intel Corporation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
LIBNAME = jsonrpc
C_SRCS = jsonrpc_server.c jsonrpc_server_tcp.c
include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk

View File

@ -0,0 +1,91 @@
/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SPDK_JSONRPC_INTERNAL_H_
#define SPDK_JSONRPC_INTERNAL_H_
#include "spdk/jsonrpc.h"
#include <assert.h>
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include "spdk/log.h"
#define SPDK_JSONRPC_RECV_BUF_SIZE (32 * 1024)
#define SPDK_JSONRPC_SEND_BUF_SIZE (32 * 1024)
#define SPDK_JSONRPC_MAX_CONNS 64
#define SPDK_JSONRPC_MAX_VALUES 1024
struct spdk_jsonrpc_server_conn {
struct spdk_jsonrpc_server *server;
int sockfd;
struct spdk_json_val values[SPDK_JSONRPC_MAX_VALUES];
size_t recv_len;
uint8_t recv_buf[SPDK_JSONRPC_RECV_BUF_SIZE];
size_t send_len;
uint8_t send_buf[SPDK_JSONRPC_SEND_BUF_SIZE];
struct spdk_json_write_ctx *json_writer;
bool batch;
};
struct spdk_jsonrpc_server {
int sockfd;
spdk_jsonrpc_handle_request_fn handle_request;
struct spdk_jsonrpc_server_conn conns[SPDK_JSONRPC_MAX_CONNS];
struct pollfd pollfds[SPDK_JSONRPC_MAX_CONNS + 1];
int num_conns;
};
/* jsonrpc_server_tcp */
int spdk_jsonrpc_server_write_cb(void *cb_ctx, const void *data, size_t size);
void spdk_jsonrpc_server_handle_request(struct spdk_jsonrpc_server_conn *conn,
const struct spdk_json_val *method,
const struct spdk_json_val *params,
const struct spdk_json_val *id);
void spdk_jsonrpc_server_handle_error(struct spdk_jsonrpc_server_conn *conn, int error,
const struct spdk_json_val *method,
const struct spdk_json_val *params,
const struct spdk_json_val *id);
/* jsonrpc_server */
int spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, size_t size);
#endif

View File

@ -0,0 +1,283 @@
/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "jsonrpc_internal.h"
struct jsonrpc_request {
const struct spdk_json_val *version;
const struct spdk_json_val *method;
const struct spdk_json_val *params;
const struct spdk_json_val *id;
};
static int
capture_val(const struct spdk_json_val *val, void *out)
{
const struct spdk_json_val **vptr = out;
*vptr = val;
return 0;
}
static const struct spdk_json_object_decoder jsonrpc_request_decoders[] = {
{"jsonrpc", offsetof(struct jsonrpc_request, version), capture_val},
{"method", offsetof(struct jsonrpc_request, method), capture_val},
{"params", offsetof(struct jsonrpc_request, params), capture_val, true},
{"id", offsetof(struct jsonrpc_request, id), capture_val, true},
};
static void
parse_single_request(struct spdk_jsonrpc_server_conn *conn, struct spdk_json_val *values)
{
bool invalid = false;
struct jsonrpc_request req = {};
if (spdk_json_decode_object(values, jsonrpc_request_decoders,
sizeof(jsonrpc_request_decoders) / sizeof(*jsonrpc_request_decoders),
&req)) {
invalid = true;
goto done;
}
if (!req.version || req.version->type != SPDK_JSON_VAL_STRING ||
!spdk_json_strequal(req.version, "2.0")) {
invalid = true;
}
if (!req.method || req.method->type != SPDK_JSON_VAL_STRING) {
req.method = NULL;
invalid = true;
}
if (req.id) {
if (req.id->type != SPDK_JSON_VAL_STRING &&
req.id->type != SPDK_JSON_VAL_NUMBER &&
req.id->type != SPDK_JSON_VAL_NULL) {
req.id = NULL;
invalid = true;
}
}
if (req.params) {
if (req.params->type != SPDK_JSON_VAL_ARRAY_BEGIN &&
req.params->type != SPDK_JSON_VAL_OBJECT_BEGIN) {
req.params = NULL;
invalid = true;
}
}
done:
if (invalid) {
spdk_jsonrpc_server_handle_error(conn, SPDK_JSONRPC_ERROR_INVALID_REQUEST, req.method, req.params,
req.id);
} else {
spdk_jsonrpc_server_handle_request(conn, req.method, req.params, req.id);
}
}
static void
parse_batch_request(struct spdk_jsonrpc_server_conn *conn, struct spdk_json_val *values)
{
size_t num_values, i;
assert(values[0].type == SPDK_JSON_VAL_ARRAY_BEGIN);
num_values = values[0].len;
values++;
assert(conn->json_writer == NULL);
if (num_values == 0) {
SPDK_TRACELOG(SPDK_TRACE_RPC, "empty batch array not allowed");
spdk_jsonrpc_server_handle_error(conn, SPDK_JSONRPC_ERROR_INVALID_REQUEST, NULL, NULL, NULL);
return;
}
i = 0;
while (i < num_values) {
struct spdk_json_val *v = &values[i];
parse_single_request(conn, v);
i += spdk_json_val_len(v);
}
if (conn->json_writer) {
/*
* There was at least one response - finish the batch array.
*/
spdk_json_write_array_end(conn->json_writer);
spdk_json_write_end(conn->json_writer);
conn->json_writer = NULL;
}
}
int
spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, size_t size)
{
ssize_t rc;
void *end = NULL;
assert(conn->json_writer == NULL);
conn->batch = false;
/* Check to see if we have received a full JSON value. */
rc = spdk_json_parse(json, size, NULL, 0, &end, 0);
if (rc == SPDK_JSON_PARSE_INCOMPLETE) {
return 0;
} else if (rc < 0 || rc > SPDK_JSONRPC_MAX_VALUES) {
SPDK_TRACELOG(SPDK_TRACE_RPC, "JSON parse error\n");
spdk_jsonrpc_server_handle_error(conn, SPDK_JSONRPC_ERROR_PARSE_ERROR, NULL, NULL, NULL);
/*
* Can't recover from parse error (no guaranteed resync point in streaming JSON).
* Return an error to indicate that the connection should be closed.
*/
return -1;
}
/* Decode a second time now that there is a full JSON value available. */
rc = spdk_json_parse(json, size, conn->values, SPDK_JSONRPC_MAX_VALUES, &end,
SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE);
if (rc < 0 || rc > SPDK_JSONRPC_MAX_VALUES) {
SPDK_TRACELOG(SPDK_TRACE_RPC, "JSON parse error on second pass\n");
spdk_jsonrpc_server_handle_error(conn, SPDK_JSONRPC_ERROR_PARSE_ERROR, NULL, NULL, NULL);
return -1;
}
assert(end != NULL);
if (conn->values[0].type == SPDK_JSON_VAL_OBJECT_BEGIN) {
parse_single_request(conn, conn->values);
} else if (conn->values[0].type == SPDK_JSON_VAL_ARRAY_BEGIN) {
conn->batch = true;
parse_batch_request(conn, conn->values);
} else {
SPDK_TRACELOG(SPDK_TRACE_RPC, "top-level JSON value was not array or object\n");
spdk_jsonrpc_server_handle_error(conn, SPDK_JSONRPC_ERROR_INVALID_REQUEST, NULL, NULL, NULL);
}
return end - json;
}
static struct spdk_json_write_ctx *
begin_response(struct spdk_jsonrpc_server_conn *conn, const struct spdk_json_val *id)
{
struct spdk_json_write_ctx *w = conn->json_writer;
if (w == NULL) {
conn->json_writer = w = spdk_json_write_begin(spdk_jsonrpc_server_write_cb, conn, 0);
}
if (w == NULL) {
return NULL;
}
spdk_json_write_object_begin(w);
spdk_json_write_name(w, "jsonrpc");
spdk_json_write_string(w, "2.0");
if (id) {
spdk_json_write_name(w, "id");
spdk_json_write_val(w, id);
}
return w;
}
static void
end_response(struct spdk_jsonrpc_server_conn *conn, struct spdk_json_write_ctx *w)
{
spdk_json_write_object_end(w);
if (!conn->batch) {
spdk_json_write_end(w);
spdk_jsonrpc_server_write_cb(conn, "\n", 1);
conn->json_writer = NULL;
}
}
struct spdk_json_write_ctx *
spdk_jsonrpc_begin_result(struct spdk_jsonrpc_server_conn *conn, const struct spdk_json_val *id)
{
struct spdk_json_write_ctx *w;
w = begin_response(conn, id);
if (w == NULL) {
return NULL;
}
spdk_json_write_name(w, "result");
return w;
}
void
spdk_jsonrpc_end_result(struct spdk_jsonrpc_server_conn *conn, struct spdk_json_write_ctx *w)
{
assert(w != NULL);
assert(w == conn->json_writer);
end_response(conn, w);
}
void
spdk_jsonrpc_send_error_response(struct spdk_jsonrpc_server_conn *conn,
const struct spdk_json_val *id,
int error_code, const char *msg)
{
struct spdk_json_write_ctx *w;
struct spdk_json_val v_null;
if (id == NULL) {
/* For error responses, if id is missing, explicitly respond with "id": null. */
v_null.type = SPDK_JSON_VAL_NULL;
id = &v_null;
}
w = begin_response(conn, id);
if (w == NULL) {
return;
}
spdk_json_write_name(w, "error");
spdk_json_write_object_begin(w);
spdk_json_write_name(w, "code");
spdk_json_write_int32(w, error_code);
spdk_json_write_name(w, "message");
spdk_json_write_string(w, msg);
spdk_json_write_object_end(w);
end_response(conn, w);
}
SPDK_LOG_REGISTER_TRACE_FLAG("rpc", SPDK_TRACE_RPC)

View File

@ -0,0 +1,354 @@
/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "jsonrpc_internal.h"
struct spdk_jsonrpc_server *
spdk_jsonrpc_server_listen(struct sockaddr *listen_addr, socklen_t addrlen,
spdk_jsonrpc_handle_request_fn handle_request)
{
struct spdk_jsonrpc_server *server;
int rc, val;
server = calloc(1, sizeof(struct spdk_jsonrpc_server));
if (server == NULL) {
return NULL;
}
server->handle_request = handle_request;
server->sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server->sockfd < 0) {
SPDK_ERRLOG("socket() failed\n");
free(server);
return NULL;
}
val = 1;
setsockopt(server->sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
setsockopt(server->sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
val = 1;
rc = ioctl(server->sockfd, FIONBIO, &val);
if (rc != 0) {
SPDK_ERRLOG("ioctl(FIONBIO) failed\n");
close(server->sockfd);
free(server);
return NULL;
}
rc = bind(server->sockfd, listen_addr, addrlen);
if (rc != 0) {
SPDK_ERRLOG("could not bind JSON-RPC server: %s\n", strerror(errno));
close(server->sockfd);
free(server);
return NULL;
}
rc = listen(server->sockfd, 512);
if (rc != 0) {
SPDK_ERRLOG("listen() failed, errno = %d\n", errno);
close(server->sockfd);
free(server);
return NULL;
}
/* Put listen socket in pollfds[0] */
server->pollfds[0].fd = server->sockfd;
server->pollfds[0].events = POLLIN;
return server;
}
void
spdk_jsonrpc_server_shutdown(struct spdk_jsonrpc_server *server)
{
int i;
close(server->sockfd);
for (i = 0; i < server->num_conns; i++) {
close(server->conns[i].sockfd);
}
free(server);
}
static void
spdk_jsonrpc_server_conn_remove(struct spdk_jsonrpc_server_conn *conn)
{
struct spdk_jsonrpc_server *server = conn->server;
int conn_idx = conn - server->conns;
close(conn->sockfd);
/* Swap conn with the last entry in conns */
server->conns[conn_idx] = server->conns[server->num_conns - 1];
server->num_conns--;
}
static int
spdk_jsonrpc_server_accept(struct spdk_jsonrpc_server *server)
{
struct spdk_jsonrpc_server_conn *conn;
struct pollfd *pfd;
int rc, conn_idx, nonblock;
rc = accept(server->sockfd, NULL, NULL);
if (rc >= 0) {
assert(server->num_conns < SPDK_JSONRPC_MAX_CONNS);
conn_idx = server->num_conns;
conn = &server->conns[conn_idx];
conn->server = server;
conn->sockfd = rc;
conn->recv_len = 0;
conn->send_len = 0;
conn->json_writer = 0;
nonblock = 1;
rc = ioctl(conn->sockfd, FIONBIO, &nonblock);
if (rc != 0) {
SPDK_ERRLOG("ioctl(FIONBIO) failed\n");
close(conn->sockfd);
return -1;
}
/* Add connection to pollfds */
pfd = &server->pollfds[conn_idx + 1];
pfd->fd = conn->sockfd;
pfd->events = POLLIN | POLLOUT;
server->num_conns++;
return 0;
}
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
return 0;
}
return -1;
}
int
spdk_jsonrpc_server_write_cb(void *cb_ctx, const void *data, size_t size)
{
struct spdk_jsonrpc_server_conn *conn = cb_ctx;
if (SPDK_JSONRPC_SEND_BUF_SIZE - conn->send_len < size) {
SPDK_ERRLOG("Not enough space in send buf\n");
return -1;
}
memcpy(conn->send_buf + conn->send_len, data, size);
conn->send_len += size;
return 0;
}
void
spdk_jsonrpc_server_handle_request(struct spdk_jsonrpc_server_conn *conn,
const struct spdk_json_val *method, const struct spdk_json_val *params,
const struct spdk_json_val *id)
{
conn->server->handle_request(conn, method, params, id);
}
void
spdk_jsonrpc_server_handle_error(struct spdk_jsonrpc_server_conn *conn, int error,
const struct spdk_json_val *method, const struct spdk_json_val *params,
const struct spdk_json_val *id)
{
const char *msg;
switch (error) {
case SPDK_JSONRPC_ERROR_PARSE_ERROR:
msg = "Parse error";
break;
case SPDK_JSONRPC_ERROR_INVALID_REQUEST:
msg = "Invalid request";
break;
case SPDK_JSONRPC_ERROR_METHOD_NOT_FOUND:
msg = "Method not found";
break;
case SPDK_JSONRPC_ERROR_INVALID_PARAMS:
msg = "Invalid parameters";
break;
case SPDK_JSONRPC_ERROR_INTERNAL_ERROR:
msg = "Internal error";
break;
default:
msg = "Error";
break;
}
spdk_jsonrpc_send_error_response(conn, id, error, msg);
}
static int
spdk_jsonrpc_server_conn_recv(struct spdk_jsonrpc_server_conn *conn)
{
ssize_t rc;
size_t recv_avail = SPDK_JSONRPC_RECV_BUF_SIZE - conn->recv_len;
rc = recv(conn->sockfd, conn->recv_buf + conn->recv_len, recv_avail, 0);
if (rc == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
return 0;
}
SPDK_TRACELOG(SPDK_TRACE_RPC, "recv() failed: %s\n", strerror(errno));
return -1;
}
if (rc == 0) {
SPDK_TRACELOG(SPDK_TRACE_RPC, "remote closed connection\n");
return -1;
}
conn->recv_len += rc;
rc = spdk_jsonrpc_parse_request(conn, conn->recv_buf, conn->recv_len);
if (rc < 0) {
SPDK_ERRLOG("jsonrpc parse request failed\n");
return -1;
}
if (rc > 0) {
/*
* Successfully parsed a request - move any data past the end of the
* parsed request down to the beginning.
*/
assert((size_t)rc <= conn->recv_len);
memmove(conn->recv_buf, conn->recv_buf + rc, conn->recv_len - rc);
conn->recv_len -= rc;
}
return 0;
}
static int
spdk_jsonrpc_server_conn_send(struct spdk_jsonrpc_server_conn *conn)
{
ssize_t rc;
rc = send(conn->sockfd, conn->send_buf, conn->send_len, 0);
if (rc < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
return 0;
}
SPDK_TRACELOG(SPDK_TRACE_RPC, "send() failed: %s\n", strerror(errno));
return -1;
}
if (rc == 0) {
SPDK_TRACELOG(SPDK_TRACE_RPC, "remote closed connection\n");
return -1;
}
conn->send_len -= rc;
return 0;
}
int
spdk_jsonrpc_server_poll(struct spdk_jsonrpc_server *server)
{
int rc, i;
struct pollfd *pfd;
struct spdk_jsonrpc_server_conn *conn;
rc = poll(server->pollfds, server->num_conns + 1, 0);
if (rc < 0) {
if (errno == EINTR) {
return 0;
}
SPDK_ERRLOG("jsonrpc poll() failed\n");
return -1;
}
if (rc == 0) {
/* No sockets are ready */
return 0;
}
/* Check listen socket */
if (server->num_conns < SPDK_JSONRPC_MAX_CONNS) {
pfd = &server->pollfds[0];
if (pfd->revents) {
spdk_jsonrpc_server_accept(server);
}
pfd->revents = 0;
}
for (i = 0; i < server->num_conns; i++) {
pfd = &server->pollfds[i + 1];
conn = &server->conns[i];
if (conn->send_len) {
/*
* If there is any data to send, keep sending it until the send buffer
* is empty. Each response should be allowed the full send buffer, so
* don't accept any new requests until the previous response is sent out.
*/
if (pfd->revents & POLLOUT) {
rc = spdk_jsonrpc_server_conn_send(conn);
if (rc != 0) {
SPDK_TRACELOG(SPDK_TRACE_RPC, "closing conn due to send failure\n");
spdk_jsonrpc_server_conn_remove(conn);
}
}
} else {
/*
* No data to send - we can receive a new request.
*/
if (pfd->revents & POLLIN) {
rc = spdk_jsonrpc_server_conn_recv(conn);
if (rc != 0) {
SPDK_TRACELOG(SPDK_TRACE_RPC, "closing conn due to recv failure\n");
spdk_jsonrpc_server_conn_remove(conn);
}
}
}
pfd->revents = 0;
}
return 0;
}

View File

@ -34,10 +34,12 @@
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
JSON_DIR := $(SPDK_ROOT_DIR)/lib/json
JSONRPC_DIR := $(SPDK_ROOT_DIR)/lib/jsonrpc
C_SRCS = $(TEST_FILE) $(OTHER_FILES)
CFLAGS += -I$(JSON_DIR)
CFLAGS += -I$(JSONRPC_DIR)
CFLAGS += -I$(SPDK_ROOT_DIR)/lib
CFLAGS += -I$(SPDK_ROOT_DIR)/test

View File

@ -34,7 +34,7 @@
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
DIRS-y = log json nvme memory ioat
DIRS-y = log json jsonrpc nvme memory ioat
.PHONY: all clean $(DIRS-y)

44
test/lib/jsonrpc/Makefile Normal file
View File

@ -0,0 +1,44 @@
#
# BSD LICENSE
#
# Copyright (c) Intel Corporation.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Intel Corporation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
DIRS-y = server
.PHONY: all clean $(DIRS-y)
all: $(DIRS-y)
clean: $(DIRS-y)
include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk

13
test/lib/jsonrpc/jsonrpc.sh Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -xe
testdir=$(readlink -f $(dirname $0))
rootdir=$testdir/../../..
source $rootdir/scripts/autotest_common.sh
timing_enter jsonrpc
$testdir/server/jsonrpc_server_ut
timing_exit jsonrpc

1
test/lib/jsonrpc/server/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
jsonrpc_server_ut

View File

@ -0,0 +1,41 @@
#
# BSD LICENSE
#
# Copyright (c) Intel Corporation.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Intel Corporation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..)
TEST_FILE = jsonrpc_server_ut.c
LIBS += $(SPDK_ROOT_DIR)/lib/json/libspdk_json.a \
$(SPDK_ROOT_DIR)/lib/log/libspdk_log.a \
include $(SPDK_ROOT_DIR)/mk/json.unittest.mk

View File

@ -0,0 +1,435 @@
/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "spdk_cunit.h"
#include "jsonrpc_server.c"
#include <stdio.h>
#include <string.h>
#define MAX_PARAMS 100
#define MAX_REQS 100
struct req {
int error;
bool got_method;
bool got_id;
bool got_params;
struct spdk_json_val method;
struct spdk_json_val id;
struct spdk_json_val params[MAX_PARAMS];
};
static uint8_t g_buf[1000];
static struct req g_reqs[MAX_REQS];
static struct req *g_cur_req;
static struct spdk_json_val *g_params;
static size_t g_num_reqs;
#define PARSE_PASS(in, trailing) \
memcpy(g_buf, in, sizeof(in) - 1); \
g_num_reqs = 0; \
g_cur_req = NULL; \
CU_ASSERT(spdk_jsonrpc_parse_request(&conn, g_buf, sizeof(in) - 1) == sizeof(in) - sizeof(trailing))
#define PARSE_FAIL(in) \
memcpy(g_buf, in, sizeof(in) - 1); \
g_num_reqs = 0; \
g_cur_req = 0; \
CU_ASSERT(spdk_jsonrpc_parse_request(&conn, g_buf, sizeof(in) - 1) < 0)
#define REQ_BEGIN(expected_error) \
if (g_cur_req == NULL) { \
g_cur_req = g_reqs; \
} else { \
g_cur_req++; \
} \
CU_ASSERT(g_cur_req - g_reqs <= (ptrdiff_t)g_num_reqs); \
CU_ASSERT(g_cur_req->error == expected_error)
#define REQ_BEGIN_VALID() REQ_BEGIN(0)
#define REQ_BEGIN_INVALID(expected_error) REQ_BEGIN(expected_error)
#define REQ_METHOD(name) \
CU_ASSERT(g_cur_req->got_method); \
CU_ASSERT(spdk_json_strequal(&g_cur_req->method, name) == true)
#define REQ_METHOD_MISSING() \
CU_ASSERT(g_cur_req->got_method == false)
#define REQ_ID_NUM(num) \
CU_ASSERT(g_cur_req->got_id); \
CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_NUMBER); \
CU_ASSERT(memcmp(g_cur_req->id.start, num, sizeof(num) - 1) == 0)
#define REQ_ID_STRING(str) \
CU_ASSERT(g_cur_req->got_id); \
CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_STRING); \
CU_ASSERT(memcmp(g_cur_req->id.start, str, sizeof(str) - 1) == 0)
#define REQ_ID_MISSING() \
CU_ASSERT(g_cur_req->got_id == false)
#define REQ_PARAMS_MISSING() \
CU_ASSERT(g_cur_req->got_params == false)
#define REQ_PARAMS_BEGIN() \
CU_ASSERT(g_cur_req->got_params); \
g_params = g_cur_req->params
#define PARAM_ARRAY_BEGIN() \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_ARRAY_BEGIN); \
g_params++
#define PARAM_ARRAY_END() \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_ARRAY_END); \
g_params++
#define PARAM_OBJECT_BEGIN() \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_OBJECT_BEGIN); \
g_params++
#define PARAM_OBJECT_END() \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_OBJECT_END); \
g_params++
#define PARAM_NUM(num) \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_NUMBER); \
CU_ASSERT(g_params->len == sizeof(num) - 1); \
CU_ASSERT(memcmp(g_params->start, num, g_params->len) == 0); \
g_params++
#define PARAM_NAME(str) \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_NAME); \
CU_ASSERT(g_params->len == sizeof(str) - 1); \
CU_ASSERT(memcmp(g_params->start, str, g_params->len) == 0); \
g_params++
#define PARAM_STRING(str) \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_STRING); \
CU_ASSERT(g_params->len == sizeof(str) - 1); \
CU_ASSERT(memcmp(g_params->start, str, g_params->len) == 0); \
g_params++
static void
ut_handle(struct spdk_jsonrpc_server_conn *conn, int error, const struct spdk_json_val *method,
const struct spdk_json_val *params, const struct spdk_json_val *id)
{
struct req *r;
SPDK_CU_ASSERT_FATAL(g_num_reqs != MAX_REQS);
r = &g_reqs[g_num_reqs++];
r->error = error;
if (method) {
r->got_method = true;
r->method = *method;
} else {
r->got_method = false;
}
if (params) {
r->got_params = true;
SPDK_CU_ASSERT_FATAL(spdk_json_val_len(params) < MAX_PARAMS);
memcpy(r->params, params, spdk_json_val_len(params) * sizeof(struct spdk_json_val));
} else {
r->got_params = false;
}
if (id) {
r->got_id = true;
r->id = *id;
} else {
r->got_id = false;
}
}
void
spdk_jsonrpc_server_handle_error(struct spdk_jsonrpc_server_conn *conn, int error,
const struct spdk_json_val *method, const struct spdk_json_val *params,
const struct spdk_json_val *id)
{
ut_handle(conn, error, method, params, id);
}
void
spdk_jsonrpc_server_handle_request(struct spdk_jsonrpc_server_conn *conn,
const struct spdk_json_val *method, const struct spdk_json_val *params,
const struct spdk_json_val *id)
{
ut_handle(conn, 0, method, params, id);
}
int
spdk_jsonrpc_server_write_cb(void *cb_ctx, const void *data, size_t size)
{
/* TODO */
return -1;
}
static void
test_parse_request(void)
{
struct spdk_jsonrpc_server server = {};
struct spdk_jsonrpc_server_conn conn = {};
conn.server = &server;
/* rpc call with positional parameters */
PARSE_PASS("{\"jsonrpc\":\"2.0\",\"method\":\"subtract\",\"params\":[42,23],\"id\":1}", "");
REQ_BEGIN_VALID();
REQ_METHOD("subtract");
REQ_ID_NUM("1");
REQ_PARAMS_BEGIN();
PARAM_ARRAY_BEGIN();
PARAM_NUM("42");
PARAM_NUM("23");
PARAM_ARRAY_END();
/* rpc call with named parameters */
PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": {\"subtrahend\": 23, \"minuend\": 42}, \"id\": 3}",
"");
REQ_BEGIN_VALID();
REQ_METHOD("subtract");
REQ_ID_NUM("3");
REQ_PARAMS_BEGIN();
PARAM_OBJECT_BEGIN();
PARAM_NAME("subtrahend");
PARAM_NUM("23");
PARAM_NAME("minuend");
PARAM_NUM("42");
PARAM_OBJECT_END();
/* notification */
PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"update\", \"params\": [1,2,3,4,5]}", "");
REQ_BEGIN_VALID();
REQ_METHOD("update");
REQ_ID_MISSING();
REQ_PARAMS_BEGIN();
PARAM_ARRAY_BEGIN();
PARAM_NUM("1");
PARAM_NUM("2");
PARAM_NUM("3");
PARAM_NUM("4");
PARAM_NUM("5");
PARAM_ARRAY_END();
/* invalid JSON */
PARSE_FAIL("{\"jsonrpc\": \"2.0\", \"method\": \"foobar, \"params\": \"bar\", \"baz]");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
REQ_METHOD_MISSING();
REQ_ID_MISSING();
REQ_PARAMS_MISSING();
/* invalid request (method must be a string; params must be array or object) */
PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": 1, \"params\": \"bar\"}", "");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
REQ_METHOD_MISSING();
REQ_ID_MISSING();
REQ_PARAMS_MISSING();
/* batch, invalid JSON */
PARSE_FAIL(
"["
"{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"},"
"{\"jsonrpc\": \"2.0\", \"method\""
"]");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
REQ_METHOD_MISSING();
REQ_ID_MISSING();
REQ_PARAMS_MISSING();
/* empty array */
PARSE_PASS("[]", "");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
REQ_METHOD_MISSING();
REQ_ID_MISSING();
REQ_PARAMS_MISSING();
/* invalid batch */
PARSE_PASS("[1]", "");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
REQ_METHOD_MISSING();
REQ_ID_MISSING();
REQ_PARAMS_MISSING();
/* invalid batch */
PARSE_PASS("[1,2,3]", "");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
REQ_METHOD_MISSING();
REQ_ID_MISSING();
REQ_PARAMS_MISSING();
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
REQ_METHOD_MISSING();
REQ_ID_MISSING();
REQ_PARAMS_MISSING();
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
REQ_METHOD_MISSING();
REQ_ID_MISSING();
REQ_PARAMS_MISSING();
/* batch */
PARSE_PASS(
"["
"{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"},"
"{\"jsonrpc\": \"2.0\", \"method\": \"notify_hello\", \"params\": [7]},"
"{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": [42,23], \"id\": \"2\"},"
"{\"foo\": \"boo\"},"
"{\"jsonrpc\": \"2.0\", \"method\": \"foo.get\", \"params\": {\"name\": \"myself\"}, \"id\": \"5\"},"
"{\"jsonrpc\": \"2.0\", \"method\": \"get_data\", \"id\": \"9\"}"
"]", "");
REQ_BEGIN_VALID();
REQ_METHOD("sum");
REQ_ID_STRING("1");
REQ_PARAMS_BEGIN();
PARAM_ARRAY_BEGIN();
PARAM_NUM("1");
PARAM_NUM("2");
PARAM_NUM("4");
PARAM_ARRAY_END();
REQ_BEGIN_VALID();
REQ_METHOD("notify_hello");
REQ_ID_MISSING();
REQ_PARAMS_BEGIN();
PARAM_ARRAY_BEGIN();
PARAM_NUM("7");
PARAM_ARRAY_END();
REQ_BEGIN_VALID();
REQ_METHOD("subtract");
REQ_ID_STRING("2");
REQ_PARAMS_BEGIN();
PARAM_ARRAY_BEGIN();
PARAM_NUM("42");
PARAM_NUM("23");
PARAM_ARRAY_END();
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
REQ_METHOD_MISSING();
REQ_ID_MISSING();
REQ_PARAMS_MISSING();
REQ_BEGIN_VALID();
REQ_METHOD("foo.get");
REQ_ID_STRING("5");
REQ_PARAMS_BEGIN();
PARAM_OBJECT_BEGIN();
PARAM_NAME("name");
PARAM_STRING("myself");
PARAM_OBJECT_END();
REQ_BEGIN_VALID();
REQ_METHOD("get_data");
REQ_ID_STRING("9");
REQ_PARAMS_MISSING();
}
static void
test_parse_request_streaming(void)
{
struct spdk_jsonrpc_server server = {};
struct spdk_jsonrpc_server_conn conn = {};
size_t len, i;
conn.server = &server;
/*
* Two valid requests end to end in the same buffer.
* Parse should return the first one and point to the beginning of the second one.
*/
PARSE_PASS(
"{\"jsonrpc\":\"2.0\",\"method\":\"a\",\"params\":[1],\"id\":1}"
"{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}",
"{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}");
REQ_BEGIN_VALID();
REQ_METHOD("a");
REQ_ID_NUM("1");
REQ_PARAMS_BEGIN();
PARAM_ARRAY_BEGIN();
PARAM_NUM("1");
PARAM_ARRAY_END();
/* Partial (but not invalid) requests - parse should not consume anything. */
strcpy(g_buf, "{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}");
len = strlen(g_buf);
/* Try every partial length up to the full request length */
for (i = 0; i < len; i++) {
int rc = spdk_jsonrpc_parse_request(&conn, g_buf, i);
/* Partial request - no data consumed */
CU_ASSERT(rc == 0);
}
/* Verify that full request can be parsed successfully */
CU_ASSERT(spdk_jsonrpc_parse_request(&conn, g_buf, len) == (ssize_t)len);
}
int main(int argc, char **argv)
{
CU_pSuite suite = NULL;
unsigned int num_failures;
if (CU_initialize_registry() != CUE_SUCCESS) {
return CU_get_error();
}
suite = CU_add_suite("jsonrpc", NULL, NULL);
if (suite == NULL) {
CU_cleanup_registry();
return CU_get_error();
}
if (
CU_add_test(suite, "parse_request", test_parse_request) == NULL ||
CU_add_test(suite, "parse_request_streaming", test_parse_request_streaming) == NULL) {
CU_cleanup_registry();
return CU_get_error();
}
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
num_failures = CU_get_number_of_failures();
CU_cleanup_registry();
return num_failures;
}

View File

@ -20,6 +20,10 @@ test/lib/json/parse/json_parse_ut
test/lib/json/util/json_util_ut
test/lib/json/write/json_write_ut
make -C test/lib/jsonrpc CONFIG_WERROR=y
test/lib/jsonrpc/server/jsonrpc_server_ut
make -C test/lib/log CONFIG_WERROR=y
test/lib/log/log_ut