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