Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=50903
authormarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Tue, 15 Mar 2011 17:48:15 +0000 (17:48 +0000)
committermarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Tue, 15 Mar 2011 17:48:15 +0000 (17:48 +0000)
If a connector is stopped, not not process any keep-alive connections. The exact behaviours are:
- HTTP BIO - blocks until request line is read and then returns 503
- HTTP NIO - uses polling, so returns 503 immediately connector is stopped
- HTTP APR - blocks until request line is read and then returns 503
- AJP BIO - blocks until request packet is received and then returns 503 - subsequent requests will timeout
- AJP APR - no change - subsequent requests will timeout

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

java/org/apache/coyote/ajp/AjpProcessor.java
java/org/apache/coyote/http11/Http11AprProcessor.java
java/org/apache/coyote/http11/Http11NioProcessor.java
java/org/apache/coyote/http11/Http11Processor.java
test/org/apache/catalina/connector/TestConnector.java [new file with mode: 0644]
test/org/apache/catalina/startup/TesterServlet.java [new file with mode: 0644]
test/org/apache/catalina/startup/TomcatBaseTest.java
webapps/docs/changelog.xml

index 3c5ac23..b018b02 100644 (file)
@@ -287,6 +287,13 @@ public class AjpProcessor extends AbstractAjpProcessor {
                 error = true;
             }
 
+            if (endpoint.isPaused()) {
+                // 503 - Service unavailable
+                response.setStatus(503);
+                adapter.log(request, response, 0);
+                error = true;
+            }
+
             // Process the request in the adapter
             if (!error) {
                 try {
index 0471e97..2f4f1aa 100644 (file)
@@ -249,15 +249,24 @@ public class Http11AprProcessor extends AbstractHttp11Processor {
                     openSocket = true;
                     // Add the socket to the poller
                     endpoint.getPoller().add(socketRef);
-                    break;
+                    if (endpoint.isPaused()) {
+                        // 503 - Service unavailable
+                        response.setStatus(503);
+                        adapter.log(request, response, 0);
+                        error = true;
+                    } else {
+                        break;
+                    }
                 }
-                request.setStartTime(System.currentTimeMillis());
-                keptAlive = true;
-                if (!disableUploadTimeout) {
-                    Socket.timeoutSet(socketRef,
-                            connectionUploadTimeout * 1000);
+                if (!endpoint.isPaused()) {
+                    request.setStartTime(System.currentTimeMillis());
+                    keptAlive = true;
+                    if (!disableUploadTimeout) {
+                        Socket.timeoutSet(socketRef,
+                                connectionUploadTimeout * 1000);
+                    }
+                    inputBuffer.parseHeaders();
                 }
-                inputBuffer.parseHeaders();
             } catch (IOException e) {
                 error = true;
                 break;
index 571a9d6..e03dec4 100644 (file)
@@ -330,20 +330,29 @@ public class Http11NioProcessor extends AbstractHttp11Processor {
                         // associated with socket
                         recycle = false;
                     }
-                    break;
-                }
-                keptAlive = true;
-                if ( !inputBuffer.parseHeaders() ) {
-                    //we've read part of the request, don't recycle it
-                    //instead associate it with the socket
-                    openSocket = true;
-                    recycle = false;
-                    break;
+                    if (endpoint.isPaused()) {
+                        // 503 - Service unavailable
+                        response.setStatus(503);
+                        adapter.log(request, response, 0);
+                        error = true;
+                    } else {
+                        break;
+                    }
                 }
-                request.setStartTime(System.currentTimeMillis());
-                if (!disableUploadTimeout) { //only for body, not for request headers
-                    socket.getIOChannel().socket().setSoTimeout(
-                            connectionUploadTimeout);
+                if (!endpoint.isPaused()) {
+                    keptAlive = true;
+                    if ( !inputBuffer.parseHeaders() ) {
+                        //we've read part of the request, don't recycle it
+                        //instead associate it with the socket
+                        openSocket = true;
+                        recycle = false;
+                        break;
+                    }
+                    request.setStartTime(System.currentTimeMillis());
+                    if (!disableUploadTimeout) { //only for body, not for request headers
+                        socket.getIOChannel().socket().setSoTimeout(
+                                connectionUploadTimeout);
+                    }
                 }
             } catch (IOException e) {
                 if (log.isDebugEnabled()) {
index 1d7361b..74ad4d0 100644 (file)
@@ -194,14 +194,21 @@ public class Http11Processor extends AbstractHttp11Processor {
                     }
                 }
                 inputBuffer.parseRequestLine(false);
-                request.setStartTime(System.currentTimeMillis());
-                keptAlive = true;
-                if (disableUploadTimeout) {
-                    socket.getSocket().setSoTimeout(soTimeout);
+                if (endpoint.isPaused()) {
+                    // 503 - Service unavailable
+                    response.setStatus(503);
+                    adapter.log(request, response, 0);
+                    error = true;
                 } else {
-                    socket.getSocket().setSoTimeout(connectionUploadTimeout);
+                    request.setStartTime(System.currentTimeMillis());
+                    keptAlive = true;
+                    if (disableUploadTimeout) {
+                        socket.getSocket().setSoTimeout(soTimeout);
+                    } else {
+                        socket.getSocket().setSoTimeout(connectionUploadTimeout);
+                    }
+                    inputBuffer.parseHeaders();
                 }
-                inputBuffer.parseHeaders();
             } catch (IOException e) {
                 error = true;
                 break;
diff --git a/test/org/apache/catalina/connector/TestConnector.java b/test/org/apache/catalina/connector/TestConnector.java
new file mode 100644 (file)
index 0000000..c84e3f6
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.catalina.connector;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.startup.TesterServlet;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+/**
+ * Test cases for {@link Connector}. 
+ */
+public class TestConnector extends TomcatBaseTest {
+
+    public void testStop() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+        
+        Context root = tomcat.addContext("", TEMP_DIR);
+        Wrapper w =
+            Tomcat.addServlet(root, "tester", new TesterServlet());
+        w.setAsyncSupported(true);
+        root.addServletMapping("/", "tester");
+
+        Connector connector = tomcat.getConnector();
+        
+        tomcat.start();
+
+        ByteChunk bc = new ByteChunk();
+        int rc = getUrl("http://localhost:" + getPort() + "/", bc, null, null);
+        
+        assertEquals(200, rc);
+        assertEquals("OK", bc.toString());
+        
+        rc = -1;
+        bc.recycle();
+
+        connector.stop();
+
+        rc = getUrl("http://localhost:" + getPort() + "/", bc, 1000,
+                null, null);
+        assertEquals(503, rc);
+    }
+}
diff --git a/test/org/apache/catalina/startup/TesterServlet.java b/test/org/apache/catalina/startup/TesterServlet.java
new file mode 100644 (file)
index 0000000..5334ce8
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.catalina.startup;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class TesterServlet extends HttpServlet {
+
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        
+        resp.setContentType("text/plain");
+        PrintWriter out = resp.getWriter();
+        out.print("OK");
+    }
+}
index fa7c28f..56bbb9b 100644 (file)
@@ -49,6 +49,8 @@ public abstract class TomcatBaseTest extends TestCase {
     private File tempDir;
     private static int port = 8000;
 
+    public static final String TEMP_DIR = System.getProperty("java.io.tmpdir");
+
     /**
      * Make Tomcat instance accessible to sub-classes.
      */
@@ -190,11 +192,18 @@ public abstract class TomcatBaseTest extends TestCase {
     public static int getUrl(String path, ByteChunk out,
             Map<String, List<String>> reqHead,
             Map<String, List<String>> resHead) throws IOException {
+        return getUrl(path, out, 1000000, reqHead, resHead);
+    }
+    
+    public static int getUrl(String path, ByteChunk out, int readTimeout,
+            Map<String, List<String>> reqHead,
+            Map<String, List<String>> resHead) throws IOException {
 
         URL url = new URL(path);
         HttpURLConnection connection = 
             (HttpURLConnection) url.openConnection();
-        connection.setReadTimeout(1000000);
+        connection.setUseCaches(false);
+        connection.setReadTimeout(readTimeout);
         if (reqHead != null) {
             for (Map.Entry<String, List<String>> entry : reqHead.entrySet()) {
                 StringBuilder valueList = new StringBuilder();
index faab229..02749f8 100644 (file)
         <bug>50887</bug>: Add support for configuring the JSSE provider used to
         convert client certificates. Based on a patch by pknopp. (markt)
       </add>
+      <fix>
+        <bug>50903</bug>: When a connector is stopped, ensure that requests that
+        are currently in a keep-alive state and waiting for client data are not
+        processed. Requests where processing has started will continue to
+        completion. (markt) 
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Web applications">