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
186 public void bind(final SSLSocket sslSocket, final Socket baseSocket) throws IOException {
187 super.bind(new SocketHolder(sslSocket, baseSocket));
188 }
189
190 @Override
191 public void sendRequestHeader(final ClassicHttpRequest request)
192 throws HttpException, IOException {
193 Args.notNull(request, "HTTP request");
194 final SocketHolder socketHolder = ensureOpen();
195 this.requestWriter.write(request, this.outbuffer, socketHolder.getOutputStream());
196 onRequestSubmitted(request);
197 incrementRequestCount();
198 }
199
200 @Override
201 public void sendRequestEntity(final ClassicHttpRequest request) throws HttpException, IOException {
202 Args.notNull(request, "HTTP request");
203 final SocketHolder socketHolder = ensureOpen();
204 final HttpEntity entity = request.getEntity();
205 if (entity == null) {
206 return;
207 }
208 final long len = this.outgoingContentStrategy.determineLength(request);
209 if (len == ContentLengthStrategy.UNDEFINED) {
210 throw new LengthRequiredException();
211 }
212 try (final OutputStream outStream = createContentOutputStream(
213 len, this.outbuffer, new OutputStream() {
214
215 final OutputStream socketOutputStream = socketHolder.getOutputStream();
216 final InputStream socketInputStream = socketHolder.getInputStream();
217 final SSLSocket sslSocket = socketHolder.getSSLSocket();
218
219 long totalBytes;
220
221 void checkTLS(final SSLSocket sslSocket) throws IOException {
222 if (sslSocket.isInputShutdown()) {
223 throw new ConnectionClosedException();
224 }
225 }
226
227 void checkForEarlyResponse(final long totalBytesSent, final int nextWriteSize) throws IOException {
228 if (responseOutOfOrderStrategy.isEarlyResponseDetected(
229 request,
230 DefaultBHttpClientConnection.this,
231 socketInputStream,
232 totalBytesSent,
233 nextWriteSize)) {
234 throw new ResponseOutOfOrderException();
235 }
236 }
237
238 @Override
239 public void write(final byte[] b) throws IOException {
240 if (sslSocket != null) {
241 checkTLS(sslSocket);
242 }
243 if (responseOutOfOrderStrategy != null) {
244 checkForEarlyResponse(totalBytes, b.length);
245 }
246 totalBytes += b.length;
247 socketOutputStream.write(b);
248 }
249
250 @Override
251 public void write(final byte[] b, final int off, final int len) throws IOException {
252 if (sslSocket != null) {
253 checkTLS(sslSocket);
254 }
255 if (responseOutOfOrderStrategy != null) {
256 checkForEarlyResponse(totalBytes, len);
257 }
258 totalBytes += len;
259 socketOutputStream.write(b, off, len);
260 }
261
262 @Override
263 public void write(final int b) throws IOException {
264 if (sslSocket != null) {
265 checkTLS(sslSocket);
266 }
267 if (responseOutOfOrderStrategy != null) {
268 checkForEarlyResponse(totalBytes, 1);
269 }
270 totalBytes++;
271 socketOutputStream.write(b);
272 }
273
274 @Override
275 public void flush() throws IOException {
276 socketOutputStream.flush();
277 }
278
279 @Override
280 public void close() throws IOException {
281 socketOutputStream.close();
282 }
283
284 }, entity.getTrailers())) {
285 entity.writeTo(outStream);
286 } catch (final ResponseOutOfOrderException ex) {
287 if (len > 0) {
288 this.consistent = false;
289 }
290 }
291 }
292
293 @Override
294 public boolean isConsistent() {
295 return this.consistent;
296 }
297
298 @Override
299 public void terminateRequest(final ClassicHttpRequest request) throws HttpException, IOException {
300 Args.notNull(request, "HTTP request");
301 final SocketHolder socketHolder = ensureOpen();
302 final HttpEntity entity = request.getEntity();
303 if (entity == null) {
304 return;
305 }
306 final Iterator<String> it = MessageSupport.iterateTokens(request, HttpHeaders.CONNECTION);
307 while (it.hasNext()) {
308 final String token = it.next();
309 if (HeaderElements.CLOSE.equalsIgnoreCase(token)) {
310 this.consistent = false;
311 return;
312 }
313 }
314 final long len = this.outgoingContentStrategy.determineLength(request);
315 if (len == ContentLengthStrategy.CHUNKED) {
316 try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers())) {
317
318 }
319 } else if (len >= 0 && len <= 1024) {
320 try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), null)) {
321 entity.writeTo(outStream);
322 }
323 } else {
324 this.consistent = false;
325 }
326 }
327
328 @Override
329 public ClassicHttpResponse receiveResponseHeader() throws HttpException, IOException {
330 final SocketHolder socketHolder = ensureOpen();
331 final ClassicHttpResponse response = this.responseParser.parse(this.inBuffer, socketHolder.getInputStream());
332 if (response == null) {
333 throw new NoHttpResponseException("The target server failed to respond");
334 }
335 final ProtocolVersion transportVersion = response.getVersion();
336 if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) {
337 throw new UnsupportedHttpVersionException(transportVersion);
338 }
339 this.version = transportVersion;
340 onResponseReceived(response);
341 final int status = response.getCode();
342 if (status < HttpStatus.SC_INFORMATIONAL) {
343 throw new ProtocolException("Invalid response: " + status);
344 }
345 if (response.getCode() >= HttpStatus.SC_SUCCESS) {
346 incrementResponseCount();
347 }
348 return response;
349 }
350
351 @Override
352 public void receiveResponseEntity( final ClassicHttpResponse response) throws HttpException, IOException {
353 Args.notNull(response, "HTTP response");
354 final SocketHolder socketHolder = ensureOpen();
355 final long len = this.incomingContentStrategy.determineLength(response);
356 response.setEntity(createIncomingEntity(response, this.inBuffer, socketHolder.getInputStream(), len));
357 }
358 }