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.nio.reactor;
29
30 import java.io.IOException;
31 import java.nio.ByteBuffer;
32 import java.nio.CharBuffer;
33 import java.nio.channels.ReadableByteChannel;
34 import java.nio.channels.WritableByteChannel;
35 import java.nio.charset.CharacterCodingException;
36 import java.nio.charset.Charset;
37 import java.nio.charset.CharsetEncoder;
38 import java.nio.charset.CoderResult;
39 import java.nio.charset.CodingErrorAction;
40
41 import org.apache.http.nio.reactor.SessionOutputBuffer;
42 import org.apache.http.nio.util.ByteBufferAllocator;
43 import org.apache.http.nio.util.ExpandableBuffer;
44 import org.apache.http.nio.util.HeapByteBufferAllocator;
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.CharArrayBuffer;
50 import org.apache.http.util.CharsetUtils;
51
52
53
54
55
56
57
58 @SuppressWarnings("deprecation")
59 public class SessionOutputBufferImpl extends ExpandableBuffer implements SessionOutputBuffer {
60
61 private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF};
62
63 private final CharsetEncoder charEncoder;
64 private final int lineBufferSize;
65
66 private CharBuffer charBuffer;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public SessionOutputBufferImpl(
82 final int bufferSize,
83 final int lineBufferSize,
84 final CharsetEncoder charEncoder,
85 final ByteBufferAllocator allocator) {
86 super(bufferSize, allocator != null ? allocator : HeapByteBufferAllocator.INSTANCE);
87 this.lineBufferSize = Args.positive(lineBufferSize, "Line buffer size");
88 this.charEncoder = charEncoder;
89 }
90
91
92
93
94
95
96 @Deprecated
97 public SessionOutputBufferImpl(
98 final int bufferSize,
99 final int lineBufferSize,
100 final ByteBufferAllocator allocator,
101 final HttpParams params) {
102 super(bufferSize, allocator);
103 this.lineBufferSize = Args.positive(lineBufferSize, "Line buffer size");
104 final String charsetName = (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET);
105 final Charset charset = CharsetUtils.lookup(charsetName);
106 if (charset != null) {
107 this.charEncoder = charset.newEncoder();
108 final CodingErrorAction a1 = (CodingErrorAction) params.getParameter(
109 CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION);
110 this.charEncoder.onMalformedInput(a1 != null ? a1 : CodingErrorAction.REPORT);
111 final CodingErrorAction a2 = (CodingErrorAction) params.getParameter(
112 CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION);
113 this.charEncoder.onUnmappableCharacter(a2 != null? a2 : CodingErrorAction.REPORT);
114 } else {
115 this.charEncoder = null;
116 }
117 }
118
119
120
121
122
123 @Deprecated
124 public SessionOutputBufferImpl(
125 final int bufferSize,
126 final int lineBufferSize,
127 final HttpParams params) {
128 this(bufferSize, lineBufferSize, HeapByteBufferAllocator.INSTANCE, params);
129 }
130
131
132
133
134
135
136
137 public SessionOutputBufferImpl(final int bufferSize) {
138 this(bufferSize, 256, null, HeapByteBufferAllocator.INSTANCE);
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152 public SessionOutputBufferImpl(
153 final int bufferSize,
154 final int lineBufferSize,
155 final Charset charset) {
156 this(bufferSize, lineBufferSize,
157 charset != null ? charset.newEncoder() : null, HeapByteBufferAllocator.INSTANCE);
158 }
159
160
161
162
163
164
165
166
167
168 public SessionOutputBufferImpl(
169 final int bufferSize,
170 final int lineBufferSize) {
171 this(bufferSize, lineBufferSize, null, HeapByteBufferAllocator.INSTANCE);
172 }
173
174 public void reset(final HttpParams params) {
175 clear();
176 }
177
178 @Override
179 public int flush(final WritableByteChannel channel) throws IOException {
180 Args.notNull(channel, "Channel");
181 setOutputMode();
182 return channel.write(this.buffer);
183 }
184
185 @Override
186 public void write(final ByteBuffer src) {
187 if (src == null) {
188 return;
189 }
190 setInputMode();
191 final int requiredCapacity = this.buffer.position() + src.remaining();
192 ensureCapacity(requiredCapacity);
193 this.buffer.put(src);
194 }
195
196 @Override
197 public void write(final ReadableByteChannel src) throws IOException {
198 if (src == null) {
199 return;
200 }
201 setInputMode();
202 src.read(this.buffer);
203 }
204
205 private void write(final byte[] b) {
206 if (b == null) {
207 return;
208 }
209 setInputMode();
210 final int off = 0;
211 final int len = b.length;
212 final int requiredCapacity = this.buffer.position() + len;
213 ensureCapacity(requiredCapacity);
214 this.buffer.put(b, off, len);
215 }
216
217 private void writeCRLF() {
218 write(CRLF);
219 }
220
221 @Override
222 public void writeLine(final CharArrayBuffer lineBuffer) throws CharacterCodingException {
223 if (lineBuffer == null) {
224 return;
225 }
226 setInputMode();
227
228 if (lineBuffer.length() > 0 ) {
229 if (this.charEncoder == null) {
230 final int requiredCapacity = this.buffer.position() + lineBuffer.length();
231 ensureCapacity(requiredCapacity);
232 if (this.buffer.hasArray()) {
233 final byte[] b = this.buffer.array();
234 final int len = lineBuffer.length();
235 final int off = this.buffer.position();
236 for (int i = 0; i < len; i++) {
237 b[off + i] = (byte) lineBuffer.charAt(i);
238 }
239 this.buffer.position(off + len);
240 } else {
241 for (int i = 0; i < lineBuffer.length(); i++) {
242 this.buffer.put((byte) lineBuffer.charAt(i));
243 }
244 }
245 } else {
246 if (this.charBuffer == null) {
247 this.charBuffer = CharBuffer.allocate(this.lineBufferSize);
248 }
249 this.charEncoder.reset();
250
251 int remaining = lineBuffer.length();
252 int offset = 0;
253 while (remaining > 0) {
254 int l = this.charBuffer.remaining();
255 boolean eol = false;
256 if (remaining <= l) {
257 l = remaining;
258
259 eol = true;
260 }
261 this.charBuffer.put(lineBuffer.buffer(), offset, l);
262 this.charBuffer.flip();
263
264 boolean retry = true;
265 while (retry) {
266 final CoderResult result = this.charEncoder.encode(this.charBuffer, this.buffer, eol);
267 if (result.isError()) {
268 result.throwException();
269 }
270 if (result.isOverflow()) {
271 expand();
272 }
273 retry = !result.isUnderflow();
274 }
275 this.charBuffer.compact();
276 offset += l;
277 remaining -= l;
278 }
279
280 boolean retry = true;
281 while (retry) {
282 final CoderResult result = this.charEncoder.flush(this.buffer);
283 if (result.isError()) {
284 result.throwException();
285 }
286 if (result.isOverflow()) {
287 expand();
288 }
289 retry = !result.isUnderflow();
290 }
291 }
292 }
293 writeCRLF();
294 }
295
296 @Override
297 public void writeLine(final String s) throws IOException {
298 if (s == null) {
299 return;
300 }
301 if (s.length() > 0) {
302 final CharArrayBuffer tmp = new CharArrayBuffer(s.length());
303 tmp.append(s);
304 writeLine(tmp);
305 } else {
306 write(CRLF);
307 }
308 }
309
310 }