Home » HttpComponents-Core-4.0.1 » org.apache.http.io » [javadoc | source]

    1   /*
    2    * $HeadURL: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpcore/tags/4.0-alpha2/src/java/org/apache/http/io/ChunkedOutputStream.java $
    3    * $Revision: 390883 $
    4    * $Date: 2006-04-02 20:39:50 +0200 (Sun, 02 Apr 2006) $
    5    *
    6    * ====================================================================
    7    *
    8    *  Copyright 2002-2004 The Apache Software Foundation
    9    *
   10    *  Licensed under the Apache License, Version 2.0 (the "License");
   11    *  you may not use this file except in compliance with the License.
   12    *  You may obtain a copy of the License at
   13    *
   14    *      http://www.apache.org/licenses/LICENSE-2.0
   15    *
   16    *  Unless required by applicable law or agreed to in writing, software
   17    *  distributed under the License is distributed on an "AS IS" BASIS,
   18    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   19    *  See the License for the specific language governing permissions and
   20    *  limitations under the License.
   21    * ====================================================================
   22    *
   23    * This software consists of voluntary contributions made by many
   24    * individuals on behalf of the Apache Software Foundation.  For more
   25    * information on the Apache Software Foundation, please see
   26    * <http://www.apache.org/>.
   27    *
   28    */
   29   
   30   package org.apache.http.io;
   31   
   32   import java.io.IOException;
   33   import java.io.OutputStream;
   34   
   35   /**
   36    * <p>This class implements chunked transfer coding as described in the 
   37    * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6.1</a> 
   38    * of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>. 
   39    * Writes are buffered to an internal buffer (2048 default size). Chunks are guaranteed 
   40    * to be at least as large as the buffer size (except for the last chunk).</p>
   41    * 
   42    * <h>3.6.1 Chunked Transfer Coding</h>
   43    * <p>
   44    * The chunked encoding modifies the body of a message in order to transfer it as a series 
   45    * of chunks, each with its own size indicator, followed by an OPTIONAL trailer containing 
   46    * entity-header fields. This allows dynamically produced content to be transferred along 
   47    * with the information necessary for the recipient to verify that it has received the full 
   48    * message.
   49    * </p>
   50    * <pre>
   51    *  Chunked-Body   = *chunk
   52    *                   last-chunk
   53    *                   trailer
   54    *                   CRLF
   55    *
   56    *  chunk          = chunk-size [ chunk-extension ] CRLF
   57    *                   chunk-data CRLF
   58    *  chunk-size     = 1*HEX
   59    *  last-chunk     = 1*("0") [ chunk-extension ] CRLF
   60    *
   61    *  chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
   62    *  chunk-ext-name = token
   63    *  chunk-ext-val  = token | quoted-string
   64    *  chunk-data     = chunk-size(OCTET)
   65    *  trailer        = *(entity-header CRLF)
   66    * </pre>
   67    * <p>
   68    * The chunk-size field is a string of hex digits indicating the size of the chunk. The 
   69    * chunked encoding is ended by any chunk whose size is zero, followed by the trailer, 
   70    * which is terminated by an empty line.
   71    * </p>
   72    * <p>
   73    * The trailer allows the sender to include additional HTTP header fields at the end 
   74    * of the message. The Trailer header field can be used to indicate which header fields 
   75    * are included in a trailer (see section 14.40).
   76    * </p>
   77    * <p>
   78    * A server using chunked transfer-coding in a response MUST NOT use the trailer for any 
   79    * header fields unless at least one of the following is true:
   80    * </p>
   81    * <p>
   82    * a)the request included a TE header field that indicates "trailers" is acceptable in 
   83    * the transfer-coding of the response, as described in section 14.39; or,
   84    * </p>
   85    * <p>
   86    * b)the server is the origin server for the response, the trailer fields consist entirely 
   87    * of optional metadata, and the recipient could use the message (in a manner acceptable 
   88    * to the origin server) without receiving this metadata. In other words, the origin server 
   89    * is willing to accept the possibility that the trailer fields might be silently discarded 
   90    * along the path to the client.
   91    * </p>
   92    * <p>
   93    * This requirement prevents an interoperability failure when the message is being received 
   94    * by an HTTP/1.1 (or later) proxy and forwarded to an HTTP/1.0 recipient. It avoids a 
   95    * situation where compliance with the protocol would have necessitated a possibly infinite 
   96    * buffer on the proxy. 
   97    * </p>
   98    * 
   99    * @author Mohammad Rezaei, Goldman, Sachs & Co.
  100    * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
  101    */
  102   public class ChunkedOutputStream extends OutputStream {
  103   
  104       // ----------------------------------------------------- Instance Variables
  105       private final HttpDataTransmitter out;
  106   
  107       private byte[] cache;
  108   
  109       private int cachePosition = 0;
  110   
  111       private boolean wroteLastChunk = false;
  112   
  113       /** True if the stream is closed. */
  114       private boolean closed = false;
  115       
  116       // ----------------------------------------------------------- Constructors
  117       /**
  118        * Wraps a stream and chunks the output.
  119        * @param out the transmitter to wrap
  120        * @param bufferSize minimum chunk size (excluding last chunk)
  121        * @throws IOException
  122        * 
  123        * @since 3.0
  124        */
  125       public ChunkedOutputStream(final HttpDataTransmitter out, int bufferSize)
  126               throws IOException {
  127           super();
  128           this.cache = new byte[bufferSize];
  129           this.out = out;
  130       }
  131   
  132       /**
  133        * Wraps a data transmitter and chunks the output. The default buffer size of 2048 was 
  134        * chosen because the chunk overhead is less than 0.5%
  135        * @param datatransmitter the transmitter to wrap
  136        * @throws IOException
  137        */
  138       public ChunkedOutputStream(final HttpDataTransmitter datatransmitter) 
  139               throws IOException {
  140           this(datatransmitter, 2048);
  141       }
  142   
  143       // ----------------------------------------------------------- Internal methods
  144       /**
  145        * Writes the cache out onto the underlying stream
  146        * @throws IOException
  147        * 
  148        * @since 3.0
  149        */
  150       protected void flushCache() throws IOException {
  151           if (this.cachePosition > 0) {
  152               this.out.writeLine(Integer.toHexString(this.cachePosition));
  153               this.out.write(this.cache, 0, this.cachePosition);
  154               this.out.writeLine("");
  155               this.cachePosition = 0;
  156           }
  157       }
  158   
  159       /**
  160        * Writes the cache and bufferToAppend to the underlying stream
  161        * as one large chunk
  162        * @param bufferToAppend
  163        * @param off
  164        * @param len
  165        * @throws IOException
  166        * 
  167        * @since 3.0
  168        */
  169       protected void flushCacheWithAppend(byte bufferToAppend[], int off, int len) throws IOException {
  170           this.out.writeLine(Integer.toHexString(this.cachePosition + len));
  171           this.out.write(this.cache, 0, this.cachePosition);
  172           this.out.write(bufferToAppend, off, len);
  173           this.out.writeLine("");
  174           this.cachePosition = 0;
  175       }
  176   
  177       protected void writeClosingChunk() throws IOException {
  178           // Write the final chunk.
  179           this.out.writeLine("0");
  180           this.out.writeLine("");
  181       }
  182   
  183       // ----------------------------------------------------------- Public Methods
  184       /**
  185        * Must be called to ensure the internal cache is flushed and the closing chunk is written.
  186        * @throws IOException
  187        * 
  188        * @since 3.0
  189        */
  190       public void finish() throws IOException {
  191           if (!this.wroteLastChunk) {
  192               flushCache();
  193               writeClosingChunk();
  194               this.wroteLastChunk = true;
  195           }
  196       }
  197   
  198       // -------------------------------------------- OutputStream Methods
  199       public void write(int b) throws IOException {
  200           if (this.closed) {
  201               throw new IOException("Attempted write to closed stream.");
  202           }
  203           this.cache[this.cachePosition] = (byte) b;
  204           this.cachePosition++;
  205           if (this.cachePosition == this.cache.length) flushCache();
  206       }
  207   
  208       /**
  209        * Writes the array. If the array does not fit within the buffer, it is
  210        * not split, but rather written out as one large chunk.
  211        * @param b
  212        * @throws IOException
  213        * 
  214        * @since 3.0
  215        */
  216       public void write(byte b[]) throws IOException {
  217           write(b, 0, b.length);
  218       }
  219   
  220       public void write(byte src[], int off, int len) throws IOException {
  221           if (this.closed) {
  222               throw new IOException("Attempted write to closed stream.");
  223           }
  224           if (len >= this.cache.length - this.cachePosition) {
  225               flushCacheWithAppend(src, off, len);
  226           } else {
  227               System.arraycopy(src, off, cache, this.cachePosition, len);
  228               this.cachePosition += len;
  229           }
  230       }
  231   
  232       /**
  233        * Flushes the underlying stream, but leaves the internal buffer alone.
  234        * @throws IOException
  235        */
  236       public void flush() throws IOException {
  237           this.out.flush();
  238       }
  239   
  240       /**
  241        * Finishes writing to the underlying stream, but does NOT close the underlying stream.
  242        * @throws IOException
  243        */
  244       public void close() throws IOException {
  245           if (!this.closed) {
  246               this.closed = true;
  247               finish();
  248               this.out.flush();
  249           }
  250       }
  251   }

Home » HttpComponents-Core-4.0.1 » org.apache.http.io » [javadoc | source]