/* * Copyright 2012 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package io.netty.handler.stream; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.CharsetUtil; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.Channels; import java.util.concurrent.atomic.AtomicBoolean; import static io.netty.util.ReferenceCountUtil.releaseLater; import static org.junit.Assert.*; public class ChunkedWriteHandlerTest { private static final byte[] BYTES = new byte[1024 * 64]; private static final File TMP; static { for (int i = 0; i < BYTES.length; i++) { BYTES[i] = (byte) i; } FileOutputStream out = null; try { TMP = File.createTempFile("netty-chunk-", ".tmp"); TMP.deleteOnExit(); out = new FileOutputStream(TMP); out.write(BYTES); out.flush(); } catch (IOException e) { throw new RuntimeException(e); } finally { if (out != null) { try { out.close(); } catch (IOException e) { // ignore } } } } // See #310 @Test public void testChunkedStream() { check(new ChunkedStream(new ByteArrayInputStream(BYTES))); check(new ChunkedStream(new ByteArrayInputStream(BYTES)), new ChunkedStream(new ByteArrayInputStream(BYTES)), new ChunkedStream(new ByteArrayInputStream(BYTES))); } @Test public void testChunkedNioStream() { check(new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES)))); check(new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES))), new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES))), new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES)))); } @Test public void testChunkedFile() throws IOException { check(new ChunkedFile(TMP)); check(new ChunkedFile(TMP), new ChunkedFile(TMP), new ChunkedFile(TMP)); } @Test public void testChunkedNioFile() throws IOException { check(new ChunkedNioFile(TMP)); check(new ChunkedNioFile(TMP), new ChunkedNioFile(TMP), new ChunkedNioFile(TMP)); } // Test case which shows that there is not a bug like stated here: // http://stackoverflow.com/a/10426305 @Test public void testListenerNotifiedWhenIsEnd() { ByteBuf buffer = Unpooled.copiedBuffer("Test", CharsetUtil.ISO_8859_1); ChunkedInput<ByteBuf> input = new ChunkedInput<ByteBuf>() { private boolean done; private final ByteBuf buffer = releaseLater(Unpooled.copiedBuffer("Test", CharsetUtil.ISO_8859_1)); @Override public boolean isEndOfInput() throws Exception { return done; } @Override public void close() throws Exception { // NOOP } @Override public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { if (done) { return null; } done = true; return buffer.duplicate().retain(); } }; final AtomicBoolean listenerNotified = new AtomicBoolean(false); final ChannelFutureListener listener = new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { listenerNotified.set(true); } }; EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); ch.writeAndFlush(input).addListener(listener).syncUninterruptibly(); ch.checkException(); ch.finish(); // the listener should have been notified assertTrue(listenerNotified.get()); assertEquals(releaseLater(buffer), releaseLater(ch.readOutbound())); assertNull(ch.readOutbound()); } @Test public void testChunkedMessageInput() { ChunkedInput<Object> input = new ChunkedInput<Object>() { private boolean done; @Override public boolean isEndOfInput() throws Exception { return done; } @Override public void close() throws Exception { // NOOP } @Override public Object readChunk(ChannelHandlerContext ctx) throws Exception { if (done) { return false; } done = true; return 0; } }; EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); ch.writeAndFlush(input).syncUninterruptibly(); ch.checkException(); assertTrue(ch.finish()); assertEquals(0, ch.readOutbound()); assertNull(ch.readOutbound()); } private static void check(ChunkedInput<?>... inputs) { EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); for (ChunkedInput<?> input: inputs) { ch.writeOutbound(input); } assertTrue(ch.finish()); int i = 0; int read = 0; for (;;) { ByteBuf buffer = (ByteBuf) ch.readOutbound(); if (buffer == null) { break; } while (buffer.isReadable()) { assertEquals(BYTES[i++], buffer.readByte()); read++; if (i == BYTES.length) { i = 0; } } buffer.release(); } assertEquals(BYTES.length * inputs.length, read); } }