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.Charset;
35 import java.nio.charset.CharsetEncoder;
36 import java.nio.charset.CoderResult;
37 import java.nio.charset.CodingErrorAction;
38
39 import org.apache.http.annotation.NotThreadSafe;
40 import org.apache.http.io.BufferInfo;
41 import org.apache.http.io.SessionOutputBuffer;
42 import org.apache.http.io.HttpTransportMetrics;
43 import org.apache.http.params.CoreConnectionPNames;
44 import org.apache.http.params.HttpParams;
45 import org.apache.http.params.HttpProtocolParams;
46 import org.apache.http.protocol.HTTP;
47 import org.apache.http.util.ByteArrayBuffer;
48 import org.apache.http.util.CharArrayBuffer;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 @NotThreadSafe
69 public abstract class AbstractSessionOutputBuffer implements SessionOutputBuffer, BufferInfo {
70
71 private static final Charset ASCII = Charset.forName("US-ASCII");
72 private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF};
73
74 private OutputStream outstream;
75 private ByteArrayBuffer buffer;
76
77 private Charset charset;
78 private CharsetEncoder encoder;
79 private ByteBuffer bbuf;
80 private boolean ascii = true;
81 private int minChunkLimit = 512;
82
83 private HttpTransportMetricsImpl metrics;
84
85 private CodingErrorAction onMalformedInputAction;
86 private CodingErrorAction onUnMappableInputAction;
87
88
89
90
91
92
93
94
95 protected void init(final OutputStream outstream, int buffersize, final HttpParams params) {
96 if (outstream == null) {
97 throw new IllegalArgumentException("Input stream may not be null");
98 }
99 if (buffersize <= 0) {
100 throw new IllegalArgumentException("Buffer size may not be negative or zero");
101 }
102 if (params == null) {
103 throw new IllegalArgumentException("HTTP parameters may not be null");
104 }
105 this.outstream = outstream;
106 this.buffer = new ByteArrayBuffer(buffersize);
107 this.charset = Charset.forName(HttpProtocolParams.getHttpElementCharset(params));
108 this.ascii = this.charset.equals(ASCII);
109 this.encoder = null;
110 this.minChunkLimit = params.getIntParameter(CoreConnectionPNames.MIN_CHUNK_LIMIT, 512);
111 this.metrics = createTransportMetrics();
112 this.onMalformedInputAction = HttpProtocolParams.getMalformedInputAction(params);
113 this.onUnMappableInputAction = HttpProtocolParams.getUnmappableInputAction(params);
114 }
115
116
117
118
119 protected HttpTransportMetricsImpl createTransportMetrics() {
120 return new HttpTransportMetricsImpl();
121 }
122
123
124
125
126 public int capacity() {
127 return this.buffer.capacity();
128 }
129
130
131
132
133 public int length() {
134 return this.buffer.length();
135 }
136
137
138
139
140 public int available() {
141 return capacity() - length();
142 }
143
144 protected void flushBuffer() throws IOException {
145 int len = this.buffer.length();
146 if (len > 0) {
147 this.outstream.write(this.buffer.buffer(), 0, len);
148 this.buffer.clear();
149 this.metrics.incrementBytesTransferred(len);
150 }
151 }
152
153 public void flush() throws IOException {
154 flushBuffer();
155 this.outstream.flush();
156 }
157
158 public void write(final byte[] b, int off, int len) throws IOException {
159 if (b == null) {
160 return;
161 }
162
163
164
165 if (len > this.minChunkLimit || len > this.buffer.capacity()) {
166
167 flushBuffer();
168
169 this.outstream.write(b, off, len);
170 this.metrics.incrementBytesTransferred(len);
171 } else {
172
173 int freecapacity = this.buffer.capacity() - this.buffer.length();
174 if (len > freecapacity) {
175
176 flushBuffer();
177 }
178
179 this.buffer.append(b, off, len);
180 }
181 }
182
183 public void write(final byte[] b) throws IOException {
184 if (b == null) {
185 return;
186 }
187 write(b, 0, b.length);
188 }
189
190 public void write(int b) throws IOException {
191 if (this.buffer.isFull()) {
192 flushBuffer();
193 }
194 this.buffer.append(b);
195 }
196
197
198
199
200
201
202
203
204
205
206 public void writeLine(final String s) throws IOException {
207 if (s == null) {
208 return;
209 }
210 if (s.length() > 0) {
211 if (this.ascii) {
212 for (int i = 0; i < s.length(); i++) {
213 write(s.charAt(i));
214 }
215 } else {
216 CharBuffer cbuf = CharBuffer.wrap(s);
217 writeEncoded(cbuf);
218 }
219 }
220 write(CRLF);
221 }
222
223
224
225
226
227
228
229
230
231
232 public void writeLine(final CharArrayBuffer charbuffer) throws IOException {
233 if (charbuffer == null) {
234 return;
235 }
236 if (this.ascii) {
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 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.encoder == null) {
263 this.encoder = this.charset.newEncoder();
264 this.encoder.onMalformedInput(this.onMalformedInputAction);
265 this.encoder.onUnmappableCharacter(this.onUnMappableInputAction);
266 }
267 if (this.bbuf == null) {
268 this.bbuf = ByteBuffer.allocate(1024);
269 }
270 this.encoder.reset();
271 while (cbuf.hasRemaining()) {
272 CoderResult result = this.encoder.encode(cbuf, this.bbuf, true);
273 handleEncodingResult(result);
274 }
275 CoderResult result = this.encoder.flush(this.bbuf);
276 handleEncodingResult(result);
277 this.bbuf.clear();
278 }
279
280 private void handleEncodingResult(final CoderResult result) throws IOException {
281 if (result.isError()) {
282 result.throwException();
283 }
284 this.bbuf.flip();
285 while (this.bbuf.hasRemaining()) {
286 write(this.bbuf.get());
287 }
288 this.bbuf.compact();
289 }
290
291 public HttpTransportMetrics getMetrics() {
292 return this.metrics;
293 }
294
295 }