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 package org.apache.hc.core5.http.impl.io;
28
29 import java.io.ByteArrayInputStream;
30 import java.io.ByteArrayOutputStream;
31 import java.io.InputStream;
32 import java.net.Socket;
33 import java.nio.charset.StandardCharsets;
34
35 import org.apache.hc.core5.http.ClassicHttpRequest;
36 import org.apache.hc.core5.http.ClassicHttpResponse;
37 import org.apache.hc.core5.http.ContentType;
38 import org.apache.hc.core5.http.HttpEntity;
39 import org.apache.hc.core5.http.LengthRequiredException;
40 import org.apache.hc.core5.http.Method;
41 import org.apache.hc.core5.http.NotImplementedException;
42 import org.apache.hc.core5.http.config.Http1Config;
43 import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
44 import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
45 import org.apache.hc.core5.http.io.entity.StringEntity;
46 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
47 import org.junit.jupiter.api.Assertions;
48 import org.junit.jupiter.api.BeforeEach;
49 import org.junit.jupiter.api.Test;
50 import org.mockito.Mock;
51 import org.mockito.Mockito;
52 import org.mockito.MockitoAnnotations;
53
54 public class TestDefaultBHttpClientConnection {
55
56 @Mock
57 private Socket socket;
58
59 private DefaultBHttpClientConnection conn;
60
61 @BeforeEach
62 public void prepareMocks() {
63 MockitoAnnotations.openMocks(this);
64 conn = new DefaultBHttpClientConnection(Http1Config.DEFAULT,
65 null, null,
66 DefaultContentLengthStrategy.INSTANCE,
67 DefaultContentLengthStrategy.INSTANCE,
68 DefaultHttpRequestWriterFactory.INSTANCE,
69 DefaultHttpResponseParserFactory.INSTANCE);
70 }
71
72 @Test
73 public void testBasics() throws Exception {
74 Assertions.assertFalse(conn.isOpen());
75 Assertions.assertEquals("[Not bound]", conn.toString());
76 }
77
78 @Test
79 public void testReadResponseHead() throws Exception {
80 final String s = "HTTP/1.1 200 OK\r\nUser-Agent: test\r\n\r\n";
81 final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII));
82 Mockito.when(socket.getInputStream()).thenReturn(inStream);
83
84 conn.bind(socket);
85
86 Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount());
87
88 final ClassicHttpResponse response = conn.receiveResponseHeader();
89 Assertions.assertNotNull(response);
90 Assertions.assertEquals(200, response.getCode());
91 Assertions.assertTrue(response.containsHeader("User-Agent"));
92 Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
93 }
94
95 @Test
96 public void testReadResponseEntityWithoutContentLength() throws Exception {
97 final String s = "HTTP/1.1 200 OK\r\nServer: test\r\n\r\n123";
98 final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII));
99 Mockito.when(socket.getInputStream()).thenReturn(inStream);
100
101 conn.bind(socket);
102
103 Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount());
104
105 final ClassicHttpResponse response = conn.receiveResponseHeader();
106
107 Assertions.assertNotNull(response);
108 Assertions.assertEquals(200, response.getCode());
109 Assertions.assertTrue(response.containsHeader("Server"));
110 Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
111
112 conn.receiveResponseEntity(response);
113
114 final HttpEntity entity = response.getEntity();
115 Assertions.assertNotNull(entity);
116 Assertions.assertEquals(-1, entity.getContentLength());
117 Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
118
119 final InputStream content = entity.getContent();
120 Assertions.assertNotNull(content);
121 Assertions.assertEquals(3, content.available());
122 Assertions.assertEquals('1', content.read());
123 Assertions.assertEquals('2', content.read());
124 Assertions.assertEquals('3', content.read());
125 }
126
127 @Test
128 public void testReadResponseEntityWithContentLength() throws Exception {
129 final String s = "HTTP/1.1 200 OK\r\nServer: test\r\nContent-Length: 3\r\n\r\n123";
130 final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII));
131 Mockito.when(socket.getInputStream()).thenReturn(inStream);
132
133 conn.bind(socket);
134
135 Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount());
136
137 final ClassicHttpResponse response = conn.receiveResponseHeader();
138
139 Assertions.assertNotNull(response);
140 Assertions.assertEquals(200, response.getCode());
141 Assertions.assertTrue(response.containsHeader("Server"));
142 Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
143
144 conn.receiveResponseEntity(response);
145
146 final HttpEntity entity = response.getEntity();
147 Assertions.assertNotNull(entity);
148 Assertions.assertEquals(3, entity.getContentLength());
149 Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
150 final InputStream content = entity.getContent();
151 Assertions.assertNotNull(content);
152 Assertions.assertTrue(content instanceof ContentLengthInputStream);
153 }
154
155 @Test
156 public void testReadResponseEntityChunkCoded() throws Exception {
157 final String s = "HTTP/1.1 200 OK\r\nServer: test\r\nTransfer-Encoding: " +
158 "chunked\r\n\r\n3\r\n123\r\n0\r\n\r\n";
159 final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII));
160 Mockito.when(socket.getInputStream()).thenReturn(inStream);
161
162 conn.bind(socket);
163
164 Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount());
165
166 final ClassicHttpResponse response = conn.receiveResponseHeader();
167
168 Assertions.assertNotNull(response);
169 Assertions.assertEquals(200, response.getCode());
170 Assertions.assertTrue(response.containsHeader("Server"));
171 Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
172
173 conn.receiveResponseEntity(response);
174
175 final HttpEntity entity = response.getEntity();
176 Assertions.assertNotNull(entity);
177 Assertions.assertEquals(-1, entity.getContentLength());
178 Assertions.assertTrue(entity.isChunked());
179 Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
180 final InputStream content = entity.getContent();
181 Assertions.assertNotNull(content);
182 Assertions.assertTrue(content instanceof ChunkedInputStream);
183 }
184
185 @Test
186 public void testReadResponseEntityIdentity() throws Exception {
187 final String s = "HTTP/1.1 200 OK\r\nServer: test\r\nTransfer-Encoding: identity\r\n\r\n123";
188 final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII));
189 Mockito.when(socket.getInputStream()).thenReturn(inStream);
190
191 conn.bind(socket);
192
193 Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount());
194
195 final ClassicHttpResponse response = conn.receiveResponseHeader();
196
197 Assertions.assertNotNull(response);
198 Assertions.assertEquals(200, response.getCode());
199 Assertions.assertTrue(response.containsHeader("Server"));
200 Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
201
202 Assertions.assertThrows(NotImplementedException.class, () ->
203 conn.receiveResponseEntity(response));
204 }
205
206 @Test
207 public void testReadResponseNoEntity() throws Exception {
208 final String s = "HTTP/1.1 200 OK\r\nServer: test\r\n\r\n";
209 final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII));
210 Mockito.when(socket.getInputStream()).thenReturn(inStream);
211
212 conn.bind(socket);
213
214 Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount());
215
216 final ClassicHttpResponse response = conn.receiveResponseHeader();
217
218 Assertions.assertNotNull(response);
219 Assertions.assertEquals(200, response.getCode());
220 Assertions.assertTrue(response.containsHeader("Server"));
221 Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
222
223 conn.receiveResponseEntity(response);
224
225 final HttpEntity entity = response.getEntity();
226 Assertions.assertNotNull(entity);
227 final InputStream content = entity.getContent();
228 Assertions.assertNotNull(content);
229 Assertions.assertTrue(content instanceof IdentityInputStream);
230 }
231
232 @Test
233 public void testWriteRequestHead() throws Exception {
234 final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
235 Mockito.when(socket.getOutputStream()).thenReturn(outStream);
236
237 conn.bind(socket);
238
239 Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount());
240
241 final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/stuff");
242 request.addHeader("User-Agent", "test");
243
244 conn.sendRequestHeader(request);
245 conn.flush();
246
247 Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount());
248 final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
249 Assertions.assertEquals("GET /stuff HTTP/1.1\r\nUser-Agent: test\r\n\r\n", s);
250 }
251
252 @Test
253 public void testWriteRequestEntityWithContentLength() throws Exception {
254 final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
255 Mockito.when(socket.getOutputStream()).thenReturn(outStream);
256
257 conn.bind(socket);
258
259 Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount());
260
261 final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff");
262 request.addHeader("User-Agent", "test");
263 request.addHeader("Content-Length", "3");
264 request.setEntity(new StringEntity("123", ContentType.TEXT_PLAIN));
265
266 conn.sendRequestHeader(request);
267 conn.sendRequestEntity(request);
268 conn.flush();
269
270 Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount());
271 final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
272 Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: 3\r\n\r\n123", s);
273 }
274
275 @Test
276 public void testWriteRequestEntityChunkCoded() throws Exception {
277 final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
278 Mockito.when(socket.getOutputStream()).thenReturn(outStream);
279
280 conn.bind(socket);
281
282 Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount());
283
284 final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff");
285 request.addHeader("User-Agent", "test");
286 request.addHeader("Transfer-Encoding", "chunked");
287 request.setEntity(new StringEntity("123", ContentType.TEXT_PLAIN));
288
289 conn.sendRequestHeader(request);
290 conn.sendRequestEntity(request);
291 conn.flush();
292
293 Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount());
294 final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
295 Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nTransfer-Encoding: " +
296 "chunked\r\n\r\n3\r\n123\r\n0\r\n\r\n", s);
297 }
298
299 @Test
300 public void testWriteRequestEntityNoContentLength() throws Exception {
301 final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
302 Mockito.when(socket.getOutputStream()).thenReturn(outStream);
303
304 conn.bind(socket);
305
306 Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount());
307
308 final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff");
309 request.addHeader("User-Agent", "test");
310 request.setEntity(new StringEntity("123", ContentType.TEXT_PLAIN));
311
312 conn.sendRequestHeader(request);
313 Assertions.assertThrows(LengthRequiredException.class, () ->
314 conn.sendRequestEntity(request));
315 }
316
317 @Test
318 public void testWriteRequestNoEntity() throws Exception {
319 final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
320 Mockito.when(socket.getOutputStream()).thenReturn(outStream);
321
322 conn.bind(socket);
323
324 Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount());
325
326 final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff");
327 request.addHeader("User-Agent", "test");
328
329 conn.sendRequestHeader(request);
330 conn.sendRequestEntity(request);
331 conn.flush();
332
333 Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount());
334 final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
335 Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\n\r\n", s);
336 }
337
338 @Test
339 public void testTerminateRequestChunkedEntity() throws Exception {
340 final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
341 Mockito.when(socket.getOutputStream()).thenReturn(outStream);
342
343 conn.bind(socket);
344
345 Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount());
346
347 final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff");
348 request.addHeader("User-Agent", "test");
349 request.addHeader("Transfer-Encoding", "chunked");
350 final StringEntity entity = new StringEntity("123", ContentType.TEXT_PLAIN, true);
351
352 request.setEntity(entity);
353
354 conn.sendRequestHeader(request);
355 conn.terminateRequest(request);
356 conn.flush();
357
358 Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount());
359 final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
360 Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nTransfer-Encoding: " +
361 "chunked\r\n\r\n0\r\n\r\n", s);
362 Assertions.assertTrue(conn.isConsistent());
363 }
364
365 @Test
366 public void testTerminateRequestContentLengthShort() throws Exception {
367 final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
368 Mockito.when(socket.getOutputStream()).thenReturn(outStream);
369
370 conn.bind(socket);
371
372 Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount());
373
374 final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff");
375 request.addHeader("User-Agent", "test");
376 request.addHeader("Content-Length", "3");
377 final StringEntity entity = new StringEntity("123", ContentType.TEXT_PLAIN, true);
378 request.setEntity(entity);
379
380 conn.sendRequestHeader(request);
381 conn.terminateRequest(request);
382 conn.flush();
383
384 Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount());
385 final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
386 Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: " +
387 "3\r\n\r\n123", s);
388 Assertions.assertTrue(conn.isConsistent());
389 }
390
391 @Test
392 public void testTerminateRequestContentLengthLong() throws Exception {
393 final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
394 Mockito.when(socket.getOutputStream()).thenReturn(outStream);
395
396 conn.bind(socket);
397
398 Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount());
399
400 final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff");
401 request.addHeader("User-Agent", "test");
402 request.addHeader("Content-Length", "3000");
403 final ByteArrayEntity entity = new ByteArrayEntity(new byte[3000], ContentType.TEXT_PLAIN, true);
404 request.setEntity(entity);
405
406 conn.sendRequestHeader(request);
407 conn.terminateRequest(request);
408 conn.flush();
409
410 Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount());
411 final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
412 Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: " +
413 "3000\r\n\r\n", s);
414 Assertions.assertFalse(conn.isConsistent());
415 }
416
417 }