1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.danube.http;
14
15 import org.abstracthorizon.danube.support.RuntimeIOException;
16
17 import java.io.IOException;
18 import java.io.OutputStream;
19
20
21
22
23
24
25 public class HTTPBufferedOutputStream extends OutputStream {
26
27
28 public static final String CRLF = "\r\n";
29
30
31 protected byte[] buffer;
32
33
34 protected int ptr;
35
36
37 protected HTTPConnectionImpl connection;
38
39
40 protected OutputStream outputStream;
41
42
43 protected boolean supporessOutput;
44
45
46 protected int bufferSize;
47
48
49 protected boolean closed = false;
50
51
52 protected long limitedContentLength = -1;
53
54
55 protected int sentbytes = 0;
56
57
58 protected boolean chunkEncoding = false;
59
60
61
62
63
64
65
66
67 public HTTPBufferedOutputStream(HTTPConnectionImpl connection, OutputStream outputStream, int defaultBufferSize) {
68 this.connection = connection;
69 this.outputStream = outputStream;
70 this.bufferSize = defaultBufferSize;
71 }
72
73
74
75
76 public void resetInternals() {
77 closed = false;
78 supporessOutput = false;
79 ptr = 0;
80 limitedContentLength = -1;
81 sentbytes = 0;
82 chunkEncoding = false;
83 }
84
85
86
87
88
89
90 public void setSupporessOutput(boolean suppressOutput) {
91 this.supporessOutput = suppressOutput;
92 }
93
94
95
96
97
98 public boolean isSupporessOutput() {
99 return supporessOutput;
100 }
101
102
103
104
105
106
107 public int getBufferSize() {
108 return bufferSize;
109 }
110
111
112
113
114
115
116
117 public void setBufferSize(int size) {
118 bufferSize = size;
119 if (buffer != null) {
120 if (size > buffer.length) {
121 byte[] newBuffer = new byte[size];
122 System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
123 buffer = newBuffer;
124 } else if (size < buffer.length) {
125 try {
126 flush();
127 } catch (IOException e) {
128 throw new RuntimeIOException(e);
129 }
130 buffer = new byte[size];
131 }
132 }
133 }
134
135
136
137
138
139 public long getLimitedContentLength() {
140 return limitedContentLength;
141 }
142
143
144
145
146
147 public void setLimitedContentLength(long limitedContentLength) {
148 this.limitedContentLength = limitedContentLength;
149 }
150
151
152
153
154
155 public boolean isChunkEncoding() {
156 return chunkEncoding;
157 }
158
159
160
161
162
163 public void setChunkEncoding(boolean chunkEncoding) {
164 this.chunkEncoding = chunkEncoding;
165 }
166
167
168
169
170
171
172 protected void checkBuffer() throws IOException {
173 if (buffer == null) {
174 buffer = new byte[bufferSize];
175 ptr = 0;
176 }
177 }
178
179
180
181
182
183 public void flushImpl() throws IOException {
184 if (closed) {
185 throw new IOException("Already closed");
186 }
187 if (ptr > 0) {
188 if (!connection.isCommited()) {
189 connection.commitHeaders();
190 }
191
192 if ((limitedContentLength >= 0) && ((sentbytes + ptr) > limitedContentLength)) {
193 if (chunkEncoding) {
194 outputStream.write((Long.toString(limitedContentLength - sentbytes, 16) + CRLF).getBytes());
195 outputStream.write(buffer, 0, (int)(limitedContentLength - sentbytes));
196 outputStream.write(CRLF.getBytes());
197
198 } else {
199 outputStream.write(buffer, 0, (int)(limitedContentLength - sentbytes));
200
201 }
202 throw new IOException("Content length exceeded asked value; requested to be sent " + (sentbytes + ptr) + ", limit " + limitedContentLength);
203 } else {
204 if (chunkEncoding) {
205 outputStream.write((Integer.toString(ptr, 16) + "\r\n").getBytes());
206 outputStream.write(buffer, 0, ptr);
207 outputStream.write(CRLF.getBytes());
208
209 } else {
210 outputStream.write(buffer, 0, ptr);
211
212 }
213 }
214 ptr = 0;
215 }
216 }
217
218 @Override
219 public void close() throws IOException {
220 if (!connection.isCommited()) {
221
222 connection.getResponseHeaders().removeAll("Transfer-Encoding");
223 chunkEncoding = false;
224 if (!supporessOutput) {
225 connection.getResponseHeaders().putOnly("Content-Length", Integer.toString(ptr));
226 }
227 }
228 flushImpl();
229 if (!connection.isCommited()) {
230 connection.commitHeaders();
231 }
232 if (chunkEncoding) {
233 outputStream.write("0\r\n\r\n".getBytes());
234 }
235 outputStream.flush();
236 closed = true;
237 }
238
239 @Override
240 public void write(int b) throws IOException {
241 if (closed) {
242 throw new IOException("Already closed");
243 }
244 if (!supporessOutput) {
245 if ((limitedContentLength >= 0) && ((sentbytes + 1) > limitedContentLength)) {
246 throw new IOException("Content length exceeded asked value; requested to be sent " + (sentbytes + 1) + ", limit " + limitedContentLength);
247 }
248
249 checkBuffer();
250 buffer[ptr] = (byte)b;
251 if (ptr == buffer.length) {
252 flushImpl();
253 }
254 }
255 }
256
257 @Override
258 public void write(byte[] buf, int start, int len) throws IOException {
259 if (closed) {
260 throw new IOException("Already closed");
261 }
262 if (!supporessOutput) {
263 if ((limitedContentLength >= 0) && ((sentbytes + len) > limitedContentLength)) {
264 throw new IOException("Content length exceeded asked value; requested to be sent " + (sentbytes + len) + ", limit " + limitedContentLength);
265 }
266
267 checkBuffer();
268 if (len > (buffer.length - ptr)) {
269 flushImpl();
270 if (!connection.isCommited()) {
271 connection.commitHeaders();
272 }
273 if (chunkEncoding) {
274 outputStream.write((Integer.toString(len, 16) + "\r\n").getBytes());
275 outputStream.write(buf, start, len);
276 outputStream.write(CRLF.getBytes());
277
278 } else {
279 outputStream.write(buf, start, len);
280 }
281 } else {
282 System.arraycopy(buf, start, buffer, ptr, len);
283 ptr = ptr + len;
284 if (ptr == buffer.length) {
285 flushImpl();
286 }
287 }
288 }
289 }
290
291 @Override
292 public void write(byte[] buf) throws IOException {
293 if (closed) {
294 throw new IOException("Already closed");
295 }
296 if (!supporessOutput) {
297 write(buf, 0, buf.length);
298 }
299 }
300 }