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