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.Consts;
40 import org.apache.http.io.BufferInfo;
41 import org.apache.http.io.HttpTransportMetrics;
42 import org.apache.http.io.SessionOutputBuffer;
43 import org.apache.http.params.CoreConnectionPNames;
44 import org.apache.http.params.CoreProtocolPNames;
45 import org.apache.http.params.HttpParams;
46 import org.apache.http.protocol.HTTP;
47 import org.apache.http.util.Args;
48 import org.apache.http.util.ByteArrayBuffer;
49 import org.apache.http.util.CharArrayBuffer;
50
51
52
53
54
55
56
57
58
59
60
61
62
63 @Deprecated
64 public abstract class AbstractSessionOutputBuffer implements SessionOutputBuffer, BufferInfo {
65
66 private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF};
67
68 private OutputStream outStream;
69 private ByteArrayBuffer buffer;
70 private Charset charset;
71 private boolean ascii;
72 private int minChunkLimit;
73 private HttpTransportMetricsImpl metrics;
74 private CodingErrorAction onMalformedCharAction;
75 private CodingErrorAction onUnmappableCharAction;
76
77 private CharsetEncoder encoder;
78 private ByteBuffer bbuf;
79
80 protected AbstractSessionOutputBuffer(
81 final OutputStream outStream,
82 final int bufferSize,
83 final Charset charset,
84 final int minChunkLimit,
85 final CodingErrorAction malformedCharAction,
86 final CodingErrorAction unmappableCharAction) {
87 super();
88 Args.notNull(outStream, "Input stream");
89 Args.notNegative(bufferSize, "Buffer size");
90 this.outStream = outStream;
91 this.buffer = new ByteArrayBuffer(bufferSize);
92 this.charset = charset != null ? charset : Consts.ASCII;
93 this.ascii = this.charset.equals(Consts.ASCII);
94 this.encoder = null;
95 this.minChunkLimit = minChunkLimit >= 0 ? minChunkLimit : 512;
96 this.metrics = createTransportMetrics();
97 this.onMalformedCharAction = malformedCharAction != null ? malformedCharAction :
98 CodingErrorAction.REPORT;
99 this.onUnmappableCharAction = unmappableCharAction != null? unmappableCharAction :
100 CodingErrorAction.REPORT;
101 }
102
103 public AbstractSessionOutputBuffer() {
104 }
105
106 protected void init(final OutputStream outStream, final int bufferSize, final HttpParams params) {
107 Args.notNull(outStream, "Input stream");
108 Args.notNegative(bufferSize, "Buffer size");
109 Args.notNull(params, "HTTP parameters");
110 this.outStream = outStream;
111 this.buffer = new ByteArrayBuffer(bufferSize);
112 final String charset = (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET);
113 this.charset = charset != null ? Charset.forName(charset) : Consts.ASCII;
114 this.ascii = this.charset.equals(Consts.ASCII);
115 this.encoder = null;
116 this.minChunkLimit = params.getIntParameter(CoreConnectionPNames.MIN_CHUNK_LIMIT, 512);
117 this.metrics = createTransportMetrics();
118 final CodingErrorAction a1 = (CodingErrorAction) params.getParameter(
119 CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION);
120 this.onMalformedCharAction = a1 != null ? a1 : CodingErrorAction.REPORT;
121 final CodingErrorAction a2 = (CodingErrorAction) params.getParameter(
122 CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION);
123 this.onUnmappableCharAction = a2 != null? a2 : CodingErrorAction.REPORT;
124 }
125
126
127
128
129 protected HttpTransportMetricsImpl createTransportMetrics() {
130 return new HttpTransportMetricsImpl();
131 }
132
133
134
135
136 @Override
137 public int capacity() {
138 return this.buffer.capacity();
139 }
140
141
142
143
144 @Override
145 public int length() {
146 return this.buffer.length();
147 }
148
149
150
151
152 @Override
153 public int available() {
154 return capacity() - length();
155 }
156
157 protected void flushBuffer() throws IOException {
158 final int len = this.buffer.length();
159 if (len > 0) {
160 this.outStream.write(this.buffer.buffer(), 0, len);
161 this.buffer.clear();
162 this.metrics.incrementBytesTransferred(len);
163 }
164 }
165
166 @Override
167 public void flush() throws IOException {
168 flushBuffer();
169 this.outStream.flush();
170 }
171
172 @Override
173 public void write(final byte[] b, final int off, final int len) throws IOException {
174 if (b == null) {
175 return;
176 }
177
178
179
180 if (len > this.minChunkLimit || len > this.buffer.capacity()) {
181
182 flushBuffer();
183
184 this.outStream.write(b, off, len);
185 this.metrics.incrementBytesTransferred(len);
186 } else {
187
188 final int freecapacity = this.buffer.capacity() - this.buffer.length();
189 if (len > freecapacity) {
190
191 flushBuffer();
192 }
193
194 this.buffer.append(b, off, len);
195 }
196 }
197
198 @Override
199 public void write(final byte[] b) throws IOException {
200 if (b == null) {
201 return;
202 }
203 write(b, 0, b.length);
204 }
205
206 @Override
207 public void write(final int b) throws IOException {
208 if (this.buffer.isFull()) {
209 flushBuffer();
210 }
211 this.buffer.append(b);
212 }
213
214
215
216
217
218
219
220
221
222
223 @Override
224 public void writeLine(final String s) throws IOException {
225 if (s == null) {
226 return;
227 }
228 if (s.length() > 0) {
229 if (this.ascii) {
230 for (int i = 0; i < s.length(); i++) {
231 write(s.charAt(i));
232 }
233 } else {
234 final CharBuffer cbuf = CharBuffer.wrap(s);
235 writeEncoded(cbuf);
236 }
237 }
238 write(CRLF);
239 }
240
241
242
243
244
245
246
247
248
249
250 @Override
251 public void writeLine(final CharArrayBuffer charbuffer) throws IOException {
252 if (charbuffer == null) {
253 return;
254 }
255 if (this.ascii) {
256 int off = 0;
257 int remaining = charbuffer.length();
258 while (remaining > 0) {
259 int chunk = this.buffer.capacity() - this.buffer.length();
260 chunk = Math.min(chunk, remaining);
261 if (chunk > 0) {
262 this.buffer.append(charbuffer, off, chunk);
263 }
264 if (this.buffer.isFull()) {
265 flushBuffer();
266 }
267 off += chunk;
268 remaining -= chunk;
269 }
270 } else {
271 final CharBuffer cbuf = CharBuffer.wrap(charbuffer.buffer(), 0, charbuffer.length());
272 writeEncoded(cbuf);
273 }
274 write(CRLF);
275 }
276
277 private void writeEncoded(final CharBuffer cbuf) throws IOException {
278 if (!cbuf.hasRemaining()) {
279 return;
280 }
281 if (this.encoder == null) {
282 this.encoder = this.charset.newEncoder();
283 this.encoder.onMalformedInput(this.onMalformedCharAction);
284 this.encoder.onUnmappableCharacter(this.onUnmappableCharAction);
285 }
286 if (this.bbuf == null) {
287 this.bbuf = ByteBuffer.allocate(1024);
288 }
289 this.encoder.reset();
290 while (cbuf.hasRemaining()) {
291 final CoderResult result = this.encoder.encode(cbuf, this.bbuf, true);
292 handleEncodingResult(result);
293 }
294 final CoderResult result = this.encoder.flush(this.bbuf);
295 handleEncodingResult(result);
296 this.bbuf.clear();
297 }
298
299 private void handleEncodingResult(final CoderResult result) throws IOException {
300 if (result.isError()) {
301 result.throwException();
302 }
303 this.bbuf.flip();
304 while (this.bbuf.hasRemaining()) {
305 write(this.bbuf.get());
306 }
307 this.bbuf.compact();
308 }
309
310 @Override
311 public HttpTransportMetrics getMetrics() {
312 return this.metrics;
313 }
314
315 }