Allow WebSockets to micromanage shutdowns
They have their reasons, mostly event-related.
This commit is contained in:
parent
a3ec0942f8
commit
cf56921c4e
|
@ -1854,10 +1854,11 @@ struct client
|
||||||
|
|
||||||
int socket_fd; ///< The network socket
|
int socket_fd; ///< The network socket
|
||||||
bool received_eof; ///< Whether EOF has been received yet
|
bool received_eof; ///< Whether EOF has been received yet
|
||||||
bool closing; ///< Whether we're just flushing buffers
|
bool flushing; ///< No more data to write, send FIN
|
||||||
bool half_closed; ///< Transport half-closed while closing
|
bool closing; ///< No more data to read or write
|
||||||
|
bool half_closed; ///< Conn. half-closed while flushing
|
||||||
struct write_queue write_queue; ///< Write queue
|
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 read_watcher; ///< The socket can be read from
|
||||||
ev_io write_watcher; ///< The socket can be written to
|
ev_io write_watcher; ///< The socket can be written to
|
||||||
|
@ -1881,17 +1882,6 @@ struct client_vtable
|
||||||
void (*finalize) (struct client *client);
|
void (*finalize) (struct client *client);
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
static void
|
||||||
client_destroy (struct client *self)
|
client_destroy (struct client *self)
|
||||||
{
|
{
|
||||||
|
@ -1907,12 +1897,36 @@ client_destroy (struct client *self)
|
||||||
ev_io_stop (EV_DEFAULT_ &self->write_watcher);
|
ev_io_stop (EV_DEFAULT_ &self->write_watcher);
|
||||||
xclose (self->socket_fd);
|
xclose (self->socket_fd);
|
||||||
write_queue_free (&self->write_queue);
|
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);
|
free (self);
|
||||||
|
|
||||||
try_finish_quit (ctx);
|
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
|
/// 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
|
/// 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.
|
/// it wanted to. The client implementation will not receive any further data.
|
||||||
|
@ -1924,8 +1938,8 @@ client_close (struct client *self)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
self->closing = true;
|
self->closing = true;
|
||||||
ev_timer_start (EV_DEFAULT_ &self->flush_timeout_watcher);
|
ev_timer_start (EV_DEFAULT_ &self->close_timeout_watcher);
|
||||||
ev_feed_event (EV_DEFAULT_ &self->write_watcher, EV_WRITE);
|
client_shutdown (self);
|
||||||
|
|
||||||
// We assume the remote client doesn't want our data if it half-closes
|
// We assume the remote client doesn't want our data if it half-closes
|
||||||
if (self->received_eof)
|
if (self->received_eof)
|
||||||
|
@ -1996,7 +2010,7 @@ on_client_writable (EV_P_ ev_io *watcher, int revents)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ev_io_stop (EV_A_ watcher);
|
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))
|
if (!shutdown (client->socket_fd, SHUT_WR))
|
||||||
client->half_closed = true;
|
client->half_closed = true;
|
||||||
|
@ -2023,8 +2037,8 @@ client_new (EV_P_ size_t size, int sock_fd)
|
||||||
struct client *self = xcalloc (1, size);
|
struct client *self = xcalloc (1, size);
|
||||||
|
|
||||||
self->write_queue = write_queue_make ();
|
self->write_queue = write_queue_make ();
|
||||||
ev_timer_init (&self->flush_timeout_watcher, on_client_timeout, 5., 0.);
|
ev_timer_init (&self->close_timeout_watcher, on_client_timeout, 5., 0.);
|
||||||
self->flush_timeout_watcher.data = self;
|
self->close_timeout_watcher.data = self;
|
||||||
|
|
||||||
set_blocking (sock_fd, false);
|
set_blocking (sock_fd, false);
|
||||||
self->socket_fd = sock_fd;
|
self->socket_fd = sock_fd;
|
||||||
|
@ -2305,10 +2319,7 @@ static void
|
||||||
client_ws_close_cb (struct ws_handler *handler, bool half_close)
|
client_ws_close_cb (struct ws_handler *handler, bool half_close)
|
||||||
{
|
{
|
||||||
FIND_CONTAINER (self, handler, struct client_ws, handler);
|
FIND_CONTAINER (self, handler, struct client_ws, handler);
|
||||||
if (half_close)
|
(half_close ? client_shutdown : client_destroy) (&self->client);
|
||||||
; // FIXME: we should probably call something like client_shutdown()
|
|
||||||
else
|
|
||||||
client_destroy (&self->client);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
Loading…
Reference in New Issue