1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.http.impl.io;
29
30 import java.io.IOException;
31 import java.io.OutputStream;
32 import java.nio.ByteBuffer;
33 import java.nio.CharBuffer;
34 import java.nio.charset.CharsetEncoder;
35 import java.nio.charset.CoderResult;
36
37 import org.apache.http.io.BufferInfo;
38 import org.apache.http.io.HttpTransportMetrics;
39 import org.apache.http.io.SessionOutputBuffer;
40 import org.apache.http.protocol.HTTP;
41 import org.apache.http.util.Args;
42 import org.apache.http.util.Asserts;
43 import org.apache.http.util.ByteArrayBuffer;
44 import org.apache.http.util.CharArrayBuffer;
45
46
47
48
49
50
51
52
53
54
55
56 public class SessionOutputBufferImpl implements SessionOutputBuffer, BufferInfo {
57
58 private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF};
59
60 private final HttpTransportMetricsImpl metrics;
61 private final ByteArrayBuffer buffer;
62 private final int fragementSizeHint;
63 private final CharsetEncoder encoder;
64
65 private OutputStream outStream;
66 private ByteBuffer bbuf;
67
68
69
70
71
72
73
74
75
76
77
78
79 public SessionOutputBufferImpl(
80 final HttpTransportMetricsImpl metrics,
81 final int bufferSize,
82 final int fragementSizeHint,
83 final CharsetEncoder charEncoder) {
84 super();
85 Args.positive(bufferSize, "Buffer size");
86 Args.notNull(metrics, "HTTP transport metrcis");
87 this.metrics = metrics;
88 this.buffer = new ByteArrayBuffer(bufferSize);
89 this.fragementSizeHint = fragementSizeHint >= 0 ? fragementSizeHint : 0;
90 this.encoder = charEncoder;
91 }
92
93 public SessionOutputBufferImpl(
94 final HttpTransportMetricsImpl metrics,
95 final int bufferSize) {
96 this(metrics, bufferSize, bufferSize, null);
97 }
98
99 public void bind(final OutputStream outStream) {
100 this.outStream = outStream;
101 }
102
103 public boolean isBound() {
104 return this.outStream != null;
105 }
106
107 @Override
108 public int capacity() {
109 return this.buffer.capacity();
110 }
111
112 @Override
113 public int length() {
114 return this.buffer.length();
115 }
116
117 @Override
118 public int available() {
119 return capacity() - length();
120 }
121
122 private void streamWrite(final byte[] b, final int off, final int len) throws IOException {
123 Asserts.notNull(outStream, "Output stream");
124 this.outStream.write(b, off, len);
125 }
126
127 private void flushStream() throws IOException {
128 if (this.outStream != null) {
129 this.outStream.flush();
130 }
131 }
132
133 private void flushBuffer() throws IOException {
134 final int len = this.buffer.length();
135 if (len > 0) {
136 streamWrite(this.buffer.buffer(), 0, len);
137 this.buffer.clear();
138 this.metrics.incrementBytesTransferred(len);
139 }
140 }
141
142 @Override
143 public void flush() throws IOException {
144 flushBuffer();
145 flushStream();
146 }
147
148 @Override
149 public void write(final byte[] b, final int off, final int len) throws IOException {
150 if (b == null) {
151 return;
152 }
153
154
155
156 if (len > this.fragementSizeHint || len > this.buffer.capacity()) {
157
158 flushBuffer();
159
160 streamWrite(b, off, len);
161 this.metrics.incrementBytesTransferred(len);
162 } else {
163
164 final int freecapacity = this.buffer.capacity() - this.buffer.length();
165 if (len > freecapacity) {
166
167 flushBuffer();
168 }
169
170 this.buffer.append(b, off, len);
171 }
172 }
173
174 @Override
175 public void write(final byte[] b) throws IOException {
176 if (b == null) {
177 return;
178 }
179 write(b, 0, b.length);
180 }
181
182 @Override
183 public void write(final int b) throws IOException {
184 if (this.fragementSizeHint > 0) {
185 if (this.buffer.isFull()) {
186 flushBuffer();
187 }
188 this.buffer.append(b);
189 } else {
190 flushBuffer();
191 this.outStream.write(b);
192 }
193 }
194
195
196
197
198
199
200
201
202
203
204 @Override
205 public void writeLine(final String s) throws IOException {
206 if (s == null) {
207 return;
208 }
209 if (s.length() > 0) {
210 if (this.encoder == null) {
211 for (int i = 0; i < s.length(); i++) {
212 write(s.charAt(i));
213 }
214 } else {
215 final CharBuffer cbuf = CharBuffer.wrap(s);
216 writeEncoded(cbuf);
217 }
218 }
219 write(CRLF);
220 }
221
222
223
224
225
226
227
228
229
230
231 @Override
232 public void writeLine(final CharArrayBuffer charbuffer) throws IOException {
233 if (charbuffer == null) {
234 return;
235 }
236 if (this.encoder == null) {
237 int off = 0;
238 int remaining = charbuffer.length();
239 while (remaining > 0) {
240 int chunk = this.buffer.capacity() - this.buffer.length();
241 chunk = Math.min(chunk, remaining);
242 if (chunk > 0) {
243 this.buffer.append(charbuffer, off, chunk);
244 }
245 if (this.buffer.isFull()) {
246 flushBuffer();
247 }
248 off += chunk;
249 remaining -= chunk;
250 }
251 } else {
252 final CharBuffer cbuf = CharBuffer.wrap(charbuffer.buffer(), 0, charbuffer.length());
253 writeEncoded(cbuf);
254 }
255 write(CRLF);
256 }
257
258 private void writeEncoded(final CharBuffer cbuf) throws IOException {
259 if (!cbuf.hasRemaining()) {
260 return;
261 }
262 if (this.bbuf == null) {
263 this.bbuf = ByteBuffer.allocate(1024);
264 }
265 this.encoder.reset();
266 while (cbuf.hasRemaining()) {
267 final CoderResult result = this.encoder.encode(cbuf, this.bbuf, true);
268 handleEncodingResult(result);
269 }
270 final CoderResult result = this.encoder.flush(this.bbuf);
271 handleEncodingResult(result);
272 this.bbuf.clear();
273 }
274
275 private void handleEncodingResult(final CoderResult result) throws IOException {
276 if (result.isError()) {
277 result.throwException();
278 }
279 this.bbuf.flip();
280 while (this.bbuf.hasRemaining()) {
281 write(this.bbuf.get());
282 }
283 this.bbuf.compact();
284 }
285
286 @Override
287 public HttpTransportMetrics getMetrics() {
288 return this.metrics;
289 }
290
291 }