net.lightbody.bmp.proxy.jetty.http.HttpTunnel.java Source code

Java tutorial

Introduction

Here is the source code for net.lightbody.bmp.proxy.jetty.http.HttpTunnel.java

Source

// ========================================================================
// $Id: HttpTunnel.java,v 1.11 2005/10/05 11:14:37 gregwilkins Exp $
// Copyright 2002-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed 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 net.lightbody.bmp.proxy.jetty.http;

import net.lightbody.bmp.proxy.jetty.log.LogFactory;
import net.lightbody.bmp.proxy.jetty.util.IO;
import net.lightbody.bmp.proxy.jetty.util.LogSupport;
import org.apache.commons.logging.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.Socket;

/* ------------------------------------------------------------ */
/** HTTP Tunnel.
 * A HTTP Tunnel can be used to take over a HTTP connection in order to
 * tunnel another protocol over it.  The prime example is the CONNECT method
 * handled by the ProxyHandler to setup a SSL tunnel between the client and
 * the real server.
 *
 * @see HttpConnection
 * @version $Revision: 1.11 $
 * @author Greg Wilkins (gregw)
 */
public class HttpTunnel {
    private static Log log = LogFactory.getLog(HttpTunnel.class);

    private Thread _thread;
    private int _timeoutMs;
    private Socket _socket;
    private InputStream _sIn;
    private OutputStream _sOut;
    private InputStream _in;
    private OutputStream _out;

    /* ------------------------------------------------------------ */
    /** Constructor. 
     */
    protected HttpTunnel() {
    }

    /* ------------------------------------------------------------ */
    /** Constructor. 
     * @param socket The tunnel socket.
     * @param timeoutMs The maximum time to wait for a read on the tunnel. Note that
     * sotimer exceptions are ignored by the tunnel.
     * @param in Alternative input stream or null if using normal socket stream
     * @param out Alternative output stream or null if using normal socket stream
     * @param timeoutMs
     * @throws IOException 
     */
    public HttpTunnel(Socket socket, InputStream in, OutputStream out) throws IOException {
        _socket = socket;
        _sIn = in;
        _sOut = out;
        if (_sIn == null)
            _sIn = _socket.getInputStream();
        if (_sOut == null)
            _sOut = socket.getOutputStream();
        _timeoutMs = 30000;
    }

    /* ------------------------------------------------------------ */
    /** handle method.
     * This method is called by the HttpConnection.handleNext() method if
     * this HttpTunnel has been set on that connection.
     * The default implementation of this method copies between the HTTP
     * socket and the socket passed in the constructor.
     * @param in 
     * @param out 
     */
    public void handle(InputStream in, OutputStream out) {
        Copy copy = new Copy();
        _in = in;
        _out = out;
        try {
            _thread = Thread.currentThread();
            copy.start();

            copydata(_sIn, _out);
        } catch (Exception e) {
            LogSupport.ignore(log, e);
        } finally {
            try {
                _in.close();
                if (_socket != null) {
                    _socket.shutdownOutput();
                    _socket.close();
                } else {
                    _sIn.close();
                    _sOut.close();
                }
            } catch (Exception e) {
                LogSupport.ignore(log, e);
            }
            copy.interrupt();
        }
    }

    /* ------------------------------------------------------------ */
    private void copydata(InputStream in, OutputStream out) throws java.io.IOException {
        long timestamp = 0;
        long byteCount = 0;
        while (true) {
            try {
                byteCount = copyBytes(in, out, -1);
                timestamp = 0;
                if (byteCount == -1) {
                    return;
                }
            } catch (InterruptedIOException e) {
                LogSupport.ignore(log, e);
                if (timestamp == 0)
                    timestamp = System.currentTimeMillis();
                else if (_timeoutMs > 0 && (System.currentTimeMillis() - timestamp) > _timeoutMs)
                    throw e;
            }
        }
    }

    /* ------------------------------------------------------------------- */
    /** Copy Stream in to Stream for byteCount bytes or until EOF or exception.
      * @return Copied bytes count or -1 if no bytes were read *and* EOF was reached
    */
    public static int copyBytes(InputStream in, OutputStream out, long byteCount) throws IOException {
        byte buffer[] = new byte[IO.bufferSize];
        int len = IO.bufferSize;
        int totalCount = 0;

        if (byteCount >= 0) {
            totalCount = (int) byteCount;
            while (byteCount > 0) {
                try {
                    if (byteCount < IO.bufferSize)
                        len = in.read(buffer, 0, (int) byteCount);
                    else
                        len = in.read(buffer, 0, IO.bufferSize);
                    if (len == -1 && totalCount == byteCount)
                        totalCount = (int) byteCount - 1;
                } catch (InterruptedIOException e) {
                    if (totalCount == byteCount)
                        throw e;
                    LogSupport.ignore(log, e);
                    len = 0;
                }

                if (len <= 0)
                    break;

                byteCount -= len;
                out.write(buffer, 0, len);
            }
            totalCount -= byteCount;
        } else {
            while (len > 0) {
                try {
                    len = in.read(buffer, 0, IO.bufferSize);
                    if (len == -1 && totalCount == 0)
                        totalCount = -1;
                } catch (InterruptedIOException e) {
                    if (totalCount == 0)
                        throw e;
                    LogSupport.ignore(log, e);
                    len = 0;
                }
                if (len > 0) {
                    out.write(buffer, 0, len);
                    totalCount += len;
                }
            }
        }
        return totalCount;
    }

    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    /** Copy thread.
     * Helper thread to copy from the HTTP input to the sockets output
     */
    private class Copy extends Thread {
        public void run() {
            try {
                copydata(_in, _sOut);
            } catch (Exception e) {
                LogSupport.ignore(log, e);
            } finally {
                try {
                    _out.close();
                    if (_socket != null) {
                        _socket.shutdownInput();
                        _socket.close();
                    } else {
                        _sOut.close();
                        _sIn.close();
                    }
                } catch (Exception e) {
                    LogSupport.ignore(log, e);
                }
                _thread.interrupt();
            }
        }
    }

    /* ------------------------------------------------------------ */
    /**
     * @return Returns the socket.
     */
    public Socket getSocket() {
        return _socket;
    }

    /* ------------------------------------------------------------ */
    /**
     * @return Returns the timeoutMs.
     */
    public int getTimeoutMs() {
        return _timeoutMs;
    }

    /* ------------------------------------------------------------ */
    /**
     * @param timeoutMs The timeoutMs to set.
     */
    public void setTimeoutMs(int timeoutMs) {
        _timeoutMs = timeoutMs;
    }

}