numam-spdk/lib/jsonrpc/jsonrpc_server_tcp.c
Daniel Verkamp e6d1a5ac36 jsonrpc_server: allow user to specify socket type
Change-Id: I811d8c4e3659775519ca61dae31c1fe17cd7142a
Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
2016-12-15 08:06:33 -07:00

358 lines
8.7 KiB
C

/*-
* 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(int domain, int protocol,
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(domain, SOCK_STREAM, protocol);
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));
if (protocol == IPPROTO_TCP) {
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;
}