LocalTestServer and related stuff. No actual test cases yet.

git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@510634 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Roland Weber 2007-02-22 19:44:36 +00:00
parent 8055c100ca
commit 6c68de5452
7 changed files with 1293 additions and 0 deletions

View File

@ -34,6 +34,7 @@ import org.apache.http.cookie.TestAllCookie;
import org.apache.http.impl.cookie.TestAllCookieImpl;
import org.apache.http.conn.ssl.TestAllSSL;
import org.apache.http.impl.client.TestAllHttpClientImpl;
import org.apache.http.impl.conn.TestAllConnImpl;
import junit.framework.*;
@ -48,6 +49,7 @@ public class TestAll extends TestCase {
suite.addTest(TestAllCookie.suite());
suite.addTest(TestAllCookieImpl.suite());
suite.addTest(TestAllHttpClientImpl.suite());
suite.addTest(TestAllConnImpl.suite());
suite.addTest(TestAllSSL.suite());
return suite;
}

View File

@ -0,0 +1,54 @@
/*
* $HeadURL$
* $Revision$
* $Date$
* ====================================================================
* 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.http.impl.conn;
import junit.framework.*;
public class TestAllConnImpl extends TestCase {
public TestAllConnImpl(String testName) {
super(testName);
}
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(TestLocalServer.suite());
return suite;
}
public static void main(String args[]) {
String[] testCaseName = { TestAllConnImpl.class.getName() };
junit.textui.TestRunner.main(testCaseName);
}
}

View File

@ -0,0 +1,127 @@
/*
* $HeadURL$
* $Revision$
* $Date$
* ====================================================================
* 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.http.impl.conn;
import java.net.Socket;
import junit.framework.*;
import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpClientConnection;
import org.apache.http.message.HttpGet;
import org.apache.http.message.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.apache.http.localserver.ServerTestBase;
/**
* This is more a test for the {@link LocalTestServer LocalTestServer}
* than anything else.
*/
public class TestLocalServer extends ServerTestBase {
public TestLocalServer(String testName) {
super(testName);
}
public static Test suite() {
return new TestSuite(TestLocalServer.class);
}
public static void main(String args[]) {
String[] testCaseName = { TestLocalServer.class.getName() };
junit.textui.TestRunner.main(testCaseName);
}
public void testEcho() throws Exception {
final String message = "Hello, world!";
final String charset = "UTF-8";
final HttpHost target = getServerHttp();
HttpPost request = new HttpPost("/echo/");
request.setHeader("Host", target.getHostName());
request.setEntity(new StringEntity(message, charset));
HttpClientConnection conn = connectTo(target);
HttpResponse response = httpExecutor.execute
(request, conn, httpContext);
assertEquals("wrong status in response", HttpStatus.SC_OK,
response.getStatusLine().getStatusCode());
String received = EntityUtils.toString(response.getEntity());
conn.close();
assertEquals("wrong echo", message, received);
}
public void testRandom() throws Exception {
final HttpHost target = getServerHttp();
int[] sizes = new int[] {
10, 2048, 4100, 0, -1
};
for (int i=0; i<sizes.length; i++) {
String uri = "/random/" + sizes[i];
if (sizes[i] < 0)
uri += "/";
HttpGet request = new HttpGet(uri);
HttpClientConnection conn = connectTo(target);
HttpResponse response = httpExecutor.execute
(request, conn, httpContext);
assertEquals("(" + sizes[i] + ") wrong status in response",
HttpStatus.SC_OK,
response.getStatusLine().getStatusCode());
byte[] data = EntityUtils.toByteArray(response.getEntity());
if (sizes[i] >= 0)
assertEquals("(" + sizes[i] + ") wrong length of response",
sizes[i], data.length);
conn.close();
}
}
}

View File

@ -0,0 +1,114 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* 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.http.localserver;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.MethodNotSupportedException;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.util.EntityUtils;
/**
* A handler that echos the incoming request entity.
*
* @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
*
*
* <!-- empty lines to avoid 'svn diff' problems -->
* @version $Revision$
*/
public class EchoHandler
implements HttpRequestHandler {
// public default constructor
/**
* Handles a request by echoing the incoming request entity.
* If there is no request entity, an empty document is returned.
*
* @param request the request
* @param response the response
* @param context the context
*
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
*/
public void handle(final HttpRequest request,
final HttpResponse response,
final HttpContext context)
throws HttpException, IOException {
String method = request.getRequestLine().getMethod().toUpperCase();
if (!"GET".equals(method) &&
!"POST".equals(method) &&
!"PUT".equals(method)
) {
throw new MethodNotSupportedException
(method + " not supported by " + getClass().getName());
}
HttpEntity entity = null;
if (request instanceof HttpEntityEnclosingRequest)
entity = ((HttpEntityEnclosingRequest)request).getEntity();
// For some reason, just putting the incoming entity into
// the response will not work. We have to buffer the message.
byte[] data = null;
if (entity == null) {
data = new byte [0];
} else {
data = EntityUtils.toByteArray(entity);
}
ByteArrayEntity bae = new ByteArrayEntity(data);
bae.setContentType(entity.getContentType());
entity = bae;
response.setStatusCode(HttpStatus.SC_OK);
response.setEntity(entity);
} // handle
} // class EchoHandler

View File

@ -0,0 +1,508 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* 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.http.localserver;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetSocketAddress;
import java.net.BindException;
import java.util.Set;
import java.util.HashSet;
import java.util.Collections;
import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpServerConnection;
import org.apache.http.HttpStatus;
import org.apache.http.MethodNotSupportedException;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpExecutionContext;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.protocol.HttpRequestHandlerRegistry;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.apache.http.util.EntityUtils;
/**
* Local HTTP server for tests that require one.
* Based on the <code>ElementalHttpServer</code> example in HttpCore.
*
* @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
*
*
* <!-- empty lines to avoid 'svn diff' problems -->
* @version $Revision$
*/
public class LocalTestServer {
/**
* The local network address to bind to.
* This is an IP number rather than "localhost" to avoid surprises
* on hosts that map "localhost" to an IPv6 address or something else.
*/
public final static String TEST_SERVER_ADDR = "127.0.0.1";
/** The suggested port number for a local test server. */
public final static int TEST_SERVER_PORT = 39888;
/** The request handler registry. */
public HttpRequestHandlerRegistry handlerRegistry;
/** The server-side connection re-use strategy. */
public ConnectionReuseStrategy reuseStrategy;
/**
* The HTTP processor.
* If the interceptors are thread safe and the list is not
* modified during operation, the processor is thread safe.
*/
public BasicHttpProcessor httpProcessor;
/** The server parameters. */
public HttpParams serverParams;
/** The server socket, while being served. */
protected ServerSocket servicedSocket;
/** The request listening thread, while listening. */
protected Thread listenerThread;
/**
* Creates a new test server.
*
* @param proc the HTTP processors to be used by the server, or
* <code>null</code> to use a
* {@link #newProcessor default} processor
* @param params the parameters to be used by the server, or
* <code>null</code> to use
* {@link #newDefaultParams default} parameters
*/
public LocalTestServer(BasicHttpProcessor proc, HttpParams params) {
handlerRegistry = new HttpRequestHandlerRegistry();
reuseStrategy = new DefaultConnectionReuseStrategy();
httpProcessor = (proc != null) ? proc : newProcessor();
serverParams = (params != null) ? params : newDefaultParams();
}
/**
* Obtains an HTTP protocol processor with default interceptors.
*
* @return a protocol processor for server-side use
*/
public static BasicHttpProcessor newProcessor() {
BasicHttpProcessor httpproc = new BasicHttpProcessor();
httpproc.addInterceptor(new ResponseDate());
httpproc.addInterceptor(new ResponseServer());
httpproc.addInterceptor(new ResponseContent());
httpproc.addInterceptor(new ResponseConnControl());
return httpproc;
}
/**
* Obtains a set of reasonable default parameters for a server.
*
* @return default parameters
*/
public static HttpParams newDefaultParams() {
HttpParams params = new BasicHttpParams(null);
params
.setIntParameter(HttpConnectionParams.SO_TIMEOUT,
5000)
.setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE,
8 * 1024)
.setBooleanParameter(HttpConnectionParams.STALE_CONNECTION_CHECK,
false)
.setBooleanParameter(HttpConnectionParams.TCP_NODELAY,
true)
.setParameter(HttpProtocolParams.ORIGIN_SERVER,
"Jakarta-HttpComponents-LocalTestServer/1.1");
return params;
}
/**
* {@link #register Registers} a set of default request handlers.
* <pre>
* URI pattern Handler
* ----------- -------
* /echo/* {@link EchoHandler EchoHandler}
* /random/* {@link RandomHandler RandomHandler}
* </pre>
*/
public void registerDefaultHandlers() {
handlerRegistry.register("/echo/*", new EchoHandler());
handlerRegistry.register("/random/*", new RandomHandler());
}
/**
* Registers a handler with the local registry.
*
* @param pattern the URL pattern to match
* @param handler the handler to apply
*/
public void register(String pattern, HttpRequestHandler handler) {
handlerRegistry.register(pattern, handler);
}
/**
* Unregisters a handler from the local registry.
*
* @param pattern the URL pattern
*/
public void unregister(String pattern) {
handlerRegistry.unregister(pattern);
}
/**
* Specifies the connection re-use strategy.
*
* @param strategy the re-use strategy
*/
public void setReuseStrategy(ConnectionReuseStrategy strategy) {
reuseStrategy = strategy;
}
/**
* Starts this test server.
* Since stopping and starting this server in quick succession may
* leave a local port bound to an already closed server socket for
* a while, the port number can not be guaranteed.
* Use {@link #getServicePort getServicePort} to obtain the port number.
*
* @param port the port number to try first,
* 0 or negative if you don't care
*/
public void start(int port) throws Exception {
if (servicedSocket != null)
throw new IllegalStateException
(this.toString() + " already running");
if (port <= 0)
port = TEST_SERVER_PORT;
final int stop = port + 10;
ServerSocket ssock = new ServerSocket();
ssock.setReuseAddress(true);
while (!ssock.isBound()) {
try {
InetSocketAddress addr = new
InetSocketAddress(TEST_SERVER_ADDR, port);
ssock.bind(addr);
} catch (BindException bx) {
port++; // try next one
if (port >= stop)
throw bx; // no use trying any more
}
}
servicedSocket = ssock;
listenerThread = new Thread(new RequestListener());
listenerThread.setDaemon(false);
listenerThread.start();
}
/**
* Stops this test server.
*/
public void stop() throws Exception {
if (servicedSocket == null)
return; // not running
try {
servicedSocket.close();
} catch (IOException iox) {
System.out.println("error stopping " + this);
iox.printStackTrace(System.out);
} finally {
servicedSocket = null;
}
if (listenerThread != null) {
listenerThread.interrupt();
listenerThread = null;
}
}
public String toString() {
ServerSocket ssock = servicedSocket; // avoid synchronization
StringBuffer sb = new StringBuffer(80);
sb.append("LocalTestServer/");
if (ssock == null)
sb.append("stopped");
else
sb.append(ssock.getLocalSocketAddress());
return sb.toString();
}
/**
* Obtains the port this server is servicing.
*
* @return the service port
*/
public int getServicePort() {
ServerSocket ssock = servicedSocket; // avoid synchronization
if (ssock == null)
throw new IllegalStateException("not running");
return ssock.getLocalPort();
}
/**
* The request listener.
* Accepts incoming connections and launches a service thread.
*/
public class RequestListener implements Runnable {
/** The workers launched from here. */
private Set workerThreads =
Collections.synchronizedSet(new HashSet());
public void run() {
try {
while ((servicedSocket != null) &&
(listenerThread == Thread.currentThread()) &&
!Thread.interrupted()) {
try {
accept();
} catch (Exception e) {
ServerSocket ssock = servicedSocket;
if ((ssock != null) && !ssock.isClosed()) {
System.out.println
(LocalTestServer.this.toString() +
" could not accept");
e.printStackTrace(System.out);
}
// otherwise ignore the exception silently
break;
}
}
} finally {
cleanup();
}
}
protected void accept() throws IOException {
// Set up HTTP connection
Socket socket = servicedSocket.accept();
DefaultHttpServerConnection conn =
new DefaultHttpServerConnection();
conn.bind(socket, serverParams);
// Set up the HTTP service
HttpService httpService = new HttpService(
httpProcessor,
new DefaultConnectionReuseStrategy(),
new DefaultHttpResponseFactory());
httpService.setParams(serverParams);
httpService.setHandlerResolver(handlerRegistry);
// Start worker thread
Thread t = new Thread(new Worker(httpService, conn));
workerThreads.add(t);
t.setDaemon(true);
t.start();
} // accept
protected void cleanup() {
Thread[] threads = (Thread[]) workerThreads.toArray(new Thread[0]);
for (int i=0; i<threads.length; i++) {
if (threads[i] != null)
threads[i].interrupt();
}
}
/**
* A worker for serving incoming requests.
*/
public class Worker implements Runnable {
private final HttpService httpservice;
private final HttpServerConnection conn;
public Worker(
final HttpService httpservice,
final HttpServerConnection conn) {
this.httpservice = httpservice;
this.conn = conn;
}
public void run() {
HttpContext context = new HttpExecutionContext(null);
try {
while ((servicedSocket != null) &&
this.conn.isOpen() && !Thread.interrupted()) {
this.httpservice.handleRequest(this.conn, context);
}
} catch (IOException ex) {
// ignore silently
} catch (HttpException ex) {
// ignore silently
} finally {
workerThreads.remove(Thread.currentThread());
try {
this.conn.shutdown();
} catch (IOException ignore) {}
}
}
} // class Worker
} // class RequestListener
/*
static class HttpFileHandler implements HttpRequestHandler {
private final String docRoot;
public HttpFileHandler(final String docRoot) {
super();
this.docRoot = docRoot;
}
public void handle(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
String method = request.getRequestLine().getMethod().toUpperCase();
if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) {
throw new MethodNotSupportedException(method + " method not supported");
}
String target = request.getRequestLine().getUri();
if (request instanceof HttpEntityEnclosingRequest) {
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
byte[] entityContent = EntityUtils.toByteArray(entity);
System.out.println("Incoming entity content (bytes): " + entityContent.length);
}
final File file = new File(this.docRoot, URLDecoder.decode(target));
if (!file.exists()) {
response.setStatusCode(HttpStatus.SC_NOT_FOUND);
EntityTemplate body = new EntityTemplate(new ContentProducer() {
public void writeTo(final OutputStream outstream) throws IOException {
OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
writer.write("<html><body><h1>");
writer.write("File ");
writer.write(file.getPath());
writer.write(" not found");
writer.write("</h1></body></html>");
writer.flush();
}
});
body.setContentType("text/html; charset=UTF-8");
response.setEntity(body);
System.out.println("File " + file.getPath() + " not found");
} else if (!file.canRead() || file.isDirectory()) {
response.setStatusCode(HttpStatus.SC_FORBIDDEN);
EntityTemplate body = new EntityTemplate(new ContentProducer() {
public void writeTo(final OutputStream outstream) throws IOException {
OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
writer.write("<html><body><h1>");
writer.write("Access denied");
writer.write("</h1></body></html>");
writer.flush();
}
});
body.setContentType("text/html; charset=UTF-8");
response.setEntity(body);
System.out.println("Cannot read file " + file.getPath());
} else {
response.setStatusCode(HttpStatus.SC_OK);
FileEntity body = new FileEntity(file, "text/html");
response.setEntity(body);
System.out.println("Serving file " + file.getPath());
}
}
}
*/
}

View File

@ -0,0 +1,255 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* 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.http.localserver;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.MethodNotSupportedException;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.util.EntityUtils;
/**
* A handler that generates random data.
*
* @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
*
*
* <!-- empty lines to avoid 'svn diff' problems -->
* @version $Revision$
*/
public class RandomHandler
implements HttpRequestHandler {
// public default constructor
/**
* Handles a request by generating random data.
* The length of the response can be specified in the request URI
* as a number after the last /. For example /random/whatever/20
* will generate 20 random bytes in the printable ASCII range.
* If the request URI ends with /, a random number of random bytes
* is generated, but at least one.
*
* @param request the request
* @param response the response
* @param context the context
*
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
*/
public void handle(final HttpRequest request,
final HttpResponse response,
final HttpContext context)
throws HttpException, IOException {
String method = request.getRequestLine().getMethod().toUpperCase();
if (!"GET".equals(method) && !"HEAD".equals(method)) {
throw new MethodNotSupportedException
(method + " not supported by " + getClass().getName());
}
String uri = request.getRequestLine().getUri();
int slash = uri.lastIndexOf('/');
int length = -1;
if (slash < uri.length()-1) {
try {
// no more than Integer, 2 GB ought to be enough for anybody
length = Integer.parseInt(uri.substring(slash+1));
if (length < 0) {
response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
response.setReasonPhrase("LENGTH " + length);
}
} catch (NumberFormatException nfx) {
response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
response.setReasonPhrase(nfx.toString());
}
} else {
// random length, but make sure at least something is sent
length = 1 + (int)(Math.random() * 79.0);
}
if (length >= 0) {
response.setStatusCode(HttpStatus.SC_OK);
if (!"HEAD".equals(method)) {
RandomEntity entity = new RandomEntity(length);
entity.setContentType("text/plain; charset=US-ASCII");
response.setEntity(entity);
} else {
response.setHeader("Content-Type",
"text/plain; charset=US-ASCII");
response.setHeader("Content-Length",
String.valueOf(length));
}
}
} // handle
/**
* An entity that generates random data.
* This is an outgoing entity, it supports {@link #writeTo writeTo}
* but not {@link #getContent getContent}.
*/
public static class RandomEntity extends AbstractHttpEntity {
/** The range from which to generate random data. */
private final static byte[] RANGE;
static {
byte[] range = null;
try {
range = ("abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"
).getBytes("US-ASCII");
} catch (UnsupportedEncodingException uex) {
// never, US-ASCII is guaranteed
}
RANGE = range;
}
/** The length of the random data to generate. */
protected final long length;
/**
* Creates a new entity generating the given amount of data.
*
* @param len the number of random bytes to generate,
* 0 to maxint
*/
public RandomEntity(long len) {
if (len < 0L)
throw new IllegalArgumentException
("Length must not be negative");
if (len > (long) Integer.MAX_VALUE)
throw new IllegalArgumentException
("Length must not exceed Integer.MAX_VALUE");
length = len;
}
/**
* Tells that this entity is not streaming.
*
* @return false
*/
public final boolean isStreaming() {
return false;
}
/**
* Tells that this entity is repeatable, in a way.
* Repetitions will generate different random data,
* unless perchance the same random data is generated twice.
*
* @return <code>true</code>
*/
public boolean isRepeatable() {
return true;
}
/**
* Obtains the size of the random data.
*
* @return the number of random bytes to generate
*/
public long getContentLength() {
return length;
}
/**
* Not supported.
* This method throws an exception.
*
* @return never anything
*/
public InputStream getContent() {
throw new UnsupportedOperationException();
}
/**
* Generates the random content.
*
* @param out where to write the content to
*/
public void writeTo(OutputStream out) throws IOException {
final int blocksize = 2048;
int remaining = (int) length; // range checked in constructor
byte[] data = new byte[Math.min(remaining, blocksize)];
while (remaining > 0) {
final int end = Math.min(remaining, data.length);
double value = 0.0;
for (int i = 0; i < end; i++) {
// we get 5 random characters out of one random value
if (i%5 == 0) {
value = Math.random();
}
value = value * (double)RANGE.length;
int d = (int) value;
value = value - (double)d;
data[i] = RANGE[d];
}
out.write(data, 0, end);
out.flush();
remaining = remaining - end;
}
out.close();
} // writeTo
} // class RandomEntity
} // class RandomHandler

View File

@ -0,0 +1,233 @@
/*
* $HeadURL$
* $Revision$
* $Date$
* ====================================================================
* 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.http.localserver;
import java.net.Socket;
import junit.framework.*;
import org.apache.http.HttpHost;
import org.apache.http.HttpVersion;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.RequestConnControl;
import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.protocol.HttpExecutionContext;
import org.apache.http.impl.DefaultHttpClientConnection;
import org.apache.http.conn.Scheme;
import org.apache.http.conn.SchemeRegistry;
import org.apache.http.conn.SocketFactory;
import org.apache.http.conn.PlainSocketFactory;
/**
* Base class for tests using {@link LocalTestServer LocalTestServer}.
* Note that the test server will be {@link #setUp set up} before each
* individual tests and {@link #tearDown teared down} afterwards.
* Use this base class <i>exclusively</i> for tests that require the
* server. If you have some tests that require the server and others
* that don't, split them in two different classes.
*/
public abstract class ServerTestBase extends TestCase {
/** The local server for testing. */
protected LocalTestServer localServer;
/** The available schemes. */
protected SchemeRegistry supportedSchemes;
/** The default parameters for the client side. */
protected HttpParams defaultParams;
/** The HTTP processor for the client side. */
protected BasicHttpProcessor httpProcessor;
/** The default context for the client side. */
protected HttpExecutionContext httpContext;
/** The request executor for the client side. */
protected HttpRequestExecutor httpExecutor;
protected ServerTestBase(String testName) {
super(testName);
}
/**
* Prepares the local server for testing.
* Derived classes that override this method MUST call
* the implementation here. That SHOULD be done at the
* beginning of the overriding method.
* <br/>
* Derived methods can modify for example the default parameters
* being set up, or the interceptors.
* <p>
* This method will re-use the helper objects from a previous run
* if they are still available. For example, the local test server
* will be re-started rather than re-created.
* Tests that modify the helper objects should afterwards
* set the respective attributes to <code>null</code> to force
* re-creation for subsequent tests. Of course that shouldn't
* be done to the test server, or only after shutting that down.
*
* @throws Exception in case of a problem
*/
protected void setUp() throws Exception {
if (defaultParams == null) {
defaultParams = new BasicHttpParams(null);
HttpProtocolParams.setVersion
(defaultParams, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset
(defaultParams, "UTF-8");
HttpProtocolParams.setUserAgent
(defaultParams, "Jakarta-HttpComponents-Test/1.1");
HttpProtocolParams.setUseExpectContinue
(defaultParams, false);
}
if (supportedSchemes == null) {
supportedSchemes = new SchemeRegistry();
SocketFactory sf = PlainSocketFactory.getSocketFactory();
supportedSchemes.register(new Scheme("http", sf, 80));
}
if (httpProcessor == null) {
httpProcessor = new BasicHttpProcessor();
httpProcessor.addInterceptor(new RequestContent());
httpProcessor.addInterceptor(new RequestConnControl()); // optional
}
if (httpContext == null) {
httpContext = new HttpExecutionContext(null);
}
if (httpExecutor == null) {
httpExecutor = new HttpRequestExecutor(httpProcessor);
httpExecutor.setParams(defaultParams);
}
if (localServer == null) {
localServer = new LocalTestServer(null, null);
localServer.registerDefaultHandlers();
}
localServer.start(getPreferredPort());
} // setUp
/**
* Unprepares the local server for testing.
* This stops the test server. All helper objects, including the
* test server, remain stored in the attributes for the next test.
*
* @see #setUp setUp()
*/
protected void tearDown() throws Exception {
localServer.stop();
}
/**
* Obtains the preferred service port for the local test server.
*
* @return the preferred service port, or negative for don't care
*/
protected int getPreferredPort() {
return -1;
}
/**
* Obtains the address of the local test server.
*
* @return the test server host, with a scheme name of "http"
*/
protected HttpHost getServerHttp() {
return new HttpHost(LocalTestServer.TEST_SERVER_ADDR,
localServer.getServicePort(),
"http");
}
/**
* Opens a connection to the given target.
* Maps to {@link #connectTo(String,int) connectTo(host,port)}.
* This method performs a scheme lookup and determines the
* default port for the scheme if necessary.
*
* @param target the target to connect to
*
* @return a new connection opened to the target
*
* @throws Exception in case of a problem
*/
protected DefaultHttpClientConnection connectTo(HttpHost target)
throws Exception {
Scheme schm = supportedSchemes.get(target.getSchemeName());
int port = schm.resolvePort(target.getPort());
return connectTo(target.getHostName(), port);
}
/**
* Opens a connection.
*
* @param hostname the host name to connect to
* @param port the port to connect to
*
* @return a new connection opened to the target
*
* @throws Exception in case of a problem
*/
protected DefaultHttpClientConnection connectTo(String hostname, int port)
throws Exception {
DefaultHttpClientConnection conn = new DefaultHttpClientConnection();
Socket sock = new Socket(hostname, port);
conn.bind(sock, defaultParams);
return conn;
}
} // class ServerTestBase