Hi!

The apr_socket_sendfile() has Windows specific flag
APR_SENDFILE_DISCONNECT_SOCKET. When this flag specified
instructs Winsock to disconnect socket and prepare it for
reuse after file send.

There are several problems with this flag:
1. The TCP socket may be subject to the TCP TIME_WAIT state.
   That means apr_sock_sendfile() could occupy worker thread
   for significant time (30 seconds)

2. When flag specified socket descriptor is removed from
   apr_socket_t and will not be cleaned up. The caller
   expected to maintain original socket descriptor and release
   if APR_SENDFILE_DISCONNECT_SOCKET is used.

As far I see Apache HTTP Server doesn't use APR_SENDFILE_DISCONNECT_SOCKET
flag.

As far I understand proper way to reuse sockets is to use DisconnectEx() [1].
Application should use DisconnectEx() in overlapped I/O mode and wait
for it completion using completion port or thread pool. When overlapped I/O
is done the socket could be added to reuse list and later used for AcceptEx() or
ConnectEx().

So I propose to remove APR_SENDFILE_DISCONNECT_SOCKET flag in APR 2.0 for the
reasons above. Patch is attached.

Any objections?

[1] 
https://docs.microsoft.com/en-gb/windows/win32/api/mswsock/nc-mswsock-lpfn_disconnectex

-- 
Ivan Zhakov
Index: CHANGES
===================================================================
--- CHANGES     (revision 1866558)
+++ CHANGES     (working copy)
@@ -224,6 +224,8 @@
   *) apr_socket_listen: Allow larger listen backlog values on Windows 8+.
      [Evgeny Kotkov <evgeny.kotkov visualsvn.com>]
 
+  *) Windows platform: Remove APR_SENDFILE_DISCONNECT_SOCKET flag. [Ivan 
Zhakov]
+
 Changes for APR and APR-util 1.6.x and later:
 
   *) http://svn.apache.org/viewvc/apr/apr/branches/1.6.x/CHANGES?view=markup
Index: include/apr_network_io.h
===================================================================
--- include/apr_network_io.h    (revision 1866558)
+++ include/apr_network_io.h    (working copy)
@@ -281,12 +281,7 @@
 };
 
 #if APR_HAS_SENDFILE
-/** 
- * Support reusing the socket on platforms which support it (from disconnect,
- * specifically Win32.
- * @remark Optional flag passed into apr_socket_sendfile() 
- */
-#define APR_SENDFILE_DISCONNECT_SOCKET      1
+/* APR_SENDFILE_DISCONNECT_SOCKET has been removed in APR 2.0. */
 #endif
 
 /** A structure to encapsulate headers and trailers for apr_socket_sendfile */
Index: network_io/win32/sendrecv.c
===================================================================
--- network_io/win32/sendrecv.c (revision 1866934)
+++ network_io/win32/sendrecv.c (working copy)
@@ -267,7 +267,6 @@
     apr_size_t nbytes;
     TRANSMIT_FILE_BUFFERS tfb, *ptfb = NULL;
     apr_size_t bytes_to_send;   /* Bytes to send out of the file (not 
including headers) */
-    int disconnected = 0;
     int sendv_trailers = 0;
     char hdtrbuf[4096];
     LPFN_TRANSMITFILE pfn_transmit_file = NULL;
@@ -274,6 +273,10 @@
     static GUID wsaid_transmitfile = WSAID_TRANSMITFILE;
     DWORD dw;
 
+    if (flags & 1 /* APR_SENDFILE_DISCONNECT_SOCKET */) {
+        return APR_EINVAL;
+    }
+
     /* According to documentation TransmitFile() should not be used directly.
      * Pointer to function should retrieved using WSAIoctl:
      * 
https://docs.microsoft.com/en-gb/windows/win32/api/mswsock/nf-mswsock-transmitfile#remarks
@@ -388,13 +391,6 @@
                     sendv_trailers = 1;
                 }
             }
-            /* Disconnect the socket after last send */
-            if ((flags & APR_SENDFILE_DISCONNECT_SOCKET)
-                    && !sendv_trailers) {
-                dwFlags |= TF_REUSE_SOCKET;
-                dwFlags |= TF_DISCONNECT;
-                disconnected = 1;
-            }
         }
 
         sock->overlapped->Offset = (DWORD)(curoff);
@@ -419,22 +415,20 @@
                                                  ? sock->timeout_ms : 
INFINITE));
                 if (rv == WAIT_OBJECT_0) {
                     status = APR_SUCCESS;
-                    if (!disconnected) {
-                        if (!WSAGetOverlappedResult(sock->socketdes,
-                                                    sock->overlapped,
-                                                    &xmitbytes,
-                                                    FALSE,
-                                                    &dwFlags)) {
-                            status = apr_get_netos_error();
-                        }
-                        /* Ugly code alert: WSAGetOverlappedResult returns
-                         * a count of all bytes sent. This loop only
-                         * tracks bytes sent out of the file.
-                         */
-                        else if (ptfb) {
-                            xmitbytes -= (ptfb->HeadLength + ptfb->TailLength);
-                        }
+                    if (!WSAGetOverlappedResult(sock->socketdes,
+                                                sock->overlapped,
+                                                &xmitbytes,
+                                                FALSE,
+                                                &dwFlags)) {
+                        status = apr_get_netos_error();
                     }
+                    /* Ugly code alert: WSAGetOverlappedResult returns
+                     * a count of all bytes sent. This loop only
+                     * tracks bytes sent out of the file.
+                     */
+                    else if (ptfb) {
+                        xmitbytes -= (ptfb->HeadLength + ptfb->TailLength);
+                    }
                 }
                 else if (rv == WAIT_TIMEOUT) {
                     status = APR_FROM_OS_ERROR(WAIT_TIMEOUT);
@@ -473,17 +467,6 @@
                 return rv;
             *len += nbytes;
         }
-
-    
-        /* Mark the socket as disconnected, but do not close it.
-         * Note: The application must have stored the socket prior to making
-         * the call to apr_socket_sendfile in order to either reuse it 
-         * or close it.
-         */
-        if (disconnected) {
-            sock->disconnected = 1;
-            sock->socketdes = INVALID_SOCKET;
-        }
     }
 
     return status;

Reply via email to