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;
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 if (responseOutOfOrderStrategy != null) {
223 checkForEarlyResponse(totalBytes, b.length);
224 }
225 totalBytes += b.length;
226 socketOutputStream.write(b);
227 }
228
229 @Override
230 public void write(final byte[] b, final int off, final int len) throws IOException {
231 if (responseOutOfOrderStrategy != null) {
232 checkForEarlyResponse(totalBytes, len);
233 }
234 totalBytes += len;
235 socketOutputStream.write(b, off, len);
236 }
237
238 @Override
239 public void write(final int b) throws IOException {
240 if (responseOutOfOrderStrategy != null) {
241 checkForEarlyResponse(totalBytes, 1);
242 }
243 totalBytes++;
244 socketOutputStream.write(b);
245 }
246
247 @Override
248 public void flush() throws IOException {
249 socketOutputStream.flush();
250 }
251
252 @Override
253 public void close() throws IOException {
254 socketOutputStream.close();
255 }
256
257 }, entity.getTrailers())) {
258 entity.writeTo(outStream);
259 } catch (final ResponseOutOfOrderException ex) {
260 if (len > 0) {
261 this.consistent = false;
262 }
263 }
264 }
265
266 @Override
267 public boolean isConsistent() {
268 return this.consistent;
269 }
270
271 @Override
272 public void terminateRequest(final ClassicHttpRequest request) throws HttpException, IOException {
273 Args.notNull(request, "HTTP request");
274 final SocketHolder socketHolder = ensureOpen();
275 final HttpEntity entity = request.getEntity();
276 if (entity == null) {
277 return;
278 }
279 final Iterator<String> ti = new BasicTokenIterator(request.headerIterator(HttpHeaders.CONNECTION));
280 while (ti.hasNext()) {
281 final String token = ti.next();
282 if (HeaderElements.CLOSE.equalsIgnoreCase(token)) {
283 this.consistent = false;
284 return;
285 }
286 }
287 final long len = this.outgoingContentStrategy.determineLength(request);
288 if (len == ContentLengthStrategy.CHUNKED) {
289 try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers())) {
290
291 }
292 } else if (len >= 0 && len <= 1024) {
293 try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), null)) {
294 entity.writeTo(outStream);
295 }
296 } else {
297 this.consistent = false;
298 }
299 }
300
301 @Override
302 public ClassicHttpResponse receiveResponseHeader() throws HttpException, IOException {
303 final SocketHolder socketHolder = ensureOpen();
304 final ClassicHttpResponse response = this.responseParser.parse(this.inBuffer, socketHolder.getInputStream());
305 if (response == null) {
306 throw new NoHttpResponseException("The target server failed to respond");
307 }
308 final ProtocolVersion transportVersion = response.getVersion();
309 if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) {
310 throw new UnsupportedHttpVersionException(transportVersion);
311 }
312 this.version = transportVersion;
313 onResponseReceived(response);
314 final int status = response.getCode();
315 if (status < HttpStatus.SC_INFORMATIONAL) {
316 throw new ProtocolException("Invalid response: " + status);
317 }
318 if (response.getCode() >= HttpStatus.SC_SUCCESS) {
319 incrementResponseCount();
320 }
321 return response;
322 }
323
324 @Override
325 public void receiveResponseEntity( final ClassicHttpResponse response) throws HttpException, IOException {
326 Args.notNull(response, "HTTP response");
327 final SocketHolder socketHolder = ensureOpen();
328 final long len = this.incomingContentStrategy.determineLength(response);
329 response.setEntity(createIncomingEntity(response, this.inBuffer, socketHolder.getInputStream(), len));
330 }
331 }