]> granicus.if.org Git - apache/commitdiff
Win32: Reset the acceptex context on a GetQueuedCompletionStatus() failure. We were
authorBill Stoddard <stoddard@apache.org>
Tue, 22 Aug 2000 01:24:07 +0000 (01:24 +0000)
committerBill Stoddard <stoddard@apache.org>
Tue, 22 Aug 2000 01:24:07 +0000 (01:24 +0000)
losing contexts before this fix, and if we lost enough, the server would stop responding.

GetQueuedCompletionStatus can fail with ERROR_NETNAME_DELETED when a client closes
its end of a connection and we try to reuse the accept socket.  Really bizzare... I would
have thought the AcceptEx using the recycled socket would have failed instead...

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@86128 13f79535-47bb-0310-9956-ffa450edef68

server/mpm/winnt/mpm_winnt.c

index b546216a6971cfc1b7623863874d25db0c4d50aa..1b764f38fe26fdb124b6dd22f874d7265b1619c6 100644 (file)
@@ -912,50 +912,60 @@ static apr_inline apr_status_t reset_acceptex_context(PCOMP_CONTEXT context)
 {
     DWORD BytesRead;
     SOCKET nsd;
-    int rc;
-
-    /* recreate and initialize the accept socket if it is not being reused */
-    apr_get_os_sock(&nsd, context->lr->sd);
-    if (context->accept_socket == INVALID_SOCKET) {
-        context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-        if (context->accept_socket == INVALID_SOCKET) {
-            rc = WSAGetLastError();
-            ap_log_error(APLOG_MARK,APLOG_ERR, rc, server_conf,
-                         "reset_acceptex_context: socket() failed. Process will exit.");
-            return rc;
-        }
-        
-        /* SO_UPDATE_ACCEPT_CONTEXT is required for shutdown() to work */
-        if (setsockopt(context->accept_socket, SOL_SOCKET,
-                       SO_UPDATE_ACCEPT_CONTEXT, (char *)&nsd,
-                       sizeof(nsd))) {
-            ap_log_error(APLOG_MARK, APLOG_WARNING, WSAGetLastError(),
-                         server_conf,
-                         "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
-            /* Not a failure condition. Keep running. */
-        }
-    }
+    int rc, i;
 
-    /* reset the completion context */
+    /* reset the buffer pools */
     apr_clear_pool(context->ptrans);
     context->sock = NULL;
     context->conn_io = ap_bcreate(context->ptrans, B_RDWR);
     context->recv_buf = context->conn_io->inbase;
     context->recv_buf_size = context->conn_io->bufsiz - 2*PADDED_ADDR_SIZE;
 
+    /* recreate and initialize the accept socket if it is not being reused */
+    apr_get_os_sock(&nsd, context->lr->sd);
+
     /* AcceptEx on the completion context. The completion context will be signaled
-     * when a connection is accepted. */
-    if (!AcceptEx(nsd, context->accept_socket, context->recv_buf, 0,
-                  PADDED_ADDR_SIZE, PADDED_ADDR_SIZE, &BytesRead, 
-                  (LPOVERLAPPED) context)) {
-        rc = WSAGetLastError();
-        if (rc != ERROR_IO_PENDING) {
-            ap_log_error(APLOG_MARK, APLOG_INFO, rc, server_conf,
-                         "reset_acceptex_context: AcceptEx failed for "
-                         "listening socket: %d and accept socket: %d", 
-                         nsd, context->accept_socket);
-            return rc;
+     * when a connection is accepted. Hack Alert: TransmitFile, under certain 
+     * circumstances, can 'recycle' accept sockets, saving the overhead of calling 
+     * socket(). Occasionally this fails (usually when the client closes his end 
+     * of the connection early). When this occurs, AcceptEx will fail with 10022, 
+     * Invalid Parameter. When this occurs, just open a fresh accept socket and 
+     * retry the call.
+     */
+    for (i=0; i<2; i++) {
+        if (context->accept_socket == INVALID_SOCKET) {
+            context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+            if (context->accept_socket == INVALID_SOCKET) {
+                rc = WSAGetLastError();
+                ap_log_error(APLOG_MARK,APLOG_ERR, rc, server_conf,
+                             "reset_acceptex_context: socket() failed. Process will exit.");
+                return rc;
+            }
+
+            /* SO_UPDATE_ACCEPT_CONTEXT is required for shutdown() to work */
+            if (setsockopt(context->accept_socket, SOL_SOCKET,
+                           SO_UPDATE_ACCEPT_CONTEXT, (char *)&nsd, sizeof(nsd))) {
+                ap_log_error(APLOG_MARK, APLOG_WARNING, WSAGetLastError(),
+                             server_conf,
+                             "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
+            }
+        }
+
+        if (!AcceptEx(nsd, context->accept_socket, context->recv_buf, 0,
+                      PADDED_ADDR_SIZE, PADDED_ADDR_SIZE, &BytesRead, 
+                      (LPOVERLAPPED) context)) {
+            rc = WSAGetLastError();
+            if (rc != ERROR_IO_PENDING) {
+                ap_log_error(APLOG_MARK, APLOG_INFO, rc, server_conf,
+                             "reset_acceptex_context: AcceptEx failed for "
+                             "listening socket: %d and accept socket: %d", 
+                             nsd, context->accept_socket);
+                closesocket(context->accept_socket);
+                context->accept_socket = INVALID_SOCKET;
+                continue;
+            }
         }
+        break;
     }
 
     context->lr->count++;
@@ -981,22 +991,13 @@ static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
         else {
             /* Prepare the completion context for reuse */
             if ((rc = reset_acceptex_context(context)) != APR_SUCCESS) {
-                /* Retry once, this time requesting a new socket */
-                if (context->accept_socket != INVALID_SOCKET) {
+                ap_log_error(APLOG_MARK, APLOG_CRIT, rc, server_conf,
+                             "Child %d: winnt_get_connection: reset_acceptex_context failed.",
+                             my_pid); 
+                if (context->accept_socket != INVALID_SOCKET)
                     closesocket(context->accept_socket);
-                    context->accept_socket = INVALID_SOCKET;
-                }
-                if ((rc = reset_acceptex_context(context)) != APR_SUCCESS) {
-                    /* Failed again, so give up, but leave the thread up 
-                     * Should we signal a shutdown now? 
-                     */
-                    ap_log_error(APLOG_MARK, APLOG_ERR, rc, server_conf,
-                                 "Child %d: winnt_get_connection: reset_acceptex_context failed.",
-                                 my_pid); 
-                    if (context->accept_socket != INVALID_SOCKET)
-                        closesocket(context->accept_socket);
-                    CloseHandle(context->Overlapped.hEvent);
-                }
+                CloseHandle(context->Overlapped.hEvent);
+                /* Probably should just die now... */
             }
         }
     }
@@ -1014,10 +1015,30 @@ static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
         if (!rc) {
             rc = GetLastError();
             if (rc != ERROR_OPERATION_ABORTED) {
-                /* Is this a deadly condition? Hummm... */
-                ap_log_error(APLOG_MARK,APLOG_ERR, rc, server_conf,
+                /* Is this a deadly condition? 
+                 * We sometimes get ERROR_NETNAME_DELETED when a client
+                 * disconnects when attempting to reuse sockets. Not sure why 
+                 * we see this now and not during AcceptEx(). Reset the
+                 * AcceptEx context and continue...
+                 */
+                ap_log_error(APLOG_MARK,APLOG_INFO, rc, server_conf,
                              "Child %d: - GetQueuedCompletionStatus() failed", 
                              my_pid);
+                /* Reset the completion context */
+                if (pol) {
+                    context = (PCOMP_CONTEXT) pol;
+                    if (context->accept_socket != INVALID_SOCKET)
+                        closesocket(context->accept_socket);
+                    if ((rc = reset_acceptex_context(context)) != APR_SUCCESS) {
+                        ap_log_error(APLOG_MARK, APLOG_CRIT, rc, server_conf,
+                                     "Child %d: winnt_get_connection: reset_acceptex_context failed.",
+                                     my_pid); 
+                        if (context->accept_socket != INVALID_SOCKET)
+                            closesocket(context->accept_socket);
+                        CloseHandle(context->Overlapped.hEvent);
+                        /* Probably should just die now... */
+                    }
+                }
             }
             else {
                 /* Sometimes we catch ERROR_OPERATION_ABORTED completion packets
@@ -1026,7 +1047,6 @@ static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
                 ap_log_error(APLOG_MARK,APLOG_INFO, rc, server_conf,
                              "Child %d: - Draining ERROR_OPERATION_ABORTED packet off "
                              "the completion port.", my_pid);
-
             }
             continue;
         }