jsonrpc: add JSON-RPC 2.0 library
Change-Id: I4f58792c3af1f85f55144717478f868ebe5b1700 Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
parent
4957d2642a
commit
376d117c90
88
include/spdk/jsonrpc.h
Normal file
88
include/spdk/jsonrpc.h
Normal 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
|
@ -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
39
lib/jsonrpc/Makefile
Normal 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
|
91
lib/jsonrpc/jsonrpc_internal.h
Normal file
91
lib/jsonrpc/jsonrpc_internal.h
Normal 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
|
283
lib/jsonrpc/jsonrpc_server.c
Normal file
283
lib/jsonrpc/jsonrpc_server.c
Normal 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)
|
354
lib/jsonrpc/jsonrpc_server_tcp.c
Normal file
354
lib/jsonrpc/jsonrpc_server_tcp.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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
44
test/lib/jsonrpc/Makefile
Normal 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
13
test/lib/jsonrpc/jsonrpc.sh
Executable 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
1
test/lib/jsonrpc/server/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
jsonrpc_server_ut
|
41
test/lib/jsonrpc/server/Makefile
Normal file
41
test/lib/jsonrpc/server/Makefile
Normal 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
|
435
test/lib/jsonrpc/server/jsonrpc_server_ut.c
Normal file
435
test/lib/jsonrpc/server/jsonrpc_server_ut.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user