Commit f3440d33 authored by Philip Withnall's avatar Philip Withnall Committed by Olivier Crête

pseudotcp: Add pseudo_tcp_socket_shutdown() support

This is analogous to the UNIX shutdown() function, allowing either or
both sides of a pseudo-TCP connection to be shut down.
parent 78ec8485
......@@ -457,6 +457,7 @@ struct _PseudoTcpSocketPrivate {
PseudoTcpCallbacks callbacks;
Shutdown shutdown; /* only used if !support_fin_ack */
gboolean shutdown_reads;
gint error;
// TCB data
......@@ -1116,7 +1117,8 @@ pseudo_tcp_socket_recv(PseudoTcpSocket *self, char * buffer, size_t len)
/* Received a FIN from the peer, so return 0. RFC 793, §3.5, Case 2. */
if (priv->support_fin_ack &&
pseudo_tcp_state_has_received_fin (priv->state)) {
(priv->shutdown_reads ||
pseudo_tcp_state_has_received_fin (priv->state))) {
return 0;
}
......@@ -1191,22 +1193,51 @@ pseudo_tcp_socket_close(PseudoTcpSocket *self, gboolean force)
{
PseudoTcpSocketPrivate *priv = self->priv;
DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "Closing socket %p : %s", self,
DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Closing socket %p %s", self,
force ? "forcefully" : "gracefully");
/* Forced closure by sending an RST segment. RFC 1122, §4.2.2.13. */
if (force && priv->state != TCP_CLOSED) {
closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL);
return;
}
/* Fall back to shutdown(). */
pseudo_tcp_socket_shutdown (self, PSEUDO_TCP_SHUTDOWN_RDWR);
}
void
pseudo_tcp_socket_shutdown (PseudoTcpSocket *self, PseudoTcpShutdown how)
{
PseudoTcpSocketPrivate *priv = self->priv;
DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Shutting down socket %p: %u", self, how);
/* FIN-ACK--only stuff below here. */
if (!priv->support_fin_ack) {
priv->shutdown = force ? SD_FORCEFUL : SD_GRACEFUL;
priv->shutdown = SD_GRACEFUL;
return;
}
/* Forced closure by sending an RST segment. RFC 1122, §4.2.2.13. */
if (force) {
closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL);
/* What needs shutting down? */
switch (how) {
case PSEUDO_TCP_SHUTDOWN_RD:
case PSEUDO_TCP_SHUTDOWN_RDWR:
priv->shutdown_reads = TRUE;
break;
case PSEUDO_TCP_SHUTDOWN_WR:
/* Handled below. */
break;
default:
DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Invalid shutdown method: %u.", how);
break;
}
if (how == PSEUDO_TCP_SHUTDOWN_RD) {
return;
}
/* Unforced closure. */
/* Unforced write closure. */
switch (priv->state) {
case TCP_LISTEN:
case TCP_SYN_SENT:
......
......@@ -189,6 +189,24 @@ typedef enum {
WR_FAIL
} PseudoTcpWriteResult;
/**
* PseudoTcpShutdown:
* @PSEUDO_TCP_SHUTDOWN_RD: Shut down the local reader only
* @PSEUDO_TCP_SHUTDOWN_WR: Shut down the local writer only
* @PSEUDO_TCP_SHUTDOWN_RDWR: Shut down both reading and writing
*
* Options for which parts of a connection to shut down when calling
* pseudo_tcp_socket_shutdown(). These correspond to the values passed to POSIX
* shutdown().
*
* Since: UNRELEASED
*/
typedef enum {
PSEUDO_TCP_SHUTDOWN_RD,
PSEUDO_TCP_SHUTDOWN_WR,
PSEUDO_TCP_SHUTDOWN_RDWR,
} PseudoTcpShutdown;
/**
* PseudoTcpCallbacks:
* @user_data: A user defined pointer to be passed to the callbacks
......@@ -314,12 +332,16 @@ gint pseudo_tcp_socket_send(PseudoTcpSocket *self, const char * buffer,
* @self: The #PseudoTcpSocket object.
* @force: %TRUE to close the socket forcefully, %FALSE to close it gracefully
*
* Close the socket for sending. IF @force is set to %FALSE, the socket will
* finish sending pending data before closing.
* Close the socket for sending. If @force is set to %FALSE, the socket will
* finish sending pending data before closing. If it is set to %TRUE, the socket
* will discard pending data and close the connection immediately (sending a TCP
* RST segment).
*
* The socket will only be fully closed once the peer has also closed their end
* of the connection — until that point, pseudo_tcp_socket_recv() can still be
* called to receive data from the peer.
* The socket will be closed in both directions – sending and receiving – and
* any pending received data must be read before calling this function, by
* calling pseudo_tcp_socket_recv() until it blocks. If any pending data is in
* the receive buffer when pseudo_tcp_socket_close() is called, a TCP RST
* segment will be sent to the peer to notify it of the data loss.
*
<note>
<para>
......@@ -336,6 +358,24 @@ gint pseudo_tcp_socket_send(PseudoTcpSocket *self, const char * buffer,
*/
void pseudo_tcp_socket_close(PseudoTcpSocket *self, gboolean force);
/**
* pseudo_tcp_socket_shutdown:
* @self: The #PseudoTcpSocket object.
* @how: The directions of the connection to shut down.
*
* Shut down sending, receiving, or both on the socket, depending on the value
* of @how. The behaviour of pseudo_tcp_socket_send() and
* pseudo_tcp_socket_recv() will immediately change after this function returns
* (depending on the value of @how), though the socket may continue to process
* network traffic in the background even if sending or receiving data is
* forbidden.
*
* This is equivalent to the POSIX shutdown() function. Setting @how to
* %PSEUDO_TCP_SHUTDOWN_RDWR is equivalent to calling pseudo_tcp_socket_close().
*
* Since: UNRELEASED
*/
void pseudo_tcp_socket_shutdown (PseudoTcpSocket *self, PseudoTcpShutdown how);
/**
* pseudo_tcp_socket_get_error:
......
......@@ -286,11 +286,13 @@ PseudoTcpState
PseudoTcpWriteResult
PseudoTcpCallbacks
PseudoTcpDebugLevel
PseudoTcpShutdown
pseudo_tcp_socket_new
pseudo_tcp_socket_connect
pseudo_tcp_socket_recv
pseudo_tcp_socket_send
pseudo_tcp_socket_close
pseudo_tcp_socket_shutdown
pseudo_tcp_socket_is_closed
pseudo_tcp_socket_is_closed_remotely
pseudo_tcp_socket_get_error
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment