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.OutputStream;
32 import java.net.Socket;
33 import java.nio.charset.CharsetDecoder;
34 import java.nio.charset.CharsetEncoder;
35 import java.util.Iterator;
36
37 import org.apache.hc.core5.http.ClassicHttpRequest;
38 import org.apache.hc.core5.http.ClassicHttpResponse;
39 import org.apache.hc.core5.http.ContentLengthStrategy;
40 import org.apache.hc.core5.http.HeaderElements;
41 import org.apache.hc.core5.http.HttpEntity;
42 import org.apache.hc.core5.http.HttpException;
43 import org.apache.hc.core5.http.HttpHeaders;
44 import org.apache.hc.core5.http.HttpStatus;
45 import org.apache.hc.core5.http.HttpVersion;
46 import org.apache.hc.core5.http.LengthRequiredException;
47 import org.apache.hc.core5.http.ProtocolException;
48 import org.apache.hc.core5.http.ProtocolVersion;
49 import org.apache.hc.core5.http.UnsupportedHttpVersionException;
50 import org.apache.hc.core5.http.config.Http1Config;
51 import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
52 import org.apache.hc.core5.http.io.HttpClientConnection;
53 import org.apache.hc.core5.http.io.HttpMessageParser;
54 import org.apache.hc.core5.http.io.HttpMessageParserFactory;
55 import org.apache.hc.core5.http.io.HttpMessageWriter;
56 import org.apache.hc.core5.http.io.HttpMessageWriterFactory;
57 import org.apache.hc.core5.http.message.BasicTokenIterator;
58 import org.apache.hc.core5.util.Args;
59
60
61
62
63
64
65 public class DefaultBHttpClientConnection extends BHttpConnectionBase
66 implements HttpClientConnection {
67
68 private final HttpMessageParser<ClassicHttpResponse> responseParser;
69 private final HttpMessageWriter<ClassicHttpRequest> requestWriter;
70 private final ContentLengthStrategy incomingContentStrategy;
71 private final ContentLengthStrategy outgoingContentStrategy;
72 private volatile boolean consistent;
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 public DefaultBHttpClientConnection(
93 final Http1Config http1Config,
94 final CharsetDecoder charDecoder,
95 final CharsetEncoder charEncoder,
96 final ContentLengthStrategy incomingContentStrategy,
97 final ContentLengthStrategy outgoingContentStrategy,
98 final HttpMessageWriterFactory<ClassicHttpRequest> requestWriterFactory,
99 final HttpMessageParserFactory<ClassicHttpResponse> responseParserFactory) {
100 super(http1Config, charDecoder, charEncoder);
101 this.requestWriter = (requestWriterFactory != null ? requestWriterFactory :
102 DefaultHttpRequestWriterFactory.INSTANCE).create();
103 this.responseParser = (responseParserFactory != null ? responseParserFactory :
104 DefaultHttpResponseParserFactory.INSTANCE).create(http1Config);
105 this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy :
106 DefaultContentLengthStrategy.INSTANCE;
107 this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy :
108 DefaultContentLengthStrategy.INSTANCE;
109 this.consistent = true;
110 }
111
112 public DefaultBHttpClientConnection(
113 final Http1Config http1Config,
114 final CharsetDecoder charDecoder,
115 final CharsetEncoder charEncoder) {
116 this(http1Config, charDecoder, charEncoder, null, null, null, null);
117 }
118
119 public DefaultBHttpClientConnection(final Http1Config http1Config) {
120 this(http1Config, null, null);
121 }
122
123 protected void onResponseReceived(final ClassicHttpResponse response) {
124 }
125
126 protected void onRequestSubmitted(final ClassicHttpRequest request) {
127 }
128
129 @Override
130 public void bind(final Socket socket) throws IOException {
131 super.bind(socket);
132 }
133
134 @Override
135 public void sendRequestHeader(final ClassicHttpRequest request)
136 throws HttpException, IOException {
137 Args.notNull(request, "HTTP request");
138 final SocketHolder socketHolder = ensureOpen();
139 this.requestWriter.write(request, this.outbuffer, socketHolder.getOutputStream());
140 onRequestSubmitted(request);
141 incrementRequestCount();
142 }
143
144 @Override
145 public void sendRequestEntity(final ClassicHttpRequest request) throws HttpException, IOException {
146 Args.notNull(request, "HTTP request");
147 final SocketHolder socketHolder = ensureOpen();
148 final HttpEntity entity = request.getEntity();
149 if (entity == null) {
150 return;
151 }
152 final long len = this.outgoingContentStrategy.determineLength(request);
153 if (len == ContentLengthStrategy.UNDEFINED) {
154 throw new LengthRequiredException();
155 }
156 try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers())) {
157 entity.writeTo(outStream);
158 }
159 }
160
161 @Override
162 public boolean isConsistent() {
163 return this.consistent;
164 }
165
166 @Override
167 public void terminateRequest(final ClassicHttpRequest request) throws HttpException, IOException {
168 Args.notNull(request, "HTTP request");
169 final SocketHolder socketHolder = ensureOpen();
170 final HttpEntity entity = request.getEntity();
171 if (entity == null) {
172 return;
173 }
174 final Iterator<String> ti = new BasicTokenIterator(request.headerIterator(HttpHeaders.CONNECTION));
175 while (ti.hasNext()) {
176 final String token = ti.next();
177 if (HeaderElements.CLOSE.equalsIgnoreCase(token)) {
178 this.consistent = false;
179 return;
180 }
181 }
182 final long len = this.outgoingContentStrategy.determineLength(request);
183 if (len == ContentLengthStrategy.CHUNKED) {
184 try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers())) {
185
186 }
187 } else if (len >= 0 && len <= 1024) {
188 try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), null)) {
189 entity.writeTo(outStream);
190 }
191 } else {
192 this.consistent = false;
193 }
194 }
195
196 @Override
197 public ClassicHttpResponse receiveResponseHeader() throws HttpException, IOException {
198 final SocketHolder socketHolder = ensureOpen();
199 final ClassicHttpResponse response = this.responseParser.parse(this.inBuffer, socketHolder.getInputStream());
200 final ProtocolVersion transportVersion = response.getVersion();
201 if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) {
202 throw new UnsupportedHttpVersionException(transportVersion);
203 }
204 this.version = transportVersion;
205 onResponseReceived(response);
206 final int status = response.getCode();
207 if (status < HttpStatus.SC_INFORMATIONAL) {
208 throw new ProtocolException("Invalid response: " + status);
209 }
210 if (response.getCode() >= HttpStatus.SC_SUCCESS) {
211 incrementResponseCount();
212 }
213 return response;
214 }
215
216 @Override
217 public void receiveResponseEntity( final ClassicHttpResponse response) throws HttpException, IOException {
218 Args.notNull(response, "HTTP response");
219 final SocketHolder socketHolder = ensureOpen();
220 final long len = this.incomingContentStrategy.determineLength(response);
221 response.setEntity(createIncomingEntity(response, this.inBuffer, socketHolder.getInputStream(), len));
222 }
223 }