Author: costin Date: Thu Nov 26 06:42:49 2009 New Revision: 884413 URL: http://svn.apache.org/viewvc?rev=884413&view=rev Log: Proxy service - CONNECT doesn't seem to work ( probably because it didn't have a test ), the rest works pretty well. Also includes a small socks server I used while testing. Almost all work is non-blocking and done in the selector thread.
Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/CopyCallback.java (with props) tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/HttpProxyService.java (with props) tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/ProxyFlushedCallback.java (with props) tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/SocksServer.java (with props) tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/StaticContentService.java (with props) Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/CopyCallback.java URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/CopyCallback.java?rev=884413&view=auto ============================================================================== --- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/CopyCallback.java (added) +++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/CopyCallback.java Thu Nov 26 06:42:49 2009 @@ -0,0 +1,57 @@ +/* + */ +package org.apache.tomcat.lite.proxy; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.apache.tomcat.lite.http.HttpChannel; +import org.apache.tomcat.lite.io.IOBuffer; +import org.apache.tomcat.lite.io.IOChannel; +import org.apache.tomcat.lite.io.IOConnector; + +/** + * Used by socks and http proxy. Will copy received data to a different + * channel. + */ +public class CopyCallback implements IOConnector.DataReceivedCallback { + IOChannel mOutBuffer; + + public CopyCallback(IOChannel sc) { + mOutBuffer = sc; + } + + @Override + public void handleReceived(IOChannel ch) throws IOException { + IOBuffer inBuffer = ch.getIn(); + IOChannel outBuffer = mOutBuffer; + if (outBuffer == null && + ch instanceof HttpChannel) { + outBuffer = + (IOChannel) ((HttpChannel)ch).getRequest().getAttribute("P"); + } + // body. + while (true) { + if (outBuffer == null || outBuffer.getOut() == null) { + return; + } + if (outBuffer.getOut().isAppendClosed()) { + return; + } + + ByteBuffer bb = outBuffer.getOut().getWriteBuffer(); + int rd = inBuffer.read(bb); + outBuffer.getOut().releaseWriteBuffer(rd); + + if (rd == 0) { + outBuffer.startSending(); + return; + } + if (rd < 0) { + outBuffer.getOut().close(); + outBuffer.startSending(); + return; + } + } + } + } \ No newline at end of file Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/CopyCallback.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/HttpProxyService.java URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/HttpProxyService.java?rev=884413&view=auto ============================================================================== --- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/HttpProxyService.java (added) +++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/HttpProxyService.java Thu Nov 26 06:42:49 2009 @@ -0,0 +1,370 @@ +/* + */ +package org.apache.tomcat.lite.proxy; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.tomcat.lite.http.HttpChannel; +import org.apache.tomcat.lite.http.HttpConnector; +import org.apache.tomcat.lite.http.HttpRequest; +import org.apache.tomcat.lite.http.HttpResponse; +import org.apache.tomcat.lite.http.MultiMap; +import org.apache.tomcat.lite.http.HttpChannel.HttpService; +import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted; +import org.apache.tomcat.lite.io.CBuffer; +import org.apache.tomcat.lite.io.IOChannel; +import org.apache.tomcat.lite.io.IOConnector; +import org.apache.tomcat.lite.io.CBuffer; +import org.apache.tomcat.lite.io.SocketConnector; + +/** + * Http callback for the server-side. Will forward all requests to + * a remote http server - either using proxy mode ( GET http://... ) or + * forward requests ( GET /foo -> will be served by the remote server ). + * + * This is not blocking (except the connect, which currenly blocks on dns). + */ +public class HttpProxyService implements HttpService { + + // target - when used in forwarding mode. + String target = "localhost"; + int port = 8802; + + static Logger log = Logger.getLogger("HttpProxy"); + public static boolean debug = false; + boolean keepOpen = true; + + // client side - this connect to the real server that generates the resp. + ProxyClientCallback clientHeadersReceived = new ProxyClientCallback(); + + HttpConnector httpConnector; + IOConnector ioConnector; + + public HttpProxyService withSelector(IOConnector pool) { + this.ioConnector = pool; + return this; + } + + public HttpProxyService withHttpClient(HttpConnector pool) { + this.httpConnector = pool; + return this; + } + + public HttpProxyService withTarget(String host, int port) { + this.target = host; + this.port = port; + return this; + } + + private IOConnector getSelector() { + if (ioConnector == null) { + ioConnector = new SocketConnector(); + } + return ioConnector; + } + + private HttpConnector getHttpConnector() { + if (httpConnector == null) { + httpConnector = new HttpConnector(getSelector()); + } + return httpConnector; + } + + // Connects to the target CONNECT server, as client, forwards + static class ProxyConnectClientConnection implements IOConnector.ConnectedCallback { + + IOChannel serverNet; + private HttpChannel serverHttp; + + public ProxyConnectClientConnection(HttpChannel sproc) throws IOException { + this.serverNet = sproc.getSink(); + this.serverHttp = sproc; + } + + @Override + public void handleConnected(IOChannel ioch) throws IOException { + if (!ioch.isOpen()) { + serverNet.close(); + log.severe("Connection failed"); + return; + } + afterClientConnect(ioch); + + ioch.setDataReceivedCallback(new CopyCallback(serverNet)); + //ioch.setDataFlushedCallback(new ProxyFlushedCallback(serverNet, ioch)); + serverNet.setDataReceivedCallback(new CopyCallback(ioch)); + //serverNet.setDataFlushedCallback(new ProxyFlushedCallback(ioch, serverNet)); + + ioch.sendHandleReceivedCallback(); + } + + static byte[] OK = "HTTP/1.1 200 OK\r\n\r\n".getBytes(); + + protected void afterClientConnect(IOChannel clientCh) throws IOException { + serverNet.getOut().queue(OK); + serverNet.startSending(); + + serverHttp.resetBuffers(); // no buffers + serverHttp.release(); // no longer used + } + } + + /** + * Parse the req, dispatch the connection. + */ + @Override + public void service(HttpRequest serverHttpReq, HttpResponse serverHttpRes) + throws IOException { + + String dstHost = target; // default target ( for normal req ). + int dstPort = port; + + // TODO: more flexibility/callbacks on selecting the target, acl, etc + if (serverHttpReq.method().equals("CONNECT")) { + // SSL proxy - just connect and forward all packets + // TODO: optimize, add error checking + String[] hostPort = serverHttpReq.requestURI().toString().split(":"); + String host = hostPort[0]; + int port = 443; + if (hostPort.length > 1) { + port = Integer.parseInt(hostPort[1]); + } + if (log.isLoggable(Level.FINE)) { + HttpChannel server = serverHttpReq.getHttpChannel(); + log.info("NEW: " + server.getId() + " " + dstHost + " " + + server.getRequest().getMethod() + + " " + server.getRequest().getRequestURI() + " " + + server.getIn()); + } + + try { + getSelector().connect(host, port, + new ProxyConnectClientConnection(serverHttpReq.getHttpChannel())); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return; + } + + + CBuffer origURIx = serverHttpReq.requestURI(); + String origURI = origURIx.toString(); + if (origURI.startsWith("http://")) { + // Real proxy - extract client address, modify the uri. + // TODO: optimize the strings. + int start = origURI.indexOf('/', 7); + String hostPortS = (start == -1) ? + origURI.subSequence(7, origURI.length()).toString() : + origURI.subSequence(7, start).toString(); + String[] hostPort = hostPortS.split(":"); + + dstHost = hostPort[0]; + dstPort = (hostPort.length > 1) ? Integer.parseInt(hostPort[1]) : + 80; + + if (start >= 0) { + serverHttpReq.requestURI().set(origURI.substring(start)); + } else { + serverHttpReq.requestURI().set("/"); + } + } else { + // Adjust the host header. + CBuffer hostHdr = + serverHttpReq.getMimeHeaders().getHeader("host"); + if (hostHdr != null) { + hostHdr.recycle(); + CBuffer cb = hostHdr; + cb.append(dstHost); + if (dstPort != 80) { + cb.append(':'); + cb.appendInt(dstPort); + } + } + } + if (debug) { + HttpChannel server = serverHttpReq.getHttpChannel(); + log.info("START: " + server.getId() + " " + dstHost + " " + + server.getRequest().getMethod() + + " " + server.getRequest().getRequestURI() + " " + + server.getIn()); + } + + // Send the request with a non-blocking write + HttpChannel serverHttp = serverHttpReq.getHttpChannel(); + + // Client connection + HttpChannel httpClient = getHttpConnector().get(dstHost, dstPort); + + serverHttp.getRequest().setAttribute("CLIENT", httpClient); + httpClient.getRequest().setAttribute("SERVER", serverHttp); + serverHttp.getRequest().setAttribute("P", httpClient); + httpClient.getRequest().setAttribute("P", serverHttp); + + httpClient.setHttpService(clientHeadersReceived); + + // Will send the original request (TODO: small changes) + // Response is not affected ( we use the callback ) + httpClient.getRequest().method().set(serverHttp.getRequest().method()); + httpClient.getRequest().requestURI().set(serverHttp.getRequest().requestURI()); + if (serverHttp.getRequest().queryString().length() != 0) { + httpClient.getRequest().queryString().set(serverHttp.getRequest().queryString()); + } + + httpClient.getRequest().protocol().set(serverHttp.getRequest().protocol()); + + //cstate.reqHeaders.addValue(name) + copyHeaders(serverHttp.getRequest().getMimeHeaders(), + httpClient.getRequest().getMimeHeaders() /*dest*/); + + // For debug + httpClient.getRequest().getMimeHeaders().remove("Accept-Encoding"); + + if (!keepOpen) { + httpClient.getRequest().getMimeHeaders().setValue("Connection").set("Close"); + } + + // Any data + serverHttp.setDataReceivedCallback(copy); + copy.handleReceived(serverHttp); + + httpClient.sendRequest(); + + + //serverHttp.handleReceived(serverHttp.getSink()); + //httpClient.flush(); // send any data still there + + httpClient.setCompletedCallback(done); + // Will call release() + serverHttp.setCompletedCallback(done); + + serverHttpReq.async(); + } + + static HttpDoneCallback done = new HttpDoneCallback(); + static CopyCallback copy = new CopyCallback(null); + // POST: after sendRequest(ch) we need to forward the body !! + + + static void copyHeaders(MultiMap mimeHeaders, MultiMap dest) + throws IOException { + for (int i = 0; i < mimeHeaders.size(); i++) { + CBuffer name = mimeHeaders.getName(i); + CBuffer val = dest.addValue(name.toString()); + val.set(mimeHeaders.getValue(i)); + } + } + + /** + * HTTP _CLIENT_ callback - from tomcat to final target. + */ + public class ProxyClientCallback implements HttpService { + /** + * Headers received from the client (content http server). + * TODO: deal with version missmatches. + */ + @Override + public void service(HttpRequest clientHttpReq, HttpResponse clientHttpRes) throws IOException { + HttpChannel serverHttp = (HttpChannel) clientHttpReq.getAttribute("SERVER"); + + try { + serverHttp.getResponse().setStatus(clientHttpRes.getStatus()); + serverHttp.getResponse().getMessageBuffer().set(clientHttpRes.getMessageBuffer()); + copyHeaders(clientHttpRes.getMimeHeaders(), + serverHttp.getResponse().getMimeHeaders()); + + serverHttp.getResponse().getMimeHeaders().addValue("TomcatProxy").set("True"); + + clientHttpReq.getHttpChannel().setDataReceivedCallback(copy); + copy.handleReceived(clientHttpReq.getHttpChannel()); + + serverHttp.sendHeaders(); + serverHttp.startSending(); + + + //clientHttpReq.flush(); // send any data still there + + // if (clientHttpReq.getHttpChannel().getIn().isClosedAndEmpty()) { + // serverHttp.getOut().close(); // all data from client already in buffers + // } + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + static final class HttpDoneCallback implements RequestCompleted { + + public HttpDoneCallback() { + } + + @Override + public void handle(HttpChannel doneCh, Object extraData) throws IOException { + HttpChannel serverCh = + (HttpChannel) doneCh.getRequest().getAttribute("SERVER"); + HttpChannel clientCh = doneCh; + String tgt = "C"; + if (serverCh == null) { + serverCh = doneCh; + clientCh = + (HttpChannel) doneCh.getRequest().getAttribute("CLIENT"); + tgt = "S"; + } + if (serverCh == null || clientCh == null) { + return; + } + if (doneCh.getError()) { + serverCh.abort("Proxy error"); + clientCh.abort("Proxy error"); + return; + } + + if (log.isLoggable(Level.FINE)) { + HttpChannel peerCh = + (HttpChannel) doneCh.getRequest().getAttribute("SERVER"); + if (peerCh == null) { + peerCh = + (HttpChannel) doneCh.getRequest().getAttribute("CLIENT"); + } else { + + } + log.info(tgt + " " + peerCh.getId() + " " + + doneCh.getTarget() + " " + + doneCh.getRequest().getMethod() + + " " + doneCh.getRequest().getRequestURI() + " " + + doneCh.getResponse().getStatus() + " IN:" + doneCh.getIn() + + " OUT:" + doneCh.getOut() + + " SIN:" + peerCh.getIn() + + " SOUT:" + peerCh.getOut() ); + } + // stop forwarding. After this call the client object will be + // recycled + //clientCB.outBuffer = null; + + // We must releaes both at same time + synchronized (this) { + + serverCh.complete(); + + if (clientCh.getRequest().getAttribute("SERVER") == null) { + return; + } + if (clientCh.isDone() && serverCh.isDone()) { + clientCh.getRequest().setAttribute("SERVER", null); + serverCh.getRequest().setAttribute("CLIENT", null); + clientCh.getRequest().setAttribute("P", null); + serverCh.getRequest().setAttribute("P", null); + // Reuse the objects. + serverCh.release(); + clientCh.release(); + } + } + } + } + + +} Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/HttpProxyService.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/ProxyFlushedCallback.java URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/ProxyFlushedCallback.java?rev=884413&view=auto ============================================================================== --- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/ProxyFlushedCallback.java (added) +++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/ProxyFlushedCallback.java Thu Nov 26 06:42:49 2009 @@ -0,0 +1,25 @@ +/* + */ +package org.apache.tomcat.lite.proxy; + +import java.io.IOException; + +import org.apache.tomcat.lite.io.IOChannel; +import org.apache.tomcat.lite.io.IOConnector; + +public final class ProxyFlushedCallback implements IOConnector.DataFlushedCallback { + IOChannel peerCh; + + public ProxyFlushedCallback(IOChannel ch2, IOChannel clientChannel2) { + peerCh = ch2; + } + + @Override + public void handleFlushed(IOChannel ch) throws IOException { + if (ch.getOut().isClosedAndEmpty()) { + if (!peerCh.getOut().isAppendClosed()) { + peerCh.close(); + } + } + } +} \ No newline at end of file Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/ProxyFlushedCallback.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/SocksServer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/SocksServer.java?rev=884413&view=auto ============================================================================== --- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/SocksServer.java (added) +++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/SocksServer.java Thu Nov 26 06:42:49 2009 @@ -0,0 +1,449 @@ +/* + */ +package org.apache.tomcat.lite.proxy; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.tomcat.lite.io.IOBuffer; +import org.apache.tomcat.lite.io.IOChannel; +import org.apache.tomcat.lite.io.IOConnector; +import org.apache.tomcat.lite.io.SocketConnector; + +/** + * A test for the selector package, and helper for the proxy - + * a SOCKS4a server. + * + * Besides the connection initialization, it's almost the + * same as the CONNECT method in http proxy. + * + * http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol + * http://www.smartftp.com/Products/SmartFTP/RFC/socks4a.protocol + * http://www.faqs.org/rfcs/rfc1928.html + * https://svn.torproject.org/svn/tor/trunk/doc/spec/socks-extensions.txt + * + * In firefox, set network.proxy.socks_remote_dns = true to do DNS via proxy. + * + * Also interesting: + * http://transocks.sourceforge.net/ + * + * @author Costin Manolache + */ +public class SocksServer implements Runnable, IOConnector.ConnectedCallback { + protected int port = 2080; + + protected IOConnector ioConnector; + protected static Logger log = Logger.getLogger("SocksServer"); + + protected long idleTimeout = 10 * 60000; // 10 min + + protected long lastConnection = 0; + protected long totalConTime = 0; + protected AtomicInteger totalConnections = new AtomicInteger(); + + protected AtomicInteger active = new AtomicInteger(); + + protected long inBytes; + protected long outBytes; + protected static int sockets; + + public int getPort() { + return port; + } + + public int getActive() { + return active.get(); + } + + public int getTotal() { + return totalConnections.get(); + } + + public void setPort(int port) { + this.port = port; + } + + public void handleAccepted(IOChannel accepted) throws IOException { + lastConnection = System.currentTimeMillis(); + active.incrementAndGet(); + totalConnections.incrementAndGet(); + sockets++; + + final SocksServerConnection socksCon = new SocksServerConnection(accepted); + socksCon.pool = ioConnector; + socksCon.server = this; + + accepted.setDataReceivedCallback(socksCon); + socksCon.handleReceived(accepted); + } + + /** + * Exit if no activity happens. + */ + public void setIdleTimeout(long to) { + idleTimeout = to; + } + + public long getIdleTimeout() { + return idleTimeout; + } + + public void stop() { + ioConnector.stop(); + } + + public void initServer() throws IOException { + if (ioConnector == null) { + ioConnector = new SocketConnector(); + } + ioConnector.acceptor(this, Integer.toString(port), null); + + final Timer timer = new Timer(true /* daemon */); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + try { + // if lastConnection == 0 - it'll terminate on first timer + float avg = (totalConnections.get() > 0) ? + totalConTime / totalConnections.get() : 0; + System.err.println("Socks:" + + "\ttotal=" + totalConnections + + "\tin=" + inBytes + + "\tout=" + outBytes + + "\tavg=" + (int) avg); + if (active.get() <= 0 + && idleTimeout > 0 + && System.currentTimeMillis() - lastConnection > idleTimeout) { + System.err.println("Idle timeout"); + stop(); + this.cancel(); + timer.cancel(); + } + } catch (Throwable t) { + log.log(Level.SEVERE, "Error in timer", t); + } + } + }, 5 * 60 * 1000, 5 * 60 * 1000); // 5 + + + } + + + + public static class SocksServerConnection implements IOConnector.DataReceivedCallback, IOConnector.ConnectedCallback { + + protected SocksServer server; + + boolean headReceived; + boolean head5Received = false; + + ByteBuffer headBuffer = ByteBuffer.allocate(256); + ByteBuffer headReadBuffer = headBuffer.duplicate(); + + ByteBuffer headResBuffer = ByteBuffer.allocate(256); + IOConnector pool; + byte ver; + byte cmd; + long startTime = System.currentTimeMillis(); + + static final int CMD_CONNECT = 0; + static final byte CMD_RESOLVE = (byte) 0xF0; + + int port; + byte[] hostB = new byte[4]; + CharBuffer userId = CharBuffer.allocate(256); + CharBuffer hostName = CharBuffer.allocate(256); + + SocketAddress sa = null; + + private byte atyp; + + IOChannel serverCh; + + public SocksServerConnection(IOChannel accepted) { + this.serverCh = accepted; + } + + protected void afterClientConnect(IOChannel clientCh) throws IOException { + headResBuffer.clear(); + if (ver == 4) { + headResBuffer.put((byte) 0); + headResBuffer.put((byte) 90); + for (int i = 0; i < 6; i++ ) { + headResBuffer.put((byte) 0); + } + } else { + headResBuffer.put((byte) 5); + headResBuffer.put((byte) 0); + headResBuffer.put((byte) 0); + headResBuffer.put((byte) 1); // ip + + headResBuffer.put(hostB); + int port2 = clientCh.getPort(true); + headResBuffer.putShort((short) port2); + } + + headResBuffer.flip(); + + serverCh.getOut().queue(headResBuffer); + log.fine("Connected " + sa.toString()); + + if (headReadBuffer.remaining() > 0) { + serverCh.getOut().queue(headReadBuffer); + } + serverCh.startSending(); + } + + public void afterClose() { + long conTime = System.currentTimeMillis() - startTime; + int a = server.active.decrementAndGet(); + if (a < 0) { + System.err.println("negative !!"); + server.active.set(0); + } +// System.err.println(sa + "\tsR:" + +// received +// + "\tcR:" + clientReceived +// + "\tactive:" + a +// + "\ttotC:" + server.totalConnections +// + "\ttime:" + conTime); +// server.inBytes += received; +// server.totalConTime += conTime; +// server.outBytes += clientReceived; + } + + + protected int parseHead() throws IOException { + // data is between 0 and pos. + int pos = headBuffer.position(); + headReadBuffer.clear(); + headReadBuffer.limit(pos); + if (headReadBuffer.remaining() < 2) { + return -1; + } + + ByteBuffer bb = headReadBuffer; + ver = bb.get(); + if (ver == 5) { + return parseHead5(); + } + if (headReadBuffer.remaining() < 8) { + return -1; + } + cmd = bb.get(); + port = bb.getShort(); + bb.get(hostB); + userId.clear(); + int rc = readStringZ(bb, userId); + // Mozilla userid: MOZ ... + if (rc == -1) { + return rc; + } + if (hostB[0] == 0 && hostB[1] == 0 && hostB[2] == 0) { + // 0.0.0.x + atyp = 3; + hostName.clear(); + rc = readStringZ(bb, hostName); + if (rc == -1) { + return rc; + } + } else { + atyp = 1; + } + + headReceived = true; + + return 4; + } + + protected int parseHead5_2() throws IOException { + // data is between 0 and pos. + int pos = headBuffer.position(); + + headReadBuffer.clear(); + headReadBuffer.limit(pos); + + if (headReadBuffer.remaining() < 7) { + return -1; + } + + ByteBuffer bb = headReadBuffer; + ver = bb.get(); + cmd = bb.get(); + bb.get(); // reserved + atyp = bb.get(); + if (atyp == 1) { + bb.get(hostB); + } else if (atyp == 3) { + hostName.clear(); + int rc = readStringN(bb, hostName); + if (rc == -1) { + return rc; + } + } // ip6 not supported right now, easy to add + + port = bb.getShort(); + + head5Received = true; + + return 5; + } + + private int parseHead5() { + ByteBuffer bb = headReadBuffer; + int nrMethods = ((int)bb.get()) & 0xFF; + if (bb.remaining() < nrMethods) { + return -1; + } + for (int i = 0; i < nrMethods; i++) { + // ignore + bb.get(); + } + return 5; + } + + private int readStringZ(ByteBuffer bb, CharBuffer bc) throws IOException { + bc.clear(); + while (true) { + if (!bb.hasRemaining()) { + return -1; // not complete + } + byte b = bb.get(); + if (b == 0) { + bc.flip(); + return 0; + } else { + bc.put((char) b); + } + } + } + + private int readStringN(ByteBuffer bb, CharBuffer bc) throws IOException { + bc.clear(); + int len = ((int) bb.get()) & 0xff; + for (int i = 0; i < len; i++) { + if (!bb.hasRemaining()) { + return -1; // not complete + } + byte b = bb.get(); + bc.put((char) b); + } + bc.flip(); + return len; + } + + static ExecutorService connectTP = Executors.newCachedThreadPool(); + + protected void startClientConnection() throws IOException { + // TODO: use different thread ? + if (atyp == 3) { + connectTP.execute(new Runnable() { + + public void run() { + try { + sa = new InetSocketAddress(hostName.toString(), port); + pool.connect(hostName.toString(), port, + SocksServerConnection.this); + } catch (Exception ex) { + log.severe("Error connecting"); + } + } + }); + } else { + InetAddress addr = InetAddress.getByAddress(hostB); + pool.connect(addr.toString(), port, this); + } // TODO: ip6 + } + + public void handleConnected(IOChannel ioch) throws IOException { + ioch.setDataReceivedCallback(new CopyCallback(serverCh)); + //ioch.setDataFlushedCallback(new ProxyFlushedCallback(serverCh, ioch)); + + serverCh.setDataReceivedCallback(new CopyCallback(ioch)); + //serverCh.setDataFlushedCallback(new ProxyFlushedCallback(ioch, serverCh)); + + afterClientConnect(ioch); + + ioch.sendHandleReceivedCallback(); + } + + + @Override + public void handleReceived(IOChannel net) throws IOException { + IOBuffer ch = net.getIn(); + //SelectorChannel ch = (SelectorChannel) ioch; + if (!headReceived) { + int rd = ch.read(headBuffer); + if (rd == 0) { + return; + } + if (rd == -1) { + ch.close(); + } + + rd = parseHead(); + if (rd < 0) { + return; // need more + } + if (rd == 5) { + headResBuffer.clear(); + headResBuffer.put((byte) 5); + headResBuffer.put((byte) 0); + headResBuffer.flip(); + net.getOut().queue(headResBuffer); + net.startSending(); + headReceived = true; + headBuffer.clear(); + return; + } else { + headReceived = true; + head5Received = true; + startClientConnection(); + } + } + + if (!head5Received) { + int rd = ch.read(headBuffer); + if (rd == 0) { + return; + } + if (rd == -1) { + ch.close(); + } + + rd = parseHead5_2(); + if (rd < 0) { + return; // need more + } + + startClientConnection(); + } + } + } + + @Override + public void run() { + try { + initServer(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + public void handleConnected(IOChannel ch) throws IOException { + handleAccepted(ch); + } +} Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/SocksServer.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/StaticContentService.java URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/StaticContentService.java?rev=884413&view=auto ============================================================================== --- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/StaticContentService.java (added) +++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/StaticContentService.java Thu Nov 26 06:42:49 2009 @@ -0,0 +1,117 @@ +/* + * 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.tomcat.lite.proxy; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.logging.Logger; + +import org.apache.tomcat.lite.http.HttpRequest; +import org.apache.tomcat.lite.http.HttpResponse; +import org.apache.tomcat.lite.http.HttpChannel.HttpService; +import org.apache.tomcat.lite.io.BBuffer; +import org.apache.tomcat.lite.io.BBucket; +import org.apache.tomcat.lite.io.IOBuffer; + +/* + * + * Serve static content, from memory. + */ +public class StaticContentService implements HttpService { + protected Logger log = Logger.getLogger("coyote.static"); + protected BBucket mb; + + protected boolean chunked = false; + + protected String contentType = "text/plain"; + + + public StaticContentService() { + } + + public StaticContentService chunked() { + chunked = true; + return this; + } + + public StaticContentService setData(byte[] data) { + mb = BBuffer.wrapper(data, 0, data.length); + return this; + } + + public StaticContentService withLen(int len) { + byte[] data = new byte[len]; + for (int i = 0; i < len; i++) { + data[i] = 'A'; + } + mb = BBuffer.wrapper(data, 0, data.length); + return this; + } + + + public StaticContentService setData(CharSequence data) { + try { + IOBuffer tmp = new IOBuffer(null); + tmp.append(data); + mb = tmp.readAll(null); + } catch (IOException e) { + } + return this; + } + + public StaticContentService setContentType(String ct) { + this.contentType = ct; + return this; + } + + public void setFile(String path) { + try { + FileInputStream fis = new FileInputStream(path); + BBuffer bc = BBuffer.allocate(4096); + + byte b[] = new byte[4096]; + int rd = 0; + while ((rd = fis.read(b)) > 0) { + bc.append(b, 0, rd); + } + mb = bc; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void service(HttpRequest httpReq, HttpResponse res) throws IOException { + + res.setStatus(200); + + if (!chunked) { + res.setContentLength(mb.remaining()); + } + res.setContentType(contentType); + + res.sendHead(); + + if (chunked) { + res.getBody() + .queue(BBuffer.wrapper(mb, 0, mb.remaining())); + res.flush(); + } + + res.getBody().queue(BBuffer.wrapper(mb, 0, mb.remaining())); + } +} \ No newline at end of file Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/proxy/StaticContentService.java ------------------------------------------------------------------------------ svn:eol-style = native --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org