William A. Rowe, Jr. wrote:
Just to summarize, there are three conditions we need to consider:
1) we hit the TransmitFile recycle bug many times in a row
2) we have encountered an incompatible firewall or VPN
3) the IP address has changed


You seem to have the failcases easily reproduced. Would you tack in
some quick code that simply uses getsockopt(foo) (any option you like)
to see if simply getting socket options for a now-broken listen socket
will fail?

Actually I have not been able to reproduce the AcceptEx error for 3), however I think the following will address all three cases and introduces the WindowsSocketsWorkaround directive:

Index: mpm/winnt/child.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/child.c,v
retrieving revision 1.13
diff -u -d -b -r1.13 child.c
--- mpm/winnt/child.c   28 Feb 2003 14:02:42 -0000      1.13
+++ mpm/winnt/child.c   3 Mar 2003 22:31:15 -0000
@@ -498,7 +498,7 @@
     PCOMP_CONTEXT context = NULL;
     DWORD BytesRead;
     SOCKET nlsd;
-    int rv;
+    int rv, err_count = 0;

apr_os_sock_get(&nlsd, lr->sd);

@@ -538,15 +538,38 @@
             rv = apr_get_netos_error();
             if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) ||
                 (rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) {
-                /* Hack alert. Occasionally, TransmitFile will not recycle the
-                 * accept socket (usually when the client disconnects early).
-                 * Get a new socket and try the call again.
+                /* Hack alert, we can get here because:
+                 * 1) Occasionally, TransmitFile will not recycle the accept socket
+                 *    (usually when the client disconnects early).
+                 * 2) There is VPN or Firewall software installed with buggy AcceptEx 
implementation
+                 * 3) The webserver is using a dynamic address and it has changed
                  */
+                Sleep(0);
+                if (++err_count > 1000) {
+                    apr_int32_t disconnected;
+
+                    /* abitrary socket call to test if the Listening socket is still 
valid */
+                    apr_status_t listen_rv =  apr_socket_opt_get(lr->sd, 
APR_SO_DISCONNECTED, &disconnected);
+
+                    if (listen_rv == APR_SUCCESS) {
+                        ap_log_error(APLOG_MARK,APLOG_ERR, listen_rv, ap_server_conf,
+                                     "AcceptEx error: If this occurs constantly and NO 
requests are being served "
+                                     "try using the WindowsSocketsWorkaround directive set 
to 'on'.");
+                        err_count = 0;
+                    }
+                    else {
+                        ap_log_error(APLOG_MARK,APLOG_ERR, listen_rv, ap_server_conf,
+                                     "The Listening socket is no longer valid. Dynamic 
address changed?");
+                        break;
+                    }
+                }
+
                 closesocket(context->accept_socket);
                 context->accept_socket = INVALID_SOCKET;
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf,
-                       "winnt_accept: AcceptEx failed due to early client "
-                       "disconnect. Reallocate the accept socket and try again.");
+                       "winnt_accept: AcceptEx failed, either early client disconnect, 
"
+                       "dynamic address renewal, or incompatible VPN or Firewall 
software.");
+
                 continue;
             }
             else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) &&
@@ -558,6 +581,7 @@
                 Sleep(100);
                 continue;
             }
+            err_count = 0;

             /* Wait for pending i/o.
              * Wake up once per second to check for shutdown .
@@ -701,7 +725,7 @@
         ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL);

         /* Grab a connection off the network */
-        if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+        if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
windows_sockets_workaround == 1) {
             context = win9x_get_connection(context);
         }
         else {
@@ -769,7 +793,7 @@
 static void create_listener_thread()
 {
     int tid;
-    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
windows_sockets_workaround == 1) {
         _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) win9x_accept,
                        NULL, 0, &tid);
     } else {
@@ -840,7 +864,7 @@
      * Create the worker thread dispatch IOCompletionPort
      * on Windows NT/2000
      */
-    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
+    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS && 
windows_sockets_workaround != 1) {
         /* Create the worker thread dispatch IOCP */
         ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
                                                     NULL,
@@ -1007,7 +1031,7 @@
     }

     /* Shutdown the worker threads */
-    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
windows_sockets_workaround == 1) {
         for (i = 0; i < threads_created; i++) {
             add_job(INVALID_SOCKET);
         }
@@ -1065,7 +1089,7 @@

     CloseHandle(allowed_globals.jobsemaphore);
     apr_thread_mutex_destroy(allowed_globals.jobmutex);
-    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
+    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS && 
windows_sockets_workaround != 1) {
        apr_thread_mutex_destroy(qlock);
         CloseHandle(qwait_event);
     }
Index: mpm/winnt/mpm_winnt.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/mpm_winnt.c,v
retrieving revision 1.298
diff -u -d -b -r1.298 mpm_winnt.c
--- mpm/winnt/mpm_winnt.c       3 Feb 2003 17:53:25 -0000       1.298
+++ mpm/winnt/mpm_winnt.c       3 Mar 2003 22:31:15 -0000
@@ -102,6 +102,7 @@
 static DWORD parent_pid;
 DWORD my_pid;

+int windows_sockets_workaround = 0;
 int ap_threads_per_child = 0;
 static int thread_limit = DEFAULT_THREAD_LIMIT;
 static int first_thread_limit = 0;
@@ -217,6 +218,24 @@
     }
     return NULL;
 }
+static const char *set_sockets_workaround (cmd_parms *cmd, void *dummy, char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    windows_sockets_workaround = 0;
+    if (!strcasecmp(arg, "on")) {
+        windows_sockets_workaround = 1;
+    }
+    else if (strcasecmp(arg, "off")) {
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "WARNING: setting WindowsSocketsWorkaround to off");
+    }
+    return NULL;
+}
+

 static const command_rec winnt_cmds[] = {
 LISTEN_COMMANDS,
@@ -224,6 +243,9 @@
   "Number of threads each child creates" ),
 AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF,
   "Maximum worker threads in a server for this run of Apache"),
+AP_INIT_TAKE1("WindowsSocketsWorkaround", set_sockets_workaround, NULL, RSRC_CONF,
+  "Work around the buggy Winsock provider implementations of certain VPN or Firewall 
software"),
+
 { NULL }
 };

Index: mpm/winnt/mpm_winnt.h
===================================================================
RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/mpm_winnt.h,v
retrieving revision 1.41
diff -u -d -b -r1.41 mpm_winnt.h
--- mpm/winnt/mpm_winnt.h       3 Feb 2003 17:53:25 -0000       1.41
+++ mpm/winnt/mpm_winnt.h       3 Mar 2003 22:31:15 -0000
@@ -101,6 +101,7 @@

/* From winnt.c: */

+extern int windows_sockets_workaround;
 extern OSVERSIONINFO osver;
 extern void clean_child_exit(int);

I'll also put some doc together for the new directive.
....Allan
Index: mpm/winnt/child.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/child.c,v
retrieving revision 1.13
diff -u -d -b -r1.13 child.c
--- mpm/winnt/child.c   28 Feb 2003 14:02:42 -0000      1.13
+++ mpm/winnt/child.c   3 Mar 2003 22:31:15 -0000
@@ -498,7 +498,7 @@
     PCOMP_CONTEXT context = NULL;
     DWORD BytesRead;
     SOCKET nlsd;
-    int rv;
+    int rv, err_count = 0;
 
     apr_os_sock_get(&nlsd, lr->sd);
 
@@ -538,15 +538,38 @@
             rv = apr_get_netos_error();
             if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) ||
                 (rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) {
-                /* Hack alert. Occasionally, TransmitFile will not recycle the 
-                 * accept socket (usually when the client disconnects early). 
-                 * Get a new socket and try the call again.
+                /* Hack alert, we can get here because: 
+                 * 1) Occasionally, TransmitFile will not recycle the accept socket 
+                 *    (usually when the client disconnects early). 
+                 * 2) There is VPN or Firewall software installed with buggy AcceptEx 
implementation
+                 * 3) The webserver is using a dynamic address and it has changed
                  */
+                Sleep(0);
+                if (++err_count > 1000) { 
+                    apr_int32_t disconnected;
+
+                    /* abitrary socket call to test if the Listening socket is still 
valid */
+                    apr_status_t listen_rv =  apr_socket_opt_get(lr->sd, 
APR_SO_DISCONNECTED, &disconnected);
+
+                    if (listen_rv == APR_SUCCESS) {
+                        ap_log_error(APLOG_MARK,APLOG_ERR, listen_rv, ap_server_conf,
+                                     "AcceptEx error: If this occurs constantly and 
NO requests are being served "
+                                     "try using the WindowsSocketsWorkaround 
directive set to 'on'.");
+                        err_count = 0;
+                    }
+                    else {
+                        ap_log_error(APLOG_MARK,APLOG_ERR, listen_rv, ap_server_conf,
+                                     "The Listening socket is no longer valid. 
Dynamic address changed?");
+                        break;
+                    }
+                }
+
                 closesocket(context->accept_socket);
                 context->accept_socket = INVALID_SOCKET;
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf,
-                       "winnt_accept: AcceptEx failed due to early client "
-                       "disconnect. Reallocate the accept socket and try again.");
+                       "winnt_accept: AcceptEx failed, either early client 
disconnect, "
+                       "dynamic address renewal, or incompatible VPN or Firewall 
software.");
+          
                 continue;
             }
             else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) &&
@@ -558,6 +581,7 @@
                 Sleep(100);
                 continue;
             }
+            err_count = 0;  
 
             /* Wait for pending i/o. 
              * Wake up once per second to check for shutdown .
@@ -701,7 +725,7 @@
         ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL);
 
         /* Grab a connection off the network */
-        if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+        if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
windows_sockets_workaround == 1) {
             context = win9x_get_connection(context);
         }
         else {
@@ -769,7 +793,7 @@
 static void create_listener_thread()
 {
     int tid;
-    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
windows_sockets_workaround == 1) {
         _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) win9x_accept,
                        NULL, 0, &tid);
     } else {
@@ -840,7 +864,7 @@
      * Create the worker thread dispatch IOCompletionPort
      * on Windows NT/2000
      */
-    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
+    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS && 
windows_sockets_workaround != 1) {
         /* Create the worker thread dispatch IOCP */
         ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
                                                     NULL,
@@ -1007,7 +1031,7 @@
     }
 
     /* Shutdown the worker threads */
-    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+    if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
windows_sockets_workaround == 1) {
         for (i = 0; i < threads_created; i++) {
             add_job(INVALID_SOCKET);
         }
@@ -1065,7 +1089,7 @@
 
     CloseHandle(allowed_globals.jobsemaphore);
     apr_thread_mutex_destroy(allowed_globals.jobmutex);
-    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
+    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS && 
windows_sockets_workaround != 1) {
        apr_thread_mutex_destroy(qlock);
         CloseHandle(qwait_event);
     }
Index: mpm/winnt/mpm_winnt.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/mpm_winnt.c,v
retrieving revision 1.298
diff -u -d -b -r1.298 mpm_winnt.c
--- mpm/winnt/mpm_winnt.c       3 Feb 2003 17:53:25 -0000       1.298
+++ mpm/winnt/mpm_winnt.c       3 Mar 2003 22:31:15 -0000
@@ -102,6 +102,7 @@
 static DWORD parent_pid;
 DWORD my_pid;
 
+int windows_sockets_workaround = 0;
 int ap_threads_per_child = 0;
 static int thread_limit = DEFAULT_THREAD_LIMIT;
 static int first_thread_limit = 0;
@@ -217,6 +218,24 @@
     }
     return NULL;
 }
+static const char *set_sockets_workaround (cmd_parms *cmd, void *dummy, char *arg) 
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    windows_sockets_workaround = 0;
+    if (!strcasecmp(arg, "on")) {
+        windows_sockets_workaround = 1;
+    }
+    else if (strcasecmp(arg, "off")) {
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
+                         "WARNING: setting WindowsSocketsWorkaround to off");
+    }
+    return NULL;
+}
+
 
 static const command_rec winnt_cmds[] = {
 LISTEN_COMMANDS,
@@ -224,6 +243,9 @@
   "Number of threads each child creates" ),
 AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF,
   "Maximum worker threads in a server for this run of Apache"),
+AP_INIT_TAKE1("WindowsSocketsWorkaround", set_sockets_workaround, NULL, RSRC_CONF,
+  "Work around the buggy Winsock provider implementations of certain VPN or Firewall 
software"),
+
 { NULL }
 };
 
Index: mpm/winnt/mpm_winnt.h
===================================================================
RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/mpm_winnt.h,v
retrieving revision 1.41
diff -u -d -b -r1.41 mpm_winnt.h
--- mpm/winnt/mpm_winnt.h       3 Feb 2003 17:53:25 -0000       1.41
+++ mpm/winnt/mpm_winnt.h       3 Mar 2003 22:31:15 -0000
@@ -101,6 +101,7 @@
 
 /* From winnt.c: */
 
+extern int windows_sockets_workaround;
 extern OSVERSIONINFO osver;
 extern void clean_child_exit(int);
 

Reply via email to