net.lightbody.bmp.proxy.jetty.http.HttpTunnel Maven / Gradle / Ivy
The newest version!
// ========================================================================
// $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 (byteCount0)
{
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;
}
}