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