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
44 try (InputStream stream = streamContent(maxSize, actualSize, chunkSize)) {
45
46
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
61 try (InputStream stream = streamContent(maxSize, actualSize, chunkSize, contentLength)) {
62
63
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
77 try (InputStream stream = streamContent(maxSize, actualSize, chunkSize)) {
78
79
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
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
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
119 ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
120 verify(response).setEntity(entityCaptor.capture());
121 HttpEntity bufferedEntity = entityCaptor.getValue();
122
123
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 }