You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2016/06/02 13:08:38 UTC
svn commit: r1746554 -
/tomcat/trunk/test/org/apache/coyote/TestIoTimeouts.java
Author: markt
Date: Thu Jun 2 13:08:38 2016
New Revision: 1746554
URL: http://svn.apache.org/viewvc?rev=1746554&view=rev
Log:
Add new test case for read timeout during non-blocking IO
Added:
tomcat/trunk/test/org/apache/coyote/TestIoTimeouts.java (with props)
Added: tomcat/trunk/test/org/apache/coyote/TestIoTimeouts.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/TestIoTimeouts.java?rev=1746554&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/coyote/TestIoTimeouts.java (added)
+++ tomcat/trunk/test/org/apache/coyote/TestIoTimeouts.java Thu Jun 2 13:08:38 2016
@@ -0,0 +1,241 @@
+/*
+ * 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.coyote;
+
+import java.io.IOException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.startup.SimpleHttpClient;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+
+public class TestIoTimeouts extends TomcatBaseTest {
+
+ @Test
+ public void testNonBlockingReadWithNoTimeout() {
+ // Sends complete request in 3 packets
+ ChunkedClient client = new ChunkedClient(true);
+ client.doRequest();
+ Assert.assertTrue(client.isResponse200());
+ Assert.assertTrue(client.isResponseBodyOK());
+ Assert.assertNull(EchoListener.t);
+ }
+
+
+ @Test
+ public void testNonBlockingReadTimeout() {
+ // Sends incomplete request (no end chunk) so read times out
+ ChunkedClient client = new ChunkedClient(false);
+ client.doRequest();
+ Assert.assertFalse(client.isResponse200());
+ Assert.assertFalse(client.isResponseBodyOK());
+ // Socket will be closed before the error handler runs. Closing the
+ // socket triggers the client code's return from the doRequest() method
+ // above so we need to wait at this point for the error handler to be
+ // triggered.
+ int count = 0;
+ // Shouldn't need to wait long but allow plenty of time as the CI
+ // systems are sometimes slow.
+ while (count < 100 && EchoListener.t == null) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ count++;
+ }
+ Assert.assertNotNull(EchoListener.t);
+ }
+
+
+ private class ChunkedClient extends SimpleHttpClient {
+
+ private final boolean sendEndChunk;
+
+
+ public ChunkedClient(boolean sendEndChunk) {
+ this.sendEndChunk = sendEndChunk;
+ }
+
+
+ private Exception doRequest() {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Wrapper w = Tomcat.addServlet(root, "Test", new NonBlockingEchoServlet());
+ w.setAsyncSupported(true);
+ root.addServletMapping("/test", "Test");
+
+ try {
+ tomcat.start();
+ setPort(tomcat.getConnector().getLocalPort());
+
+ // Open connection
+ connect();
+
+ int packetCount = 2;
+ if (sendEndChunk) {
+ packetCount++;
+ }
+
+ String[] request = new String[packetCount];
+ request[0] =
+ "POST /test HTTP/1.1" + CRLF +
+ "Host: localhost:8080" + CRLF +
+ "Transfer-Encoding: chunked" + CRLF +
+ "Connection: close" + CRLF +
+ CRLF;
+ request[1] =
+ "b8" + CRLF +
+ "{" + CRLF +
+ " \"tenantId\": \"dotCom\", " + CRLF +
+ " \"locale\": \"en-US\", " + CRLF +
+ " \"defaultZoneId\": \"25\", " + CRLF +
+ " \"itemIds\": [\"StaplesUSCAS/en-US/2/<EOF>/<EOF>\"] , " + CRLF +
+ " \"assetStoreId\": \"5051\", " + CRLF +
+ " \"zipCode\": \"98109\"" + CRLF +
+ "}" + CRLF;
+ if (sendEndChunk) {
+ request[2] =
+ "0" + CRLF +
+ CRLF;
+ }
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+
+ // Close the connection
+ disconnect();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ if (getResponseBody() == null) {
+ return false;
+ }
+ if (!getResponseBody().contains("98109")) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+
+ private static class NonBlockingEchoServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ // Need to be in async mode to use non-blocking I/O
+ AsyncContext ac = req.startAsync();
+ ac.setTimeout(10000);
+
+ ServletInputStream sis = null;
+ ServletOutputStream sos = null;
+
+ try {
+ sis = req.getInputStream();
+ sos = resp.getOutputStream();
+ } catch (IOException ioe) {
+ throw new ServletException(ioe);
+ }
+
+ EchoListener listener = new EchoListener(ac, sis, sos);
+ sis.setReadListener(listener);
+ sos.setWriteListener(listener);
+ }
+ }
+
+
+ private static class EchoListener implements ReadListener, WriteListener {
+
+ private static volatile Throwable t;
+
+ private final AsyncContext ac;
+ private final ServletInputStream sis;
+ private final ServletOutputStream sos;
+ private final byte[] buffer = new byte[8192];
+
+ public EchoListener(AsyncContext ac, ServletInputStream sis, ServletOutputStream sos) {
+ t = null;
+ this.ac = ac;
+ this.sis = sis;
+ this.sos = sos;
+ }
+
+ @Override
+ public void onWritePossible() throws IOException {
+ if (sis.isFinished()) {
+ sos.flush();
+ ac.complete();
+ return;
+ }
+ while (sis.isReady()) {
+ int read = sis.read(buffer);
+ if (read > 0) {
+ sos.write(buffer, 0, read);
+ if (!sos.isReady()) {
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDataAvailable() throws IOException {
+ if (sos.isReady()) {
+ onWritePossible();
+ }
+ }
+
+ @Override
+ public void onAllDataRead() throws IOException {
+ if (sos.isReady()) {
+ onWritePossible();
+ }
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ t = throwable;
+ ac.complete();
+ }
+ }
+}
Propchange: tomcat/trunk/test/org/apache/coyote/TestIoTimeouts.java
------------------------------------------------------------------------------
svn:eol-style = native
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org