Home | History | Annotate | Download | only in io
      1 /*
      2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/ChunkedOutputStream.java $
      3  * $Revision: 645081 $
      4  * $Date: 2008-04-05 04:36:42 -0700 (Sat, 05 Apr 2008) $
      5  *
      6  * ====================================================================
      7  * Licensed to the Apache Software Foundation (ASF) under one
      8  * or more contributor license agreements.  See the NOTICE file
      9  * distributed with this work for additional information
     10  * regarding copyright ownership.  The ASF licenses this file
     11  * to you under the Apache License, Version 2.0 (the
     12  * "License"); you may not use this file except in compliance
     13  * with the License.  You may obtain a copy of the License at
     14  *
     15  *   http://www.apache.org/licenses/LICENSE-2.0
     16  *
     17  * Unless required by applicable law or agreed to in writing,
     18  * software distributed under the License is distributed on an
     19  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     20  * KIND, either express or implied.  See the License for the
     21  * specific language governing permissions and limitations
     22  * under the License.
     23  * ====================================================================
     24  *
     25  * This software consists of voluntary contributions made by many
     26  * individuals on behalf of the Apache Software Foundation.  For more
     27  * information on the Apache Software Foundation, please see
     28  * <http://www.apache.org/>.
     29  *
     30  */
     31 
     32 package org.apache.http.impl.io;
     33 
     34 import java.io.IOException;
     35 import java.io.OutputStream;
     36 
     37 import org.apache.http.io.SessionOutputBuffer;
     38 
     39 /**
     40  * Implements chunked transfer coding.
     41  * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>,
     42  * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">section 3.6.1</a>.
     43  * Writes are buffered to an internal buffer (2048 default size).
     44  *
     45  * @author Mohammad Rezaei (Goldman, Sachs &amp; Co.)
     46  * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
     47  *
     48  * @since 4.0
     49  *
     50  * @deprecated Please use {@link java.net.URL#openConnection} instead.
     51  *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
     52  *     for further details.
     53  */
     54 @Deprecated
     55 public class ChunkedOutputStream extends OutputStream {
     56 
     57     // ----------------------------------------------------- Instance Variables
     58     private final SessionOutputBuffer out;
     59 
     60     private byte[] cache;
     61 
     62     private int cachePosition = 0;
     63 
     64     private boolean wroteLastChunk = false;
     65 
     66     /** True if the stream is closed. */
     67     private boolean closed = false;
     68 
     69     // ----------------------------------------------------------- Constructors
     70     /**
     71      * Wraps a session output buffer and chunks the output.
     72      * @param out the session output buffer to wrap
     73      * @param bufferSize minimum chunk size (excluding last chunk)
     74      * @throws IOException
     75      */
     76     public ChunkedOutputStream(final SessionOutputBuffer out, int bufferSize)
     77             throws IOException {
     78         super();
     79         this.cache = new byte[bufferSize];
     80         this.out = out;
     81     }
     82 
     83     /**
     84      * Wraps a session output buffer and chunks the output. The default buffer
     85      * size of 2048 was chosen because the chunk overhead is less than 0.5%
     86      *
     87      * @param out       the output buffer to wrap
     88      * @throws IOException
     89      */
     90     public ChunkedOutputStream(final SessionOutputBuffer out)
     91             throws IOException {
     92         this(out, 2048);
     93     }
     94 
     95     // ----------------------------------------------------------- Internal methods
     96     /**
     97      * Writes the cache out onto the underlying stream
     98      * @throws IOException
     99      */
    100     protected void flushCache() throws IOException {
    101         if (this.cachePosition > 0) {
    102             this.out.writeLine(Integer.toHexString(this.cachePosition));
    103             this.out.write(this.cache, 0, this.cachePosition);
    104             this.out.writeLine("");
    105             this.cachePosition = 0;
    106         }
    107     }
    108 
    109     /**
    110      * Writes the cache and bufferToAppend to the underlying stream
    111      * as one large chunk
    112      * @param bufferToAppend
    113      * @param off
    114      * @param len
    115      * @throws IOException
    116      */
    117     protected void flushCacheWithAppend(byte bufferToAppend[], int off, int len) throws IOException {
    118         this.out.writeLine(Integer.toHexString(this.cachePosition + len));
    119         this.out.write(this.cache, 0, this.cachePosition);
    120         this.out.write(bufferToAppend, off, len);
    121         this.out.writeLine("");
    122         this.cachePosition = 0;
    123     }
    124 
    125     protected void writeClosingChunk() throws IOException {
    126         // Write the final chunk.
    127         this.out.writeLine("0");
    128         this.out.writeLine("");
    129     }
    130 
    131     // ----------------------------------------------------------- Public Methods
    132     /**
    133      * Must be called to ensure the internal cache is flushed and the closing chunk is written.
    134      * @throws IOException
    135      */
    136     public void finish() throws IOException {
    137         if (!this.wroteLastChunk) {
    138             flushCache();
    139             writeClosingChunk();
    140             this.wroteLastChunk = true;
    141         }
    142     }
    143 
    144     // -------------------------------------------- OutputStream Methods
    145     public void write(int b) throws IOException {
    146         if (this.closed) {
    147             throw new IOException("Attempted write to closed stream.");
    148         }
    149         this.cache[this.cachePosition] = (byte) b;
    150         this.cachePosition++;
    151         if (this.cachePosition == this.cache.length) flushCache();
    152     }
    153 
    154     /**
    155      * Writes the array. If the array does not fit within the buffer, it is
    156      * not split, but rather written out as one large chunk.
    157      * @param b
    158      * @throws IOException
    159      */
    160     public void write(byte b[]) throws IOException {
    161         write(b, 0, b.length);
    162     }
    163 
    164     public void write(byte src[], int off, int len) throws IOException {
    165         if (this.closed) {
    166             throw new IOException("Attempted write to closed stream.");
    167         }
    168         if (len >= this.cache.length - this.cachePosition) {
    169             flushCacheWithAppend(src, off, len);
    170         } else {
    171             System.arraycopy(src, off, cache, this.cachePosition, len);
    172             this.cachePosition += len;
    173         }
    174     }
    175 
    176     /**
    177      * Flushes the content buffer and the underlying stream.
    178      * @throws IOException
    179      */
    180     public void flush() throws IOException {
    181         flushCache();
    182         this.out.flush();
    183     }
    184 
    185     /**
    186      * Finishes writing to the underlying stream, but does NOT close the underlying stream.
    187      * @throws IOException
    188      */
    189     public void close() throws IOException {
    190         if (!this.closed) {
    191             this.closed = true;
    192             finish();
    193             this.out.flush();
    194         }
    195     }
    196 }
    197