View Javadoc

1   package com.atlassian.httpclient.apache.httpcomponents;
2   
3   import org.apache.http.HttpEntity;
4   import org.apache.http.HttpException;
5   import org.apache.http.HttpResponse;
6   import org.apache.http.nio.ContentDecoder;
7   import org.apache.http.nio.IOControl;
8   import org.apache.http.protocol.HttpContext;
9   import org.junit.Test;
10  import org.junit.runner.RunWith;
11  import org.mockito.ArgumentCaptor;
12  import org.mockito.Mock;
13  import org.mockito.runners.MockitoJUnitRunner;
14  
15  import java.io.IOException;
16  import java.io.InputStream;
17  import java.nio.ByteBuffer;
18  
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertFalse;
21  import static org.junit.Assert.assertTrue;
22  import static org.mockito.Mockito.doReturn;
23  import static org.mockito.Mockito.verify;
24  
25  @RunWith(MockitoJUnitRunner.class)
26  public class BoundedAsyncResponseConsumerTest {
27  
28      @Mock
29      private HttpContext context;
30      @Mock
31      private HttpEntity entity;
32      @Mock
33      private IOControl ioControl;
34      @Mock
35      private HttpResponse response;
36  
37      @Test
38      public void testContentReturnedIfActualLengthEqualToMaxLength() throws Exception {
39          int maxSize = 4096;
40          int actualSize = 4096;
41          int chunkSize = 19;
42  
43          // stream 1024 bytes of content through the truncating response consumer
44          try (InputStream stream = streamContent(maxSize, actualSize, chunkSize)) {
45  
46              // verify that exactly 4096 bytes have been buffered
47              byte[] buffer = new byte[2 * maxSize];
48              assertEquals(actualSize, stream.read(buffer));
49              assertEquals(-1, stream.read(buffer));
50          }
51      }
52  
53      @Test
54      public void testContentReturnedIfContentLengthZeroButActualLengthEqualToMaxLength() throws Exception {
55          int maxSize = 4096;
56          int actualSize = 4096;
57          int chunkSize = 19;
58          int contentLength = 0;
59  
60          // stream 1024 bytes of content through the truncating response consumer
61          try (InputStream stream = streamContent(maxSize, actualSize, chunkSize, contentLength)) {
62  
63              // verify that exactly 4096 bytes have been buffered
64              byte[] buffer = new byte[2 * maxSize];
65              assertEquals(actualSize, stream.read(buffer));
66              assertEquals(-1, stream.read(buffer));
67          }
68      }
69  
70      @Test
71      public void testContentReturnedIfActualLengthLowerThanMaxLength() throws Exception {
72          int maxSize = 4096;
73          int actualSize = 1024;
74          int chunkSize = 21;
75  
76          // stream 1024 bytes of content through the truncating response consumer
77          try (InputStream stream = streamContent(maxSize, actualSize, chunkSize)) {
78  
79              // verify that exactly 1024 bytes have been buffered
80              byte[] buffer = new byte[maxSize];
81              assertEquals(actualSize, stream.read(buffer));
82              assertEquals(-1, stream.read(buffer));
83          }
84      }
85  
86      @Test(expected = EntityTooLargeException.class)
87      public void testThrowsIfActualLengthExceedsMaxLength() throws IOException, HttpException {
88          int maxSize = 16 * 1024;
89          int actualSize = 32 * 1024;
90          int chunkSize = 4096;
91  
92          // stream 1024 bytes of content through the truncating response consumer
93          streamContent(maxSize, actualSize, chunkSize);
94      }
95  
96      @Test(expected = EntityTooLargeException.class)
97      public void testThrowsIfActualLengthExceedsMaxLengthChunked() throws IOException, HttpException {
98          int maxSize = 16 * 1024;
99          int actualSize = 32 * 1024;
100         int chunkSize = 4096;
101 
102         // stream 1024 bytes of content through the truncating response consumer
103         streamContent(maxSize, actualSize, chunkSize, -1);
104     }
105 
106     private InputStream streamContent(int maxEntitySize, int actualSize, int chunkSize) throws IOException, HttpException {
107         return streamContent(maxEntitySize, actualSize, chunkSize, actualSize);
108     }
109 
110     private InputStream streamContent(int maxEntitySize, int actualSize, int chunkSize, int contentLength) throws IOException, HttpException {
111         doReturn((long) contentLength).when(entity).getContentLength();
112         doReturn(entity).when(response).getEntity();
113 
114         BoundedAsyncResponseConsumer consumer = new BoundedAsyncResponseConsumer(maxEntitySize);
115 
116         consumer.responseReceived(response);
117 
118         // buffering entity should now be set on the response
119         ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
120         verify(response).setEntity(entityCaptor.capture());
121         HttpEntity bufferedEntity = entityCaptor.getValue();
122 
123         // now create a ContentDecoder that allows reading 8 bytes at a time of a total of 1024 bytes
124         StubDecoder decoder = new StubDecoder(actualSize);
125         for (int i = 0; i < actualSize; i += chunkSize) {
126             assertFalse(decoder.isCompleted());
127             decoder.makeAvailable(chunkSize);
128             consumer.consumeContent(decoder, ioControl);
129         }
130 
131         assertTrue(decoder.isCompleted());
132         consumer.responseCompleted(context);
133 
134         return bufferedEntity.getContent();
135     }
136 
137     private static class StubDecoder implements ContentDecoder {
138 
139         private static final byte CONTENT = 33;
140         private long available;
141         private long remaining;
142 
143         StubDecoder(long length) {
144             remaining = length;
145         }
146 
147         @Override
148         public int read(ByteBuffer dst) throws IOException {
149             int bytesRead = 0;
150             while (remaining > 0 && available > 0 && dst.hasRemaining()) {
151                 dst.put(CONTENT);
152                 ++bytesRead;
153                 --available;
154                 --remaining;
155             }
156 
157             return remaining == 0 && bytesRead == 0 ? -1 : bytesRead;
158         }
159 
160         @Override
161         public boolean isCompleted() {
162             return remaining == 0;
163         }
164 
165         void makeAvailable(int length) {
166             this.available += length;
167         }
168     }
169 }