mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ChunkedOutputStream.java

branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
equal deleted inserted replaced
-1:000000000000 0:75f0bfe59373
1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27
28 package ch.boye.httpclientandroidlib.impl.io;
29
30 import java.io.IOException;
31 import java.io.OutputStream;
32
33 import ch.boye.httpclientandroidlib.io.SessionOutputBuffer;
34
35 /**
36 * Implements chunked transfer coding. The content is sent in small chunks.
37 * Entities transferred using this output stream can be of unlimited length.
38 * Writes are buffered to an internal buffer (2048 default size).
39 * <p>
40 * Note that this class NEVER closes the underlying stream, even when close
41 * gets called. Instead, the stream will be marked as closed and no further
42 * output will be permitted.
43 *
44 *
45 * @since 4.0
46 */
47 public class ChunkedOutputStream extends OutputStream {
48
49 // ----------------------------------------------------- Instance Variables
50 private final SessionOutputBuffer out;
51
52 private byte[] cache;
53
54 private int cachePosition = 0;
55
56 private boolean wroteLastChunk = false;
57
58 /** True if the stream is closed. */
59 private boolean closed = false;
60
61 // ----------------------------------------------------------- Constructors
62 /**
63 * Wraps a session output buffer and chunk-encodes the output.
64 *
65 * @param out The session output buffer
66 * @param bufferSize The minimum chunk size (excluding last chunk)
67 * @throws IOException in case of an I/O error
68 */
69 public ChunkedOutputStream(final SessionOutputBuffer out, int bufferSize)
70 throws IOException {
71 super();
72 this.cache = new byte[bufferSize];
73 this.out = out;
74 }
75
76 /**
77 * Wraps a session output buffer and chunks the output. The default buffer
78 * size of 2048 was chosen because the chunk overhead is less than 0.5%
79 *
80 * @param out the output buffer to wrap
81 * @throws IOException in case of an I/O error
82 */
83 public ChunkedOutputStream(final SessionOutputBuffer out)
84 throws IOException {
85 this(out, 2048);
86 }
87
88 // ----------------------------------------------------------- Internal methods
89 /**
90 * Writes the cache out onto the underlying stream
91 */
92 protected void flushCache() throws IOException {
93 if (this.cachePosition > 0) {
94 this.out.writeLine(Integer.toHexString(this.cachePosition));
95 this.out.write(this.cache, 0, this.cachePosition);
96 this.out.writeLine("");
97 this.cachePosition = 0;
98 }
99 }
100
101 /**
102 * Writes the cache and bufferToAppend to the underlying stream
103 * as one large chunk
104 */
105 protected void flushCacheWithAppend(byte bufferToAppend[], int off, int len) throws IOException {
106 this.out.writeLine(Integer.toHexString(this.cachePosition + len));
107 this.out.write(this.cache, 0, this.cachePosition);
108 this.out.write(bufferToAppend, off, len);
109 this.out.writeLine("");
110 this.cachePosition = 0;
111 }
112
113 protected void writeClosingChunk() throws IOException {
114 // Write the final chunk.
115 this.out.writeLine("0");
116 this.out.writeLine("");
117 }
118
119 // ----------------------------------------------------------- Public Methods
120 /**
121 * Must be called to ensure the internal cache is flushed and the closing
122 * chunk is written.
123 * @throws IOException in case of an I/O error
124 */
125 public void finish() throws IOException {
126 if (!this.wroteLastChunk) {
127 flushCache();
128 writeClosingChunk();
129 this.wroteLastChunk = true;
130 }
131 }
132
133 // -------------------------------------------- OutputStream Methods
134 public void write(int b) throws IOException {
135 if (this.closed) {
136 throw new IOException("Attempted write to closed stream.");
137 }
138 this.cache[this.cachePosition] = (byte) b;
139 this.cachePosition++;
140 if (this.cachePosition == this.cache.length) flushCache();
141 }
142
143 /**
144 * Writes the array. If the array does not fit within the buffer, it is
145 * not split, but rather written out as one large chunk.
146 */
147 public void write(byte b[]) throws IOException {
148 write(b, 0, b.length);
149 }
150
151 /**
152 * Writes the array. If the array does not fit within the buffer, it is
153 * not split, but rather written out as one large chunk.
154 */
155 public void write(byte src[], int off, int len) throws IOException {
156 if (this.closed) {
157 throw new IOException("Attempted write to closed stream.");
158 }
159 if (len >= this.cache.length - this.cachePosition) {
160 flushCacheWithAppend(src, off, len);
161 } else {
162 System.arraycopy(src, off, cache, this.cachePosition, len);
163 this.cachePosition += len;
164 }
165 }
166
167 /**
168 * Flushes the content buffer and the underlying stream.
169 */
170 public void flush() throws IOException {
171 flushCache();
172 this.out.flush();
173 }
174
175 /**
176 * Finishes writing to the underlying stream, but does NOT close the underlying stream.
177 */
178 public void close() throws IOException {
179 if (!this.closed) {
180 this.closed = true;
181 finish();
182 this.out.flush();
183 }
184 }
185 }

mercurial