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.InputStream;
32 import java.io.OutputStream;
33 import java.net.Socket;
34 import java.nio.charset.CharsetDecoder;
35 import java.nio.charset.CharsetEncoder;
36 import java.util.Iterator;
37
38 import org.apache.hc.core5.http.ClassicHttpRequest;
39 import org.apache.hc.core5.http.ClassicHttpResponse;
40 import org.apache.hc.core5.http.ContentLengthStrategy;
41 import org.apache.hc.core5.http.HeaderElements;
42 import org.apache.hc.core5.http.HttpEntity;
43 import org.apache.hc.core5.http.HttpException;
44 import org.apache.hc.core5.http.HttpHeaders;
45 import org.apache.hc.core5.http.HttpStatus;
46 import org.apache.hc.core5.http.HttpVersion;
47 import org.apache.hc.core5.http.LengthRequiredException;
48 import org.apache.hc.core5.http.ProtocolException;
49 import org.apache.hc.core5.http.ProtocolVersion;
50 import org.apache.hc.core5.http.UnsupportedHttpVersionException;
51 import org.apache.hc.core5.http.config.Http1Config;
52 import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
53 import org.apache.hc.core5.http.io.HttpClientConnection;
54 import org.apache.hc.core5.http.io.HttpMessageParser;
55 import org.apache.hc.core5.http.io.HttpMessageParserFactory;
56 import org.apache.hc.core5.http.io.HttpMessageWriter;
57 import org.apache.hc.core5.http.io.HttpMessageWriterFactory;
58 import org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy;
59 import org.apache.hc.core5.http.message.BasicTokenIterator;
60 import org.apache.hc.core5.util.Args;
61
62
63
64
65
66
67 public class DefaultBHttpClientConnection extends BHttpConnectionBase
68 implements HttpClientConnection {
69
70 private final HttpMessageParser<ClassicHttpResponse> responseParser;
71 private final HttpMessageWriter<ClassicHttpRequest> requestWriter;
72 private final ContentLengthStrategy incomingContentStrategy;
73 private final ContentLengthStrategy outgoingContentStrategy;
74 private final ResponseOutOfOrderStrategy responseOutOfOrderStrategy;
75 private volatile boolean consistent;
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 public DefaultBHttpClientConnection(
98 final Http1Config http1Config,
99 final CharsetDecoder charDecoder,
100 final CharsetEncoder charEncoder,
101 final ContentLengthStrategy incomingContentStrategy,
102 final ContentLengthStrategy outgoingContentStrategy,
103 final ResponseOutOfOrderStrategy responseOutOfOrderStrategy,
104 final HttpMessageWriterFactory<ClassicHttpRequest> requestWriterFactory,
105 final HttpMessageParserFactory<ClassicHttpResponse> responseParserFactory) {
106 super(http1Config, charDecoder, charEncoder);
107 this.requestWriter = (requestWriterFactory != null ? requestWriterFactory :
108 DefaultHttpRequestWriterFactory.INSTANCE).create();
109 this.responseParser = (responseParserFactory != null ? responseParserFactory :
110 DefaultHttpResponseParserFactory.INSTANCE).create(http1Config);
111 this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy :
112 DefaultContentLengthStrategy.INSTANCE;
113 this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy :
114 DefaultContentLengthStrategy.INSTANCE;
115 this.responseOutOfOrderStrategy = responseOutOfOrderStrategy != null ? responseOutOfOrderStrategy :
116 NoResponseOutOfOrderStrategy.INSTANCE;
117 this.consistent = true;
118 }
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 public DefaultBHttpClientConnection(
139 final Http1Config http1Config,
140 final CharsetDecoder charDecoder,
141 final CharsetEncoder charEncoder,
142 final ContentLengthStrategy incomingContentStrategy,
143 final ContentLengthStrategy outgoingContentStrategy,
144 final HttpMessageWriterFactory<ClassicHttpRequest> requestWriterFactory,
145 final HttpMessageParserFactory<ClassicHttpResponse> responseParserFactory) {
146 this(
147 http1Config,
148 charDecoder,
149 charEncoder,
150 incomingContentStrategy,
151 outgoingContentStrategy,
152 null,
153 requestWriterFactory,
154 responseParserFactory);
155 }
156
157 public DefaultBHttpClientConnection(
158 final Http1Config http1Config,
159 final CharsetDecoder charDecoder,
160 final CharsetEncoder charEncoder) {
161 this(http1Config, charDecoder, charEncoder, null, null, null, null);
162 }
163
164 public DefaultBHttpClientConnection(final Http1Config http1Config) {
165 this(http1Config, null, null);
166 }
167
168 protected void onResponseReceived(final ClassicHttpResponse response) {
169 }
170
171 protected void onRequestSubmitted(final ClassicHttpRequest request) {
172 }
173
174 @Override
175 public void bind(final Socket socket) throws IOException {
176 super.bind(socket);
177 }
178
179 @Override
180 public void sendRequestHeader(final ClassicHttpRequest request)
181 throws HttpException, IOException {
182 Args.notNull(request, "HTTP request");
183 final SocketHolder socketHolder = ensureOpen();
184 this.requestWriter.write(request, this.outbuffer, socketHolder.getOutputStream());
185 onRequestSubmitted(request);
186 incrementRequestCount();
187 }
188
189 @Override
190 public void sendRequestEntity(final ClassicHttpRequest request) throws HttpException, IOException {
191 Args.notNull(request, "HTTP request");
192 final SocketHolder socketHolder = ensureOpen();
193 final HttpEntity entity = request.getEntity();
194 if (entity == null) {
195 return;
196 }
197 final long len = this.outgoingContentStrategy.determineLength(request);
198 if (len == ContentLengthStrategy.UNDEFINED) {
199 throw new LengthRequiredException();
200 }
201 try (final OutputStream outStream = createContentOutputStream(
202 len, this.outbuffer, new OutputStream() {
203
204 final OutputStream socketOutputStream = socketHolder.getOutputStream();
205 final InputStream socketInputStream = socketHolder.getInputStream();
206
207 long totalBytes;
208
209 void checkForEarlyResponse(final long totalBytesSent, final int nextWriteSize) throws IOException {
210 if (responseOutOfOrderStrategy.isEarlyResponseDetected(
211 request,
212 DefaultBHttpClientConnection.this,
213 socketInputStream,
214 totalBytesSent,
215 nextWriteSize)) {
216 throw new ResponseOutOfOrderException();
217 }
218 }
219
220 @Override
221 public void write(final byte[] b) throws IOException {
222 checkForEarlyResponse(totalBytes, b.length);
223 totalBytes += b.length;
224 socketOutputStream.write(b);
225 }
226
227 @Override
228 public void write(final byte[] b, final int off, final int len) throws IOException {
229 checkForEarlyResponse(totalBytes, len);
230 totalBytes += len;
231 socketOutputStream.write(b, off, len);
232 }
233
234 @Override
235 public void write(final int b) throws IOException {
236 checkForEarlyResponse(totalBytes, 1);
237 totalBytes++;
238 socketOutputStream.write(b);
239 }
240
241 @Override
242 public void flush() throws IOException {
243 socketOutputStream.flush();
244 }
245
246 @Override
247 public void close() throws IOException {
248 socketOutputStream.close();
249 }
250
251 }, entity.getTrailers())) {
252 entity.writeTo(outStream);
253 } catch (final ResponseOutOfOrderException ex) {
254 if (len > 0) {
255 this.consistent = false;
256 }
257 }
258 }
259
260 @Override
261 public boolean isConsistent() {
262 return this.consistent;
263 }
264
265 @Override
266 public void terminateRequest(final ClassicHttpRequest request) throws HttpException, IOException {
267 Args.notNull(request, "HTTP request");
268 final SocketHolder socketHolder = ensureOpen();
269 final HttpEntity entity = request.getEntity();
270 if (entity == null) {
271 return;
272 }
273 final Iterator<String> ti = new BasicTokenIterator(request.headerIterator(HttpHeaders.CONNECTION));
274 while (ti.hasNext()) {
275 final String token = ti.next();
276 if (HeaderElements.CLOSE.equalsIgnoreCase(token)) {
277 this.consistent = false;
278 return;
279 }
280 }
281 final long len = this.outgoingContentStrategy.determineLength(request);
282 if (len == ContentLengthStrategy.CHUNKED) {
283 try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers())) {
284
285 }
286 } else if (len >= 0 && len <= 1024) {
287 try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), null)) {
288 entity.writeTo(outStream);
289 }
290 } else {
291 this.consistent = false;
292 }
293 }
294
295 @Override
296 public ClassicHttpResponse receiveResponseHeader() throws HttpException, IOException {
297 final SocketHolder socketHolder = ensureOpen();
298 final ClassicHttpResponse response = this.responseParser.parse(this.inBuffer, socketHolder.getInputStream());
299 final ProtocolVersion transportVersion = response.getVersion();
300 if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) {
301 throw new UnsupportedHttpVersionException(transportVersion);
302 }
303 this.version = transportVersion;
304 onResponseReceived(response);
305 final int status = response.getCode();
306 if (status < HttpStatus.SC_INFORMATIONAL) {
307 throw new ProtocolException("Invalid response: " + status);
308 }
309 if (response.getCode() >= HttpStatus.SC_SUCCESS) {
310 incrementResponseCount();
311 }
312 return response;
313 }
314
315 @Override
316 public void receiveResponseEntity( final ClassicHttpResponse response) throws HttpException, IOException {
317 Args.notNull(response, "HTTP response");
318 final SocketHolder socketHolder = ensureOpen();
319 final long len = this.incomingContentStrategy.determineLength(response);
320 response.setEntity(createIncomingEntity(response, this.inBuffer, socketHolder.getInputStream(), len));
321 }
322 }