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

Reply via email to