Add drain_and_close_socket()
authorFabian Keil <fk@fabiankeil.de>
Fri, 12 Oct 2012 11:17:48 +0000 (11:17 +0000)
committerFabian Keil <fk@fabiankeil.de>
Fri, 12 Oct 2012 11:17:48 +0000 (11:17 +0000)
Apparently on some platforms immediately closing a client socket
with both unread and written-but-not-yet-transmitted data may result
in the connection being teared down prematurely, in which case
the client ends up with an incomplete response.

To prevent this, drain_and_close_socket() shuts down our side of
the connection (on platforms with shutdown()), drains the unread
data on the socket and finally calls close_socket().

I suspect that this was the main cause of the page truncation
issues reported with Opera in #3464439.

configure.in
jbsockets.c
jbsockets.h

index 5d164b7..16bbcc4 100644 (file)
@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl
-dnl $Id: configure.in,v 1.172 2012/10/12 11:12:22 fabiankeil Exp $
+dnl $Id: configure.in,v 1.173 2012/10/12 11:15:17 fabiankeil Exp $
 dnl
 dnl Written by and Copyright (C) 2001-2010 the
 dnl Privoxy team. http://www.privoxy.org/
@@ -32,7 +32,7 @@ dnl =================================================================
 dnl AutoConf Initialization
 dnl =================================================================
 
-AC_REVISION($Revision: 1.172 $)
+AC_REVISION($Revision: 1.173 $)
 AC_INIT(jcc.c)
 
 if test ! -f config.h.in; then
@@ -761,6 +761,7 @@ AC_CHECK_FUNCS([ \
  regcomp \
  select \
  setlocale \
+ shutdown \
  snprintf \
  socket \
  strchr \
index 5622651..2e20a83 100644 (file)
@@ -1,4 +1,4 @@
-const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.113 2012/03/09 16:23:50 fabiankeil Exp $";
+const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.114 2012/03/09 17:56:41 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jbsockets.c,v $
@@ -703,6 +703,68 @@ void close_socket(jb_socket fd)
 #else
    close(fd);
 #endif
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  drain_and_close_socket
+ *
+ * Description :  Closes a TCP/IP socket after draining unread data
+ *
+ * Parameters  :
+ *          1  :  fd = file descriptor of the socket to be closed
+ *
+ * Returns     :  void
+ *
+ *********************************************************************/
+void drain_and_close_socket(jb_socket fd)
+{
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
+   if (socket_is_still_alive(fd))
+#endif
+   {
+      int bytes_drained_total = 0;
+      int bytes_drained;
+
+#ifdef HAVE_SHUTDOWN
+/* Apparently Windows has shutdown() but now SHUT_WR */
+#ifndef SHUT_WR
+#define SHUT_WR 1
+#endif
+      if (0 != shutdown(fd, SHUT_WR))
+      {
+         log_error(LOG_LEVEL_ERROR, "Failed to shutdown socket %d %E", fd);
+      }
+#endif
+#define ARBITRARY_DRAIN_LIMIT 10000
+      do
+      {
+         char drainage[500];
+
+         bytes_drained = read_socket(fd, drainage, sizeof(drainage));
+         if (bytes_drained < 0)
+         {
+            log_error(LOG_LEVEL_ERROR, "Failed to drain socket %d %E", fd);
+         }
+         else if (bytes_drained > 0)
+         {
+            bytes_drained_total += bytes_drained;
+            if (bytes_drained_total > ARBITRARY_DRAIN_LIMIT)
+            {
+               log_error(LOG_LEVEL_CONNECT, "Giving up draining socket %d", fd);
+               break;
+            }
+         }
+      } while (bytes_drained > 0);
+      if (bytes_drained_total != 0)
+      {
+         log_error(LOG_LEVEL_CONNECT,
+            "Drained %d bytes before closing socket %d", bytes_drained_total, fd);
+      }
+   }
+
+   close_socket(fd);
 
 }
 
index 7221bac..e41aab2 100644 (file)
@@ -1,6 +1,6 @@
 #ifndef JBSOCKETS_H_INCLUDED
 #define JBSOCKETS_H_INCLUDED
-#define JBSOCKETS_H_VERSION "$Id: jbsockets.h,v 1.19 2011/07/30 15:17:35 fabiankeil Exp $"
+#define JBSOCKETS_H_VERSION "$Id: jbsockets.h,v 1.20 2011/09/04 11:10:56 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jbsockets.h,v $
@@ -55,6 +55,7 @@ extern int write_socket(jb_socket fd, const char *buf, size_t n);
 extern int read_socket(jb_socket fd, char *buf, int n);
 extern int data_is_available(jb_socket fd, int seconds_to_wait);
 extern void close_socket(jb_socket fd);
+extern void drain_and_close_socket(jb_socket fd);
 
 extern int bind_port(const char *hostnam, int portnum, jb_socket *pfd);
 extern int accept_connection(struct client_state * csp, jb_socket fds[]);