1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.testsuite.transport.socket;
17
18 import io.netty.bootstrap.Bootstrap;
19 import io.netty.bootstrap.ServerBootstrap;
20 import io.netty.buffer.ByteBuf;
21 import io.netty.channel.Channel;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.ChannelInboundHandler;
24 import io.netty.channel.ChannelInboundHandlerAdapter;
25 import io.netty.channel.ChannelOption;
26 import io.netty.channel.DefaultFileRegion;
27 import io.netty.channel.FileRegion;
28 import io.netty.channel.SimpleChannelInboundHandler;
29 import io.netty.util.internal.PlatformDependent;
30 import org.junit.jupiter.api.Test;
31 import org.junit.jupiter.api.TestInfo;
32
33 import java.io.File;
34 import java.io.FileOutputStream;
35 import java.io.IOException;
36 import java.io.RandomAccessFile;
37 import java.nio.channels.WritableByteChannel;
38 import java.util.Random;
39 import java.util.concurrent.atomic.AtomicReference;
40
41 import static io.netty.testsuite.transport.TestsuitePermutation.randomBufferType;
42 import static org.junit.jupiter.api.Assertions.assertEquals;
43 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
44 import static org.junit.jupiter.api.Assertions.assertNotEquals;
45
46 public class SocketFileRegionTest extends AbstractSocketTest {
47
48 static final byte[] data = new byte[1048576 * 10];
49
50 static {
51 PlatformDependent.threadLocalRandom().nextBytes(data);
52 }
53
54 @Test
55 public void testFileRegion(TestInfo testInfo) throws Throwable {
56 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
57 @Override
58 public void run(ServerBootstrap serverBootstrap, Bootstrap bootstrap) throws Throwable {
59 testFileRegion(serverBootstrap, bootstrap);
60 }
61 });
62 }
63
64 @Test
65 public void testCustomFileRegion(TestInfo testInfo) throws Throwable {
66 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
67 @Override
68 public void run(ServerBootstrap serverBootstrap, Bootstrap bootstrap) throws Throwable {
69 testCustomFileRegion(serverBootstrap, bootstrap);
70 }
71 });
72 }
73
74 @Test
75 public void testFileRegionNotAutoRead(TestInfo testInfo) throws Throwable {
76 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
77 @Override
78 public void run(ServerBootstrap serverBootstrap, Bootstrap bootstrap) throws Throwable {
79 testFileRegionNotAutoRead(serverBootstrap, bootstrap);
80 }
81 });
82 }
83
84 @Test
85 public void testFileRegionVoidPromise(TestInfo testInfo) throws Throwable {
86 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
87 @Override
88 public void run(ServerBootstrap serverBootstrap, Bootstrap bootstrap) throws Throwable {
89 testFileRegionVoidPromise(serverBootstrap, bootstrap);
90 }
91 });
92 }
93
94 @Test
95 public void testFileRegionVoidPromiseNotAutoRead(TestInfo testInfo) throws Throwable {
96 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
97 @Override
98 public void run(ServerBootstrap serverBootstrap, Bootstrap bootstrap) throws Throwable {
99 testFileRegionVoidPromiseNotAutoRead(serverBootstrap, bootstrap);
100 }
101 });
102 }
103
104 @Test
105 public void testFileRegionCountLargerThenFile(TestInfo testInfo) throws Throwable {
106 run(testInfo, new Runner<ServerBootstrap, Bootstrap>() {
107 @Override
108 public void run(ServerBootstrap serverBootstrap, Bootstrap bootstrap) throws Throwable {
109 testFileRegionCountLargerThenFile(serverBootstrap, bootstrap);
110 }
111 });
112 }
113
114 public void testFileRegion(ServerBootstrap sb, Bootstrap cb) throws Throwable {
115 testFileRegion0(sb, cb, false, true, true);
116 }
117
118 public void testCustomFileRegion(ServerBootstrap sb, Bootstrap cb) throws Throwable {
119 testFileRegion0(sb, cb, false, true, false);
120 }
121
122 public void testFileRegionVoidPromise(ServerBootstrap sb, Bootstrap cb) throws Throwable {
123 testFileRegion0(sb, cb, true, true, true);
124 }
125
126 public void testFileRegionNotAutoRead(ServerBootstrap sb, Bootstrap cb) throws Throwable {
127 testFileRegion0(sb, cb, false, false, true);
128 }
129
130 public void testFileRegionVoidPromiseNotAutoRead(ServerBootstrap sb, Bootstrap cb) throws Throwable {
131 testFileRegion0(sb, cb, true, false, true);
132 }
133
134 public void testFileRegionCountLargerThenFile(ServerBootstrap sb, Bootstrap cb) throws Throwable {
135 File file = PlatformDependent.createTempFile("netty-", ".tmp", null);
136 file.deleteOnExit();
137
138 final FileOutputStream out = new FileOutputStream(file);
139 out.write(data);
140 out.close();
141
142 sb.childHandler(new SimpleChannelInboundHandler<ByteBuf>() {
143 @Override
144 protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
145
146 }
147 });
148 cb.handler(new ChannelInboundHandlerAdapter());
149
150 Channel sc = sb.bind().sync().channel();
151 Channel cc = cb.connect(sc.localAddress()).sync().channel();
152
153
154 FileRegion region = new DefaultFileRegion(
155 new RandomAccessFile(file, "r").getChannel(), 0, data.length + 1024);
156
157 assertInstanceOf(IOException.class, cc.writeAndFlush(region).await().cause());
158 cc.close().sync();
159 sc.close().sync();
160 }
161
162 private static void testFileRegion0(
163 ServerBootstrap sb, Bootstrap cb, boolean voidPromise, final boolean autoRead, boolean defaultFileRegion)
164 throws Throwable {
165 sb.childOption(ChannelOption.AUTO_READ, autoRead);
166 cb.option(ChannelOption.AUTO_READ, autoRead);
167
168 final int bufferSize = 1024;
169 final File file = PlatformDependent.createTempFile("netty-", ".tmp", null);
170 file.deleteOnExit();
171
172 final FileOutputStream out = new FileOutputStream(file);
173 final Random random = PlatformDependent.threadLocalRandom();
174
175
176 final int startOffset = random.nextInt(8192);
177 for (int i = 0; i < startOffset; i ++) {
178 out.write(random.nextInt());
179 }
180
181
182 out.write(data, bufferSize, data.length - bufferSize);
183
184
185 for (int i = random.nextInt(8192); i > 0; i --) {
186 out.write(random.nextInt());
187 }
188
189 out.close();
190
191 ChannelInboundHandler ch = new SimpleChannelInboundHandler<Object>() {
192 @Override
193 public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
194 }
195
196 @Override
197 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
198 if (!autoRead) {
199 ctx.read();
200 }
201 }
202
203 @Override
204 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
205 ctx.close();
206 }
207 };
208 TestHandler sh = new TestHandler(autoRead);
209
210 sb.childHandler(sh);
211 cb.handler(ch);
212
213 Channel sc = sb.bind().sync().channel();
214
215 Channel cc = cb.connect(sc.localAddress()).sync().channel();
216 FileRegion region = new DefaultFileRegion(
217 new RandomAccessFile(file, "r").getChannel(), startOffset, data.length - bufferSize);
218 FileRegion emptyRegion = new DefaultFileRegion(new RandomAccessFile(file, "r").getChannel(), 0, 0);
219
220 if (!defaultFileRegion) {
221 region = new FileRegionWrapper(region);
222 emptyRegion = new FileRegionWrapper(emptyRegion);
223 }
224
225
226
227
228
229 if (voidPromise) {
230 assertEquals(cc.voidPromise(), cc.write(
231 randomBufferType(cc.alloc(), data, 0, bufferSize), cc.voidPromise()));
232 assertEquals(cc.voidPromise(), cc.write(emptyRegion, cc.voidPromise()));
233 assertEquals(cc.voidPromise(), cc.writeAndFlush(region, cc.voidPromise()));
234 } else {
235 assertNotEquals(cc.voidPromise(), cc.write(
236 randomBufferType(cc.alloc(), data, 0, bufferSize)));
237 assertNotEquals(cc.voidPromise(), cc.write(emptyRegion));
238 assertNotEquals(cc.voidPromise(), cc.writeAndFlush(region));
239 }
240
241 while (sh.counter < data.length) {
242 if (sh.exception.get() != null) {
243 break;
244 }
245
246 Thread.sleep(50);
247 }
248
249 sh.channel.close().sync();
250 cc.close().sync();
251 sc.close().sync();
252
253 if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) {
254 throw sh.exception.get();
255 }
256
257 if (sh.exception.get() != null) {
258 throw sh.exception.get();
259 }
260
261
262 assertEquals(data.length, sh.counter);
263 }
264
265 private static class TestHandler extends SimpleChannelInboundHandler<ByteBuf> {
266 private final boolean autoRead;
267 volatile Channel channel;
268 final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();
269 volatile int counter;
270
271 TestHandler(boolean autoRead) {
272 this.autoRead = autoRead;
273 }
274
275 @Override
276 public void channelActive(ChannelHandlerContext ctx)
277 throws Exception {
278 channel = ctx.channel();
279 if (!autoRead) {
280 ctx.read();
281 }
282 }
283
284 @Override
285 public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
286 byte[] actual = new byte[in.readableBytes()];
287 in.readBytes(actual);
288
289 int lastIdx = counter;
290 for (int i = 0; i < actual.length; i ++) {
291 assertEquals(data[i + lastIdx], actual[i]);
292 }
293 counter += actual.length;
294 }
295
296 @Override
297 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
298 if (!autoRead) {
299 ctx.read();
300 }
301 }
302
303 @Override
304 public void exceptionCaught(ChannelHandlerContext ctx,
305 Throwable cause) throws Exception {
306 if (exception.compareAndSet(null, cause)) {
307 ctx.close();
308 }
309 }
310 }
311
312 private static final class FileRegionWrapper implements FileRegion {
313 private final FileRegion region;
314
315 FileRegionWrapper(FileRegion region) {
316 this.region = region;
317 }
318
319 @Override
320 public int refCnt() {
321 return region.refCnt();
322 }
323
324 @Override
325 public long position() {
326 return region.position();
327 }
328
329 @Override
330 @Deprecated
331 public long transfered() {
332 return region.transferred();
333 }
334
335 @Override
336 public boolean release() {
337 return region.release();
338 }
339
340 @Override
341 public long transferred() {
342 return region.transferred();
343 }
344
345 @Override
346 public long count() {
347 return region.count();
348 }
349
350 @Override
351 public boolean release(int decrement) {
352 return region.release(decrement);
353 }
354
355 @Override
356 public long transferTo(WritableByteChannel target, long position) throws IOException {
357 return region.transferTo(target, position);
358 }
359
360 @Override
361 public FileRegion retain() {
362 region.retain();
363 return this;
364 }
365
366 @Override
367 public FileRegion retain(int increment) {
368 region.retain(increment);
369 return this;
370 }
371
372 @Override
373 public FileRegion touch() {
374 region.touch();
375 return this;
376 }
377
378 @Override
379 public FileRegion touch(Object hint) {
380 region.touch(hint);
381 return this;
382 }
383 }
384 }