View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.core5.http.impl.io;
28  
29  import java.io.ByteArrayInputStream;
30  import java.io.InputStream;
31  import java.io.OutputStream;
32  import java.net.InetAddress;
33  import java.net.InetSocketAddress;
34  import java.net.Socket;
35  import java.net.SocketException;
36  import java.net.SocketTimeoutException;
37  
38  import org.apache.hc.core5.http.ClassicHttpResponse;
39  import org.apache.hc.core5.http.ContentLengthStrategy;
40  import org.apache.hc.core5.http.HttpEntity;
41  import org.apache.hc.core5.http.config.Http1Config;
42  import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
43  import org.apache.hc.core5.io.CloseMode;
44  import org.apache.hc.core5.util.Timeout;
45  import org.junit.jupiter.api.Assertions;
46  import org.junit.jupiter.api.BeforeEach;
47  import org.junit.jupiter.api.Test;
48  import org.mockito.ArgumentMatchers;
49  import org.mockito.Mock;
50  import org.mockito.Mockito;
51  import org.mockito.MockitoAnnotations;
52  
53  public class TestBHttpConnectionBase {
54  
55      @Mock
56      private Socket socket;
57  
58      private BHttpConnectionBase conn;
59  
60      @BeforeEach
61      public void prepareMocks() {
62          MockitoAnnotations.openMocks(this);
63          conn = new BHttpConnectionBase(Http1Config.DEFAULT, null, null);
64      }
65  
66      @Test
67      public void testBasics() throws Exception {
68          Assertions.assertFalse(conn.isOpen());
69          Assertions.assertNull(conn.getLocalAddress());
70          Assertions.assertNull(conn.getRemoteAddress());
71          Assertions.assertEquals("[Not bound]", conn.toString());
72      }
73  
74      @Test
75      public void testSocketBind() throws Exception {
76          final InetAddress localAddress = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
77          final int localPort = 8888;
78          final InetAddress remoteAddress = InetAddress.getByAddress(new byte[] {10, 0, 0, 2});
79          final int remotePort = 80;
80          final InetSocketAddress localSockAddress = new InetSocketAddress(localAddress, localPort);
81          final InetSocketAddress remoteSockAddress = new InetSocketAddress(remoteAddress, remotePort);
82          Mockito.when(socket.getLocalSocketAddress()).thenReturn(localSockAddress);
83          Mockito.when(socket.getRemoteSocketAddress()).thenReturn(remoteSockAddress);
84          conn.bind(socket);
85  
86          Assertions.assertEquals("127.0.0.1:8888<->10.0.0.2:80", conn.toString());
87          Assertions.assertTrue(conn.isOpen());
88  
89          Assertions.assertEquals(new InetSocketAddress(
90                  InetAddress.getByAddress(new byte[] {127, 0, 0, 1}), 8888), conn.getLocalAddress());
91          Assertions.assertEquals(new InetSocketAddress(
92                  InetAddress.getByAddress(new byte[] {10, 0, 0, 2}), 80), conn.getRemoteAddress());
93      }
94  
95      @Test
96      public void testConnectionClose() throws Exception {
97          final OutputStream outStream = Mockito.mock(OutputStream.class);
98          Mockito.when(socket.getOutputStream()).thenReturn(outStream);
99  
100         conn.bind(socket);
101         conn.ensureOpen();
102         conn.outbuffer.write(0, outStream);
103 
104         Assertions.assertTrue(conn.isOpen());
105 
106         conn.close();
107 
108         Assertions.assertFalse(conn.isOpen());
109 
110         Mockito.verify(outStream, Mockito.times(1)).write(
111                 ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt());
112         Mockito.verify(socket, Mockito.times(1)).close();
113 
114         conn.close();
115         Mockito.verify(socket, Mockito.times(1)).close();
116         Mockito.verify(outStream, Mockito.times(1)).write(
117                 ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt());
118     }
119 
120     @Test
121     public void testConnectionShutdown() throws Exception {
122         final OutputStream outStream = Mockito.mock(OutputStream.class);
123 
124         conn.bind(socket);
125         conn.ensureOpen();
126         conn.outbuffer.write(0, outStream);
127 
128         Assertions.assertTrue(conn.isOpen());
129 
130         conn.close(CloseMode.GRACEFUL);
131 
132         Assertions.assertFalse(conn.isOpen());
133 
134         Mockito.verify(outStream, Mockito.never()).write(
135                 ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt());
136         Mockito.verify(socket, Mockito.never()).shutdownInput();
137         Mockito.verify(socket, Mockito.never()).shutdownOutput();
138         Mockito.verify(socket, Mockito.times(1)).close();
139 
140         conn.close();
141         Mockito.verify(socket, Mockito.times(1)).close();
142 
143         conn.close(CloseMode.GRACEFUL);
144         Mockito.verify(socket, Mockito.times(1)).close();
145     }
146 
147     @Test
148     public void testCreateEntityLengthDelimited() throws Exception {
149         final InputStream inStream = Mockito.mock(InputStream.class);
150         final ClassicHttpResponse message = new BasicClassicHttpResponse(200, "OK");
151         message.addHeader("Content-Length", "10");
152         message.addHeader("Content-Type", "stuff");
153         message.addHeader("Content-Encoding", "chunked");
154         final HttpEntity entity = conn.createIncomingEntity(message, conn.inBuffer, inStream, 10);
155         Assertions.assertNotNull(entity);
156         Assertions.assertFalse(entity.isChunked());
157         Assertions.assertEquals(10, entity.getContentLength());
158         Assertions.assertEquals("stuff", entity.getContentType());
159         Assertions.assertEquals("chunked", entity.getContentEncoding());
160         final InputStream content = entity.getContent();
161         Assertions.assertNotNull(content);
162         Assertions.assertTrue((content instanceof ContentLengthInputStream));
163     }
164 
165     @Test
166     public void testCreateEntityInputChunked() throws Exception {
167         final InputStream inStream = Mockito.mock(InputStream.class);
168         final ClassicHttpResponse message = new BasicClassicHttpResponse(200, "OK");
169         final HttpEntity entity = conn.createIncomingEntity(message, conn.inBuffer, inStream, ContentLengthStrategy.CHUNKED);
170         Assertions.assertNotNull(entity);
171         Assertions.assertTrue(entity.isChunked());
172         Assertions.assertEquals(-1, entity.getContentLength());
173         final InputStream content = entity.getContent();
174         Assertions.assertNotNull(content);
175         Assertions.assertTrue((content instanceof ChunkedInputStream));
176     }
177 
178     @Test
179     public void testCreateEntityInputUndefined() throws Exception {
180         final InputStream inStream = Mockito.mock(InputStream.class);
181         final ClassicHttpResponse message = new BasicClassicHttpResponse(200, "OK");
182         final HttpEntity entity = conn.createIncomingEntity(message, conn.inBuffer, inStream, ContentLengthStrategy.UNDEFINED);
183         Assertions.assertNotNull(entity);
184         Assertions.assertFalse(entity.isChunked());
185         Assertions.assertEquals(-1, entity.getContentLength());
186         final InputStream content = entity.getContent();
187         Assertions.assertNotNull(content);
188         Assertions.assertTrue((content instanceof IdentityInputStream));
189     }
190 
191     @Test
192     public void testSetSocketTimeout() throws Exception {
193         conn.bind(socket);
194 
195         conn.setSocketTimeout(Timeout.ofMilliseconds(123));
196 
197         Mockito.verify(socket, Mockito.times(1)).setSoTimeout(123);
198     }
199 
200     @Test
201     public void testSetSocketTimeoutException() throws Exception {
202         conn.bind(socket);
203 
204         Mockito.doThrow(new SocketException()).when(socket).setSoTimeout(ArgumentMatchers.anyInt());
205 
206         conn.setSocketTimeout(Timeout.ofMilliseconds(123));
207 
208         Mockito.verify(socket, Mockito.times(1)).setSoTimeout(123);
209     }
210 
211     @Test
212     public void testGetSocketTimeout() throws Exception {
213         Assertions.assertEquals(Timeout.DISABLED, conn.getSocketTimeout());
214 
215         Mockito.when(socket.getSoTimeout()).thenReturn(345);
216         conn.bind(socket);
217 
218         Assertions.assertEquals(Timeout.ofMilliseconds(345), conn.getSocketTimeout());
219     }
220 
221     @Test
222     public void testGetSocketTimeoutException() throws Exception {
223         Assertions.assertEquals(Timeout.DISABLED, conn.getSocketTimeout());
224 
225         Mockito.when(socket.getSoTimeout()).thenThrow(new SocketException());
226         conn.bind(socket);
227 
228         Assertions.assertEquals(Timeout.DISABLED, conn.getSocketTimeout());
229     }
230 
231     @Test
232     public void testAwaitInputInBuffer() throws Exception {
233         final ByteArrayInputStream inStream = new ByteArrayInputStream(
234                 new byte[] {1, 2, 3, 4, 5});
235         conn.bind(socket);
236         conn.ensureOpen();
237         conn.inBuffer.read(inStream);
238 
239         Assertions.assertTrue(conn.awaitInput(Timeout.ofMilliseconds(432)));
240 
241         Mockito.verify(socket, Mockito.never()).setSoTimeout(ArgumentMatchers.anyInt());
242     }
243 
244     @Test
245     public void testAwaitInputInSocket() throws Exception {
246         final ByteArrayInputStream inStream = new ByteArrayInputStream(
247                 new byte[] {1, 2, 3, 4, 5});
248         Mockito.when(socket.getInputStream()).thenReturn(inStream);
249         Mockito.when(socket.getSoTimeout()).thenReturn(345);
250 
251         conn.bind(socket);
252         conn.ensureOpen();
253 
254         Assertions.assertTrue(conn.awaitInput(Timeout.ofMilliseconds(432)));
255 
256         Mockito.verify(socket, Mockito.times(1)).setSoTimeout(432);
257         Mockito.verify(socket, Mockito.times(1)).setSoTimeout(345);
258         }
259 
260     @Test
261     public void testAwaitInputNoData() throws Exception {
262         final InputStream inStream = Mockito.mock(InputStream.class);
263         Mockito.when(socket.getInputStream()).thenReturn(inStream);
264         Mockito.when(inStream.read(ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()))
265             .thenReturn(-1);
266 
267         conn.bind(socket);
268         conn.ensureOpen();
269 
270         Assertions.assertFalse(conn.awaitInput(Timeout.ofMilliseconds(432)));
271     }
272 
273     @Test
274     public void testStaleWhenClosed() throws Exception {
275         final OutputStream outStream = Mockito.mock(OutputStream.class);
276 
277         Mockito.when(socket.getOutputStream()).thenReturn(outStream);
278 
279         conn.bind(socket);
280         conn.ensureOpen();
281         conn.close();
282         Assertions.assertTrue(conn.isStale());
283     }
284 
285     @Test
286     public void testNotStaleWhenHasData() throws Exception {
287         final ByteArrayInputStream inStream = new ByteArrayInputStream(
288                 new byte[] {1, 2, 3, 4, 5});
289         Mockito.when(socket.getInputStream()).thenReturn(inStream);
290 
291         conn.bind(socket);
292         conn.ensureOpen();
293 
294         Assertions.assertFalse(conn.isStale());
295     }
296 
297     @Test
298     public void testStaleWhenEndOfStream() throws Exception {
299         final InputStream inStream = Mockito.mock(InputStream.class);
300         Mockito.when(socket.getInputStream()).thenReturn(inStream);
301         Mockito.when(inStream.read(ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()))
302             .thenReturn(-1);
303 
304         conn.bind(socket);
305         conn.ensureOpen();
306 
307         Assertions.assertTrue(conn.isStale());
308     }
309 
310     @Test
311     public void testNotStaleWhenTimeout() throws Exception {
312         final InputStream inStream = Mockito.mock(InputStream.class);
313         Mockito.when(socket.getInputStream()).thenReturn(inStream);
314         Mockito.when(inStream.read(ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()))
315             .thenThrow(new SocketTimeoutException());
316 
317         conn.bind(socket);
318         conn.ensureOpen();
319 
320         Assertions.assertFalse(conn.isStale());
321     }
322 
323     @Test
324     public void testStaleWhenIOError() throws Exception {
325         final InputStream inStream = Mockito.mock(InputStream.class);
326         Mockito.when(socket.getInputStream()).thenReturn(inStream);
327         Mockito.when(inStream.read(ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()))
328             .thenThrow(new SocketException());
329 
330         conn.bind(socket);
331         conn.ensureOpen();
332 
333         Assertions.assertTrue(conn.isStale());
334     }
335 
336 }