/tests/camel-itest/src/test/java/org/apache/camel/itest/http/HttpTestServer.java
Java | 404 lines | 240 code | 51 blank | 113 comment | 24 complexity | 673f6f5dd8ce1da7db4d65523af9438b MD5 | raw file
- /**
- * 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.camel.itest.http;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.Collections;
- import java.util.HashSet;
- import java.util.Set;
- import java.util.concurrent.atomic.AtomicInteger;
- import javax.net.ssl.SSLContext;
- import javax.net.ssl.SSLServerSocketFactory;
- import org.apache.camel.test.AvailablePortFinder;
- import org.apache.http.ConnectionReuseStrategy;
- import org.apache.http.HttpResponseFactory;
- import org.apache.http.HttpResponseInterceptor;
- import org.apache.http.HttpServerConnection;
- import org.apache.http.impl.DefaultConnectionReuseStrategy;
- import org.apache.http.impl.DefaultHttpResponseFactory;
- import org.apache.http.impl.DefaultHttpServerConnection;
- import org.apache.http.localserver.EchoHandler;
- import org.apache.http.localserver.RandomHandler;
- import org.apache.http.params.CoreConnectionPNames;
- import org.apache.http.params.CoreProtocolPNames;
- import org.apache.http.params.HttpParams;
- import org.apache.http.params.SyncBasicHttpParams;
- import org.apache.http.protocol.BasicHttpContext;
- import org.apache.http.protocol.BasicHttpProcessor;
- import org.apache.http.protocol.HttpContext;
- import org.apache.http.protocol.HttpExpectationVerifier;
- import org.apache.http.protocol.HttpProcessor;
- import org.apache.http.protocol.HttpRequestHandler;
- import org.apache.http.protocol.HttpRequestHandlerRegistry;
- import org.apache.http.protocol.HttpService;
- import org.apache.http.protocol.ImmutableHttpProcessor;
- import org.apache.http.protocol.ResponseConnControl;
- import org.apache.http.protocol.ResponseContent;
- import org.apache.http.protocol.ResponseDate;
- import org.apache.http.protocol.ResponseServer;
- /**
- * Copy of org.apache.http.localserver.LocalTestServer to use a specific port.
- */
- public class HttpTestServer {
-
- public static final int PORT = AvailablePortFinder.getNextAvailable(18080);
-
- /**
- * The local address to bind to.
- * The host is an IP number rather than "localhost" to avoid surprises
- * on hosts that map "localhost" to an IPv6 address or something else.
- * The port is 0 to let the system pick one.
- */
- public static final InetSocketAddress TEST_SERVER_ADDR =
- new InetSocketAddress("localhost", PORT);
- /** The request handler registry. */
- private final HttpRequestHandlerRegistry handlerRegistry;
- private final HttpService httpservice;
- /** Optional SSL context */
- private final SSLContext sslcontext;
- /** The server socket, while being served. */
- private volatile ServerSocket servicedSocket;
- /** The request listening thread, while listening. */
- private volatile ListenerThread listenerThread;
- /** Set of active worker threads */
- private final Set<Worker> workers;
- /** The number of connections this accepted. */
- private final AtomicInteger acceptedConnections = new AtomicInteger(0);
-
- static {
- //set them as system properties so Spring can use the property placeholder
- //things to set them into the URL's in the spring contexts
- System.setProperty("HttpTestServer.Port", Integer.toString(PORT));
- }
- /**
- * 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 reuseStrat the connection reuse strategy to be used by the
- * server, or <code>null</code> to use
- * {@link #newConnectionReuseStrategy() default}
- * strategy.
- * @param params the parameters to be used by the server, or
- * <code>null</code> to use
- * {@link #newDefaultParams default} parameters
- * @param sslcontext optional SSL context if the server is to leverage
- * SSL/TLS transport security
- */
- public HttpTestServer(
- final BasicHttpProcessor proc,
- final ConnectionReuseStrategy reuseStrat,
- final HttpResponseFactory responseFactory,
- final HttpExpectationVerifier expectationVerifier,
- final HttpParams params,
- final SSLContext sslcontext) {
- this.handlerRegistry = new HttpRequestHandlerRegistry();
- this.workers = Collections.synchronizedSet(new HashSet<Worker>());
- this.httpservice = new HttpService(
- proc != null ? proc : newProcessor(),
- reuseStrat != null ? reuseStrat : newConnectionReuseStrategy(),
- responseFactory != null ? responseFactory : newHttpResponseFactory(),
- handlerRegistry,
- expectationVerifier,
- params != null ? params : newDefaultParams());
- this.sslcontext = sslcontext;
- }
- /**
- * Creates a new test server with SSL/TLS encryption.
- *
- * @param sslcontext SSL context
- */
- public HttpTestServer(final SSLContext sslcontext) {
- this(null, null, null, null, null, sslcontext);
- }
- /**
- * 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 HttpTestServer(
- BasicHttpProcessor proc,
- HttpParams params) {
- this(proc, null, null, null, params, null);
- }
- /**
- * Obtains an HTTP protocol processor with default interceptors.
- *
- * @return a protocol processor for server-side use
- */
- protected HttpProcessor newProcessor() {
- return new ImmutableHttpProcessor(new HttpResponseInterceptor[] {new ResponseDate(),
- new ResponseServer(),
- new ResponseContent(),
- new ResponseConnControl()});
- }
- /**
- * Obtains a set of reasonable default parameters for a server.
- *
- * @return default parameters
- */
- protected HttpParams newDefaultParams() {
- HttpParams params = new SyncBasicHttpParams();
- params
- .setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 60000)
- .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
- .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
- .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
- .setParameter(CoreProtocolPNames.ORIGIN_SERVER,
- "LocalTestServer/1.1");
- return params;
- }
- protected ConnectionReuseStrategy newConnectionReuseStrategy() {
- return new DefaultConnectionReuseStrategy();
- }
- protected HttpResponseFactory newHttpResponseFactory() {
- return new DefaultHttpResponseFactory();
- }
- /**
- * Returns the number of connections this test server has accepted.
- */
- public int getAcceptedConnectionCount() {
- return acceptedConnections.get();
- }
- /**
- * {@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);
- }
- /**
- * Starts this test server.
- */
- public void start() throws Exception {
- if (servicedSocket != null) {
- throw new IllegalStateException(this.toString() + " already running");
- }
- ServerSocket ssock;
- if (sslcontext != null) {
- SSLServerSocketFactory sf = sslcontext.getServerSocketFactory();
- ssock = sf.createServerSocket();
- } else {
- ssock = new ServerSocket();
- }
- ssock.setReuseAddress(true); // probably pointless for port '0'
- ssock.bind(TEST_SERVER_ADDR);
- servicedSocket = ssock;
- listenerThread = new ListenerThread();
- listenerThread.setDaemon(false);
- listenerThread.start();
- }
- /**
- * Stops this test server.
- */
- public void stop() throws Exception {
- if (servicedSocket == null) {
- return; // not running
- }
- ListenerThread t = listenerThread;
- if (t != null) {
- t.shutdown();
- }
- synchronized (workers) {
- for (Worker worker : workers) {
- worker.shutdown();
- }
- }
- }
- public void awaitTermination(long timeMs) throws InterruptedException {
- if (listenerThread != null) {
- listenerThread.join(timeMs);
- }
- }
- @Override
- public String toString() {
- ServerSocket ssock = servicedSocket; // avoid synchronization
- StringBuilder sb = new StringBuilder(80);
- sb.append("LocalTestServer/");
- if (ssock == null) {
- sb.append("stopped");
- } else {
- sb.append(ssock.getLocalSocketAddress());
- }
- return sb.toString();
- }
- /**
- * Obtains the local address the server is listening on
- *
- * @return the service address
- */
- public InetSocketAddress getServiceAddress() {
- ServerSocket ssock = servicedSocket; // avoid synchronization
- if (ssock == null) {
- throw new IllegalStateException("not running");
- }
- return (InetSocketAddress) ssock.getLocalSocketAddress();
- }
- /**
- * The request listener.
- * Accepts incoming connections and launches a service thread.
- */
- class ListenerThread extends Thread {
- private volatile Exception exception;
- @Override
- public void run() {
- try {
- while (!interrupted()) {
- Socket socket = servicedSocket.accept();
- acceptedConnections.incrementAndGet();
- DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
- conn.bind(socket, httpservice.getParams());
- // Start worker thread
- Worker worker = new Worker(conn);
- workers.add(worker);
- worker.setDaemon(true);
- worker.start();
- }
- } catch (Exception ex) {
- this.exception = ex;
- } finally {
- try {
- servicedSocket.close();
- } catch (IOException ignore) {
- }
- }
- }
- public void shutdown() {
- interrupt();
- try {
- servicedSocket.close();
- } catch (IOException ignore) {
- }
- }
- public Exception getException() {
- return this.exception;
- }
- }
- class Worker extends Thread {
- private final HttpServerConnection conn;
- private volatile Exception exception;
- Worker(final HttpServerConnection conn) {
- this.conn = conn;
- }
- @Override
- public void run() {
- HttpContext context = new BasicHttpContext();
- try {
- while (this.conn.isOpen() && !Thread.interrupted()) {
- httpservice.handleRequest(this.conn, context);
- }
- } catch (Exception ex) {
- this.exception = ex;
- } finally {
- workers.remove(this);
- try {
- this.conn.shutdown();
- } catch (IOException ignore) {
- }
- }
- }
- public void shutdown() {
- interrupt();
- try {
- this.conn.shutdown();
- } catch (IOException ignore) {
- }
- }
- public Exception getException() {
- return this.exception;
- }
- }
- }