/* * ==================================================================== * 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. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.ogt.http.impl; import junit.framework.TestCase; import org.apache.ogt.http.ConnectionReuseStrategy; import org.apache.ogt.http.HttpConnection; import org.apache.ogt.http.HttpConnectionMetrics; import org.apache.ogt.http.HttpResponse; import org.apache.ogt.http.HttpVersion; import org.apache.ogt.http.StatusLine; import org.apache.ogt.http.entity.BasicHttpEntity; import org.apache.ogt.http.impl.DefaultConnectionReuseStrategy; import org.apache.ogt.http.message.BasicHttpResponse; import org.apache.ogt.http.message.BasicStatusLine; import org.apache.ogt.http.protocol.BasicHttpContext; import org.apache.ogt.http.protocol.ExecutionContext; import org.apache.ogt.http.protocol.HttpContext; public class TestDefaultConnectionReuseStrategy extends TestCase { /** A mock connection that is open and not stale. */ private HttpConnection mockConnection; /** HTTP context. */ private HttpContext context; /** The reuse strategy to be tested. */ private ConnectionReuseStrategy reuseStrategy; public TestDefaultConnectionReuseStrategy(String testName) { super(testName); } public void setUp() { // open and not stale is required for most of the tests here mockConnection = new MockConnection(true, false); reuseStrategy = new DefaultConnectionReuseStrategy(); context = new BasicHttpContext(null); context.setAttribute(ExecutionContext.HTTP_CONNECTION, mockConnection); } public void tearDown() { mockConnection = null; } // ------------------------------------------------------- TestCase Methods public void testIllegalResponseArg() throws Exception { HttpContext context = new BasicHttpContext(null); try { reuseStrategy.keepAlive(null, context); fail("IllegalArgumentException should have been thrown"); } catch (IllegalArgumentException ex) { // expected } } public void testIllegalContextArg() throws Exception { HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", false, -1); try { reuseStrategy.keepAlive(response, null); fail("IllegalArgumentException should have been thrown"); } catch (IllegalArgumentException ex) { // expected } } public void testNoContentLengthResponseHttp1_0() throws Exception { HttpResponse response = createResponse(HttpVersion.HTTP_1_0, 200, "OK", false, -1); assertFalse(reuseStrategy.keepAlive(response, context)); } public void testNoContentLengthResponseHttp1_1() throws Exception { HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", false, -1); assertFalse(reuseStrategy.keepAlive(response, context)); } public void testChunkedContent() throws Exception { HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); assertTrue(reuseStrategy.keepAlive(response, context)); } public void testClosedConnection() throws Exception { // based on testChunkedContent which is known to return true // the difference is in the mock connection passed here HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); HttpConnection mockonn = new MockConnection(false, false); context.setAttribute(ExecutionContext.HTTP_CONNECTION, mockonn); assertFalse("closed connection should not be kept alive", reuseStrategy.keepAlive(response, context)); } public void testStaleConnection() throws Exception { // based on testChunkedContent which is known to return true // the difference is in the mock connection passed here HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); HttpConnection mockonn = new MockConnection(true, true); context.setAttribute(ExecutionContext.HTTP_CONNECTION, mockonn); assertTrue("stale connection should not be detected", reuseStrategy.keepAlive(response, context)); } public void testIgnoreInvalidKeepAlive() throws Exception { HttpResponse response = createResponse(HttpVersion.HTTP_1_0, 200, "OK", false, -1); response.addHeader("Connection", "keep-alive"); assertFalse(reuseStrategy.keepAlive(response, context)); } public void testExplicitClose() throws Exception { // Use HTTP 1.1 HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); response.addHeader("Connection", "close"); assertFalse(reuseStrategy.keepAlive(response, context)); } public void testExplicitKeepAlive() throws Exception { // Use HTTP 1.0 HttpResponse response = createResponse(HttpVersion.HTTP_1_0, 200, "OK", false, 10); response.addHeader("Connection", "keep-alive"); assertTrue(reuseStrategy.keepAlive(response, context)); } public void testHTTP10Default() throws Exception { HttpResponse response = createResponse(HttpVersion.HTTP_1_0, 200, "OK"); assertFalse(reuseStrategy.keepAlive(response, context)); } public void testHTTP11Default() throws Exception { HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK"); assertTrue(reuseStrategy.keepAlive(response, context)); } public void testFutureHTTP() throws Exception { HttpResponse response = createResponse(new HttpVersion(3, 45), 200, "OK"); assertTrue(reuseStrategy.keepAlive(response, context)); } public void testBrokenConnectionDirective1() throws Exception { // Use HTTP 1.0 HttpResponse response = createResponse(HttpVersion.HTTP_1_0, 200, "OK"); response.addHeader("Connection", "keep--alive"); assertFalse(reuseStrategy.keepAlive(response, context)); } public void testBrokenConnectionDirective2() throws Exception { // Use HTTP 1.0 HttpResponse response = createResponse(HttpVersion.HTTP_1_0, 200, "OK"); response.addHeader("Connection", null); assertFalse(reuseStrategy.keepAlive(response, context)); } public void testConnectionTokens1() throws Exception { // Use HTTP 1.1 HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); response.addHeader("Connection", "yadda, cLOSe, dumdy"); assertFalse(reuseStrategy.keepAlive(response, context)); } public void testConnectionTokens2() throws Exception { // Use HTTP 1.1 HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); response.addHeader("Connection", "yadda, kEEP-alive, dumdy"); assertTrue(reuseStrategy.keepAlive(response, context)); } public void testConnectionTokens3() throws Exception { // Use HTTP 1.1 HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); response.addHeader("Connection", "yadda, keep-alive, close, dumdy"); assertFalse(reuseStrategy.keepAlive(response, context)); } public void testConnectionTokens4() throws Exception { // Use HTTP 1.1 HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); response.addHeader("Connection", "yadda, close, dumdy"); response.addHeader("Proxy-Connection", "keep-alive"); // Connection takes precedence over Proxy-Connection assertFalse(reuseStrategy.keepAlive(response, context)); } public void testConnectionTokens5() throws Exception { // Use HTTP 1.1 HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); response.addHeader("Connection", "yadda, dumdy"); response.addHeader("Proxy-Connection", "close"); // Connection takes precedence over Proxy-Connection, // even if it doesn't contain a recognized token. // Default for HTTP/1.1 is to keep alive. assertTrue(reuseStrategy.keepAlive(response, context)); } public void testConnectionTokens6() throws Exception { // Use HTTP 1.1 HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); response.addHeader("Connection", ""); response.addHeader("Proxy-Connection", "close"); // Connection takes precedence over Proxy-Connection, // even if it is empty. Default for HTTP/1.1 is to keep alive. assertTrue(reuseStrategy.keepAlive(response, context)); } public void testConnectionTokensInvalid() throws Exception { // Use HTTP 1.1 HttpResponse response = createResponse(HttpVersion.HTTP_1_1, 200, "OK", true, -1); response.addHeader("Connection", "keep-alive=true"); assertFalse(reuseStrategy.keepAlive(response, context)); } /** * Creates a response without an entity. * * @param version the HTTP version * @param status the status code * @param message the status message * * @return a response with the argument attributes, but no headers */ private final static HttpResponse createResponse(HttpVersion version, int status, String message) { StatusLine statusline = new BasicStatusLine(version, status, message); HttpResponse response = new BasicHttpResponse(statusline); return response; } // createResponse/empty /** * Creates a response with an entity. * * @param version the HTTP version * @param status the status code * @param message the status message * @param chunked whether the entity should indicate chunked encoding * @param length the content length to be indicated by the entity * * @return a response with the argument attributes, but no headers */ private final static HttpResponse createResponse(HttpVersion version, int status, String message, boolean chunked, int length) { BasicHttpEntity entity = new BasicHttpEntity(); entity.setChunked(chunked); entity.setContentLength(length); HttpResponse response = createResponse(version, status, message); response.setEntity(entity); return response; } // createResponse/entity /** * A mock connection. * This is neither client nor server connection, since the default * strategy is agnostic. It does not allow modification of it's state, * since the strategy is supposed to decide about keep-alive, but not * to modify the connection's state. */ private final static class MockConnection implements HttpConnection { private boolean iAmOpen; private boolean iAmStale; public MockConnection(boolean open, boolean stale) { iAmOpen = open; iAmStale = stale; } public final boolean isOpen() { return iAmOpen; } public void setSocketTimeout(int timeout) { } public int getSocketTimeout() { return -1; } public final boolean isStale() { return iAmStale; } public final void close() { throw new UnsupportedOperationException ("connection state must not be modified"); } public final void shutdown() { throw new UnsupportedOperationException ("connection state must not be modified"); } public HttpConnectionMetrics getMetrics() { return null; } } // class MockConnection } // class TestDefaultConnectionReuseStrategy