PageRenderTime 60ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/camel-itest/src/test/java/org/apache/camel/itest/http/HttpTestServer.java

https://gitlab.com/matticala/apache-camel
Java | 404 lines | 240 code | 51 blank | 113 comment | 24 complexity | 673f6f5dd8ce1da7db4d65523af9438b MD5 | raw file
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.camel.itest.http;
  18. import java.io.IOException;
  19. import java.net.InetSocketAddress;
  20. import java.net.ServerSocket;
  21. import java.net.Socket;
  22. import java.util.Collections;
  23. import java.util.HashSet;
  24. import java.util.Set;
  25. import java.util.concurrent.atomic.AtomicInteger;
  26. import javax.net.ssl.SSLContext;
  27. import javax.net.ssl.SSLServerSocketFactory;
  28. import org.apache.camel.test.AvailablePortFinder;
  29. import org.apache.http.ConnectionReuseStrategy;
  30. import org.apache.http.HttpResponseFactory;
  31. import org.apache.http.HttpResponseInterceptor;
  32. import org.apache.http.HttpServerConnection;
  33. import org.apache.http.impl.DefaultConnectionReuseStrategy;
  34. import org.apache.http.impl.DefaultHttpResponseFactory;
  35. import org.apache.http.impl.DefaultHttpServerConnection;
  36. import org.apache.http.localserver.EchoHandler;
  37. import org.apache.http.localserver.RandomHandler;
  38. import org.apache.http.params.CoreConnectionPNames;
  39. import org.apache.http.params.CoreProtocolPNames;
  40. import org.apache.http.params.HttpParams;
  41. import org.apache.http.params.SyncBasicHttpParams;
  42. import org.apache.http.protocol.BasicHttpContext;
  43. import org.apache.http.protocol.BasicHttpProcessor;
  44. import org.apache.http.protocol.HttpContext;
  45. import org.apache.http.protocol.HttpExpectationVerifier;
  46. import org.apache.http.protocol.HttpProcessor;
  47. import org.apache.http.protocol.HttpRequestHandler;
  48. import org.apache.http.protocol.HttpRequestHandlerRegistry;
  49. import org.apache.http.protocol.HttpService;
  50. import org.apache.http.protocol.ImmutableHttpProcessor;
  51. import org.apache.http.protocol.ResponseConnControl;
  52. import org.apache.http.protocol.ResponseContent;
  53. import org.apache.http.protocol.ResponseDate;
  54. import org.apache.http.protocol.ResponseServer;
  55. /**
  56. * Copy of org.apache.http.localserver.LocalTestServer to use a specific port.
  57. */
  58. public class HttpTestServer {
  59. public static final int PORT = AvailablePortFinder.getNextAvailable(18080);
  60. /**
  61. * The local address to bind to.
  62. * The host is an IP number rather than "localhost" to avoid surprises
  63. * on hosts that map "localhost" to an IPv6 address or something else.
  64. * The port is 0 to let the system pick one.
  65. */
  66. public static final InetSocketAddress TEST_SERVER_ADDR =
  67. new InetSocketAddress("localhost", PORT);
  68. /** The request handler registry. */
  69. private final HttpRequestHandlerRegistry handlerRegistry;
  70. private final HttpService httpservice;
  71. /** Optional SSL context */
  72. private final SSLContext sslcontext;
  73. /** The server socket, while being served. */
  74. private volatile ServerSocket servicedSocket;
  75. /** The request listening thread, while listening. */
  76. private volatile ListenerThread listenerThread;
  77. /** Set of active worker threads */
  78. private final Set<Worker> workers;
  79. /** The number of connections this accepted. */
  80. private final AtomicInteger acceptedConnections = new AtomicInteger(0);
  81. static {
  82. //set them as system properties so Spring can use the property placeholder
  83. //things to set them into the URL's in the spring contexts
  84. System.setProperty("HttpTestServer.Port", Integer.toString(PORT));
  85. }
  86. /**
  87. * Creates a new test server.
  88. *
  89. * @param proc the HTTP processors to be used by the server, or
  90. * <code>null</code> to use a
  91. * {@link #newProcessor default} processor
  92. * @param reuseStrat the connection reuse strategy to be used by the
  93. * server, or <code>null</code> to use
  94. * {@link #newConnectionReuseStrategy() default}
  95. * strategy.
  96. * @param params the parameters to be used by the server, or
  97. * <code>null</code> to use
  98. * {@link #newDefaultParams default} parameters
  99. * @param sslcontext optional SSL context if the server is to leverage
  100. * SSL/TLS transport security
  101. */
  102. public HttpTestServer(
  103. final BasicHttpProcessor proc,
  104. final ConnectionReuseStrategy reuseStrat,
  105. final HttpResponseFactory responseFactory,
  106. final HttpExpectationVerifier expectationVerifier,
  107. final HttpParams params,
  108. final SSLContext sslcontext) {
  109. this.handlerRegistry = new HttpRequestHandlerRegistry();
  110. this.workers = Collections.synchronizedSet(new HashSet<Worker>());
  111. this.httpservice = new HttpService(
  112. proc != null ? proc : newProcessor(),
  113. reuseStrat != null ? reuseStrat : newConnectionReuseStrategy(),
  114. responseFactory != null ? responseFactory : newHttpResponseFactory(),
  115. handlerRegistry,
  116. expectationVerifier,
  117. params != null ? params : newDefaultParams());
  118. this.sslcontext = sslcontext;
  119. }
  120. /**
  121. * Creates a new test server with SSL/TLS encryption.
  122. *
  123. * @param sslcontext SSL context
  124. */
  125. public HttpTestServer(final SSLContext sslcontext) {
  126. this(null, null, null, null, null, sslcontext);
  127. }
  128. /**
  129. * Creates a new test server.
  130. *
  131. * @param proc the HTTP processors to be used by the server, or
  132. * <code>null</code> to use a
  133. * {@link #newProcessor default} processor
  134. * @param params the parameters to be used by the server, or
  135. * <code>null</code> to use
  136. * {@link #newDefaultParams default} parameters
  137. */
  138. public HttpTestServer(
  139. BasicHttpProcessor proc,
  140. HttpParams params) {
  141. this(proc, null, null, null, params, null);
  142. }
  143. /**
  144. * Obtains an HTTP protocol processor with default interceptors.
  145. *
  146. * @return a protocol processor for server-side use
  147. */
  148. protected HttpProcessor newProcessor() {
  149. return new ImmutableHttpProcessor(new HttpResponseInterceptor[] {new ResponseDate(),
  150. new ResponseServer(),
  151. new ResponseContent(),
  152. new ResponseConnControl()});
  153. }
  154. /**
  155. * Obtains a set of reasonable default parameters for a server.
  156. *
  157. * @return default parameters
  158. */
  159. protected HttpParams newDefaultParams() {
  160. HttpParams params = new SyncBasicHttpParams();
  161. params
  162. .setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 60000)
  163. .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
  164. .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
  165. .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
  166. .setParameter(CoreProtocolPNames.ORIGIN_SERVER,
  167. "LocalTestServer/1.1");
  168. return params;
  169. }
  170. protected ConnectionReuseStrategy newConnectionReuseStrategy() {
  171. return new DefaultConnectionReuseStrategy();
  172. }
  173. protected HttpResponseFactory newHttpResponseFactory() {
  174. return new DefaultHttpResponseFactory();
  175. }
  176. /**
  177. * Returns the number of connections this test server has accepted.
  178. */
  179. public int getAcceptedConnectionCount() {
  180. return acceptedConnections.get();
  181. }
  182. /**
  183. * {@link #register Registers} a set of default request handlers.
  184. * <pre>
  185. * URI pattern Handler
  186. * ----------- -------
  187. * /echo/* {@link EchoHandler EchoHandler}
  188. * /random/* {@link RandomHandler RandomHandler}
  189. * </pre>
  190. */
  191. public void registerDefaultHandlers() {
  192. handlerRegistry.register("/echo/*", new EchoHandler());
  193. handlerRegistry.register("/random/*", new RandomHandler());
  194. }
  195. /**
  196. * Registers a handler with the local registry.
  197. *
  198. * @param pattern the URL pattern to match
  199. * @param handler the handler to apply
  200. */
  201. public void register(String pattern, HttpRequestHandler handler) {
  202. handlerRegistry.register(pattern, handler);
  203. }
  204. /**
  205. * Unregisters a handler from the local registry.
  206. *
  207. * @param pattern the URL pattern
  208. */
  209. public void unregister(String pattern) {
  210. handlerRegistry.unregister(pattern);
  211. }
  212. /**
  213. * Starts this test server.
  214. */
  215. public void start() throws Exception {
  216. if (servicedSocket != null) {
  217. throw new IllegalStateException(this.toString() + " already running");
  218. }
  219. ServerSocket ssock;
  220. if (sslcontext != null) {
  221. SSLServerSocketFactory sf = sslcontext.getServerSocketFactory();
  222. ssock = sf.createServerSocket();
  223. } else {
  224. ssock = new ServerSocket();
  225. }
  226. ssock.setReuseAddress(true); // probably pointless for port '0'
  227. ssock.bind(TEST_SERVER_ADDR);
  228. servicedSocket = ssock;
  229. listenerThread = new ListenerThread();
  230. listenerThread.setDaemon(false);
  231. listenerThread.start();
  232. }
  233. /**
  234. * Stops this test server.
  235. */
  236. public void stop() throws Exception {
  237. if (servicedSocket == null) {
  238. return; // not running
  239. }
  240. ListenerThread t = listenerThread;
  241. if (t != null) {
  242. t.shutdown();
  243. }
  244. synchronized (workers) {
  245. for (Worker worker : workers) {
  246. worker.shutdown();
  247. }
  248. }
  249. }
  250. public void awaitTermination(long timeMs) throws InterruptedException {
  251. if (listenerThread != null) {
  252. listenerThread.join(timeMs);
  253. }
  254. }
  255. @Override
  256. public String toString() {
  257. ServerSocket ssock = servicedSocket; // avoid synchronization
  258. StringBuilder sb = new StringBuilder(80);
  259. sb.append("LocalTestServer/");
  260. if (ssock == null) {
  261. sb.append("stopped");
  262. } else {
  263. sb.append(ssock.getLocalSocketAddress());
  264. }
  265. return sb.toString();
  266. }
  267. /**
  268. * Obtains the local address the server is listening on
  269. *
  270. * @return the service address
  271. */
  272. public InetSocketAddress getServiceAddress() {
  273. ServerSocket ssock = servicedSocket; // avoid synchronization
  274. if (ssock == null) {
  275. throw new IllegalStateException("not running");
  276. }
  277. return (InetSocketAddress) ssock.getLocalSocketAddress();
  278. }
  279. /**
  280. * The request listener.
  281. * Accepts incoming connections and launches a service thread.
  282. */
  283. class ListenerThread extends Thread {
  284. private volatile Exception exception;
  285. @Override
  286. public void run() {
  287. try {
  288. while (!interrupted()) {
  289. Socket socket = servicedSocket.accept();
  290. acceptedConnections.incrementAndGet();
  291. DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
  292. conn.bind(socket, httpservice.getParams());
  293. // Start worker thread
  294. Worker worker = new Worker(conn);
  295. workers.add(worker);
  296. worker.setDaemon(true);
  297. worker.start();
  298. }
  299. } catch (Exception ex) {
  300. this.exception = ex;
  301. } finally {
  302. try {
  303. servicedSocket.close();
  304. } catch (IOException ignore) {
  305. }
  306. }
  307. }
  308. public void shutdown() {
  309. interrupt();
  310. try {
  311. servicedSocket.close();
  312. } catch (IOException ignore) {
  313. }
  314. }
  315. public Exception getException() {
  316. return this.exception;
  317. }
  318. }
  319. class Worker extends Thread {
  320. private final HttpServerConnection conn;
  321. private volatile Exception exception;
  322. Worker(final HttpServerConnection conn) {
  323. this.conn = conn;
  324. }
  325. @Override
  326. public void run() {
  327. HttpContext context = new BasicHttpContext();
  328. try {
  329. while (this.conn.isOpen() && !Thread.interrupted()) {
  330. httpservice.handleRequest(this.conn, context);
  331. }
  332. } catch (Exception ex) {
  333. this.exception = ex;
  334. } finally {
  335. workers.remove(this);
  336. try {
  337. this.conn.shutdown();
  338. } catch (IOException ignore) {
  339. }
  340. }
  341. }
  342. public void shutdown() {
  343. interrupt();
  344. try {
  345. this.conn.shutdown();
  346. } catch (IOException ignore) {
  347. }
  348. }
  349. public Exception getException() {
  350. return this.exception;
  351. }
  352. }
  353. }