aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Janouch <p@janouch.name>2018-10-17 23:26:11 +0200
committerPřemysl Janouch <p@janouch.name>2018-10-17 23:26:54 +0200
commitcf56921c4e1959bb3962b4f99ee4a1e7665c57d4 (patch)
treeeb156946de5355fe7410e1e02e364810b184d280
parenta3ec0942f8063509a00695d6e702a7f914caa7e9 (diff)
downloadjson-rpc-shell-cf56921c4e1959bb3962b4f99ee4a1e7665c57d4.tar.gz
json-rpc-shell-cf56921c4e1959bb3962b4f99ee4a1e7665c57d4.tar.xz
json-rpc-shell-cf56921c4e1959bb3962b4f99ee4a1e7665c57d4.zip
Allow WebSockets to micromanage shutdowns
They have their reasons, mostly event-related.
-rw-r--r--demo-json-rpc-server.c59
1 files changed, 35 insertions, 24 deletions
diff --git a/demo-json-rpc-server.c b/demo-json-rpc-server.c
index 563d11c..2b99419 100644
--- a/demo-json-rpc-server.c
+++ b/demo-json-rpc-server.c
@@ -1854,10 +1854,11 @@ struct client
int socket_fd; ///< The network socket
bool received_eof; ///< Whether EOF has been received yet
- bool closing; ///< Whether we're just flushing buffers
- bool half_closed; ///< Transport half-closed while closing
+ bool flushing; ///< No more data to write, send FIN
+ bool closing; ///< No more data to read or write
+ bool half_closed; ///< Conn. half-closed while flushing
struct write_queue write_queue; ///< Write queue
- ev_timer flush_timeout_watcher; ///< Write queue flush timer
+ ev_timer close_timeout_watcher; ///< Write queue flush timer
ev_io read_watcher; ///< The socket can be read from
ev_io write_watcher; ///< The socket can be written to
@@ -1882,17 +1883,6 @@ struct client_vtable
};
static void
-client_write (struct client *self, const void *data, size_t len)
-{
- struct write_req *req = xcalloc (1, sizeof *req);
- req->data.iov_base = memcpy (xmalloc (len), data, len);
- req->data.iov_len = len;
-
- write_queue_add (&self->write_queue, req);
- ev_io_start (EV_DEFAULT_ &self->write_watcher);
-}
-
-static void
client_destroy (struct client *self)
{
// XXX: this codebase halfway pretends there could be other contexts
@@ -1907,12 +1897,36 @@ client_destroy (struct client *self)
ev_io_stop (EV_DEFAULT_ &self->write_watcher);
xclose (self->socket_fd);
write_queue_free (&self->write_queue);
- ev_timer_stop (EV_DEFAULT_ &self->flush_timeout_watcher);
+ ev_timer_stop (EV_DEFAULT_ &self->close_timeout_watcher);
free (self);
try_finish_quit (ctx);
}
+static void
+client_write (struct client *self, const void *data, size_t len)
+{
+ if (!soft_assert (!self->flushing))
+ return;
+
+ struct write_req *req = xcalloc (1, sizeof *req);
+ req->data.iov_base = memcpy (xmalloc (len), data, len);
+ req->data.iov_len = len;
+
+ write_queue_add (&self->write_queue, req);
+ ev_io_start (EV_DEFAULT_ &self->write_watcher);
+}
+
+/// Half-close the connection from our side once the write_queue is flushed.
+/// It is the caller's responsibility to destroy the connection upon EOF.
+// XXX: or we might change on_client_readable to do it anyway, seems safe
+static void
+client_shutdown (struct client *self)
+{
+ self->flushing = true;
+ ev_feed_event (EV_DEFAULT_ &self->write_watcher, EV_WRITE);
+}
+
/// Try to cleanly close the connection, waiting for the remote client to close
/// its own side of the connection as a sign that it has processed all the data
/// it wanted to. The client implementation will not receive any further data.
@@ -1924,8 +1938,8 @@ client_close (struct client *self)
return;
self->closing = true;
- ev_timer_start (EV_DEFAULT_ &self->flush_timeout_watcher);
- ev_feed_event (EV_DEFAULT_ &self->write_watcher, EV_WRITE);
+ ev_timer_start (EV_DEFAULT_ &self->close_timeout_watcher);
+ client_shutdown (self);
// We assume the remote client doesn't want our data if it half-closes
if (self->received_eof)
@@ -1996,7 +2010,7 @@ on_client_writable (EV_P_ ev_io *watcher, int revents)
return;
ev_io_stop (EV_A_ watcher);
- if (client->closing && !client->half_closed)
+ if (client->flushing && !client->half_closed)
{
if (!shutdown (client->socket_fd, SHUT_WR))
client->half_closed = true;
@@ -2023,8 +2037,8 @@ client_new (EV_P_ size_t size, int sock_fd)
struct client *self = xcalloc (1, size);
self->write_queue = write_queue_make ();
- ev_timer_init (&self->flush_timeout_watcher, on_client_timeout, 5., 0.);
- self->flush_timeout_watcher.data = self;
+ ev_timer_init (&self->close_timeout_watcher, on_client_timeout, 5., 0.);
+ self->close_timeout_watcher.data = self;
set_blocking (sock_fd, false);
self->socket_fd = sock_fd;
@@ -2305,10 +2319,7 @@ static void
client_ws_close_cb (struct ws_handler *handler, bool half_close)
{
FIND_CONTAINER (self, handler, struct client_ws, handler);
- if (half_close)
- ; // FIXME: we should probably call something like client_shutdown()
- else
- client_destroy (&self->client);
+ (half_close ? client_shutdown : client_destroy) (&self->client);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -