content: /*
   * HttpInputStream.java
   *
   * Brazil project web application toolkit,
   * export version: 2.3 
   * Copyright (c) 1999-2004 Sun Microsystems, Inc.
   *
   * Sun Public License Notice
   *
   * The contents of this file are subject to the Sun Public License Version 
   * 1.0 (the "License"). You may not use this file except in compliance with 
   * the License. A copy of the License is included as the file "license.terms",
   * and also available at http://www.sun.com/
   * 
   * The Original Code is from:
   *    Brazil project web application toolkit release 2.3.
   * The Initial Developer of the Original Code is: cstevens.
   * Portions created by cstevens are Copyright (C) Sun Microsystems, Inc.
   * All Rights Reserved.
   * 
   * Contributor(s): cstevens, suhler.
   *
   * Version:  2.2
   * Created by cstevens on 99/09/15
   * Last modified by suhler on 04/11/30 15:19:46
   *
   * Version Histories:
   *
   * 2.2 04/11/30-15:19:46 (suhler)
   *   fixed sccs version string
   *
   * 2.1 02/10/01-16:37:04 (suhler)
   *   version change
   *
   * 1.8 02/04/18-11:18:42 (suhler)
   *   changed the semantics of copyTo() to make guarantees about the number
   *   of bytes read from the stream.  This *shouldn't* break anything (I hope)
   *
   * 1.7 00/03/29-16:47:15 (cstevens)
   *   Streaming media didn't work because data from server wasn't flushed to client
   *   until the end of the request.
   *
   * 1.6 99/11/09-20:24:44 (cstevens)
   *   bugs revealed by writing tests.
   *
   * 1.5 99/10/14-13:01:39 (cstevens)
   *   Documentation.
   *
   * 1.4 99/10/08-16:56:06 (cstevens)
   *   allow passing the temporary buffer used for copying to the copyTo() method.
   *   Makes it easier to work with the Request.sendResponse(InputStream) method
   *   so that sendResponse can use the server-requested buffer size.
   *
   * 1.3 99/10/07-13:18:20 (cstevens)
   *   Documentation for HttpInputStream
   *
   * 1.2 99/09/15-14:52:08 (cstevens)
   *   import *;
   *
   * 1.2 99/09/15-14:39:36 (Codemgr)
   *   SunPro Code Manager data about conflicts, renames, etc...
   *   Name history : 1 0 util/http/HttpInputStream.java
   *
   * 1.1 99/09/15-14:39:35 (cstevens)
   *   date and time created 99/09/15 14:39:35 by cstevens
   *
   */
  
  package sunlabs.brazil.util.http;
  
  import java.io.FilterInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.PushbackInputStream;
  
  /**
   * This class is an input stream that provides added methods that are
   * of help when reading the result of an HTTP request.  By setting up
   * this input stream, the user can conveniently read lines of text and
   * copy the contents of an input stream to an output stream.
   * <p>
   * The underlying assumption of this class is that when reading the result
   * of an HTTP request, each byte in the input stream represents an 8-bit
   * ASCII character, and as such, it is perfectly valid to treat each byte
   * as a character.  Locale-based conversion is not appropriate in this
   * circumstance, so the <code>java.io.BufferedReader.readLine</code> method
   * should not be used.
   *
   * @author	Colin Stevens (colin.stevens@sun.com)
   * @version		2.2
   */
  public class HttpInputStream
      extends FilterInputStream
  {
      /**
       * The default size of the temporary buffer used when copying from an
       * input stream to an output stream.
       *
       * @see	#copyTo(OutputStream, int, byte[])
       */
      public static int defaultBufsize = 4096;
  
      /**
       * Creates a new HttpInputStream that reads its input from the
       * specified input stream.
       *
       * @param	in
       *		The underlying input stream.
       */
      public
      HttpInputStream(InputStream in)
      {
  	super(in);
      }
  
      /**
       * Reads the next line of text from the input stream.
       * <p>
       * A line is terminated by "\r", "\n", "\r\n", or the end of the input
       * stream.  The line-terminating characters are discarded.
       *
       * @return	The next line from the input stream, or <code>null</code>
       *		if the end of the input stream is reached and no bytes
       *		were found.
       *
       * @throws	IOException if the underlying input stream throws an
       *		IOException while being read.
       */
      public String
      readLine()
  	throws IOException
      {
  	return readLine(Integer.MAX_VALUE);
      }
  
      /**
       * Reads the next line of text from the input stream, up to the
       * limit specified.
       * <p>
       * A line is terminated by "\r", "\n", "\r\n", the end of the input
       * stream, or when the specified number of characters have been read.
       * The line-terminating characters are discarded.  It is not possible
       * to distinguish, based on the result, between a line that was
       * exactly <code>limit</code> characters long and a line that was
       * terminated because <code>limit</code> characters were read.
       *
       * @return	The next line from the input stream, or <code>null</code>
       *		if the end of the input stream is reached and no bytes
       *		were found.
       *
       * @throws	IOException if the underlying input stream throws an
       *		IOException while being read.
       */
      public String
      readLine(int limit)
  	throws IOException
      {
  	StringBuffer sb = new StringBuffer();
  
  	while (limit-- > 0) {
  	    int ch = read();
  	    if (ch == '\r') {
  		ch = read();
  		if (ch != '\n') {
  		    if ((in instanceof PushbackInputStream) == false) {
  			in = new PushbackInputStream(in);
  		    }
  		    ((PushbackInputStream) in).unread(ch);
  		}
  		break;
  	    } else if (ch == '\n') {
  		break;
  	    } else if (ch < 0) {
  		if (sb.length() == 0) {
  		    return null;
  		}
  		break;
  	    } else {
  		sb.append((char) ch);
  	    }
  	}
  	return sb.toString();
      }
  
      /**
       * Reads <code>buf.length</code> bytes from the input stream.  This
       * method reads repeatedly from the input stream until the specified
       * number of bytes have been read or the end of the input stream
       * is reached.
       * <p>
       * The standard <code>InputStream.read</code> method will generally
       * return less than the specified number of bytes if the underlying
       * input stream is "bursty", such as from a network source.  Sometimes
       * it is important to read the exact number of bytes desired.
       *
       * @param	buf
       *		Buffer in which the data is stored.  If buffer is of
       *		length 0, this method will return immediately.
       *
       * @return	The number of bytes read.  This will be less than
       *		<code>buf.length</code> if the end of the input stream was
       *		reached.
       *
       * @throws	IOException if the underlying input stream throws an
       *		IOException while being read.
       */
      public int
      readFully(byte[] buf)
  	throws IOException
      {
  	return readFully(buf, 0, buf.length);
      }
  
      /**
       * Reads the specified number of bytes from the input stream.  This
       * method reads repeatedly from the input stream until the specified
       * number of bytes have been read or the end of the input stream is
       * reached.
       * <p>
       * The standard <code>InputStream.read</code> method will generally
       * return less than the specified number of bytes if the underlying
       * input stream is "bursty", such as from a network source.  Sometimes
       * it is important to read the exact number of bytes desired.
       *
       * @param	buf
       *		Buffer in which the data is stored.
       *
       * @param	off
       *		The starting offset into the buffer.
       *
       * @param	len
       *		The number of bytes to read.
       *
       * @return	The number of bytes read.  This will be less than
       *		<code>len</code> if the end of the input stream was reached.
       *
       * @throws	IOException if the underlying input stream throws an
       *		IOException while being read.
       */
      public int
      readFully(byte[] buf, int off, int len)
  	throws IOException
      {
  	int total = 0;
  
  	while (len > 0) {
  	    int count = read(buf, off, len);
  	    if (count < 0) {
  		break;
  	    }
  	    total += count;
  	    off += count;
  	    len -= count;
  	}
  
  	return total;
      }
  
      /**
       * Copies bytes from this input stream to the specified output stream
       * until end of the input stream is reached.
       *
       * @param	out
       *		The output stream to copy the data to.
       *
       * @return	The number of bytes copied to the output stream.
       *
       * @throws	IOException if the underlying input stream throws an
       *		IOException while being read or if the output stream
       *		throws an IOException while being written.  It may not be
       *		possible to distinguish amongst the two conditions.
       */
      public int
      copyTo(OutputStream out)
  	throws IOException
      {
  	return copyTo(out, -1, null);
      }
  
      /**
       * Copies bytes from this input stream to the specified output stream
       * until the specified number of bytes are copied or the end of the
       * input stream is reached.
       *
       * @param	out
       *		The output stream to copy the data to.
       *
       * @param	len
       *		The number of bytes to copy, or < 0 to copy until the end
       *		of this stream.
       *
       * @return	The number of bytes copied to the output stream.
       *
       * @throws	IOException if the underlying input stream throws an
       *		IOException while being read or if the output stream
       *		throws an IOException while being written.  It may not be
       *		possible to distinguish amongst the two conditions.
       */
      public int
      copyTo(OutputStream out, int len)
  	throws IOException
      {
  	return copyTo(out, len, null);
      }
  
      /**
       * Copies bytes from this input stream to the specified output stream
       * until the specified number of bytes are copied or the end of the
       * input stream is reached.
       *
       * @param	out
       *		The output stream to copy the data to.
       *
       * @param	len
       *		The number of bytes to copy, or < 0 to copy until the end
       *		of this stream.
       *
       * @param	buf
       *		The buffer used to for holding the temporary results while
       *		copying data from this input stream to the output stream.
       *		May be <code>null</code> to allow this method copy in
       *		chunks of length <code>defaultBufsize</code>.
       *
       * @return	The number of bytes read from the input stream, and
       *		copied to the output stream.
       *
       * @throws	IOException if the underlying input stream throws an
       *		IOException while being read.
       */
  
      public int
      copyTo(OutputStream out, int len, byte[] buf)
  	throws IOException
      {
  	if (len < 0) {
  	    len = Integer.MAX_VALUE;
  	}
  	if (buf == null) {
  	    buf = new byte[Math.min(defaultBufsize, len)];
  	}
  
  	int total = 0;
  	while (len > 0) {
  	    int count = read(buf, 0, Math.min(buf.length, len));
  	    if (count < 0) {
  		break;
  	    }
  	    total += count;
  	    len -= count;
  	    try {
  		out.write(buf, 0, count);
  		out.flush();
  	    } catch (IOException e) {
  		break;
  	    }
  	}
  	return total;
      }
  }
  