 /*
  * (C) 2003-2004 Columbia University Departments of Computer Science and
  * Electrical Engineering
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, is permitted provided the original authors give
  * written consent and the following conditions are met:
  *
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above
  *    copyright notice, this list of conditions and the following
  *    disclaimer in the documentation and/or other materials
  *    provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  * OF SUCH DAMAGE.
  */
/*
 * NetCallback - forwarding TCP ports behind a firewall
 * Copyright (C) 2001 Alexander V. Konstantinou <akonstan@acm.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

import java.net.*;
import java.io.*;

/**
 * Asynchronous bridging of traffic between an input and and output stream.
 *
 * @author Alexander V. Konstantinou (akonstan@acm.org)
 * @version $Revision: 1.1.1.1 $ ; $Date: 2004/04/02 22:27:32 $
 */
public class StreamBridge extends Thread {

  /** byte input stream */
  protected final InputStream istream;

  /** byte output stream */
  protected final OutputStream ostream;

  /** true if the bridge is active, false if it should terminate */
  protected boolean active;
  /**
   * Constructs a new asynchronous bridge by spawning a thread copying
   * traffic from the input stream to the output stream.
   * <p>
   * When the source of the input stream is a network socket, and in order to
   * detect the end of stream expiration in a timely manner, the socket
   * timeout must be set to a reasonable value (e.g. 1 second)
   *
   * @param istream - the input stream
   * @param ostream - the output stream
   * @param description - a string describing the connection (used to name the
   *                      thread)
   */
  public StreamBridge(InputStream istream,
		      OutputStream ostream,
		      String description) {

    super(description);

    if (istream == null) 
      throw new NullPointerException("null istream argument");

    if (ostream == null) 
      throw new NullPointerException("null ostream argument");

    this.istream = istream;
    this.ostream = ostream;

    active = true;

    start();
  }

  /**
   * Request asynchronous termination of the stream bridge 
   */
  public void terminate() {
    active = false;
    interrupt();
  }

  /**
   * Thread copying bytes from the input stream and writing them to the
   * output stream until the end of file.
   */
  public void run() {

    try {
      byte[] buffer = new byte[4096];
      int size = 0;
      
      while ( (active) && (size != -1) ) {
	if (size > 0) {
	  ostream.write(buffer, 0, size);
	  ostream.flush();
	}
      
	try {
	  size = istream.read(buffer);
	} catch (InterruptedIOException e) {
	  size = e.bytesTransferred;
	}
      }
    } catch (IOException e) {
      // expected
    } catch (Throwable e) {
      // unexpected
      e.printStackTrace();
    } finally {
      try {
	ostream.close();
      } catch (Throwable e) { e.printStackTrace(); }

      try {
	istream.close();
      } catch (Throwable e) { e.printStackTrace(); }
    }
  }
}
