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