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