File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\util\ChunkedOutputStream.java.html

1 /*
2 * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/ChunkedOutputStream.java,v 1.16 2004/05/13 04:03:25 mbecke Exp $
3 * $Revision: 480424 $
4 * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
5 *
6 * ====================================================================
7 *
8 * Licensed to the Apache Software Foundation (ASF) under one or more
9 * contributor license agreements. See the NOTICE file distributed with
10 * this work for additional information regarding copyright ownership.
11 * The ASF licenses this file to You under the Apache License, Version 2.0
12 * (the "License"); you may not use this file except in compliance with
13 * 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, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 * ====================================================================
23 *
24 * This software consists of voluntary contributions made by many
25 * individuals on behalf of the Apache Software Foundation. For more
26 * information on the Apache Software Foundation, please see
27 * <http://www.apache.org/>.
28 *
29 */
30
31 package hudson.util;
32
33 import java.io.IOException;
34 import java.io.OutputStream;
35
36 /**
37 * Implements HTTP chunking support. Writes are buffered to an internal buffer (2048 default size).
38 * Chunks are guaranteed to be at least as large as the buffer size (except for the last chunk).
39 *
40 * @author Mohammad Rezaei, Goldman, Sachs & Co.
41 */
42 public class ChunkedOutputStream extends OutputStream {
43
44 // ------------------------------------------------------- Static Variables
45 private static final byte CRLF[] = new byte[] {(byte) 13, (byte) 10};
46
47 /** End chunk */
48 private static final byte ENDCHUNK[] = CRLF;
49
50 /** 0 */
51 private static final byte ZERO[] = new byte[] {(byte) '0'};
52
53 // ----------------------------------------------------- Instance Variables
54 private OutputStream stream = null;
55
56 private byte[] cache;
57
58 private int cachePosition = 0;
59
60 private boolean wroteLastChunk = false;
61
62 // ----------------------------------------------------------- Constructors
63 /**
64 * Wraps a stream and chunks the output.
65 * @param stream to wrap
66 * @param bufferSize minimum chunk size (excluding last chunk)
67 * @throws IOException
68 *
69 * @since 3.0
70 */
71 public ChunkedOutputStream(OutputStream stream, int bufferSize) throws IOException {
72 this.cache = new byte[bufferSize];
73 this.stream = stream;
74 }
75
76 /**
77 * Wraps a stream and chunks the output. The default buffer size of 2048 was chosen because
78 * the chunk overhead is less than 0.5%
79 * @param stream
80 * @throws IOException
81 */
82 public ChunkedOutputStream(OutputStream stream) throws IOException {
83 this(stream, 2048);
84 }
85
86 // ----------------------------------------------------------- Internal methods
87 /**
88 * Writes the cache out onto the underlying stream
89 * @throws IOException
90 *
91 * @since 3.0
92 */
93 protected void flushCache() throws IOException {
94 if (cachePosition > 0) {
95 byte chunkHeader[] = (Integer.toHexString(cachePosition) + "\r\n").getBytes("US-ASCII");
96 stream.write(chunkHeader, 0, chunkHeader.length);
97 stream.write(cache, 0, cachePosition);
98 stream.write(ENDCHUNK, 0, ENDCHUNK.length);
99 cachePosition = 0;
100 }
101 }
102
103 /**
104 * Writes the cache and bufferToAppend to the underlying stream
105 * as one large chunk
106 * @param bufferToAppend
107 * @param off
108 * @param len
109 * @throws IOException
110 *
111 * @since 3.0
112 */
113 protected void flushCacheWithAppend(byte bufferToAppend[], int off, int len) throws IOException {
114 byte chunkHeader[] = (Integer.toHexString(cachePosition + len) + "\r\n").getBytes("US-ASCII");
115 stream.write(chunkHeader, 0, chunkHeader.length);
116 stream.write(cache, 0, cachePosition);
117 stream.write(bufferToAppend, off, len);
118 stream.write(ENDCHUNK, 0, ENDCHUNK.length);
119 cachePosition = 0;
120 }
121
122 protected void writeClosingChunk() throws IOException {
123 // Write the final chunk.
124
125 stream.write(ZERO, 0, ZERO.length);
126 stream.write(CRLF, 0, CRLF.length);
127 stream.write(ENDCHUNK, 0, ENDCHUNK.length);
128 }
129
130 // ----------------------------------------------------------- Public Methods
131 /**
132 * Must be called to ensure the internal cache is flushed and the closing chunk is written.
133 * @throws IOException
134 *
135 * @since 3.0
136 */
137 public void finish() throws IOException {
138 if (!wroteLastChunk) {
139 flushCache();
140 writeClosingChunk();
141 wroteLastChunk = true;
142 }
143 }
144
145 // -------------------------------------------- OutputStream Methods
146 /**
147 * Write the specified byte to our output stream.
148 *
149 * Note: Avoid this method as it will cause an inefficient single byte chunk.
150 * Use write (byte[], int, int) instead.
151 *
152 * @param b The byte to be written
153 * @throws IOException if an input/output error occurs
154 */
155 public void write(int b) throws IOException {
156 cache[cachePosition] = (byte) b;
157 cachePosition++;
158 if (cachePosition == cache.length) flushCache();
159 }
160
161 /**
162 * Writes the array. If the array does not fit within the buffer, it is
163 * not split, but rather written out as one large chunk.
164 * @param b
165 * @throws IOException
166 *
167 * @since 3.0
168 */
169 @Override
170 public void write(byte b[]) throws IOException {
171 this.write(b, 0, b.length);
172 }
173
174 @Override
175 public void write(byte src[], int off, int len) throws IOException {
176 if (len >= cache.length - cachePosition) {
177 flushCacheWithAppend(src, off, len);
178 } else {
179 System.arraycopy(src, off, cache, cachePosition, len);
180 cachePosition += len;
181 }
182 }
183
184 /**
185 * Flushes the underlying stream, but leaves the internal buffer alone.
186 * @throws IOException
187 */
188 @Override
189 public void flush() throws IOException {
190 flushCache(); // Kohsuke: flush should flush the cache
191 stream.flush();
192 }
193
194 /**
195 * Finishes writing to the underlying stream, but does NOT close the underlying stream.
196 * @throws IOException
197 */
198 @Override
199 public void close() throws IOException {
200 finish();
201 super.close();
202 }
203 }
204