pseudotcp: Add pseudo_tcp_socket_shutdown() support
authorPhilip Withnall <philip.withnall@collabora.co.uk>
Fri, 8 Aug 2014 10:07:17 +0000 (11:07 +0100)
committerOlivier Crête <olivier.crete@collabora.com>
Thu, 21 Aug 2014 18:33:11 +0000 (14:33 -0400)
This is analogous to the UNIX shutdown() function, allowing either or
both sides of a pseudo-TCP connection to be shut down.

agent/pseudotcp.c
agent/pseudotcp.h
docs/reference/libnice/libnice-sections.txt

index d3a124d..a786f36 100644 (file)
@@ -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:
index 3ab6fc4..71aa2ef 100644 (file)
@@ -190,6 +190,24 @@ typedef enum {
 } 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
  * @PseudoTcpOpened: The #PseudoTcpSocket is now connected
@@ -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:
index c755fbd..dd185f0 100644 (file)
@@ -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