Java tutorial
/******************************************************************************* * Copyright (c) 2011 Subgraph. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Subgraph - initial API and implementation ******************************************************************************/ package com.subgraph.vega.internal.http.proxy; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.DefaultHttpResponseFactory; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.CoreConnectionPNames; import org.apache.http.params.HttpParams; import org.apache.http.protocol.BasicHttpProcessor; import org.apache.http.protocol.HttpRequestHandlerRegistry; import org.apache.http.protocol.ResponseConnControl; import com.subgraph.vega.api.http.proxy.IHttpInterceptProxy; import com.subgraph.vega.api.http.proxy.IHttpInterceptProxyEventHandler; import com.subgraph.vega.api.http.requests.IHttpRequestEngine; import com.subgraph.vega.internal.http.proxy.ssl.SSLContextRepository; public class HttpProxy implements IHttpInterceptProxy { static final String PROXY_CONTEXT_REQUEST = "proxy.request"; static final String PROXY_CONTEXT_RESPONSE = "proxy.response"; static final String PROXY_HTTP_HOST = "proxy.host"; static final String PROXY_HTTP_TRANSACTION = "proxy.transaction"; private final Logger logger = Logger.getLogger("proxy"); private final ProxyTransactionManipulator transactionManipulator; private final HttpInterceptor interceptor; private final List<IHttpInterceptProxyEventHandler> eventHandlers; private final int listenPort; private ServerSocket serverSocket; private HttpParams params; private VegaHttpService httpService; private ExecutorService executor = Executors.newCachedThreadPool(); private Thread proxyThread; private final List<ConnectionTask> connectionList; public HttpProxy(int listenPort, ProxyTransactionManipulator transactionManipulator, HttpInterceptor interceptor, IHttpRequestEngine requestEngine, SSLContextRepository sslContextRepository) { this.eventHandlers = new ArrayList<IHttpInterceptProxyEventHandler>(); this.transactionManipulator = transactionManipulator; this.interceptor = interceptor; this.listenPort = listenPort; this.params = new BasicHttpParams(); this.params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0) .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024) // .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false) .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true); BasicHttpProcessor inProcessor = new BasicHttpProcessor(); inProcessor.addInterceptor(new ResponseConnControl()); inProcessor.addInterceptor(new ResponseContentCustom()); HttpRequestHandlerRegistry registry = new HttpRequestHandlerRegistry(); registry.register("*", new ProxyRequestHandler(this, logger, requestEngine)); httpService = new VegaHttpService(inProcessor, new DefaultConnectionReuseStrategy(), new DefaultHttpResponseFactory(), registry, params, sslContextRepository); connectionList = new ArrayList<ConnectionTask>(); } @Override public void startProxy() { try { logger.info("Listening on port " + listenPort); serverSocket = new ServerSocket(listenPort); proxyThread = new Thread(createProxyLoopRunnable()); proxyThread.start(); } catch (IOException e) { logger.log(Level.WARNING, "IO error creating listening socket: " + e.getMessage(), e); } } private Runnable createProxyLoopRunnable() { return new Runnable() { @Override public void run() { proxyAcceptLoop(); } }; } private void proxyAcceptLoop() { while (!Thread.interrupted()) { Socket s; try { s = serverSocket.accept(); } catch (IOException e) { if (!Thread.interrupted()) { logger.log(Level.WARNING, "IO error processing incoming connection: " + e.getMessage(), e); } break; } logger.fine("Connection accepted from " + s.getRemoteSocketAddress()); VegaHttpServerConnection c = new VegaHttpServerConnection(params); try { c.bind(s, params); } catch (IOException e) { logger.log(Level.WARNING, "Unexpected error: " + e.getMessage(), e); continue; } final ConnectionTask task = new ConnectionTask(httpService, c, HttpProxy.this); synchronized (connectionList) { connectionList.add(task); } executor.execute(task); } synchronized (connectionList) { for (ConnectionTask task : connectionList) { task.shutdown(); } } executor.shutdownNow(); } @Override public void stopProxy() { proxyThread.interrupt(); try { // close the socket to interrupt accept() in proxyAcceptLoop() serverSocket.close(); } catch (IOException e) { logger.log(Level.WARNING, "Unexpected exception closing server socket: " + e.getMessage(), e); } } @Override public int getListenPort() { return listenPort; } @Override public void registerEventHandler(IHttpInterceptProxyEventHandler handler) { synchronized (eventHandlers) { eventHandlers.add(handler); } } @Override public void unregisterEventHandler(IHttpInterceptProxyEventHandler handler) { synchronized (eventHandlers) { eventHandlers.remove(handler); } } public boolean handleTransaction(ProxyTransaction transaction) throws InterruptedException { if (transaction.hasResponse() == false) { transactionManipulator.process(transaction.getRequest()); } else { transactionManipulator.process(transaction.getResponse().getRawResponse()); } boolean rv = interceptor.handleTransaction(transaction); if (rv == true) { transaction.await(); } return rv; } void completeRequest(ProxyTransaction transaction) { synchronized (eventHandlers) { for (IHttpInterceptProxyEventHandler h : eventHandlers) h.handleRequest(transaction); } } public void notifyClose(ConnectionTask task) { synchronized (connectionList) { connectionList.remove(task); } } }