diff --git a/README.adoc b/README.adoc index ad5f6fa..02494c7 100644 --- a/README.adoc +++ b/README.adoc @@ -4,11 +4,11 @@ * License: Apache-2.0 License * Required Java version: Java 11 * Maven coordinates: -** `io.netty5.contrib:netty-codec-multipart:5.0.0.Final-SNAPSHOT` +** `io.netty5.contrib:netty-codec-multipart:5.0.0.Alpha2-SNAPSHOT` ## Project description -This project is a porting of the Netty 4.1.78.Final Multipart codec to the new Netty 5.0.0-Alpha3 Buffer API. +This project is a porting of the Netty 4.1.80.Final-SNAPSHOT Multipart codec to the new Netty 5.0.0-Alpha5 Buffer API. ## Running the benchmarks diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml index fec635e..3b58801 100644 --- a/benchmarks/pom.xml +++ b/benchmarks/pom.xml @@ -7,7 +7,7 @@ io.netty.contrib netty-codec-multipart-parent - 5.0.0.Final-SNAPSHOT + 5.0.0.Alpha2-SNAPSHOT netty-codec-multipart-benchmarks @@ -23,7 +23,7 @@ true - io.netty.microbench + io.netty5.microbench ${os.detected.name}-${os.detected.arch} 5.8.2 @@ -130,22 +130,16 @@ io.netty - netty-codec-http - ${netty.version} - - - io.netty - netty-common - ${netty.version} - - - io.netty - netty-microbench + netty5-microbench ${netty.version} io.netty - netty-transport-native-kqueue + netty5-transport-native-epoll + + + io.netty + netty5-transport-native-kqueue diff --git a/benchmarks/src/main/java/io/netty/contrib/microbenchmarks/http/multipart/HttpPostMultipartRequestDecoderBenchmark.java b/benchmarks/src/main/java/io/netty/contrib/microbenchmarks/http/multipart/HttpPostMultipartRequestDecoderBenchmark.java index 06e27b7..3805655 100644 --- a/benchmarks/src/main/java/io/netty/contrib/microbenchmarks/http/multipart/HttpPostMultipartRequestDecoderBenchmark.java +++ b/benchmarks/src/main/java/io/netty/contrib/microbenchmarks/http/multipart/HttpPostMultipartRequestDecoderBenchmark.java @@ -15,67 +15,89 @@ */ package io.netty.contrib.microbenchmarks.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.contrib.handler.codec.http.multipart.DefaultHttpDataFactory; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder; import io.netty.contrib.handler.codec.http.multipart.InterfaceHttpData; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.CharsetUtil; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetector.Level; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.BufferAllocator; +import io.netty5.handler.codec.http.DefaultHttpContent; +import io.netty5.handler.codec.http.DefaultHttpRequest; +import io.netty5.handler.codec.http.DefaultLastHttpContent; +import io.netty5.handler.codec.http.HttpHeaderNames; +import io.netty5.handler.codec.http.HttpMethod; +import io.netty5.handler.codec.http.HttpVersion; +import io.netty5.microbench.util.AbstractMicrobenchmark; +import io.netty5.util.CharsetUtil; import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Warmup; import java.util.concurrent.TimeUnit; - +import java.util.function.Supplier; @Threads(1) @Warmup(iterations = 2) @Measurement(iterations = 3) @OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(jvmArgsAppend = {"-dsa", + "-da", + "-XX:+HeapDumpOnOutOfMemoryError", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+DebugNonSafepoints", + "-Dio.netty5.leakDetection.level=disabled", // changed to paranoid for detecting buffer leaks + "-Dio.netty5.buffer.leakDetectionEnabled=false", // changed to true for detecting buffer leaks + "-Dio.netty5.buffer.lifecycleTracingEnabled=false" // changed to true for detecting buffer leaks +}) public class HttpPostMultipartRequestDecoderBenchmark extends AbstractMicrobenchmark { - public double testHighNumberChunks(boolean big, boolean noDisk) { - String BOUNDARY = "01f136d9282f"; - int size = 8 * 1024; - int chunkNumber = 64; - StringBuilder stringBuilder = new StringBuilder(size); - stringBuilder.setLength(size); - String data = stringBuilder.toString(); - - byte[] bodyStartBytes = ("--" + BOUNDARY + "\n" + - "Content-Disposition: form-data; name=\"msg_id\"\n\n15200\n--" + - BOUNDARY + - "\nContent-Disposition: form-data; name=\"msg1\"; filename=\"file1.txt\"\n\n" + - data).getBytes(CharsetUtil.UTF_8); - byte[] bodyPartBigBytes = data.getBytes(CharsetUtil.UTF_8); - byte[] intermediaryBytes = ("\n--" + BOUNDARY + - "\nContent-Disposition: form-data; name=\"msg2\"; filename=\"file2.txt\"\n\n" + - data).getBytes(CharsetUtil.UTF_8); - byte[] finalBigBytes = ("\n" + "--" + BOUNDARY + "--\n").getBytes(CharsetUtil.UTF_8); - ByteBuf firstBuf = Unpooled.wrappedBuffer(bodyStartBytes); - ByteBuf finalBuf = Unpooled.wrappedBuffer(finalBigBytes); - ByteBuf nextBuf; - if (big) { - nextBuf = Unpooled.wrappedBuffer(bodyPartBigBytes); - } else { - nextBuf = Unpooled.wrappedBuffer(intermediaryBytes); + @State(Scope.Benchmark) + public static class Context { + final static String BOUNDARY = "01f136d9282f"; + + final Supplier bodyStartBytesSupplier; + final Supplier finalBigBytesSupplier; + final Supplier bodyPartBigBytesSupplier; + final Supplier intermediaryBytesSupplier; + + public Context() { + int size = 8 * 1024; + StringBuilder stringBuilder = new StringBuilder(size); + stringBuilder.setLength(size); + String data = stringBuilder.toString(); + + byte[] bodyStartBytes = ("--" + BOUNDARY + "\n" + + "Content-Disposition: form-data; name=\"msg_id\"\n\n15200\n--" + + BOUNDARY + + "\nContent-Disposition: form-data; name=\"msg1\"; filename=\"file1.txt\"\n\n" + + data).getBytes(CharsetUtil.UTF_8); + byte[] bodyPartBigBytes = data.getBytes(CharsetUtil.UTF_8); + byte[] intermediaryBytes = ("\n--" + BOUNDARY + + "\nContent-Disposition: form-data; name=\"msg2\"; filename=\"file2.txt\"\n\n" + + data).getBytes(CharsetUtil.UTF_8); + byte[] finalBigBytes = ("\n" + "--" + BOUNDARY + "--\n").getBytes(CharsetUtil.UTF_8); + + bodyStartBytesSupplier = BufferAllocator.onHeapUnpooled().constBufferSupplier(bodyStartBytes); + finalBigBytesSupplier = BufferAllocator.onHeapUnpooled().constBufferSupplier(finalBigBytes); + bodyPartBigBytesSupplier = BufferAllocator.onHeapUnpooled().constBufferSupplier(bodyPartBigBytes); + intermediaryBytesSupplier = BufferAllocator.onHeapUnpooled().constBufferSupplier(intermediaryBytes); } + } + + public double testHighNumberChunks(Context ctx, boolean big, boolean noDisk) { + int chunkNumber = 64; + + Buffer firstBuf = ctx.bodyStartBytesSupplier.get(); + Buffer finalBuf = ctx.finalBigBytesSupplier.get(); DefaultHttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST, "/up"); req.headers().add(HttpHeaderNames.CONTENT_TYPE, - "multipart/form-data; boundary=" + BOUNDARY); + "multipart/form-data; boundary=" + ctx.BOUNDARY); long start = System.nanoTime(); @@ -83,30 +105,25 @@ public double testHighNumberChunks(boolean big, boolean noDisk) { new DefaultHttpDataFactory(noDisk? 1024 * 1024 : 16 * 1024); HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(defaultHttpDataFactory, req); - firstBuf.retain(); - decoder.offer(new DefaultHttpContent(firstBuf)); - firstBuf.release(); + + try (firstBuf) { + decoder.offer(new DefaultHttpContent(firstBuf)); + } + for (int i = 1; i < chunkNumber; i++) { - nextBuf.retain(); - decoder.offer(new DefaultHttpContent(nextBuf)); - nextBuf.release(); - nextBuf.readerIndex(0); + try (Buffer nextBuf = big ? ctx.bodyPartBigBytesSupplier.get() : ctx.intermediaryBytesSupplier.get()) { + decoder.offer(new DefaultHttpContent(nextBuf)); + } + } + + try(finalBuf) { + decoder.offer(new DefaultLastHttpContent(finalBuf)); } - finalBuf.retain(); - decoder.offer(new DefaultLastHttpContent(finalBuf)); - finalBuf.release(); + while (decoder.hasNext()) { InterfaceHttpData httpData = decoder.next(); } - while (finalBuf.refCnt() > 0) { - finalBuf.release(); - } - while (nextBuf.refCnt() > 0) { - nextBuf.release(); - } - while (finalBuf.refCnt() > 0) { - finalBuf.release(); - } + long stop = System.nanoTime(); double time = (stop - start) / 1000000.0; defaultHttpDataFactory.cleanAllHttpData(); @@ -116,91 +133,12 @@ public double testHighNumberChunks(boolean big, boolean noDisk) { } @Benchmark - public double multipartRequestDecoderHighDisabledLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.DISABLED); - return testHighNumberChunks(false, true); - } finally { - ResourceLeakDetector.setLevel(level); - } + public double multipartRequestDecoderHigh(Context ctx) { + return testHighNumberChunks(ctx,false, true); } @Benchmark - public double multipartRequestDecoderBigDisabledLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.DISABLED); - return testHighNumberChunks(true, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderHighSimpleLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.SIMPLE); - return testHighNumberChunks(false, true); - } finally { - ResourceLeakDetector.setLevel(level); - } + public double multipartRequestDecoderBig(Context ctx) { + return testHighNumberChunks(ctx,true, true); } - - @Benchmark - public double multipartRequestDecoderBigSimpleLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.SIMPLE); - return testHighNumberChunks(true, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderHighAdvancedLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.ADVANCED); - return testHighNumberChunks(false, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderBigAdvancedLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.ADVANCED); - return testHighNumberChunks(true, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderHighParanoidLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.PARANOID); - return testHighNumberChunks(false, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderBigParanoidLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.PARANOID); - return testHighNumberChunks(true, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - } diff --git a/codec-multipart/pom.xml b/codec-multipart/pom.xml index 5649b19..4338058 100644 --- a/codec-multipart/pom.xml +++ b/codec-multipart/pom.xml @@ -7,7 +7,7 @@ io.netty.contrib netty-codec-multipart-parent - 5.0.0.Final-SNAPSHOT + 5.0.0.Alpha2-SNAPSHOT netty-codec-multipart @@ -22,34 +22,36 @@ io.netty - netty-codec-http + netty5-codec-http ${netty.version} io.netty - netty-common + netty5-common ${netty.version} io.netty - netty-buffer + netty5-buffer ${netty.version} io.netty - netty-transport + netty5-transport ${netty.version} io.netty - netty-codec + netty5-codec ${netty.version} + org.junit.jupiter junit-jupiter-engine test + org.assertj assertj-core @@ -57,7 +59,7 @@ io.netty - netty-testsuite + netty5-testsuite ${netty.version} test diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractDiskHttpData.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractDiskHttpData.java index 0fbaa19..ec66964 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractDiskHttpData.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractDiskHttpData.java @@ -15,13 +15,14 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.util.internal.EmptyArrays; +import io.netty5.util.internal.ObjectUtil; +import io.netty5.util.internal.PlatformDependent; +import io.netty5.util.internal.logging.InternalLogger; +import io.netty5.util.internal.logging.InternalLoggerFactory; import java.io.File; import java.io.IOException; @@ -30,9 +31,7 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.buffer.Unpooled.wrappedBuffer; +import java.nio.file.Files; /** * Abstract Disk HttpData implementation @@ -49,6 +48,13 @@ protected AbstractDiskHttpData(String name, Charset charset, long size) { super(name, charset, size); } + protected AbstractDiskHttpData(AbstractDiskHttpData copy) { + super(copy); + this.file = copy.file; + this.isRenamed = copy.isRenamed; + this.fileChannel = copy.fileChannel; + } + /** * * @return the real DiskFilename (basename) @@ -102,9 +108,10 @@ private File tempFile() throws IOException { } @Override - public void setContent(ByteBuf buffer) throws IOException { - ObjectUtil.checkNotNull(buffer, "buffer"); - try { + public void setContent(Buffer buffer) throws IOException { + try (buffer) { + checkAccessible(); + ObjectUtil.checkNotNullWithIAE(buffer, "buffer"); size = buffer.readableBytes(); checkSize(size); if (definedSize > 0 && definedSize < size) { @@ -126,33 +133,31 @@ public void setContent(ByteBuf buffer) throws IOException { } return; } - RandomAccessFile accessFile = new RandomAccessFile(file, "rw"); - try { + try(RandomAccessFile accessFile = new RandomAccessFile(file, "rw")) { accessFile.setLength(0); - FileChannel localfileChannel = accessFile.getChannel(); - ByteBuffer byteBuffer = buffer.nioBuffer(); - int written = 0; - while (written < size) { - written += localfileChannel.write(byteBuffer); + try(FileChannel localfileChannel = accessFile.getChannel()) { + int length = buffer.readableBytes(); + int written; + do + { + if ((written = buffer.transferTo(localfileChannel, length)) == -1) { + break; + } + length = -written; + } while (length > 0); + localfileChannel.force(false); + setCompleted(); } - buffer.readerIndex(buffer.readerIndex() + written); - localfileChannel.force(false); - } finally { - accessFile.close(); } - setCompleted(); - } finally { - // Release the buffer as it was retained before and we not need a reference to it at all - // See https://github.com/netty/netty/issues/1516 - buffer.release(); } } @Override - public void addContent(ByteBuf buffer, boolean last) + public void addContent(Buffer buffer, boolean last) throws IOException { if (buffer != null) { - try { + try (buffer) { + checkAccessible(); int localsize = buffer.readableBytes(); checkSize(size + localsize); if (definedSize > 0 && definedSize < size + localsize) { @@ -166,25 +171,17 @@ public void addContent(ByteBuf buffer, boolean last) RandomAccessFile accessFile = new RandomAccessFile(file, "rw"); fileChannel = accessFile.getChannel(); } + + int written; int remaining = localsize; - long position = fileChannel.position(); - int index = buffer.readerIndex(); - while (remaining > 0) { - int written = buffer.getBytes(index, fileChannel, position, remaining); - if (written < 0) { + do + { + if ((written = buffer.transferTo(fileChannel, remaining)) == -1) { break; } remaining -= written; - position += written; - index += written; - } - fileChannel.position(position); - buffer.readerIndex(index); + } while (remaining > 0); size += localsize - remaining; - } finally { - // Release the buffer as it was retained before and we not need a reference to it at all - // See https://github.com/netty/netty/issues/1516 - buffer.release(); } } if (last) { @@ -203,12 +200,13 @@ public void addContent(ByteBuf buffer, boolean last) fileChannel = null; setCompleted(); } else { - ObjectUtil.checkNotNull(buffer, "buffer"); + ObjectUtil.checkNotNullWithIAE(buffer, "buffer"); } } @Override public void setContent(File file) throws IOException { + checkAccessible(); long size = file.length(); checkSize(size); this.size = size; @@ -222,7 +220,8 @@ public void setContent(File file) throws IOException { @Override public void setContent(InputStream inputStream) throws IOException { - ObjectUtil.checkNotNull(inputStream, "inputStream"); + checkAccessible(); + ObjectUtil.checkNotNullWithIAE(inputStream, "inputStream"); if (file != null) { delete(); } @@ -294,6 +293,7 @@ public void delete() { @Override public byte[] get() throws IOException { + checkAccessible(); if (file == null) { return EmptyArrays.EMPTY_BYTES; } @@ -301,47 +301,41 @@ public byte[] get() throws IOException { } @Override - public ByteBuf getByteBuf() throws IOException { + public Buffer getBuffer() throws IOException { + checkAccessible(); if (file == null) { - return EMPTY_BUFFER; + return DefaultBufferAllocators.preferredAllocator().allocate(0); } - byte[] array = readFrom(file); - return wrappedBuffer(array); + return getBufferFrom(file); } @Override - public ByteBuf getChunk(int length) throws IOException { + public Buffer getChunk(int length) throws IOException { + checkAccessible(); + int remaining = length; + int read; + if (file == null || length == 0) { - return EMPTY_BUFFER; + return DefaultBufferAllocators.preferredAllocator().allocate(0); } if (fileChannel == null) { RandomAccessFile accessFile = new RandomAccessFile(file, "r"); fileChannel = accessFile.getChannel(); } - int read = 0; - ByteBuffer byteBuffer = ByteBuffer.allocate(length); + Buffer buffer = DefaultBufferAllocators.onHeapAllocator().allocate(length); try { - while (read < length) { - int readnow = fileChannel.read(byteBuffer); - if (readnow == -1) { - fileChannel.close(); - fileChannel = null; + do { + if ((read = buffer.transferFrom(fileChannel, remaining)) < 0) { break; } - read += readnow; - } + remaining -= read; + } while (remaining > 0); } catch (IOException e) { fileChannel.close(); fileChannel = null; + buffer.close(); throw e; } - if (read == 0) { - return EMPTY_BUFFER; - } - byteBuffer.flip(); - ByteBuf buffer = wrappedBuffer(byteBuffer); - buffer.readerIndex(0); - buffer.writerIndex(read); return buffer; } @@ -352,6 +346,7 @@ public String getString() throws IOException { @Override public String getString(Charset encoding) throws IOException { + checkAccessible(); if (file == null) { return ""; } @@ -370,7 +365,8 @@ public boolean isInMemory() { @Override public boolean renameTo(File dest) throws IOException { - ObjectUtil.checkNotNull(dest, "dest"); + checkAccessible(); + ObjectUtil.checkNotNullWithIAE(dest, "dest"); if (file == null) { throw new IOException("No file defined so cannot be renamed"); } @@ -446,38 +442,38 @@ public boolean renameTo(File dest) throws IOException { * @return the array of bytes */ private static byte[] readFrom(File src) throws IOException { + return Files.readAllBytes(src.toPath()); + } + + private static Buffer getBufferFrom(File src) throws IOException { long srcsize = src.length(); if (srcsize > Integer.MAX_VALUE) { throw new IllegalArgumentException( "File too big to be loaded in memory"); } - RandomAccessFile accessFile = new RandomAccessFile(src, "r"); - byte[] array = new byte[(int) srcsize]; - try { - FileChannel fileChannel = accessFile.getChannel(); - ByteBuffer byteBuffer = ByteBuffer.wrap(array); - int read = 0; - while (read < srcsize) { - read += fileChannel.read(byteBuffer); - } - } finally { - accessFile.close(); + Buffer buf = DefaultBufferAllocators.onHeapAllocator().allocate((int) srcsize); + + try (RandomAccessFile raf = new RandomAccessFile(src, "r"); FileChannel channel = raf.getChannel()) { + int remaining = (int) srcsize; + int read; + + do { + if ((read = buf.transferFrom(channel, remaining)) < 0) { + break; + } + remaining -= read; + } while (remaining > 0); + return buf; + } + + catch (IOException e) { + buf.close(); + throw e; } - return array; } @Override public File getFile() throws IOException { return file; } - - @Override - public HttpData touch() { - return this; - } - - @Override - public HttpData touch(Object hint) { - return this; - } } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractHttpData.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractHttpData.java index cc91071..fbc30de 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractHttpData.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractHttpData.java @@ -15,25 +15,27 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelException; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.internal.ObjectUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.Drop; +import io.netty5.buffer.api.internal.ResourceSupport; +import io.netty5.channel.ChannelException; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.util.internal.ObjectUtil; import java.io.IOException; import java.nio.charset.Charset; import java.util.regex.Pattern; +import static io.netty5.util.internal.ObjectUtil.checkNonEmpty; + /** * Abstract HttpData implementation */ -public abstract class AbstractHttpData extends AbstractReferenceCounted implements HttpData { +public abstract class AbstractHttpData extends ResourceSupport implements HttpData { private static final Pattern STRIP_PATTERN = Pattern.compile("(?:^\\s+|\\s+$|\\n)"); private static final Pattern REPLACE_PATTERN = Pattern.compile("[\\r\\t]"); + protected final static byte[] EMPTY_ARRAY = new byte[0]; private final String name; protected long definedSize; @@ -42,8 +44,25 @@ public abstract class AbstractHttpData extends AbstractReferenceCounted implemen private boolean completed; private long maxSize = DefaultHttpDataFactory.MAXSIZE; + private final static Drop drop = new Drop() { + @Override + public void drop(AbstractHttpData data) { + data.delete(); + } + + @Override + public Drop fork() { + return this; + } + + @Override + public void attach(AbstractHttpData mixedFileUpload) { + } + }; + protected AbstractHttpData(String name, Charset charset, long size) { - ObjectUtil.checkNotNull(name, "name"); + super(drop); + ObjectUtil.checkNotNullWithIAE(name, "name"); name = REPLACE_PATTERN.matcher(name).replaceAll(" "); name = STRIP_PATTERN.matcher(name).replaceAll(""); @@ -55,6 +74,16 @@ protected AbstractHttpData(String name, Charset charset, long size) { definedSize = size; } + protected AbstractHttpData(AbstractHttpData copy) { + super(drop); + this.name = copy.name; + this.charset = copy.charset; + this.definedSize = copy.definedSize; + this.size = copy.size; + this.completed = copy.completed; + this.maxSize = copy.maxSize; + } + @Override public long getMaxSize() { return maxSize; @@ -97,7 +126,7 @@ public Charset getCharset() { @Override public void setCharset(Charset charset) { - this.charset = ObjectUtil.checkNotNull(charset, "charset"); + this.charset = ObjectUtil.checkNotNullWithIAE(charset, "charset"); } @Override @@ -111,34 +140,35 @@ public long definedLength() { } @Override - public ByteBuf content() { + public Buffer content() { + checkAccessible(); try { - return getByteBuf(); + return getBuffer(); } catch (IOException e) { throw new ChannelException(e); } } @Override - protected void deallocate() { - delete(); + protected RuntimeException createResourceClosedException() { + return new IllegalStateException("Resource closed"); } - @Override - public HttpData retain() { - super.retain(); - return this; + protected void checkAccessible() { + if (! isAccessible()) { + throw new IllegalStateException(getClass().getName() + + " is innaccessible"); + } } - @Override - public HttpData retain(int increment) { - super.retain(increment); - return this; + protected void checkAccessible(Buffer cleanup) { + if (! isAccessible()) { + if (cleanup != null && cleanup.isAccessible()) { + cleanup.close(); + } + throw new IllegalStateException(getClass().getName() + + " is innaccessible"); + } } - @Override - public abstract HttpData touch(); - - @Override - public abstract HttpData touch(Object hint); } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractMemoryHttpData.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractMemoryHttpData.java index d6b099d..2fec2bd 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractMemoryHttpData.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractMemoryHttpData.java @@ -15,104 +15,106 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.util.internal.ObjectUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.CompositeBuffer; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.buffer.api.internal.Statics; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.util.internal.ObjectUtil; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; -import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.buffer.Unpooled.buffer; -import static io.netty.buffer.Unpooled.compositeBuffer; -import static io.netty.buffer.Unpooled.wrappedBuffer; +import java.util.Arrays; /** * Abstract Memory HttpData implementation */ public abstract class AbstractMemoryHttpData extends AbstractHttpData { - private ByteBuf byteBuf; - private int chunkPosition; + private Buffer byteBuf; protected AbstractMemoryHttpData(String name, Charset charset, long size) { super(name, charset, size); - byteBuf = EMPTY_BUFFER; + byteBuf = DefaultBufferAllocators.preferredAllocator().allocate(0); } @Override - public void setContent(ByteBuf buffer) throws IOException { - ObjectUtil.checkNotNull(buffer, "buffer"); + public void setContent(Buffer buffer) throws IOException { + checkAccessible(buffer); + ObjectUtil.checkNotNullWithIAE(buffer, "buffer"); long localsize = buffer.readableBytes(); try { checkSize(localsize); } catch (IOException e) { - buffer.release(); + buffer.close(); throw e; } if (definedSize > 0 && definedSize < localsize) { - buffer.release(); + buffer.close(); throw new IOException("Out of size: " + localsize + " > " + definedSize); } if (byteBuf != null) { - byteBuf.release(); + byteBuf.close(); } - byteBuf = buffer; - size = localsize; + setContentInternal(buffer, localsize); + } + + protected final void setContentInternal(Buffer buffer, long size) { + this.byteBuf = buffer; + this.size = size; setCompleted(); } @Override public void setContent(InputStream inputStream) throws IOException { - ObjectUtil.checkNotNull(inputStream, "inputStream"); + checkAccessible(); + ObjectUtil.checkNotNullWithIAE(inputStream, "inputStream"); byte[] bytes = new byte[4096 * 4]; - ByteBuf buffer = buffer(); + Buffer buffer = DefaultBufferAllocators.preferredAllocator().allocate(0); int written = 0; try { - int read = inputStream.read(bytes); - while (read > 0) { + int read; + while ((read = inputStream.read(bytes)) > 0) { buffer.writeBytes(bytes, 0, read); written += read; checkSize(written); - read = inputStream.read(bytes); } } catch (IOException e) { - buffer.release(); + buffer.close(); throw e; } size = written; if (definedSize > 0 && definedSize < size) { - buffer.release(); + buffer.close(); throw new IOException("Out of size: " + size + " > " + definedSize); } if (byteBuf != null) { - byteBuf.release(); + byteBuf.close(); } byteBuf = buffer; setCompleted(); } @Override - public void addContent(ByteBuf buffer, boolean last) + public void addContent(Buffer buffer, boolean last) throws IOException { + checkAccessible(buffer); if (buffer != null) { long localsize = buffer.readableBytes(); try { checkSize(size + localsize); } catch (IOException e) { - buffer.release(); + buffer.close(); throw e; } if (definedSize > 0 && definedSize < size + localsize) { - buffer.release(); + buffer.close(); throw new IOException("Out of size: " + (size + localsize) + " > " + definedSize); } @@ -121,58 +123,55 @@ public void addContent(ByteBuf buffer, boolean last) byteBuf = buffer; } else if (localsize == 0) { // Nothing to add and byteBuf already exists - buffer.release(); + buffer.close(); } else if (byteBuf.readableBytes() == 0) { // Previous buffer is empty, so just replace it - byteBuf.release(); + byteBuf.close(); byteBuf = buffer; - } else if (byteBuf instanceof CompositeByteBuf) { - CompositeByteBuf cbb = (CompositeByteBuf) byteBuf; - cbb.addComponent(true, buffer); + } else if (CompositeBuffer.isComposite(this.byteBuf)) { + CompositeBuffer cbb = (CompositeBuffer) this.byteBuf; + cbb.extendWith(buffer.send()); } else { - CompositeByteBuf cbb = compositeBuffer(Integer.MAX_VALUE); - cbb.addComponents(true, byteBuf, buffer); - byteBuf = cbb; + byteBuf = DefaultBufferAllocators.onHeapAllocator().compose(Arrays.asList(this.byteBuf.send(), buffer.send())); } } if (last) { setCompleted(); } else { - ObjectUtil.checkNotNull(buffer, "buffer"); + ObjectUtil.checkNotNullWithIAE(buffer, "buffer"); } } @Override public void setContent(File file) throws IOException { - ObjectUtil.checkNotNull(file, "file"); + checkAccessible(); + ObjectUtil.checkNotNullWithIAE(file, "file"); long newsize = file.length(); if (newsize > Integer.MAX_VALUE) { throw new IllegalArgumentException("File too big to be loaded in memory"); } checkSize(newsize); - RandomAccessFile accessFile = new RandomAccessFile(file, "r"); - ByteBuffer byteBuffer; - try { - FileChannel fileChannel = accessFile.getChannel(); - try { - byte[] array = new byte[(int) newsize]; - byteBuffer = ByteBuffer.wrap(array); - int read = 0; - while (read < newsize) { - read += fileChannel.read(byteBuffer); + Buffer buf = DefaultBufferAllocators.onHeapAllocator().allocate((int) newsize); + try (RandomAccessFile accessFile = new RandomAccessFile(file, "r"); + FileChannel fileChannel = accessFile.getChannel()) { + int bytesRead = 0; + int remaining = (int) newsize; + do { + buf.ensureWritable(remaining); + int bytes = buf.transferFrom(fileChannel, remaining); + if (bytes == -1) { + break; } - } finally { - fileChannel.close(); - } - } finally { - accessFile.close(); + bytesRead += bytes; + remaining -= bytes; + } while (bytesRead < newsize); } - byteBuffer.flip(); + if (byteBuf != null) { - byteBuf.release(); + byteBuf.close(); } - byteBuf = wrappedBuffer(Integer.MAX_VALUE, byteBuffer); + byteBuf = buf; size = newsize; setCompleted(); } @@ -180,7 +179,9 @@ public void setContent(File file) throws IOException { @Override public void delete() { if (byteBuf != null) { - byteBuf.release(); + if (byteBuf.isAccessible()) { + byteBuf.close(); + } byteBuf = null; } } @@ -188,10 +189,10 @@ public void delete() { @Override public byte[] get() { if (byteBuf == null) { - return EMPTY_BUFFER.array(); + return EMPTY_ARRAY; } byte[] array = new byte[byteBuf.readableBytes()]; - byteBuf.getBytes(byteBuf.readerIndex(), array); + byteBuf.copyInto(byteBuf.readerOffset(), array, 0, byteBuf.readableBytes()); return array; } @@ -217,28 +218,17 @@ public String getString(Charset encoding) { * @return the attached ByteBuf containing the actual bytes */ @Override - public ByteBuf getByteBuf() { + public Buffer getBuffer() { return byteBuf; } @Override - public ByteBuf getChunk(int length) throws IOException { - if (byteBuf == null || length == 0 || byteBuf.readableBytes() == 0) { - chunkPosition = 0; - return EMPTY_BUFFER; - } - int sizeLeft = byteBuf.readableBytes() - chunkPosition; - if (sizeLeft == 0) { - chunkPosition = 0; - return EMPTY_BUFFER; + public Buffer getChunk(int length) { + int readableBytes = byteBuf.readableBytes(); + if (byteBuf == null || length == 0 || readableBytes == 0) { + return DefaultBufferAllocators.preferredAllocator().allocate(0); } - int sliceLength = length; - if (sizeLeft < length) { - sliceLength = sizeLeft; - } - ByteBuf chunk = byteBuf.retainedSlice(chunkPosition, sliceLength); - chunkPosition += sliceLength; - return chunk; + return byteBuf.readSplit(Math.min(readableBytes, length)); } @Override @@ -248,7 +238,7 @@ public boolean isInMemory() { @Override public boolean renameTo(File dest) throws IOException { - ObjectUtil.checkNotNull(dest, "dest"); + ObjectUtil.checkNotNullWithIAE(dest, "dest"); if (byteBuf == null) { // empty file if (!dest.createNewFile()) { @@ -257,30 +247,19 @@ public boolean renameTo(File dest) throws IOException { return true; } int length = byteBuf.readableBytes(); - long written = 0; - RandomAccessFile accessFile = new RandomAccessFile(dest, "rw"); - try { - FileChannel fileChannel = accessFile.getChannel(); - try { - if (byteBuf.nioBufferCount() == 1) { - ByteBuffer byteBuffer = byteBuf.nioBuffer(); - while (written < length) { - written += fileChannel.write(byteBuffer); - } - } else { - ByteBuffer[] byteBuffers = byteBuf.nioBuffers(); - while (written < length) { - written += fileChannel.write(byteBuffers); - } + try(RandomAccessFile accessFile = new RandomAccessFile(dest, "rw"); + FileChannel fileChannel = accessFile.getChannel()) { + int written; + + do { + if ((written = byteBuf.transferTo(fileChannel, length)) == -1) { + break; } - fileChannel.force(false); - } finally { - fileChannel.close(); - } - } finally { - accessFile.close(); + length -= written; + } while (length > 0); + fileChannel.force(false); } - return written == length; + return length == 0; } @Override @@ -289,15 +268,8 @@ public File getFile() throws IOException { } @Override - public HttpData touch() { - return touch(null); + protected RuntimeException createResourceClosedException() { + return Statics.bufferIsClosed(getBuffer()); } - @Override - public HttpData touch(Object hint) { - if (byteBuf != null) { - byteBuf.touch(hint); - } - return this; - } } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractMixedHttpData.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractMixedHttpData.java index d6aa461..667e959 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractMixedHttpData.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/AbstractMixedHttpData.java @@ -15,22 +15,40 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.util.AbstractReferenceCounted; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.Drop; +import io.netty5.buffer.api.internal.ResourceSupport; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; -abstract class AbstractMixedHttpData extends AbstractReferenceCounted implements HttpData { +abstract class AbstractMixedHttpData extends ResourceSupport> implements HttpData { final String baseDir; final boolean deleteOnExit; D wrapped; - private final long limitSize; + protected final long limitSize; + + private final static Drop> drop = new Drop<>() { + @Override + public void drop(AbstractMixedHttpData data) { + data.delete(); + } + + @Override + public Drop> fork() { + return this; + } + + @Override + public void attach(AbstractMixedHttpData mixedFileUpload) { + } + }; AbstractMixedHttpData(long limitSize, String baseDir, boolean deleteOnExit, D initial) { + super (drop); this.limitSize = limitSize; this.wrapped = initial; this.baseDir = baseDir; @@ -46,16 +64,18 @@ public long getMaxSize() { @Override public void setMaxSize(long maxSize) { + checkAccessible(); wrapped.setMaxSize(maxSize); } @Override - public ByteBuf content() { + public Buffer content() { return wrapped.content(); } @Override public void checkSize(long newSize) throws IOException { + checkAccessible(); wrapped.checkSize(newSize); } @@ -75,32 +95,28 @@ public String getName() { } @Override - public void addContent(ByteBuf buffer, boolean last) throws IOException { + public void addContent(Buffer buffer, boolean last) throws IOException { + checkAccessible(buffer); if (wrapped instanceof AbstractMemoryHttpData) { try { checkSize(wrapped.length() + buffer.readableBytes()); if (wrapped.length() + buffer.readableBytes() > limitSize) { D diskData = makeDiskData(); - ByteBuf data = ((AbstractMemoryHttpData) wrapped).getByteBuf(); - if (data != null && data.isReadable()) { - diskData.addContent(data.retain(), false); + Buffer data = ((AbstractMemoryHttpData) wrapped).getBuffer(); + if (data != null && data.readableBytes() > 0) { + diskData.addContent(data, false); // data will be closed by this method } - wrapped.release(); + wrapped.close(); wrapped = diskData; } } catch (IOException e) { - buffer.release(); + buffer.close(); throw e; } } wrapped.addContent(buffer, last); } - @Override - protected void deallocate() { - delete(); - } - @Override public void delete() { wrapped.delete(); @@ -112,8 +128,8 @@ public byte[] get() throws IOException { } @Override - public ByteBuf getByteBuf() throws IOException { - return wrapped.getByteBuf(); + public Buffer getBuffer() throws IOException { + return wrapped.getBuffer(); } @Override @@ -147,18 +163,21 @@ public void setCharset(Charset charset) { } @Override - public void setContent(ByteBuf buffer) throws IOException { + public void setContent(Buffer buffer) throws IOException { + checkAccessible(buffer); try { checkSize(buffer.readableBytes()); } catch (IOException e) { - buffer.release(); + buffer.close(); throw e; } if (buffer.readableBytes() > limitSize) { if (wrapped instanceof AbstractMemoryHttpData) { // change to Disk - wrapped.release(); - wrapped = makeDiskData(); + D oldWrapped = wrapped; + try (oldWrapped) { + wrapped = makeDiskData(); + } } } wrapped.setContent(buffer); @@ -166,12 +185,15 @@ public void setContent(ByteBuf buffer) throws IOException { @Override public void setContent(File file) throws IOException { + checkAccessible(); checkSize(file.length()); if (file.length() > limitSize) { if (wrapped instanceof AbstractMemoryHttpData) { // change to Disk - wrapped.release(); - wrapped = makeDiskData(); + D oldWrapped = wrapped; + try (oldWrapped) { + wrapped = makeDiskData(); + } } } wrapped.setContent(file); @@ -179,10 +201,13 @@ public void setContent(File file) throws IOException { @Override public void setContent(InputStream inputStream) throws IOException { + checkAccessible(); if (wrapped instanceof AbstractMemoryHttpData) { // change to Disk even if we don't know the size - wrapped.release(); - wrapped = makeDiskData(); + D oldWrapped = wrapped; + try(oldWrapped) { + wrapped = makeDiskData(); + } } wrapped.setContent(inputStream); } @@ -218,7 +243,7 @@ public String toString() { } @Override - public ByteBuf getChunk(int length) throws IOException { + public Buffer getChunk(int length) throws IOException { return wrapped.getChunk(length); } @@ -235,45 +260,27 @@ public D copy() { @SuppressWarnings("unchecked") @Override - public D duplicate() { - return (D) wrapped.duplicate(); - } - - @SuppressWarnings("unchecked") - @Override - public D retainedDuplicate() { - return (D) wrapped.retainedDuplicate(); - } - - @SuppressWarnings("unchecked") - @Override - public D replace(ByteBuf content) { + public D replace(Buffer content) { return (D) wrapped.replace(content); } - @SuppressWarnings("unchecked") @Override - public D touch() { - wrapped.touch(); - return (D) this; + protected RuntimeException createResourceClosedException() { + return new IllegalStateException("Resource is closed"); } - @SuppressWarnings("unchecked") - @Override - public D touch(Object hint) { - wrapped.touch(hint); - return (D) this; - } - - @SuppressWarnings("unchecked") - @Override - public D retain() { - return (D) super.retain(); + protected void checkAccessible() { + if (! isAccessible()) { + throw createResourceClosedException(); + } } - @SuppressWarnings("unchecked") - @Override - public D retain(int increment) { - return (D) super.retain(increment); + protected void checkAccessible(Buffer cleanup) { + if (! isAccessible()) { + if (cleanup != null && cleanup.isAccessible()) { + cleanup.close(); + } + throw createResourceClosedException(); + } } } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/Attribute.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/Attribute.java index 611df73..00549e6 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/Attribute.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/Attribute.java @@ -15,7 +15,7 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; +import io.netty5.buffer.api.Buffer; import java.io.IOException; @@ -37,23 +37,5 @@ public interface Attribute extends HttpData { Attribute copy(); @Override - Attribute duplicate(); - - @Override - Attribute retainedDuplicate(); - - @Override - Attribute replace(ByteBuf content); - - @Override - Attribute retain(); - - @Override - Attribute retain(int increment); - - @Override - Attribute touch(); - - @Override - Attribute touch(Object hint); + Attribute replace(Buffer content); } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DefaultHttpDataFactory.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DefaultHttpDataFactory.java index 0c1ab8a..2719dba 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DefaultHttpDataFactory.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DefaultHttpDataFactory.java @@ -15,9 +15,9 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.DefaultHttpRequest; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.handler.codec.http.HttpRequest; import java.io.IOException; import java.nio.charset.Charset; @@ -313,7 +313,9 @@ public void cleanRequestHttpData(HttpRequest request) { List list = requestFileDeleteMap.remove(request); if (list != null) { for (HttpData data : list) { - data.release(); + if (data.isAccessible()) { + data.close(); + } } } } @@ -329,7 +331,9 @@ public void cleanAllHttpData() { List list = e.getValue(); for (HttpData data : list) { - data.release(); + if (data.isAccessible()) { + data.close(); + } } i.remove(); diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DiskAttribute.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DiskAttribute.java index 7698f19..fcf398f 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DiskAttribute.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DiskAttribute.java @@ -15,16 +15,16 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelException; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.util.internal.ObjectUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.buffer.api.Owned; +import io.netty5.channel.ChannelException; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.util.internal.ObjectUtil; import java.io.IOException; import java.nio.charset.Charset; -import static io.netty.buffer.Unpooled.wrappedBuffer; - /** * Disk implementation of Attributes */ @@ -100,6 +100,12 @@ public DiskAttribute(String name, String value, Charset charset, this.deleteOnExit = deleteOnExit; } + protected DiskAttribute(DiskAttribute copy) { + super(copy); + this.baseDir = copy.baseDir; + this.deleteOnExit = copy.deleteOnExit; + } + @Override public HttpDataType getHttpDataType() { return HttpDataType.Attribute; @@ -107,16 +113,18 @@ public HttpDataType getHttpDataType() { @Override public String getValue() throws IOException { + checkAccessible(); byte [] bytes = get(); return new String(bytes, getCharset()); } @Override public void setValue(String value) throws IOException { - ObjectUtil.checkNotNull(value, "value"); + checkAccessible(); + ObjectUtil.checkNotNullWithIAE(value, "value"); byte [] bytes = value.getBytes(getCharset()); checkSize(bytes.length); - ByteBuf buffer = wrappedBuffer(bytes); + Buffer buffer = DefaultBufferAllocators.onHeapAllocator().copyOf(bytes); if (definedSize > 0) { definedSize = buffer.readableBytes(); } @@ -124,12 +132,13 @@ public void setValue(String value) throws IOException { } @Override - public void addContent(ByteBuf buffer, boolean last) throws IOException { + public void addContent(Buffer buffer, boolean last) throws IOException { + checkAccessible(buffer); final long newDefinedSize = size + buffer.readableBytes(); try { checkSize(newDefinedSize); } catch (IOException e) { - buffer.release(); + buffer.close(); throw e; } if (definedSize > 0 && definedSize < newDefinedSize) { @@ -201,38 +210,14 @@ protected String getPrefix() { @Override public Attribute copy() { - final ByteBuf content = content(); + checkAccessible(); + final Buffer content = content(); return replace(content != null ? content.copy() : null); } @Override - public Attribute duplicate() { - final ByteBuf content = content(); - return replace(content != null ? content.duplicate() : null); - } - - @Override - public Attribute retainedDuplicate() { - ByteBuf content = content(); - if (content != null) { - content = content.retainedDuplicate(); - boolean success = false; - try { - Attribute duplicate = replace(content); - success = true; - return duplicate; - } finally { - if (!success) { - content.release(); - } - } - } else { - return replace(null); - } - } - - @Override - public Attribute replace(ByteBuf content) { + public Attribute replace(Buffer content) { + checkAccessible(); DiskAttribute attr = new DiskAttribute(getName(), baseDir, deleteOnExit); attr.setCharset(getCharset()); if (content != null) { @@ -247,26 +232,11 @@ public Attribute replace(ByteBuf content) { } @Override - public Attribute retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public Attribute retain() { - super.retain(); - return this; - } - - @Override - public Attribute touch() { - super.touch(); - return this; - } - - @Override - public Attribute touch(Object hint) { - super.touch(hint); - return this; + protected Owned prepareSend() { + return drop -> { + DiskAttribute attr = new DiskAttribute(this); + return attr; + }; } } + diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DiskFileUpload.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DiskFileUpload.java index e754ddb..43e7ed0 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DiskFileUpload.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/DiskFileUpload.java @@ -15,11 +15,12 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelException; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.util.internal.ObjectUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.Owned; +import io.netty5.channel.ChannelException; +import io.netty5.handler.codec.http.HttpHeaderNames; +import io.netty5.handler.codec.http.HttpHeaderValues; +import io.netty5.util.internal.ObjectUtil; import java.io.File; import java.io.IOException; @@ -63,6 +64,15 @@ public DiskFileUpload(String name, String filename, String contentType, charset, size, baseDirectory, deleteOnExitTemporaryFile); } + public DiskFileUpload(DiskFileUpload copy) { + super(copy); + this.baseDir = copy.baseDir; + this.deleteOnExit = copy.deleteOnExit; + this.filename = copy.filename; + this.contentType = copy.contentType; + this.contentTransferEncoding = copy.contentTransferEncoding; + } + @Override public HttpDataType getHttpDataType() { return HttpDataType.FileUpload; @@ -75,7 +85,7 @@ public String getFilename() { @Override public void setFilename(String filename) { - this.filename = ObjectUtil.checkNotNull(filename, "filename"); + this.filename = ObjectUtil.checkNotNullWithIAE(filename, "filename"); } @Override @@ -103,7 +113,7 @@ public int compareTo(FileUpload o) { @Override public void setContentType(String contentType) { - this.contentType = ObjectUtil.checkNotNull(contentType, "contentType"); + this.contentType = ObjectUtil.checkNotNullWithIAE(contentType, "contentType"); } @Override @@ -168,38 +178,13 @@ protected String getPrefix() { @Override public FileUpload copy() { - final ByteBuf content = content(); + final Buffer content = content(); return replace(content != null ? content.copy() : null); } @Override - public FileUpload duplicate() { - final ByteBuf content = content(); - return replace(content != null ? content.duplicate() : null); - } - - @Override - public FileUpload retainedDuplicate() { - ByteBuf content = content(); - if (content != null) { - content = content.retainedDuplicate(); - boolean success = false; - try { - FileUpload duplicate = replace(content); - success = true; - return duplicate; - } finally { - if (!success) { - content.release(); - } - } - } else { - return replace(null); - } - } - - @Override - public FileUpload replace(ByteBuf content) { + public FileUpload replace(Buffer content) { + checkAccessible(content); DiskFileUpload upload = new DiskFileUpload( getName(), getFilename(), getContentType(), getContentTransferEncoding(), getCharset(), size, baseDir, deleteOnExit); @@ -215,26 +200,10 @@ public FileUpload replace(ByteBuf content) { } @Override - public FileUpload retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public FileUpload retain() { - super.retain(); - return this; - } - - @Override - public FileUpload touch() { - super.touch(); - return this; - } - - @Override - public FileUpload touch(Object hint) { - super.touch(hint); - return this; + protected Owned prepareSend() { + return drop -> { + DiskFileUpload upload = new DiskFileUpload(this); + return upload; + }; } } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/FileUpload.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/FileUpload.java index 5c2b362..6e0512f 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/FileUpload.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/FileUpload.java @@ -15,7 +15,7 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; +import io.netty5.buffer.api.Buffer; /** * FileUpload interface that could be in memory, on temporary file or any other implementations. @@ -62,23 +62,6 @@ public interface FileUpload extends HttpData { FileUpload copy(); @Override - FileUpload duplicate(); + FileUpload replace(Buffer content); - @Override - FileUpload retainedDuplicate(); - - @Override - FileUpload replace(ByteBuf content); - - @Override - FileUpload retain(); - - @Override - FileUpload retain(int increment); - - @Override - FileUpload touch(); - - @Override - FileUpload touch(Object hint); } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/Helpers.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/Helpers.java new file mode 100644 index 0000000..42db772 --- /dev/null +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/Helpers.java @@ -0,0 +1,72 @@ +/* + * Copyright 2022 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: + * + * https://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.contrib.handler.codec.http.multipart; + +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.handler.codec.http.DefaultLastHttpContent; +import io.netty5.handler.codec.http.EmptyLastHttpContent; +import io.netty5.util.AsciiString; +import io.netty5.util.Resource; + +import java.nio.charset.Charset; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static io.netty5.util.CharsetUtil.US_ASCII; + +/** + * Various helper methods used by multipart implementation classes. + */ +public class Helpers { + + static Buffer copiedBuffer(String str, Charset charset) { + return DefaultBufferAllocators.onHeapAllocator().copyOf(str, charset); + } + + static Buffer copiedBuffer(byte[] bytes, int offset, int length) { + Buffer buf = DefaultBufferAllocators.onHeapAllocator().allocate(length); + buf.writeBytes(bytes, offset, length); + return buf; + } + + static Buffer copiedBuffer(byte[] bytes) { + Buffer buf = DefaultBufferAllocators.onHeapAllocator().allocate(bytes.length); + buf.writeBytes(bytes); + return buf; + } + + static Buffer toComposite(Buffer ... bufs) { + return DefaultBufferAllocators.onHeapAllocator().compose(Stream.of(bufs).map(Resource::send).collect(Collectors.toList())); + } + + static EmptyLastHttpContent emptyLastHttpContent() { + return new EmptyLastHttpContent(DefaultBufferAllocators.preferredAllocator()); + } + + static DefaultLastHttpContent defaultLastHttpContent() { + return new DefaultLastHttpContent(DefaultBufferAllocators.preferredAllocator().allocate(0)); + } + + static String toString(Buffer buf, int offset, int length, Charset charset) { + byte[] bytes = new byte[length]; + buf.copyInto(offset, bytes, 0, length); + if (US_ASCII.equals(charset)) { + return new AsciiString(bytes).toString(); + } + return new String(bytes, 0, length, charset); + } + +} diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpData.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpData.java index 903a45f..65851bc 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpData.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpData.java @@ -15,8 +15,7 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; +import io.netty5.buffer.api.Buffer; import java.io.File; import java.io.IOException; @@ -26,7 +25,7 @@ /** * Extended interface for InterfaceHttpData */ -public interface HttpData extends InterfaceHttpData, ByteBufHolder { +public interface HttpData extends InterfaceHttpData { /** * Returns the maxSize for this HttpData. @@ -49,17 +48,17 @@ public interface HttpData extends InterfaceHttpData, ByteBufHolder { /** * Set the content from the ChannelBuffer (erase any previous data) - *

{@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link HttpData}. + *

{@link Buffer#close()} ownership of {@code buffer} is transferred to this {@link HttpData}. * * @param buffer * must be not null * @throws IOException */ - void setContent(ByteBuf buffer) throws IOException; + void setContent(Buffer buffer) throws IOException; /** * Add the content from the ChannelBuffer - *

{@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link HttpData}. + *

{@link Buffer#close()} ownership of {@code buffer} is transferred to this {@link HttpData}. * * @param buffer * must be not null except if last is set to False @@ -67,7 +66,7 @@ public interface HttpData extends InterfaceHttpData, ByteBufHolder { * True of the buffer is the last one * @throws IOException */ - void addContent(ByteBuf buffer, boolean last) throws IOException; + void addContent(Buffer buffer, boolean last) throws IOException; /** * Set the content from the file (erase any previous data) @@ -132,13 +131,14 @@ public interface HttpData extends InterfaceHttpData, ByteBufHolder { byte[] get() throws IOException; /** - * Returns the content of the file item as a ByteBuf.
+ * Returns the content of the file item as a Buffer.
* Note: this method will allocate a lot of memory, if the data is currently stored on the file system. * * @return the content of the file item as a ByteBuf * @throws IOException + * */ - ByteBuf getByteBuf() throws IOException; + Buffer getBuffer() throws IOException; /** * Returns a ChannelBuffer for the content from the current position with at @@ -149,7 +149,7 @@ public interface HttpData extends InterfaceHttpData, ByteBufHolder { * @return a ChannelBuffer for the content from the current position or an * EMPTY_BUFFER if there is no more data to return */ - ByteBuf getChunk(int length) throws IOException; + Buffer getChunk(int length) throws IOException; /** * Returns the contents of the file item as a String, using the default @@ -217,27 +217,19 @@ public interface HttpData extends InterfaceHttpData, ByteBufHolder { */ File getFile() throws IOException; - @Override - HttpData copy(); - - @Override - HttpData duplicate(); - - @Override - HttpData retainedDuplicate(); - - @Override - HttpData replace(ByteBuf content); - - @Override - HttpData retain(); + /** + * Return the data which is held by this {@link HttpData}. + */ + Buffer content(); - @Override - HttpData retain(int increment); + /** + * Creates a deep copy of this {@link HttpData}. + */ + HttpData copy(); - @Override - HttpData touch(); + /** + * Returns a new {@link HttpData} which contains the specified {@code content}. + */ + HttpData replace(Buffer content); - @Override - HttpData touch(Object hint); } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpDataFactory.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpDataFactory.java index f084d6a..457db6a 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpDataFactory.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpDataFactory.java @@ -15,7 +15,7 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.HttpRequest; import java.nio.charset.Charset; diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostBodyUtil.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostBodyUtil.java index 7e65d6d..0f534e5 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostBodyUtil.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostBodyUtil.java @@ -15,8 +15,10 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpConstants; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.ByteCursor; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.util.ByteProcessor; /** * Shared Static object between HttpMessageDecoder, HttpPostRequestDecoder and HttpPostRequestEncoder @@ -35,6 +37,11 @@ final class HttpPostBodyUtil { */ public static final String DEFAULT_TEXT_CONTENT_TYPE = "text/plain"; + /** + * Processor used to lookup Line Feed chars. + */ + private final static ByteProcessor.IndexOfProcessor LF_PROCESSOR = new ByteProcessor.IndexOfProcessor(HttpConstants.LF); + /** * Allowed mechanism for multipart * mechanism := "7bit" @@ -76,53 +83,6 @@ public String toString() { private HttpPostBodyUtil() { } - /** - * This class intends to decrease the CPU in seeking ahead some bytes in - * HttpPostRequestDecoder - */ - static class SeekAheadOptimize { - byte[] bytes; - int readerIndex; - int pos; - int origPos; - int limit; - ByteBuf buffer; - - /** - * @param buffer buffer with a backing byte array - */ - SeekAheadOptimize(ByteBuf buffer) { - if (!buffer.hasArray()) { - throw new IllegalArgumentException("buffer hasn't backing byte array"); - } - this.buffer = buffer; - bytes = buffer.array(); - readerIndex = buffer.readerIndex(); - origPos = pos = buffer.arrayOffset() + readerIndex; - limit = buffer.arrayOffset() + buffer.writerIndex(); - } - - /** - * - * @param minus this value will be used as (currentPos - minus) to set - * the current readerIndex in the buffer. - */ - void setReadPosition(int minus) { - pos -= minus; - readerIndex = getReadPosition(pos); - buffer.readerIndex(readerIndex); - } - - /** - * - * @param index raw index of the array (pos in general) - * @return the value equivalent of raw index to be used in readerIndex(value) - */ - int getReadPosition(int index) { - return index - origPos + readerIndex; - } - } - /** * Find the first non whitespace * @return the rank of the first non whitespace @@ -159,9 +119,11 @@ static int findEndOfString(String sb) { * @return a relative position from index > 0 if LF or CRLF is found * or < 0 if not found */ - static int findLineBreak(ByteBuf buffer, int index) { - int toRead = buffer.readableBytes() - (index - buffer.readerIndex()); - int posFirstChar = buffer.bytesBefore(index, toRead, HttpConstants.LF); + static int findLineBreak(Buffer buffer, int index) { + int toRead = buffer.readableBytes() - (index - buffer.readerOffset()); + ByteCursor cursor = buffer.openCursor(index, toRead); + int posFirstChar = cursor.process(LF_PROCESSOR); + if (posFirstChar == -1) { // No LF, so neither CRLF return -1; @@ -180,7 +142,9 @@ static int findLineBreak(ByteBuf buffer, int index) { * @return a relative position from index > 0 if LF or CRLF is found * or < 0 if not found */ - static int findLastLineBreak(ByteBuf buffer, int index) { + static int findLastLineBreak(Buffer buffer, int index) { + // TODO, see if we can allocate one single Cursor, and pass it as arguments to the + // findLineBreak method int candidate = findLineBreak(buffer, index); int findCRLF = 0; if (candidate >= 0) { @@ -217,16 +181,19 @@ static int findLastLineBreak(ByteBuf buffer, int index) { * @throws IndexOutOfBoundsException * if {@code offset + delimiter.length} is greater than {@code buffer.capacity} */ - static int findDelimiter(ByteBuf buffer, int index, byte[] delimiter, boolean precededByLineBreak) { + static int findDelimiter(Buffer buffer, int index, byte[] delimiter, boolean precededByLineBreak) { final int delimiterLength = delimiter.length; - final int readerIndex = buffer.readerIndex(); - final int writerIndex = buffer.writerIndex(); + final int readerIndex = buffer.readerOffset(); + final int writerIndex = buffer.writerOffset(); int toRead = writerIndex - index; int newOffset = index; boolean delimiterNotFound = true; + // TODO refactor the following loop in order to avoid loops and instead + // rely on Cursors more appropriately while (delimiterNotFound && delimiterLength <= toRead) { // Find first position: delimiter - int posDelimiter = buffer.bytesBefore(newOffset, toRead, delimiter[0]); + ByteCursor cursor = buffer.openCursor(newOffset, toRead); + int posDelimiter = cursor.process(value -> value != delimiter[0]); if (posDelimiter < 0) { return -1; } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java index da2732b..1a8fdf2 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java @@ -15,24 +15,26 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.contrib.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize; import io.netty.contrib.handler.codec.http.multipart.HttpPostBodyUtil.TransferEncodingMechanism; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder.MultiPartStatus; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.InternalThreadLocalMap; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; +import io.netty5.util.CharsetUtil; +import io.netty5.util.internal.PlatformDependent; +import io.netty5.util.internal.StringUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.BufferAllocator; +import io.netty5.buffer.api.ByteCursor; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.handler.codec.http.HttpContent; +import io.netty5.handler.codec.http.HttpHeaderNames; +import io.netty5.handler.codec.http.HttpHeaderValues; +import io.netty5.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.LastHttpContent; +import io.netty5.handler.codec.http.QueryStringDecoder; +import io.netty5.util.ByteProcessor; import java.io.IOException; import java.nio.charset.Charset; @@ -43,7 +45,8 @@ import java.util.Map; import java.util.TreeMap; -import static io.netty.util.internal.ObjectUtil.*; +import static io.netty5.util.internal.ObjectUtil.checkNotNullWithIAE; +import static io.netty5.util.internal.ObjectUtil.checkPositiveOrZero; /** * This decoder will decode Body and can handle POST BODY. @@ -87,7 +90,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest /** * The current channelBuffer */ - private ByteBuf undecodedChunk; + private Buffer undecodedChunk; /** * Body HttpDatas current position @@ -129,6 +132,8 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest private int discardThreshold = HttpPostRequestDecoder.DEFAULT_DISCARD_THRESHOLD; + private final static ByteProcessor CTRLSPACE_PROCESSOR = value -> Character.isISOControl(value) || Character.isWhitespace(value); + /** * * @param request @@ -174,9 +179,9 @@ public HttpPostMultipartRequestDecoder(HttpDataFactory factory, HttpRequest requ * errors */ public HttpPostMultipartRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) { - this.request = checkNotNull(request, "request"); - this.charset = checkNotNull(charset, "charset"); - this.factory = checkNotNull(factory, "factory"); + this.request = checkNotNullWithIAE(request, "request"); + this.charset = checkNotNullWithIAE(charset, "charset"); + this.factory = checkNotNullWithIAE(factory, "factory"); // Fill default values String contentTypeValue = this.request.headers().get(HttpHeaderNames.CONTENT_TYPE); @@ -333,30 +338,22 @@ public HttpPostMultipartRequestDecoder offer(HttpContent content) { isLastChunk = true; } - ByteBuf buf = content.content(); + Buffer buf = content.payload(); if (undecodedChunk == null) { + BufferAllocator alloc = buf.isDirect() ? DefaultBufferAllocators.offHeapAllocator() : DefaultBufferAllocators.onHeapAllocator(); undecodedChunk = // Since the Handler will release the incoming later on, we need to copy it // // We are explicit allocate a buffer and NOT calling copy() as otherwise it may set a maxCapacity // which is not really usable for us as we may exceed it once we add more bytes. - buf.alloc().buffer(buf.readableBytes()).writeBytes(buf); + alloc.allocate(buf.readableBytes()).writeBytes(buf); } else { undecodedChunk.writeBytes(buf); } parseBody(); - if (undecodedChunk != null && undecodedChunk.writerIndex() > discardThreshold) { - if (undecodedChunk.refCnt() == 1) { - // It's safe to call discardBytes() as we are the only owner of the buffer. - undecodedChunk.discardReadBytes(); - } else { - // There seems to be multiple references of the buffer. Let's copy the data and release the buffer to - // ensure we can give back memory to the system. - ByteBuf buffer = undecodedChunk.alloc().buffer(undecodedChunk.readableBytes()); - buffer.writeBytes(undecodedChunk); - undecodedChunk.release(); - undecodedChunk = buffer; - } + if (undecodedChunk != null && undecodedChunk.writerOffset() > discardThreshold) { + // It's safe to call compact() as we are the only owner of the buffer. + undecodedChunk.compact(); } return this; } @@ -389,7 +386,7 @@ public boolean hasNext() { * is called, there is no more available InterfaceHttpData. A subsequent * call to offer(httpChunk) could enable more data. * - * Be sure to call {@link InterfaceHttpData#release()} after you are done + * Be sure to call {@link InterfaceHttpData#close()} after you are done * with processing to make sure to not leak any resources * * @return the next available InterfaceHttpData or null if none @@ -598,33 +595,19 @@ private InterfaceHttpData decodeMultipart(MultiPartStatus state) { * * @throws NotEnoughDataDecoderException */ - private static void skipControlCharacters(ByteBuf undecodedChunk) { - if (!undecodedChunk.hasArray()) { - try { - skipControlCharactersStandard(undecodedChunk); - } catch (IndexOutOfBoundsException e1) { - throw new NotEnoughDataDecoderException(e1); - } - return; - } - SeekAheadOptimize sao = new SeekAheadOptimize(undecodedChunk); - while (sao.pos < sao.limit) { - char c = (char) (sao.bytes[sao.pos++] & 0xFF); - if (!Character.isISOControl(c) && !Character.isWhitespace(c)) { - sao.setReadPosition(1); - return; - } + private static void skipControlCharacters(Buffer undecodedChunk) { + try { + skipControlCharactersStandard(undecodedChunk); + } catch (IndexOutOfBoundsException e1) { + throw new NotEnoughDataDecoderException(e1); } - throw new NotEnoughDataDecoderException("Access out of bounds"); } - private static void skipControlCharactersStandard(ByteBuf undecodedChunk) { - for (;;) { - char c = (char) undecodedChunk.readUnsignedByte(); - if (!Character.isISOControl(c) && !Character.isWhitespace(c)) { - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); - break; - } + private static void skipControlCharactersStandard(Buffer undecodedChunk) { + ByteCursor cursor = undecodedChunk.openCursor(); + int processed = cursor.process(CTRLSPACE_PROCESSOR); + if (processed > 0) { + undecodedChunk.readerOffset(undecodedChunk.readerOffset() + processed); } } @@ -643,11 +626,11 @@ private static void skipControlCharactersStandard(ByteBuf undecodedChunk) { private InterfaceHttpData findMultipartDelimiter(String delimiter, MultiPartStatus dispositionStatus, MultiPartStatus closeDelimiterStatus) { // --AaB03x or --AaB03x-- - int readerIndex = undecodedChunk.readerIndex(); + int readerIndex = undecodedChunk.readerOffset(); try { skipControlCharacters(undecodedChunk); } catch (NotEnoughDataDecoderException ignored) { - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); return null; } skipOneLine(); @@ -655,7 +638,7 @@ private InterfaceHttpData findMultipartDelimiter(String delimiter, MultiPartStat try { newline = readDelimiterOptimized(undecodedChunk, delimiter, charset); } catch (NotEnoughDataDecoderException ignored) { - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); return null; } if (newline.equals(delimiter)) { @@ -673,7 +656,7 @@ private InterfaceHttpData findMultipartDelimiter(String delimiter, MultiPartStat } return null; } - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); throw new ErrorDataDecoderException("No Multipart delimiter found"); } @@ -684,7 +667,7 @@ private InterfaceHttpData findMultipartDelimiter(String delimiter, MultiPartStat * @throws ErrorDataDecoderException */ private InterfaceHttpData findMultipartDisposition() { - int readerIndex = undecodedChunk.readerIndex(); + int readerIndex = undecodedChunk.readerOffset(); if (currentStatus == MultiPartStatus.DISPOSITION) { currentFieldAttributes = new TreeMap(CaseIgnoringComparator.INSTANCE); } @@ -695,7 +678,7 @@ private InterfaceHttpData findMultipartDisposition() { skipControlCharacters(undecodedChunk); newline = readLineOptimized(undecodedChunk, charset); } catch (NotEnoughDataDecoderException ignored) { - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); return null; } String[] contents = splitMultipartHeader(newline); @@ -968,16 +951,18 @@ public void destroy() { cleanFiles(); // Clean Memory based data for (InterfaceHttpData httpData : bodyListHttpData) { - // Might have been already released by the user - if (httpData.refCnt() > 0) { - httpData.release(); + // Might have been already closed by the user + if (httpData.isAccessible()) { + httpData.close(); } } destroyed = true; - if (undecodedChunk != null && undecodedChunk.refCnt() > 0) { - undecodedChunk.release(); + if (undecodedChunk != null) { + if (undecodedChunk.isAccessible()) { + undecodedChunk.close(); + } undecodedChunk = null; } } @@ -1022,34 +1007,28 @@ private void cleanMixedAttributes() { * Need more chunks and reset the {@code readerIndex} to the previous * value */ - private static String readLineOptimized(ByteBuf undecodedChunk, Charset charset) { - int readerIndex = undecodedChunk.readerIndex(); - ByteBuf line = null; + private static String readLineOptimized(Buffer undecodedChunk, Charset charset) { + int readerIndex = undecodedChunk.readerOffset(); try { - if (undecodedChunk.isReadable()) { - int posLfOrCrLf = HttpPostBodyUtil.findLineBreak(undecodedChunk, undecodedChunk.readerIndex()); + if (undecodedChunk.readableBytes() > 0) { + int posLfOrCrLf = HttpPostBodyUtil.findLineBreak(undecodedChunk, undecodedChunk.readerOffset()); if (posLfOrCrLf <= 0) { throw new NotEnoughDataDecoderException(); } - try { - line = undecodedChunk.alloc().heapBuffer(posLfOrCrLf); - line.writeBytes(undecodedChunk, posLfOrCrLf); - byte nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.CR) { - // force read next byte since LF is the following one - undecodedChunk.readByte(); - } - return line.toString(charset); - } finally { - line.release(); + CharSequence lineCharSeq = undecodedChunk.readCharSequence(posLfOrCrLf, charset); + byte nextByte = undecodedChunk.readByte(); + if (nextByte == HttpConstants.CR) { + // force read next byte since LF is the following one + undecodedChunk.readByte(); } + return lineCharSeq.toString(); } } catch (IndexOutOfBoundsException e) { - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); throw new NotEnoughDataDecoderException(e); } - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); throw new NotEnoughDataDecoderException(); } @@ -1068,21 +1047,21 @@ private static String readLineOptimized(ByteBuf undecodedChunk, Charset charset) * Need more chunks and reset the {@code readerIndex} to the previous * value */ - private static String readDelimiterOptimized(ByteBuf undecodedChunk, String delimiter, Charset charset) { - final int readerIndex = undecodedChunk.readerIndex(); + private static String readDelimiterOptimized(Buffer undecodedChunk, String delimiter, Charset charset) { + final int readerIndex = undecodedChunk.readerOffset(); final byte[] bdelimiter = delimiter.getBytes(charset); final int delimiterLength = bdelimiter.length; try { int delimiterPos = HttpPostBodyUtil.findDelimiter(undecodedChunk, readerIndex, bdelimiter, false); if (delimiterPos < 0) { // delimiter not found so break here ! - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); throw new NotEnoughDataDecoderException(); } StringBuilder sb = new StringBuilder(delimiter); - undecodedChunk.readerIndex(readerIndex + delimiterPos + delimiterLength); + undecodedChunk.readerOffset(readerIndex + delimiterPos + delimiterLength); // Now check if either opening delimiter or closing delimiter - if (undecodedChunk.isReadable()) { + if (undecodedChunk.readableBytes() > 0) { byte nextByte = undecodedChunk.readByte(); // first check for opening delimiter if (nextByte == HttpConstants.CR) { @@ -1092,7 +1071,7 @@ private static String readDelimiterOptimized(ByteBuf undecodedChunk, String deli } else { // error since CR must be followed by LF // delimiter not found so break here ! - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); throw new NotEnoughDataDecoderException(); } } else if (nextByte == HttpConstants.LF) { @@ -1104,7 +1083,7 @@ private static String readDelimiterOptimized(ByteBuf undecodedChunk, String deli if (nextByte == '-') { sb.append('-'); // now try to find if CRLF or LF there - if (undecodedChunk.isReadable()) { + if (undecodedChunk.readableBytes() > 0) { nextByte = undecodedChunk.readByte(); if (nextByte == HttpConstants.CR) { nextByte = undecodedChunk.readByte(); @@ -1113,7 +1092,7 @@ private static String readDelimiterOptimized(ByteBuf undecodedChunk, String deli } else { // error CR without LF // delimiter not found so break here ! - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); throw new NotEnoughDataDecoderException(); } } else if (nextByte == HttpConstants.LF) { @@ -1122,7 +1101,7 @@ private static String readDelimiterOptimized(ByteBuf undecodedChunk, String deli // No CRLF but ok however (Adobe Flash uploader) // minus 1 since we read one char ahead but // should not - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); + undecodedChunk.readerOffset(undecodedChunk.readerOffset() - 1); return sb.toString(); } } @@ -1137,10 +1116,10 @@ private static String readDelimiterOptimized(ByteBuf undecodedChunk, String deli } } } catch (IndexOutOfBoundsException e) { - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); throw new NotEnoughDataDecoderException(e); } - undecodedChunk.readerIndex(readerIndex); + undecodedChunk.readerOffset(readerIndex); throw new NotEnoughDataDecoderException(); } @@ -1153,20 +1132,20 @@ private static String readDelimiterOptimized(ByteBuf undecodedChunk, String deli * @param buffer the buffer to rewrite from current readerIndex * @param lengthToSkip the size to skip from readerIndex */ - private static void rewriteCurrentBuffer(ByteBuf buffer, int lengthToSkip) { + private static void rewriteCurrentBuffer(Buffer buffer, int lengthToSkip) { if (lengthToSkip == 0) { return; } - final int readerIndex = buffer.readerIndex(); + final int readerIndex = buffer.readerOffset(); final int readableBytes = buffer.readableBytes(); if (readableBytes == lengthToSkip) { - buffer.readerIndex(readerIndex); - buffer.writerIndex(readerIndex); + buffer.readerOffset(readerIndex); + buffer.writerOffset(readerIndex); return; } - buffer.setBytes(readerIndex, buffer, readerIndex + lengthToSkip, readableBytes - lengthToSkip); - buffer.readerIndex(readerIndex); - buffer.writerIndex(readerIndex + readableBytes - lengthToSkip); + buffer.copyInto(readerIndex + lengthToSkip, buffer, readerIndex, readableBytes - lengthToSkip); + buffer.readerOffset(readerIndex); + buffer.writerOffset(readerIndex + readableBytes - lengthToSkip); } /** @@ -1175,11 +1154,11 @@ private static void rewriteCurrentBuffer(ByteBuf buffer, int lengthToSkip) { * @return {@code true} if the last chunk is loaded (boundary delimiter found), {@code false} if need more chunks * @throws ErrorDataDecoderException */ - private static boolean loadDataMultipartOptimized(ByteBuf undecodedChunk, String delimiter, HttpData httpData) { - if (!undecodedChunk.isReadable()) { + private static boolean loadDataMultipartOptimized(Buffer undecodedChunk, String delimiter, HttpData httpData) { + if (undecodedChunk.readableBytes() == 0) { return false; } - final int startReaderIndex = undecodedChunk.readerIndex(); + final int startReaderIndex = undecodedChunk.readerOffset(); final byte[] bdelimiter = delimiter.getBytes(httpData.getCharset()); int posDelimiter = HttpPostBodyUtil.findDelimiter(undecodedChunk, startReaderIndex, bdelimiter, true); if (posDelimiter < 0) { @@ -1205,14 +1184,14 @@ private static boolean loadDataMultipartOptimized(ByteBuf undecodedChunk, String } if (posDelimiter < 0) { // not found so this chunk can be fully added - ByteBuf content = undecodedChunk.copy(); + Buffer content = undecodedChunk.copy(); try { httpData.addContent(content, false); } catch (IOException e) { throw new ErrorDataDecoderException(e); } - undecodedChunk.readerIndex(startReaderIndex); - undecodedChunk.writerIndex(startReaderIndex); + undecodedChunk.readerOffset(startReaderIndex); + undecodedChunk.writerOffset(startReaderIndex); return false; } // posDelimiter is not from startReaderIndex but from startReaderIndex + lastPosition @@ -1222,7 +1201,7 @@ private static boolean loadDataMultipartOptimized(ByteBuf undecodedChunk, String return false; } // Not fully but still some bytes to provide: httpData is not yet finished since delimiter not found - ByteBuf content = undecodedChunk.copy(startReaderIndex, posDelimiter); + Buffer content = undecodedChunk.copy(startReaderIndex, posDelimiter); try { httpData.addContent(content, false); } catch (IOException e) { @@ -1232,7 +1211,7 @@ private static boolean loadDataMultipartOptimized(ByteBuf undecodedChunk, String return false; } // Delimiter found at posDelimiter, including LF or CRLF, so httpData has its last chunk - ByteBuf content = undecodedChunk.copy(startReaderIndex, posDelimiter); + Buffer content = undecodedChunk.copy(startReaderIndex, posDelimiter); try { httpData.addContent(content, true); } catch (IOException e) { @@ -1277,26 +1256,26 @@ private static String cleanString(String field) { * @return True if one empty line was skipped */ private boolean skipOneLine() { - if (!undecodedChunk.isReadable()) { + if (undecodedChunk.readableBytes() == 0) { return false; } byte nextByte = undecodedChunk.readByte(); if (nextByte == HttpConstants.CR) { - if (!undecodedChunk.isReadable()) { - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); + if (undecodedChunk.readableBytes() == 0) { + undecodedChunk.readerOffset(undecodedChunk.readerOffset() - 1); return false; } nextByte = undecodedChunk.readByte(); if (nextByte == HttpConstants.LF) { return true; } - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 2); + undecodedChunk.readerOffset(undecodedChunk.readerOffset() - 2); return false; } if (nextByte == HttpConstants.LF) { return true; } - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); + undecodedChunk.readerOffset(undecodedChunk.readerOffset() - 1); return false; } @@ -1351,7 +1330,7 @@ private static String[] splitMultipartHeader(String sb) { * @return an array of String where values that were separated by ';' or ',' */ private static String[] splitMultipartHeaderValues(String svalue) { - List values = InternalThreadLocalMap.get().arrayList(1); + List values = new ArrayList<>(1); boolean inQuote = false; boolean escapeNext = false; int start = 0; diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestDecoder.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestDecoder.java index 9cb8ca4..829fc05 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestDecoder.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestDecoder.java @@ -15,14 +15,14 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.StringUtil; +import io.netty5.util.internal.ObjectUtil; +import io.netty5.util.internal.StringUtil; +import io.netty5.handler.codec.DecoderException; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.handler.codec.http.HttpContent; +import io.netty5.handler.codec.http.HttpHeaderNames; +import io.netty5.handler.codec.http.HttpHeaderValues; +import io.netty5.handler.codec.http.HttpRequest; import java.nio.charset.Charset; import java.util.List; @@ -84,9 +84,9 @@ public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request) { * errors */ public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) { - ObjectUtil.checkNotNull(factory, "factory"); - ObjectUtil.checkNotNull(request, "request"); - ObjectUtil.checkNotNull(charset, "charset"); + ObjectUtil.checkNotNullWithIAE(factory, "factory"); + ObjectUtil.checkNotNullWithIAE(request, "request"); + ObjectUtil.checkNotNullWithIAE(charset, "charset"); // Fill default values if (isMultipart(request)) { diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestEncoder.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestEncoder.java index e029cb0..0340783 100755 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestEncoder.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestEncoder.java @@ -15,28 +15,29 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.EmptyHttpHeaders; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.stream.ChunkedInput; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.BufferAllocator; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.util.Send; +import io.netty5.handler.codec.DecoderResult; +import io.netty5.handler.codec.http.DefaultFullHttpRequest; +import io.netty5.handler.codec.http.DefaultHttpContent; +import io.netty5.handler.codec.http.EmptyHttpHeaders; +import io.netty5.handler.codec.http.EmptyLastHttpContent; +import io.netty5.handler.codec.http.FullHttpRequest; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.handler.codec.http.HttpContent; +import io.netty5.handler.codec.http.HttpHeaderNames; +import io.netty5.handler.codec.http.HttpHeaderValues; +import io.netty5.handler.codec.http.HttpHeaders; +import io.netty5.handler.codec.http.HttpMethod; +import io.netty5.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.HttpUtil; +import io.netty5.handler.codec.http.HttpVersion; +import io.netty5.handler.codec.http.LastHttpContent; +import io.netty5.handler.stream.ChunkedInput; +import io.netty5.util.internal.ObjectUtil; +import io.netty5.util.internal.StringUtil; import java.io.File; import java.io.IOException; @@ -44,13 +45,14 @@ import java.net.URLEncoder; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty5.util.internal.ObjectUtil.checkNotNullWithIAE; import static java.util.AbstractMap.SimpleImmutableEntry; /** @@ -210,9 +212,9 @@ public HttpPostRequestEncoder( HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset, EncoderMode encoderMode) throws ErrorDataEncoderException { - this.request = checkNotNull(request, "request"); - this.charset = checkNotNull(charset, "charset"); - this.factory = checkNotNull(factory, "factory"); + this.request = checkNotNullWithIAE(request, "request"); + this.charset = checkNotNullWithIAE(charset, "charset"); + this.factory = checkNotNullWithIAE(factory, "factory"); if (HttpMethod.TRACE.equals(request.method())) { throw new ErrorDataEncoderException("Cannot create a Encoder if request is a TRACE"); } @@ -290,7 +292,7 @@ private void initMixedMultipart() { */ private static String getNewMultipartDelimiter() { // construct a generated delimiter - return Long.toHexString(PlatformDependent.threadLocalRandom().nextLong()); + return Long.toHexString(ThreadLocalRandom.current().nextLong()); } /** @@ -311,7 +313,7 @@ public List getBodyListAttributes() { * if the encoding is in error or if the finalize were already done */ public void setBodyHttpDatas(List datas) throws ErrorDataEncoderException { - ObjectUtil.checkNotNull(datas, "datas"); + ObjectUtil.checkNotNullWithIAE(datas, "datas"); globalBodySize = 0; bodyListDatas.clear(); currentFileUpload = null; @@ -336,7 +338,7 @@ public void setBodyHttpDatas(List datas) throws ErrorDataEnco */ public void addBodyAttribute(String name, String value) throws ErrorDataEncoderException { String svalue = value != null? value : StringUtil.EMPTY_STRING; - Attribute data = factory.createAttribute(request, checkNotNull(name, "name"), svalue); + Attribute data = factory.createAttribute(request, checkNotNullWithIAE(name, "name"), svalue); addBodyHttpData(data); } @@ -382,8 +384,8 @@ public void addBodyFileUpload(String name, File file, String contentType, boolea */ public void addBodyFileUpload(String name, String filename, File file, String contentType, boolean isText) throws ErrorDataEncoderException { - checkNotNull(name, "name"); - checkNotNull(file, "file"); + checkNotNullWithIAE(name, "name"); + checkNotNullWithIAE(file, "file"); if (filename == null) { filename = StringUtil.EMPTY_STRING; } @@ -447,7 +449,7 @@ public void addBodyHttpData(InterfaceHttpData data) throws ErrorDataEncoderExcep if (headerFinalized) { throw new ErrorDataEncoderException("Cannot add value once finalized"); } - bodyListDatas.add(checkNotNull(data, "data")); + bodyListDatas.add(checkNotNullWithIAE(data, "data")); if (!isMultipart) { if (data instanceof Attribute) { Attribute attribute = (Attribute) data; @@ -806,10 +808,12 @@ public HttpRequest finalizeRequest() throws ErrorDataEncoderException { HttpContent chunk = nextChunk(); if (request instanceof FullHttpRequest) { FullHttpRequest fullRequest = (FullHttpRequest) request; - ByteBuf chunkContent = chunk.content(); - if (fullRequest.content() != chunkContent) { - fullRequest.content().clear().writeBytes(chunkContent); - chunkContent.release(); + Buffer chunkContent = chunk.payload(); + if (fullRequest.payload() != chunkContent) { + fullRequest.payload().resetOffsets().writeBytes(chunkContent); + if (chunkContent.isAccessible()) { + chunkContent.close(); + } } return fullRequest; } else { @@ -854,7 +858,7 @@ private String encodeAttribute(String s, Charset charset) throws ErrorDataEncode /** * The ByteBuf currently used by the encoder */ - private ByteBuf currentBuffer; + private Buffer currentBuffer; /** * The current InterfaceHttpData to encode (used if more chunks are available) */ @@ -868,13 +872,13 @@ private String encodeAttribute(String s, Charset charset) throws ErrorDataEncode * * @return the next ByteBuf to send as an HttpChunk and modifying currentBuffer accordingly */ - private ByteBuf fillByteBuf() { + private Buffer fillByteBuf() { int length = currentBuffer.readableBytes(); if (length > HttpPostBodyUtil.chunkSize) { - return currentBuffer.readRetainedSlice(HttpPostBodyUtil.chunkSize); + return currentBuffer.readSplit(HttpPostBodyUtil.chunkSize); } else { // to continue - ByteBuf slice = currentBuffer; + Buffer slice = currentBuffer; currentBuffer = null; return slice; } @@ -894,9 +898,9 @@ private HttpContent encodeNextChunkMultipart(int sizeleft) throws ErrorDataEncod if (currentData == null) { return null; } - ByteBuf buffer; + Buffer buffer; if (currentData instanceof InternalAttribute) { - buffer = ((InternalAttribute) currentData).toByteBuf(); + buffer = ((InternalAttribute) currentData).toBuffer(); currentData = null; } else { try { @@ -913,7 +917,7 @@ private HttpContent encodeNextChunkMultipart(int sizeleft) throws ErrorDataEncod if (currentBuffer == null) { currentBuffer = buffer; } else { - currentBuffer = wrappedBuffer(currentBuffer, buffer); + currentBuffer = DefaultBufferAllocators.onHeapAllocator().compose(Arrays.asList(currentBuffer.send(), buffer.send())); } if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) { currentData = null; @@ -938,17 +942,18 @@ private HttpContent encodeNextChunkUrlEncoded(int sizeleft) throws ErrorDataEnco return null; } int size = sizeleft; - ByteBuf buffer; + Buffer buffer; // Set name= if (isKey) { String key = currentData.getName(); - buffer = wrappedBuffer(key.getBytes(charset)); + buffer = Helpers.copiedBuffer(key, charset); isKey = false; + Buffer equal = Helpers.copiedBuffer("=", charset); if (currentBuffer == null) { - currentBuffer = wrappedBuffer(buffer, wrappedBuffer("=".getBytes(charset))); + currentBuffer = DefaultBufferAllocators.onHeapAllocator().compose(Arrays.asList(buffer.send(), equal.send())); } else { - currentBuffer = wrappedBuffer(currentBuffer, buffer, wrappedBuffer("=".getBytes(charset))); + currentBuffer = DefaultBufferAllocators.onHeapAllocator().compose(Arrays.asList(currentBuffer.send(), buffer.send(), equal.send())); } // continue size -= buffer.readableBytes() + 1; @@ -966,10 +971,10 @@ private HttpContent encodeNextChunkUrlEncoded(int sizeleft) throws ErrorDataEnco } // Figure out delimiter - ByteBuf delimiter = null; + Buffer delimiter = null; if (buffer.readableBytes() < size) { isKey = true; - delimiter = iterator.hasNext() ? wrappedBuffer("&".getBytes(charset)) : null; + delimiter = iterator.hasNext() ? Helpers.copiedBuffer("&", charset) : null; } // End for current InterfaceHttpData, need potentially more data @@ -983,7 +988,7 @@ private HttpContent encodeNextChunkUrlEncoded(int sizeleft) throws ErrorDataEnco } } else { if (delimiter != null) { - currentBuffer = wrappedBuffer(currentBuffer, delimiter); + currentBuffer = DefaultBufferAllocators.onHeapAllocator().compose(Arrays.asList(currentBuffer.send(), delimiter.send())); } } if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) { @@ -996,15 +1001,15 @@ private HttpContent encodeNextChunkUrlEncoded(int sizeleft) throws ErrorDataEnco // Put it all together: name=value& if (currentBuffer == null) { if (delimiter != null) { - currentBuffer = wrappedBuffer(buffer, delimiter); + currentBuffer = DefaultBufferAllocators.onHeapAllocator().compose(Arrays.asList(buffer.send(), delimiter.send())); } else { currentBuffer = buffer; } } else { if (delimiter != null) { - currentBuffer = wrappedBuffer(currentBuffer, buffer, delimiter); + currentBuffer = DefaultBufferAllocators.onHeapAllocator().compose(Arrays.asList(currentBuffer.send(), buffer.send(), delimiter.send())); } else { - currentBuffer = wrappedBuffer(currentBuffer, buffer); + currentBuffer = DefaultBufferAllocators.onHeapAllocator().compose(Arrays.asList(currentBuffer.send(), buffer.send())); } } @@ -1025,12 +1030,6 @@ public void close() throws Exception { // cleanFiles(); } - @Deprecated - @Override - public HttpContent readChunk(ChannelHandlerContext ctx) throws Exception { - return readChunk(ctx.alloc()); - } - /** * Returns the next available HttpChunk. The caller is responsible to test if this chunk is the last one (isLast()), * in order to stop calling this getMethod. @@ -1040,12 +1039,12 @@ public HttpContent readChunk(ChannelHandlerContext ctx) throws Exception { * if the encoding is in error */ @Override - public HttpContent readChunk(ByteBufAllocator allocator) throws Exception { + public HttpContent readChunk(BufferAllocator allocator) throws Exception { if (isLastChunkSent) { return null; } else { HttpContent nextChunk = nextChunk(); - globalProgress += nextChunk.content().readableBytes(); + globalProgress += nextChunk.payload().readableBytes(); return nextChunk; } } @@ -1061,13 +1060,13 @@ public HttpContent readChunk(ByteBufAllocator allocator) throws Exception { private HttpContent nextChunk() throws ErrorDataEncoderException { if (isLastChunk) { isLastChunkSent = true; - return LastHttpContent.EMPTY_LAST_CONTENT; + return new EmptyLastHttpContent(DefaultBufferAllocators.onHeapAllocator()); } // first test if previous buffer is not empty int size = calculateRemainingSize(); if (size <= 0) { // NextChunk from buffer - ByteBuf buffer = fillByteBuf(); + Buffer buffer = fillByteBuf(); return new DefaultHttpContent(buffer); } // size > 0 @@ -1121,10 +1120,10 @@ private HttpContent lastChunk() { if (currentBuffer == null) { isLastChunkSent = true; // LastChunk with no more data - return LastHttpContent.EMPTY_LAST_CONTENT; + return new EmptyLastHttpContent(DefaultBufferAllocators.onHeapAllocator()); } // NextChunk as last non empty from buffer - ByteBuf buffer = currentBuffer; + Buffer buffer = currentBuffer; currentBuffer = null; return new DefaultHttpContent(buffer); } @@ -1167,7 +1166,7 @@ public ErrorDataEncoderException(String msg, Throwable cause) { } private static class WrappedHttpRequest implements HttpRequest { - private final HttpRequest request; + protected final HttpRequest request; WrappedHttpRequest(HttpRequest request) { this.request = request; } @@ -1190,21 +1189,11 @@ public HttpRequest setUri(String uri) { return this; } - @Override - public HttpMethod getMethod() { - return request.method(); - } - @Override public HttpMethod method() { return request.method(); } - @Override - public String getUri() { - return request.uri(); - } - @Override public String uri() { return request.uri(); @@ -1230,12 +1219,6 @@ public DecoderResult decoderResult() { return request.decoderResult(); } - @Override - @Deprecated - public DecoderResult getDecoderResult() { - return request.getDecoderResult(); - } - @Override public void setDecoderResult(DecoderResult result) { request.setDecoderResult(result); @@ -1270,21 +1253,31 @@ public FullHttpRequest setUri(String uri) { @Override public FullHttpRequest copy() { - return replace(content().copy()); + return replace(payload().copy()); } @Override - public FullHttpRequest duplicate() { - return replace(content().duplicate()); + public HttpHeaders trailingHeaders() { + if (content instanceof LastHttpContent) { + return ((LastHttpContent) content).trailingHeaders(); + } else { + return EmptyHttpHeaders.INSTANCE; + } } @Override - public FullHttpRequest retainedDuplicate() { - return replace(content().retainedDuplicate()); + public Buffer payload() { + return content.payload(); } @Override - public FullHttpRequest replace(ByteBuf content) { + public Send send() { + return payload().send().map(FullHttpRequest.class, + payload -> new DefaultFullHttpRequest( + protocolVersion(), method(), uri(), payload, headers(), trailingHeaders())); + } + + public FullHttpRequest replace(Buffer content) { DefaultFullHttpRequest duplicate = new DefaultFullHttpRequest(protocolVersion(), method(), uri(), content); duplicate.headers().set(headers()); duplicate.trailingHeaders().set(trailingHeaders()); @@ -1292,56 +1285,13 @@ public FullHttpRequest replace(ByteBuf content) { } @Override - public FullHttpRequest retain(int increment) { - content.retain(increment); - return this; - } - - @Override - public FullHttpRequest retain() { - content.retain(); - return this; - } - - @Override - public FullHttpRequest touch() { - content.touch(); - return this; - } - - @Override - public FullHttpRequest touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public ByteBuf content() { - return content.content(); - } - - @Override - public HttpHeaders trailingHeaders() { - if (content instanceof LastHttpContent) { - return ((LastHttpContent) content).trailingHeaders(); - } else { - return EmptyHttpHeaders.INSTANCE; - } - } - - @Override - public int refCnt() { - return content.refCnt(); - } - - @Override - public boolean release() { - return content.release(); + public void close() { + content.close(); } @Override - public boolean release(int decrement) { - return content.release(decrement); + public boolean isAccessible() { + return content.isAccessible(); } } } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java index 3be78c8..9af1cd6 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java @@ -15,21 +15,21 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.contrib.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder.MultiPartStatus; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException; -import io.netty.util.ByteProcessor; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.ByteCursor; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.handler.codec.http.HttpContent; +import io.netty5.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.LastHttpContent; +import io.netty5.handler.codec.http.QueryStringDecoder; +import io.netty5.util.ByteProcessor; +import io.netty5.util.internal.PlatformDependent; +import io.netty5.util.internal.StringUtil; import java.io.IOException; import java.nio.charset.Charset; @@ -38,7 +38,8 @@ import java.util.Map; import java.util.TreeMap; -import static io.netty.util.internal.ObjectUtil.*; +import static io.netty5.util.internal.ObjectUtil.checkNotNullWithIAE; +import static io.netty5.util.internal.ObjectUtil.checkPositiveOrZero; /** * This decoder will decode Body and can handle POST BODY. @@ -82,7 +83,7 @@ public class HttpPostStandardRequestDecoder implements InterfaceHttpPostRequestD /** * The current channelBuffer */ - private ByteBuf undecodedChunk; + private Buffer undecodedChunk; /** * Body HttpDatas current position @@ -148,9 +149,9 @@ public HttpPostStandardRequestDecoder(HttpDataFactory factory, HttpRequest reque * errors */ public HttpPostStandardRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) { - this.request = checkNotNull(request, "request"); - this.charset = checkNotNull(charset, "charset"); - this.factory = checkNotNull(factory, "factory"); + this.request = checkNotNullWithIAE(request, "request"); + this.charset = checkNotNullWithIAE(charset, "charset"); + this.factory = checkNotNullWithIAE(factory, "factory"); try { if (request instanceof HttpContent) { // Offer automatically if the given request is as type of HttpContent @@ -285,30 +286,23 @@ public HttpPostStandardRequestDecoder offer(HttpContent content) { isLastChunk = true; } - ByteBuf buf = content.content(); + Buffer buf = content.payload(); if (undecodedChunk == null) { undecodedChunk = // Since the Handler will release the incoming later on, we need to copy it // // We are explicit allocate a buffer and NOT calling copy() as otherwise it may set a maxCapacity // which is not really usable for us as we may exceed it once we add more bytes. - buf.alloc().buffer(buf.readableBytes()).writeBytes(buf); + buf.isDirect() ? + DefaultBufferAllocators.offHeapAllocator().allocate(buf.readableBytes()).writeBytes(buf) : + DefaultBufferAllocators.onHeapAllocator().allocate(buf.readableBytes()).writeBytes(buf); } else { + undecodedChunk.ensureWritable(buf.readableBytes()); undecodedChunk.writeBytes(buf); } parseBody(); - if (undecodedChunk != null && undecodedChunk.writerIndex() > discardThreshold) { - if (undecodedChunk.refCnt() == 1) { - // It's safe to call discardBytes() as we are the only owner of the buffer. - undecodedChunk.discardReadBytes(); - } else { - // There seems to be multiple references of the buffer. Let's copy the data and release the buffer to - // ensure we can give back memory to the system. - ByteBuf buffer = undecodedChunk.alloc().buffer(undecodedChunk.readableBytes()); - buffer.writeBytes(undecodedChunk); - undecodedChunk.release(); - undecodedChunk = buffer; - } + if (undecodedChunk != null && undecodedChunk.writerOffset() > discardThreshold) { + undecodedChunk.compact(); } return this; } @@ -341,7 +335,7 @@ public boolean hasNext() { * is called, there is no more available InterfaceHttpData. A subsequent * call to offer(httpChunk) could enable more data. * - * Be sure to call {@link InterfaceHttpData#release()} after you are done + * Be sure to call {@link InterfaceHttpData#close()} after you are done * with processing to make sure to not leak any resources * * @return the next available InterfaceHttpData or null if none @@ -405,7 +399,7 @@ protected void addHttpData(InterfaceHttpData data) { * errors */ private void parseBodyAttributesStandard() { - int firstpos = undecodedChunk.readerIndex(); + int firstpos = undecodedChunk.readerOffset(); int currentpos = firstpos; int equalpos; int ampersandpos; @@ -414,7 +408,7 @@ private void parseBodyAttributesStandard() { } boolean contRead = true; try { - while (undecodedChunk.isReadable() && contRead) { + while (undecodedChunk.readableBytes() > 0 && contRead) { char read = (char) undecodedChunk.readUnsignedByte(); currentpos++; switch (currentStatus) { @@ -422,15 +416,15 @@ private void parseBodyAttributesStandard() { if (read == '=') { currentStatus = MultiPartStatus.FIELD; equalpos = currentpos - 1; - String key = decodeAttribute(undecodedChunk.toString(firstpos, equalpos - firstpos, charset), - charset); + String key = decodeAttribute(Helpers.toString(undecodedChunk, firstpos, equalpos - firstpos, charset), charset); currentAttribute = factory.createAttribute(request, key); firstpos = currentpos; } else if (read == '&') { // special empty FIELD currentStatus = MultiPartStatus.DISPOSITION; ampersandpos = currentpos - 1; String key = decodeAttribute( - undecodedChunk.toString(firstpos, ampersandpos - firstpos, charset), charset); + Helpers.toString(undecodedChunk, firstpos, ampersandpos - firstpos, charset), charset); + // Some weird request bodies start with an '&' character, eg: &name=J&age=17. // In that case, key would be "", will get exception: // java.lang.IllegalArgumentException: Param 'name' must not be empty; @@ -440,6 +434,7 @@ private void parseBodyAttributesStandard() { currentAttribute.setValue(""); // empty addHttpData(currentAttribute); } + currentAttribute = null; firstpos = currentpos; contRead = true; @@ -449,17 +444,23 @@ private void parseBodyAttributesStandard() { if (read == '&') { currentStatus = MultiPartStatus.DISPOSITION; ampersandpos = currentpos - 1; - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); + undecodedChunk.readerOffset(firstpos); + setFinalBuffer(undecodedChunk.readSplit(ampersandpos - firstpos)); + undecodedChunk.skipReadableBytes(1); // skip ampersand + currentpos = 1; firstpos = currentpos; contRead = true; } else if (read == HttpConstants.CR) { - if (undecodedChunk.isReadable()) { + if (undecodedChunk.readableBytes() > 0) { read = (char) undecodedChunk.readUnsignedByte(); currentpos++; if (read == HttpConstants.LF) { currentStatus = MultiPartStatus.PREEPILOGUE; ampersandpos = currentpos - 2; - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); + undecodedChunk.readerOffset(firstpos); + setFinalBuffer(undecodedChunk.readSplit(ampersandpos - firstpos)); + undecodedChunk.skipReadableBytes(2); // skip CRLF + currentpos = 2; firstpos = currentpos; contRead = false; } else { @@ -472,7 +473,10 @@ private void parseBodyAttributesStandard() { } else if (read == HttpConstants.LF) { currentStatus = MultiPartStatus.PREEPILOGUE; ampersandpos = currentpos - 1; - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); + undecodedChunk.readerOffset(firstpos); + setFinalBuffer(undecodedChunk.readSplit(ampersandpos - firstpos)); + undecodedChunk.skipReadableBytes(1); // skip LF + currentpos = 1; firstpos = currentpos; contRead = false; } @@ -486,30 +490,34 @@ private void parseBodyAttributesStandard() { // special case ampersandpos = currentpos; if (ampersandpos > firstpos) { - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); + undecodedChunk.readerOffset(firstpos); + setFinalBuffer(undecodedChunk.readSplit(ampersandpos - firstpos)); + currentpos = 0; } else if (!currentAttribute.isCompleted()) { - setFinalBuffer(Unpooled.EMPTY_BUFFER); + setFinalBuffer(DefaultBufferAllocators.preferredAllocator().allocate(0)); } firstpos = currentpos; currentStatus = MultiPartStatus.EPILOGUE; } else if (contRead && currentAttribute != null && currentStatus == MultiPartStatus.FIELD) { // reset index except if to continue in case of FIELD getStatus - currentAttribute.addContent(undecodedChunk.retainedSlice(firstpos, currentpos - firstpos), + undecodedChunk.readerOffset(firstpos); + currentAttribute.addContent(undecodedChunk.readSplit(currentpos - firstpos), false); + currentpos = 0; firstpos = currentpos; } - undecodedChunk.readerIndex(firstpos); + undecodedChunk.readerOffset(firstpos); } catch (ErrorDataDecoderException e) { // error while decoding - undecodedChunk.readerIndex(firstpos); + undecodedChunk.readerOffset(firstpos); throw e; } catch (IOException e) { // error while decoding - undecodedChunk.readerIndex(firstpos); + undecodedChunk.readerOffset(firstpos); throw new ErrorDataDecoderException(e); } catch (IllegalArgumentException e) { // error while decoding - undecodedChunk.readerIndex(firstpos); + undecodedChunk.readerOffset(firstpos); throw new ErrorDataDecoderException(e); } } @@ -526,132 +534,12 @@ private void parseBodyAttributes() { if (undecodedChunk == null) { return; } - if (!undecodedChunk.hasArray()) { - parseBodyAttributesStandard(); - return; - } - SeekAheadOptimize sao = new SeekAheadOptimize(undecodedChunk); - int firstpos = undecodedChunk.readerIndex(); - int currentpos = firstpos; - int equalpos; - int ampersandpos; - if (currentStatus == MultiPartStatus.NOTSTARTED) { - currentStatus = MultiPartStatus.DISPOSITION; - } - boolean contRead = true; - try { - loop: while (sao.pos < sao.limit) { - char read = (char) (sao.bytes[sao.pos++] & 0xFF); - currentpos++; - switch (currentStatus) { - case DISPOSITION:// search '=' - if (read == '=') { - currentStatus = MultiPartStatus.FIELD; - equalpos = currentpos - 1; - String key = decodeAttribute(undecodedChunk.toString(firstpos, equalpos - firstpos, charset), - charset); - currentAttribute = factory.createAttribute(request, key); - firstpos = currentpos; - } else if (read == '&') { // special empty FIELD - currentStatus = MultiPartStatus.DISPOSITION; - ampersandpos = currentpos - 1; - String key = decodeAttribute( - undecodedChunk.toString(firstpos, ampersandpos - firstpos, charset), charset); - // Some weird request bodies start with an '&' char, eg: &name=J&age=17. - // In that case, key would be "", will get exception: - // java.lang.IllegalArgumentException: Param 'name' must not be empty; - // Just check and skip empty key. - if (!key.isEmpty()) { - currentAttribute = factory.createAttribute(request, key); - currentAttribute.setValue(""); // empty - addHttpData(currentAttribute); - } - currentAttribute = null; - firstpos = currentpos; - contRead = true; - } - break; - case FIELD:// search '&' or end of line - if (read == '&') { - currentStatus = MultiPartStatus.DISPOSITION; - ampersandpos = currentpos - 1; - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = true; - } else if (read == HttpConstants.CR) { - if (sao.pos < sao.limit) { - read = (char) (sao.bytes[sao.pos++] & 0xFF); - currentpos++; - if (read == HttpConstants.LF) { - currentStatus = MultiPartStatus.PREEPILOGUE; - ampersandpos = currentpos - 2; - sao.setReadPosition(0); - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = false; - break loop; - } else { - // Error - sao.setReadPosition(0); - throw new ErrorDataDecoderException("Bad end of line"); - } - } else { - if (sao.limit > 0) { - currentpos--; - } - } - } else if (read == HttpConstants.LF) { - currentStatus = MultiPartStatus.PREEPILOGUE; - ampersandpos = currentpos - 1; - sao.setReadPosition(0); - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = false; - break loop; - } - break; - default: - // just stop - sao.setReadPosition(0); - contRead = false; - break loop; - } - } - if (isLastChunk && currentAttribute != null) { - // special case - ampersandpos = currentpos; - if (ampersandpos > firstpos) { - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - } else if (!currentAttribute.isCompleted()) { - setFinalBuffer(Unpooled.EMPTY_BUFFER); - } - firstpos = currentpos; - currentStatus = MultiPartStatus.EPILOGUE; - } else if (contRead && currentAttribute != null && currentStatus == MultiPartStatus.FIELD) { - // reset index except if to continue in case of FIELD getStatus - currentAttribute.addContent(undecodedChunk.retainedSlice(firstpos, currentpos - firstpos), - false); - firstpos = currentpos; - } - undecodedChunk.readerIndex(firstpos); - } catch (ErrorDataDecoderException e) { - // error while decoding - undecodedChunk.readerIndex(firstpos); - throw e; - } catch (IOException e) { - // error while decoding - undecodedChunk.readerIndex(firstpos); - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - // error while decoding - undecodedChunk.readerIndex(firstpos); - throw new ErrorDataDecoderException(e); - } + parseBodyAttributesStandard(); } - private void setFinalBuffer(ByteBuf buffer) throws IOException { + private void setFinalBuffer(Buffer buffer) throws IOException { currentAttribute.addContent(buffer, true); - ByteBuf decodedBuf = decodeAttribute(currentAttribute.getByteBuf(), charset); + Buffer decodedBuf = decodeAttribute(currentAttribute.getBuffer(), charset); if (decodedBuf != null) { // override content only when ByteBuf needed decoding currentAttribute.setContent(decodedBuf); } @@ -672,21 +560,24 @@ private static String decodeAttribute(String s, Charset charset) { } } - private static ByteBuf decodeAttribute(ByteBuf b, Charset charset) { - int firstEscaped = b.forEachByte(new UrlEncodedDetector()); + private static Buffer decodeAttribute(Buffer b, Charset charset) { + ByteCursor cursor = b.openCursor(); + int firstEscaped = cursor.process(new UrlEncodedDetector()); if (firstEscaped == -1) { return null; // nothing to decode } - ByteBuf buf = b.alloc().buffer(b.readableBytes()); + cursor = b.openCursor(); + Buffer buf = b.isDirect() ? DefaultBufferAllocators.offHeapAllocator().allocate(b.readableBytes()) : + DefaultBufferAllocators.onHeapAllocator().allocate(b.readableBytes()); UrlDecoder urlDecode = new UrlDecoder(buf); - int idx = b.forEachByte(urlDecode); + int idx = cursor.process(urlDecode); if (urlDecode.nextEscapedIdx != 0) { // incomplete hex byte if (idx == -1) { idx = b.readableBytes() - 1; } idx -= urlDecode.nextEscapedIdx - 1; - buf.release(); + buf.close(); throw new ErrorDataDecoderException( String.format("Invalid hex byte at index '%d' in string: '%s'", idx, b.toString(charset))); } @@ -704,16 +595,18 @@ public void destroy() { cleanFiles(); // Clean Memory based data for (InterfaceHttpData httpData : bodyListHttpData) { - // Might have been already released by the user - if (httpData.refCnt() > 0) { - httpData.release(); + // Might have been already closed by the user + if (httpData.isAccessible()) { + httpData.close(); } } destroyed = true; - if (undecodedChunk != null && undecodedChunk.refCnt() > 0) { - undecodedChunk.release(); + if (undecodedChunk != null) { + if (undecodedChunk.isAccessible()) { + undecodedChunk.close(); + } undecodedChunk = null; } } @@ -740,18 +633,18 @@ public void removeHttpDataFromClean(InterfaceHttpData data) { private static final class UrlEncodedDetector implements ByteProcessor { @Override - public boolean process(byte value) throws Exception { + public boolean process(byte value) { return value != '%' && value != '+'; } } private static final class UrlDecoder implements ByteProcessor { - private final ByteBuf output; + private final Buffer output; private int nextEscapedIdx; private byte hiByte; - UrlDecoder(ByteBuf output) { + UrlDecoder(Buffer output) { this.output = output; } @@ -768,13 +661,13 @@ public boolean process(byte value) { ++nextEscapedIdx; return false; } - output.writeByte((hi << 4) + lo); + output.writeByte((byte) ((hi << 4) + lo)); nextEscapedIdx = 0; } } else if (value == '%') { nextEscapedIdx = 1; } else if (value == '+') { - output.writeByte(' '); + output.writeByte((byte) ' '); } else { output.writeByte(value); } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InterfaceHttpData.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InterfaceHttpData.java index 96b6230..cdf5759 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InterfaceHttpData.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InterfaceHttpData.java @@ -15,12 +15,12 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.util.ReferenceCounted; +import io.netty5.util.Resource; /** * Interface for all Objects that could be encoded/decoded using HttpPostRequestEncoder/Decoder */ -public interface InterfaceHttpData extends Comparable, ReferenceCounted { +public interface InterfaceHttpData extends Comparable, Resource { enum HttpDataType { Attribute, FileUpload, InternalAttribute } @@ -35,16 +35,4 @@ enum HttpDataType { * @return The HttpDataType */ HttpDataType getHttpDataType(); - - @Override - InterfaceHttpData retain(); - - @Override - InterfaceHttpData retain(int increment); - - @Override - InterfaceHttpData touch(); - - @Override - InterfaceHttpData touch(Object hint); } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InterfaceHttpPostRequestDecoder.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InterfaceHttpPostRequestDecoder.java index 9798672..15a3abd 100755 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InterfaceHttpPostRequestDecoder.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InterfaceHttpPostRequestDecoder.java @@ -15,7 +15,7 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.handler.codec.http.HttpContent; +import io.netty5.handler.codec.http.HttpContent; import java.util.List; @@ -111,7 +111,7 @@ public interface InterfaceHttpPostRequestDecoder { * is called, there is no more available InterfaceHttpData. A subsequent * call to offer(httpChunk) could enable more data. * - * Be sure to call {@link InterfaceHttpData#release()} after you are done + * Be sure to call {@link InterfaceHttpData#close()} after you are done * with processing to make sure to not leak any resources * * @return the next available InterfaceHttpData or null if none diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InternalAttribute.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InternalAttribute.java index c6bc056..67ef08a 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InternalAttribute.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/InternalAttribute.java @@ -15,26 +15,47 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.internal.ObjectUtil; +import io.netty5.util.internal.ObjectUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.buffer.api.Drop; +import io.netty5.buffer.api.Owned; +import io.netty5.buffer.api.internal.ResourceSupport; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** - * This Attribute is only for Encoder use to insert special command between object if needed + * This Attribute is only for Encoder and is used to insert special command between object if needed * (like Multipart Mixed mode) */ -final class InternalAttribute extends AbstractReferenceCounted implements InterfaceHttpData { - private final List value = new ArrayList(); +final class InternalAttribute extends ResourceSupport implements InterfaceHttpData { + private List value; private final Charset charset; private int size; + private final static Drop drop = new Drop<>() { + @Override + public void drop(InternalAttribute data) { + } + + @Override + public Drop fork() { + return this; + } + + @Override + public void attach(InternalAttribute mixedFileUpload) { + } + }; + InternalAttribute(Charset charset) { + super(drop); this.charset = charset; + this.value = new ArrayList<>(); } @Override @@ -43,26 +64,26 @@ public HttpDataType getHttpDataType() { } public void addValue(String value) { - ObjectUtil.checkNotNull(value, "value"); - ByteBuf buf = Unpooled.copiedBuffer(value, charset); + ObjectUtil.checkNotNullWithIAE(value, "value"); + Buffer buf = Helpers.copiedBuffer(value, charset); this.value.add(buf); size += buf.readableBytes(); } public void addValue(String value, int rank) { - ObjectUtil.checkNotNull(value, "value"); - ByteBuf buf = Unpooled.copiedBuffer(value, charset); + ObjectUtil.checkNotNullWithIAE(value, "value"); + Buffer buf = Helpers.copiedBuffer(value, charset); this.value.add(rank, buf); size += buf.readableBytes(); } public void setValue(String value, int rank) { - ObjectUtil.checkNotNull(value, "value"); - ByteBuf buf = Unpooled.copiedBuffer(value, charset); - ByteBuf old = this.value.set(rank, buf); + ObjectUtil.checkNotNullWithIAE(value, "value"); + Buffer buf = Helpers.copiedBuffer(value, charset); + Buffer old = this.value.set(rank, buf); if (old != null) { size -= old.readableBytes(); - old.release(); + old.close(); } size += buf.readableBytes(); } @@ -97,7 +118,7 @@ public int compareTo(InternalAttribute o) { @Override public String toString() { StringBuilder result = new StringBuilder(); - for (ByteBuf elt : value) { + for (Buffer elt : value) { result.append(elt.toString(charset)); } return result.toString(); @@ -107,8 +128,16 @@ public int size() { return size; } - public ByteBuf toByteBuf() { - return Unpooled.compositeBuffer().addComponents(value).writerIndex(size()).readerIndex(0); + /** + * Returns a buffer composed of all values added in this class. + *
The returned buffer must be closed by the caller.
+ * + * @return a fresh composite buffer containing all values added in this class. + * The buffer must be closed by the user. + */ + public Buffer toBuffer() { + return DefaultBufferAllocators.onHeapAllocator() + .compose(value.stream().map(Buffer::send).collect(Collectors.toList())); } @Override @@ -117,39 +146,20 @@ public String getName() { } @Override - protected void deallocate() { - // Do nothing - } - - @Override - public InterfaceHttpData retain() { - for (ByteBuf buf: value) { - buf.retain(); - } - return this; - } - - @Override - public InterfaceHttpData retain(int increment) { - for (ByteBuf buf: value) { - buf.retain(increment); - } - return this; - } - - @Override - public InterfaceHttpData touch() { - for (ByteBuf buf: value) { - buf.touch(); - } - return this; + protected RuntimeException createResourceClosedException() { + return new RuntimeException("Resource closed"); } @Override - public InterfaceHttpData touch(Object hint) { - for (ByteBuf buf: value) { - buf.touch(hint); - } - return this; + protected Owned prepareSend() { + return drop -> { + InternalAttribute copy = new InternalAttribute(charset); + copy.value = this.value; + copy.size = this.size; + this.value = Collections.emptyList(); // immutable list + this.size = 0; + return copy; + }; } } + diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MemoryAttribute.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MemoryAttribute.java index 70b5b88..2600611 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MemoryAttribute.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MemoryAttribute.java @@ -15,16 +15,18 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelException; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.util.internal.ObjectUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.buffer.api.Owned; +import io.netty5.buffer.api.internal.Statics; +import io.netty5.channel.ChannelException; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.util.Send; +import io.netty5.util.internal.ObjectUtil; import java.io.IOException; import java.nio.charset.Charset; -import static io.netty.buffer.Unpooled.*; - /** * Memory implementation of Attributes */ @@ -62,15 +64,16 @@ public HttpDataType getHttpDataType() { @Override public String getValue() { - return getByteBuf().toString(getCharset()); + return getBuffer().toString(getCharset()); } @Override public void setValue(String value) throws IOException { - ObjectUtil.checkNotNull(value, "value"); + checkAccessible(); + ObjectUtil.checkNotNullWithIAE(value, "value"); byte [] bytes = value.getBytes(getCharset()); checkSize(bytes.length); - ByteBuf buffer = wrappedBuffer(bytes); + Buffer buffer = DefaultBufferAllocators.preferredAllocator().copyOf(bytes); if (definedSize > 0) { definedSize = buffer.readableBytes(); } @@ -78,12 +81,13 @@ public void setValue(String value) throws IOException { } @Override - public void addContent(ByteBuf buffer, boolean last) throws IOException { + public void addContent(Buffer buffer, boolean last) throws IOException { + checkAccessible(buffer); int localsize = buffer.readableBytes(); try { checkSize(size + localsize); } catch (IOException e) { - buffer.release(); + buffer.close(); throw e; } if (definedSize > 0 && definedSize < size + localsize) { @@ -126,38 +130,13 @@ public String toString() { @Override public Attribute copy() { - final ByteBuf content = content(); + final Buffer content = content(); return replace(content != null ? content.copy() : null); } @Override - public Attribute duplicate() { - final ByteBuf content = content(); - return replace(content != null ? content.duplicate() : null); - } - - @Override - public Attribute retainedDuplicate() { - ByteBuf content = content(); - if (content != null) { - content = content.retainedDuplicate(); - boolean success = false; - try { - Attribute duplicate = replace(content); - success = true; - return duplicate; - } finally { - if (!success) { - content.release(); - } - } - } else { - return replace(null); - } - } - - @Override - public Attribute replace(ByteBuf content) { + public Attribute replace(Buffer content) { + checkAccessible(content); MemoryAttribute attr = new MemoryAttribute(getName()); attr.setCharset(getCharset()); if (content != null) { @@ -172,26 +151,22 @@ public Attribute replace(ByteBuf content) { } @Override - public Attribute retain() { - super.retain(); - return this; - } - - @Override - public Attribute retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public Attribute touch() { - super.touch(); - return this; + protected Owned prepareSend() { + Send send = getBuffer().send(); + return drop -> { + Buffer received = (Buffer) send.receive(); + MemoryAttribute attr = new MemoryAttribute(getName()); + attr.setCharset(getCharset()); + attr.setContentInternal(received, received.readableBytes()); + attr.setMaxSize(getMaxSize()); + attr.setCompleted(isCompleted()); + attr.definedSize = definedSize; + return attr; + }; } @Override - public Attribute touch(Object hint) { - super.touch(hint); - return this; + protected RuntimeException createResourceClosedException() { + return Statics.bufferIsClosed(getBuffer()); } } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MemoryFileUpload.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MemoryFileUpload.java index 4809a14..ce72ce1 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MemoryFileUpload.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MemoryFileUpload.java @@ -15,11 +15,13 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelException; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.util.internal.ObjectUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.Owned; +import io.netty5.util.Send; +import io.netty5.channel.ChannelException; +import io.netty5.handler.codec.http.HttpHeaderNames; +import io.netty5.handler.codec.http.HttpHeaderValues; +import io.netty5.util.internal.ObjectUtil; import java.io.IOException; import java.nio.charset.Charset; @@ -57,7 +59,7 @@ public String getFilename() { @Override public void setFilename(String filename) { - this.filename = ObjectUtil.checkNotNull(filename, "filename"); + this.filename = ObjectUtil.checkNotNullWithIAE(filename, "filename"); } @Override @@ -85,7 +87,7 @@ public int compareTo(FileUpload o) { @Override public void setContentType(String contentType) { - this.contentType = ObjectUtil.checkNotNull(contentType, "contentType"); + this.contentType = ObjectUtil.checkNotNullWithIAE(contentType, "contentType"); } @Override @@ -117,38 +119,13 @@ public String toString() { @Override public FileUpload copy() { - final ByteBuf content = content(); + final Buffer content = content(); return replace(content != null ? content.copy() : content); } @Override - public FileUpload duplicate() { - final ByteBuf content = content(); - return replace(content != null ? content.duplicate() : content); - } - - @Override - public FileUpload retainedDuplicate() { - ByteBuf content = content(); - if (content != null) { - content = content.retainedDuplicate(); - boolean success = false; - try { - FileUpload duplicate = replace(content); - success = true; - return duplicate; - } finally { - if (!success) { - content.release(); - } - } - } else { - return replace(null); - } - } - - @Override - public FileUpload replace(ByteBuf content) { + public FileUpload replace(Buffer content) { + checkAccessible(content); MemoryFileUpload upload = new MemoryFileUpload( getName(), getFilename(), getContentType(), getContentTransferEncoding(), getCharset(), size); if (content != null) { @@ -163,26 +140,19 @@ public FileUpload replace(ByteBuf content) { } @Override - public FileUpload retain() { - super.retain(); - return this; - } - - @Override - public FileUpload retain(int increment) { - super.retain(increment); - return this; - } + protected Owned prepareSend() { + Send send = getBuffer().send(); - @Override - public FileUpload touch() { - super.touch(); - return this; + return drop -> { + Buffer received = (Buffer) send.receive(); + MemoryFileUpload upload = new MemoryFileUpload( + getName(), getFilename(), getContentType(), getContentTransferEncoding(), getCharset(), size); + upload.setContentInternal(received, received.readableBytes()); + upload.setCompleted(isCompleted()); + upload.definedSize = definedLength(); + upload.setMaxSize(getMaxSize()); + return upload; + }; } - @Override - public FileUpload touch(Object hint) { - super.touch(hint); - return this; - } } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MixedAttribute.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MixedAttribute.java index 8ec0269..9b2f50a 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MixedAttribute.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MixedAttribute.java @@ -15,8 +15,10 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpConstants; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.Owned; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.util.Send; import java.io.IOException; import java.nio.charset.Charset; @@ -90,6 +92,10 @@ public MixedAttribute(String name, String value, long limitSize, Charset charset makeInitialAttributeFromValue(name, value, limitSize, charset, baseDir, deleteOnExit)); } + public MixedAttribute(String baseDir, boolean deleteOnExit, long limitSize, Attribute attribute) { + super(limitSize, baseDir, deleteOnExit, attribute); + } + @Override public String getValue() throws IOException { return wrapped.getValue(); @@ -108,50 +114,12 @@ Attribute makeDiskData() { } @Override - public Attribute copy() { - // for binary compatibility - return super.copy(); - } - - @Override - public Attribute duplicate() { - // for binary compatibility - return super.duplicate(); - } - - @Override - public Attribute replace(ByteBuf content) { - // for binary compatibility - return super.replace(content); - } - - @Override - public Attribute retain() { - // for binary compatibility - return super.retain(); - } - - @Override - public Attribute retain(int increment) { - // for binary compatibility - return super.retain(increment); - } - - @Override - public Attribute retainedDuplicate() { - // for binary compatibility - return super.retainedDuplicate(); - } - - @Override - public Attribute touch() { - // for binary compatibility - return super.touch(); - } - - @Override - public Attribute touch(Object hint) { - // for binary compatibility - return super.touch(hint); + protected Owned> prepareSend() { + Send send = wrapped.send(); + return drop -> { + Attribute receivedAttr = (Attribute) send.receive(); + MixedAttribute copy = new MixedAttribute(baseDir, deleteOnExit, limitSize, receivedAttr); + return copy; + }; } } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MixedFileUpload.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MixedFileUpload.java index f6c35a2..3004924 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MixedFileUpload.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/MixedFileUpload.java @@ -15,7 +15,8 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; +import io.netty5.buffer.api.Owned; +import io.netty5.util.Send; import java.nio.charset.Charset; @@ -38,7 +39,16 @@ public MixedFileUpload(String name, String filename, String contentType, size > limitSize ? new DiskFileUpload(name, filename, contentType, contentTransferEncoding, charset, size) : new MemoryFileUpload(name, filename, contentType, contentTransferEncoding, charset, size) - ); + ); + } + + private MixedFileUpload(long limitSize, String baseDir, boolean deleteOnExit, FileUpload fileUpload) { + super(limitSize, baseDir, deleteOnExit, fileUpload); + } + + @Override + public String getContentType() { + return wrapped.getContentType(); } @Override @@ -51,6 +61,11 @@ public String getFilename() { return wrapped.getFilename(); } + @Override + public void setContentType(String contentType) { + wrapped.setContentType(contentType); + } + @Override public void setContentTransferEncoding(String contentTransferEncoding) { wrapped.setContentTransferEncoding(contentTransferEncoding); @@ -61,16 +76,6 @@ public void setFilename(String filename) { wrapped.setFilename(filename); } - @Override - public void setContentType(String contentType) { - wrapped.setContentType(contentType); - } - - @Override - public String getContentType() { - return wrapped.getContentType(); - } - @Override FileUpload makeDiskData() { DiskFileUpload diskFileUpload = new DiskFileUpload( @@ -81,50 +86,11 @@ FileUpload makeDiskData() { } @Override - public FileUpload copy() { - // for binary compatibility - return super.copy(); - } - - @Override - public FileUpload duplicate() { - // for binary compatibility - return super.duplicate(); - } - - @Override - public FileUpload retainedDuplicate() { - // for binary compatibility - return super.retainedDuplicate(); - } - - @Override - public FileUpload replace(ByteBuf content) { - // for binary compatibility - return super.replace(content); - } - - @Override - public FileUpload touch() { - // for binary compatibility - return super.touch(); - } - - @Override - public FileUpload touch(Object hint) { - // for binary compatibility - return super.touch(hint); - } - - @Override - public FileUpload retain() { - // for binary compatibility - return super.retain(); - } - - @Override - public FileUpload retain(int increment) { - // for binary compatibility - return super.retain(increment); + protected Owned> prepareSend() { + Send send = wrapped.send(); + return drop -> { + FileUpload received = (FileUpload) send.receive(); + return new MixedFileUpload(limitSize, baseDir, deleteOnExit, received); + }; } } diff --git a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/package-info.java b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/package-info.java index 9a08ebe..30cf958 100644 --- a/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/package-info.java +++ b/codec-multipart/src/main/java/io/netty/contrib/handler/codec/http/multipart/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 The Netty Project + * Copyright 2022 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 diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/AbstractDiskHttpDataTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/AbstractDiskHttpDataTest.java index f07ba30..1ffee2e 100644 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/AbstractDiskHttpDataTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/AbstractDiskHttpDataTest.java @@ -15,9 +15,10 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.util.internal.PlatformDependent; +import io.netty5.util.internal.PlatformDependent; +import io.netty5.buffer.BufferUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.Owned; import org.junit.jupiter.api.Test; import java.io.File; @@ -25,8 +26,9 @@ import java.nio.charset.Charset; import java.util.Arrays; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; -import static io.netty.util.CharsetUtil.UTF_8; +import static io.netty5.util.CharsetUtil.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -37,13 +39,12 @@ public class AbstractDiskHttpDataTest { @Test public void testGetChunk() throws Exception { - TestHttpData test = new TestHttpData("test", UTF_8, 0); - try { + try(TestHttpData test = new TestHttpData("test", UTF_8, 0)) { File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null); tmpFile.deleteOnExit(); FileOutputStream fos = new FileOutputStream(tmpFile); byte[] bytes = new byte[4096]; - PlatformDependent.threadLocalRandom().nextBytes(bytes); + ThreadLocalRandom.current().nextBytes(bytes); try { fos.write(bytes); fos.flush(); @@ -51,16 +52,14 @@ public void testGetChunk() throws Exception { fos.close(); } test.setContent(tmpFile); - ByteBuf buf1 = test.getChunk(1024); - assertEquals(buf1.readerIndex(), 0); - assertEquals(buf1.writerIndex(), 1024); - ByteBuf buf2 = test.getChunk(1024); - assertEquals(buf2.readerIndex(), 0); - assertEquals(buf2.writerIndex(), 1024); - assertFalse(Arrays.equals(ByteBufUtil.getBytes(buf1), ByteBufUtil.getBytes(buf2)), + Buffer buf1 = test.getChunk(1024); + assertEquals(buf1.readerOffset(), 0); + assertEquals(buf1.writerOffset(), 1024); + Buffer buf2 = test.getChunk(1024); + assertEquals(buf2.readerOffset(), 0); + assertEquals(buf2.writerOffset(), 1024); + assertFalse(Arrays.equals(BufferUtil.getBytes(buf1), BufferUtil.getBytes(buf2)), "Arrays should not be equal"); - } finally { - test.delete(); } } @@ -96,33 +95,31 @@ protected boolean deleteOnExit() { } @Override - public HttpData copy() { - return null; - } - - @Override - public HttpData duplicate() { + public HttpDataType getHttpDataType() { return null; } @Override - public HttpData retainedDuplicate() { + public HttpData copy() { return null; } @Override - public HttpData replace(ByteBuf content) { + public HttpData replace(Buffer content) { return null; } @Override - public HttpDataType getHttpDataType() { - return null; + public int compareTo(InterfaceHttpData o) { + return 0; } @Override - public int compareTo(InterfaceHttpData o) { - return 0; + protected Owned prepareSend() { + return drop -> { + TestHttpData test = new TestHttpData(getName(), getCharset(), definedLength()); + return test; + }; } } } diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/AbstractMemoryHttpDataTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/AbstractMemoryHttpDataTest.java index 82d6235..9356e61 100644 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/AbstractMemoryHttpDataTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/AbstractMemoryHttpDataTest.java @@ -15,12 +15,11 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.PlatformDependent; - +import io.netty5.util.internal.PlatformDependent; +import io.netty5.buffer.BufferInputStream; +import io.netty5.buffer.BufferUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.Owned; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; @@ -29,28 +28,24 @@ import java.io.FileOutputStream; import java.nio.charset.Charset; import java.security.SecureRandom; -import java.util.Arrays; import java.util.Random; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; -import static io.netty.util.CharsetUtil.*; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static io.netty5.util.CharsetUtil.UTF_8; +import static org.junit.jupiter.api.Assertions.*; /** {@link AbstractMemoryHttpData} test cases. */ public class AbstractMemoryHttpDataTest { @Test public void testSetContentFromFile() throws Exception { - TestHttpData test = new TestHttpData("test", UTF_8, 0); - try { + try(TestHttpData test = new TestHttpData("test", UTF_8, 0)) { File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null); tmpFile.deleteOnExit(); FileOutputStream fos = new FileOutputStream(tmpFile); byte[] bytes = new byte[4096]; - PlatformDependent.threadLocalRandom().nextBytes(bytes); + ThreadLocalRandom.current().nextBytes(bytes); try { fos.write(bytes); fos.flush(); @@ -58,27 +53,23 @@ public void testSetContentFromFile() throws Exception { fos.close(); } test.setContent(tmpFile); - ByteBuf buf = test.getByteBuf(); - assertEquals(buf.readerIndex(), 0); - assertEquals(buf.writerIndex(), bytes.length); + Buffer buf = test.getBuffer(); + assertEquals(buf.readerOffset(), 0); + assertEquals(buf.writerOffset(), bytes.length); assertArrayEquals(bytes, test.get()); - assertArrayEquals(bytes, ByteBufUtil.getBytes(buf)); - } finally { - //release the ByteBuf - test.delete(); + assertArrayEquals(bytes, BufferUtil.getBytes(buf)); } } @Test public void testRenameTo() throws Exception { - TestHttpData test = new TestHttpData("test", UTF_8, 0); - try { + try(TestHttpData test = new TestHttpData("test", UTF_8, 0)) { File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null); tmpFile.deleteOnExit(); final int totalByteCount = 4096; byte[] bytes = new byte[totalByteCount]; - PlatformDependent.threadLocalRandom().nextBytes(bytes); - ByteBuf content = Unpooled.wrappedBuffer(bytes); + ThreadLocalRandom.current().nextBytes(bytes); + Buffer content = Helpers.copiedBuffer(bytes); test.setContent(content); boolean succ = test.renameTo(tmpFile); assertTrue(succ); @@ -100,9 +91,6 @@ public void testRenameTo() throws Exception { } finally { fis.close(); } - } finally { - //release the ByteBuf in AbstractMemoryHttpData - test.delete(); } } /** @@ -115,15 +103,15 @@ public void testSetContentFromStream() throws Exception { // definedSize=0 TestHttpData test = new TestHttpData("test", UTF_8, 0); String contentStr = "foo_test"; - ByteBuf buf = Unpooled.wrappedBuffer(contentStr.getBytes(UTF_8)); - buf.markReaderIndex(); - ByteBufInputStream is = new ByteBufInputStream(buf); + Buffer buf = Helpers.copiedBuffer(contentStr.getBytes(UTF_8)); + BufferInputStream is = new BufferInputStream(buf.send()); try { test.setContent(is); - assertFalse(buf.isReadable()); + assertFalse(buf.readableBytes() > 0); assertEquals(test.getString(UTF_8), contentStr); - buf.resetReaderIndex(); - assertTrue(ByteBufUtil.equals(buf, test.getByteBuf())); + buf = Helpers.copiedBuffer(contentStr.getBytes(UTF_8)); + Buffer buf2 = test.getBuffer(); + assertTrue(BufferUtil.equals(buf, buf.readerOffset(), buf2, buf2.readerOffset(), buf2.readableBytes())); } finally { is.close(); } @@ -143,11 +131,11 @@ public void testSetContentFromStream() throws Exception { data.setContent(new ByteArrayInputStream(bytes)); // Validate stored data. - ByteBuf buffer = data.getByteBuf(); + Buffer buffer = data.getBuffer(); - assertEquals(0, buffer.readerIndex()); - assertEquals(bytes.length, buffer.writerIndex()); - assertArrayEquals(bytes, Arrays.copyOf(buffer.array(), bytes.length)); + assertEquals(0, buffer.readerOffset()); + assertEquals(bytes.length, buffer.writerOffset()); + assertArrayEquals(bytes, BufferUtil.getBytes(buffer)); assertArrayEquals(bytes, data.get()); } } @@ -171,25 +159,20 @@ public InterfaceHttpData.HttpDataType getHttpDataType() { } @Override - public HttpData copy() { + protected Owned prepareSend() { throw reject(); } @Override - public HttpData duplicate() { + public HttpData copy() { throw reject(); } @Override - public HttpData retainedDuplicate() { + public HttpData replace(Buffer content) { throw reject(); } - @Override - public HttpData replace(ByteBuf content) { - return null; - } - @Override public int compareTo(InterfaceHttpData o) { throw reject(); diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DefaultHttpDataFactoryTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DefaultHttpDataFactoryTest.java index 5db9168..957a207 100644 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DefaultHttpDataFactoryTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DefaultHttpDataFactoryTest.java @@ -15,23 +15,19 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.DefaultHttpRequest; +import io.netty5.handler.codec.http.HttpRequest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static io.netty.handler.codec.http.HttpHeaderValues.IDENTITY; -import static io.netty.handler.codec.http.HttpMethod.POST; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import static io.netty.contrib.handler.codec.http.multipart.HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE; -import static io.netty.util.CharsetUtil.UTF_8; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static io.netty5.util.CharsetUtil.UTF_8; +import static io.netty5.handler.codec.http.HttpHeaderValues.IDENTITY; +import static io.netty5.handler.codec.http.HttpMethod.POST; +import static io.netty5.handler.codec.http.HttpVersion.HTTP_1_1; +import static org.junit.jupiter.api.Assertions.*; public class DefaultHttpDataFactoryTest { // req1 equals req2 @@ -85,33 +81,25 @@ public void cleanRequestHttpDataShouldIdentifiesRequestsByTheirIdentities() thro req2, "file2", "file2.txt", DEFAULT_TEXT_CONTENT_TYPE, IDENTITY.toString(), UTF_8, 123 ); - file1.setContent(Unpooled.copiedBuffer("file1 content", UTF_8)); - file2.setContent(Unpooled.copiedBuffer("file2 content", UTF_8)); + file1.setContent(Helpers.copiedBuffer("file1 content", UTF_8)); + file2.setContent(Helpers.copiedBuffer("file2 content", UTF_8)); // Assert that they are not deleted - assertNotNull(attribute1.getByteBuf()); - assertNotNull(attribute2.getByteBuf()); - assertNotNull(file1.getByteBuf()); - assertNotNull(file2.getByteBuf()); - assertEquals(1, attribute1.refCnt()); - assertEquals(1, attribute2.refCnt()); - assertEquals(1, file1.refCnt()); - assertEquals(1, file2.refCnt()); + assertNotNull(attribute1.getBuffer()); + assertNotNull(attribute2.getBuffer()); + assertNotNull(file1.getBuffer()); + assertNotNull(file2.getBuffer()); // Clean up by req1 factory.cleanRequestHttpData(req1); // Assert that data belonging to req1 has been cleaned up - assertNull(attribute1.getByteBuf()); - assertNull(file1.getByteBuf()); - assertEquals(0, attribute1.refCnt()); - assertEquals(0, file1.refCnt()); + assertNull(attribute1.getBuffer()); + assertNull(file1.getBuffer()); // But not req2 - assertNotNull(attribute2.getByteBuf()); - assertNotNull(file2.getByteBuf()); - assertEquals(1, attribute2.refCnt()); - assertEquals(1, file2.refCnt()); + assertNotNull(attribute2.getBuffer()); + assertNotNull(file2.getBuffer()); } @Test @@ -127,8 +115,8 @@ public void removeHttpDataFromCleanShouldIdentifiesDataByTheirIdentities() throw req1, "file", "file.txt", DEFAULT_TEXT_CONTENT_TYPE, IDENTITY.toString(), UTF_8, 123 ); - file1.setContent(Unpooled.copiedBuffer("file content", UTF_8)); - file2.setContent(Unpooled.copiedBuffer("file content", UTF_8)); + file1.setContent(Helpers.copiedBuffer("file content", UTF_8)); + file2.setContent(Helpers.copiedBuffer("file content", UTF_8)); // Before doing anything, assert that the data items are equal assertEquals(attribute1.hashCode(), attribute2.hashCode()); @@ -144,21 +132,15 @@ public void removeHttpDataFromCleanShouldIdentifiesDataByTheirIdentities() throw factory.cleanRequestHttpData(req1); // Assert that attribute1 and file1 have been cleaned up - assertNull(attribute1.getByteBuf()); - assertNull(file1.getByteBuf()); - assertEquals(0, attribute1.refCnt()); - assertEquals(0, file1.refCnt()); + assertNull(attribute1.getBuffer()); + assertNull(file1.getBuffer()); // But not attribute2 and file2 - assertNotNull(attribute2.getByteBuf()); - assertNotNull(file2.getByteBuf()); - assertEquals(1, attribute2.refCnt()); - assertEquals(1, file2.refCnt()); + assertNotNull(attribute2.getBuffer()); + assertNotNull(file2.getBuffer()); // Cleanup attribute2 and file2 manually to avoid memory leak, not via factory - attribute2.release(); - file2.release(); - assertEquals(0, attribute2.refCnt()); - assertEquals(0, file2.refCnt()); + attribute2.close(); + file2.close(); } } diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DeleteFileOnExitHookTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DeleteFileOnExitHookTest.java index 11a4598..47acc0c 100644 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DeleteFileOnExitHookTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DeleteFileOnExitHookTest.java @@ -15,9 +15,9 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpRequest; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.handler.codec.http.DefaultHttpRequest; +import io.netty5.handler.codec.http.HttpRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -25,11 +25,9 @@ import java.io.FilenameFilter; import java.io.IOException; -import static io.netty.handler.codec.http.HttpMethod.POST; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static io.netty5.handler.codec.http.HttpMethod.POST; +import static io.netty5.handler.codec.http.HttpVersion.HTTP_1_1; +import static org.junit.jupiter.api.Assertions.*; /** * Test DeleteFileOnExitHook @@ -50,7 +48,7 @@ public void setUp() throws IOException { fu = defaultHttpDataFactory.createFileUpload( REQUEST, "attribute1", "tmp_f.txt", "text/plain", null, null, 0); - fu.setContent(Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4})); + fu.setContent(DefaultBufferAllocators.onHeapAllocator().copyOf(new byte[]{1, 2, 3, 4})); assertTrue(fu.getFile().exists()); } @@ -72,12 +70,12 @@ public boolean accept(File dir, String name) { } @Test - public void testAfterHttpDataReleaseCheckFileExist() throws IOException { + public void testAfterHttpDataReleaseCheckFileExist() throws Exception { String filePath = fu.getFile().getPath(); assertTrue(DeleteFileOnExitHook.checkFileExist(filePath)); - fu.release(); + fu.close(); assertFalse(DeleteFileOnExitHook.checkFileExist(filePath)); } } diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DiskFileUploadTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DiskFileUploadTest.java index fde3cbf..04d49de 100644 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DiskFileUploadTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/DiskFileUploadTest.java @@ -15,13 +15,12 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.PlatformDependent; - +import io.netty5.buffer.BufferInputStream; +import io.netty5.util.CharsetUtil; +import io.netty5.util.internal.PlatformDependent; +import io.netty5.buffer.BufferUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.DefaultBufferAllocators; import org.junit.jupiter.api.Test; import java.io.File; @@ -30,94 +29,88 @@ import java.io.IOException; import java.io.InputStream; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; public class DiskFileUploadTest { @Test public void testSpecificCustomBaseDir() throws IOException { File baseDir = new File("target/DiskFileUploadTest/testSpecificCustomBaseDir"); baseDir.mkdirs(); // we don't need to clean it since it is in volatile files anyway - DiskFileUpload f = + try (DiskFileUpload f = new DiskFileUpload("d1", "d1", "application/json", null, null, 100, - baseDir.getAbsolutePath(), false); + baseDir.getAbsolutePath(), false)) { - f.setContent(Unpooled.EMPTY_BUFFER); + f.setContent(DefaultBufferAllocators.preferredAllocator().allocate(0)); - assertTrue(f.getFile().getAbsolutePath().startsWith(baseDir.getAbsolutePath())); - assertTrue(f.getFile().exists()); - assertEquals(0, f.getFile().length()); - f.delete(); + assertTrue(f.getFile().getAbsolutePath().startsWith(baseDir.getAbsolutePath())); + assertTrue(f.getFile().exists()); + assertEquals(0, f.getFile().length()); + } } @Test public final void testDiskFileUploadEquals() { - DiskFileUpload f2 = - new DiskFileUpload("d1", "d1", "application/json", null, null, 100); - assertEquals(f2, f2); - f2.delete(); + try (DiskFileUpload f2 = + new DiskFileUpload("d1", "d1", "application/json", null, null, 100)) { + assertEquals(f2, f2); + } } @Test public void testEmptyBufferSetMultipleTimes() throws IOException { - DiskFileUpload f = - new DiskFileUpload("d1", "d1", "application/json", null, null, 100); + try(DiskFileUpload f = + new DiskFileUpload("d1", "d1", "application/json", null, null, 100)) { - f.setContent(Unpooled.EMPTY_BUFFER); + f.setContent(DefaultBufferAllocators.preferredAllocator().allocate(0)); - assertTrue(f.getFile().exists()); - assertEquals(0, f.getFile().length()); - f.setContent(Unpooled.EMPTY_BUFFER); - assertTrue(f.getFile().exists()); - assertEquals(0, f.getFile().length()); - f.delete(); + assertTrue(f.getFile().exists()); + assertEquals(0, f.getFile().length()); + f.setContent(DefaultBufferAllocators.preferredAllocator().allocate(0)); + assertTrue(f.getFile().exists()); + assertEquals(0, f.getFile().length()); + } } @Test public void testEmptyBufferSetAfterNonEmptyBuffer() throws IOException { - DiskFileUpload f = - new DiskFileUpload("d1", "d1", "application/json", null, null, 100); + try(DiskFileUpload f = + new DiskFileUpload("d1", "d1", "application/json", null, null, 100)) { - f.setContent(Unpooled.wrappedBuffer(new byte[] { 1, 2, 3, 4 })); + f.setContent(DefaultBufferAllocators.onHeapAllocator().copyOf(new byte[]{1, 2, 3, 4})); - assertTrue(f.getFile().exists()); - assertEquals(4, f.getFile().length()); - f.setContent(Unpooled.EMPTY_BUFFER); - assertTrue(f.getFile().exists()); - assertEquals(0, f.getFile().length()); - f.delete(); + assertTrue(f.getFile().exists()); + assertEquals(4, f.getFile().length()); + f.setContent(DefaultBufferAllocators.preferredAllocator().allocate(0)); + assertTrue(f.getFile().exists()); + assertEquals(0, f.getFile().length()); + } } @Test public void testNonEmptyBufferSetMultipleTimes() throws IOException { - DiskFileUpload f = - new DiskFileUpload("d1", "d1", "application/json", null, null, 100); + try(DiskFileUpload f = + new DiskFileUpload("d1", "d1", "application/json", null, null, 100)) { - f.setContent(Unpooled.wrappedBuffer(new byte[] { 1, 2, 3, 4 })); + f.setContent(DefaultBufferAllocators.onHeapAllocator().copyOf(new byte[]{1, 2, 3, 4})); - assertTrue(f.getFile().exists()); - assertEquals(4, f.getFile().length()); - f.setContent(Unpooled.wrappedBuffer(new byte[] { 1, 2})); - assertTrue(f.getFile().exists()); - assertEquals(2, f.getFile().length()); - f.delete(); + assertTrue(f.getFile().exists()); + assertEquals(4, f.getFile().length()); + f.setContent(DefaultBufferAllocators.onHeapAllocator().copyOf(new byte[]{1, 2})); + assertTrue(f.getFile().exists()); + assertEquals(2, f.getFile().length()); + } } @Test public void testAddContents() throws Exception { - DiskFileUpload f1 = new DiskFileUpload("file1", "file1", "application/json", null, null, 0); - try { + try (DiskFileUpload f1 = new DiskFileUpload("file1", "file1", "application/json", null, null, 0)) { byte[] jsonBytes = new byte[4096]; - PlatformDependent.threadLocalRandom().nextBytes(jsonBytes); + ThreadLocalRandom.current().nextBytes(jsonBytes); - f1.addContent(Unpooled.wrappedBuffer(jsonBytes, 0, 1024), false); - f1.addContent(Unpooled.wrappedBuffer(jsonBytes, 1024, jsonBytes.length - 1024), true); + f1.addContent(Helpers.copiedBuffer(jsonBytes, 0, 1024), false); + f1.addContent(Helpers.copiedBuffer(jsonBytes, 1024, jsonBytes.length - 1024), true); assertArrayEquals(jsonBytes, f1.get()); File file = f1.getFile(); @@ -140,49 +133,38 @@ public void testAddContents() throws Exception { } finally { fis.close(); } - } finally { - f1.delete(); } } @Test public void testSetContentFromByteBuf() throws Exception { - DiskFileUpload f1 = new DiskFileUpload("file2", "file2", "application/json", null, null, 0); - try { + try (DiskFileUpload f1 = new DiskFileUpload("file2", "file2", "application/json", null, null, 0)) { String json = "{\"hello\":\"world\"}"; byte[] bytes = json.getBytes(CharsetUtil.UTF_8); - f1.setContent(Unpooled.wrappedBuffer(bytes)); + f1.setContent(Helpers.copiedBuffer(bytes)); assertEquals(json, f1.getString()); assertArrayEquals(bytes, f1.get()); File file = f1.getFile(); assertEquals((long) bytes.length, file.length()); assertArrayEquals(bytes, doReadFile(file, bytes.length)); - } finally { - f1.delete(); } } @Test public void testSetContentFromInputStream() throws Exception { String json = "{\"hello\":\"world\",\"foo\":\"bar\"}"; - DiskFileUpload f1 = new DiskFileUpload("file3", "file3", "application/json", null, null, 0); - try { + try (DiskFileUpload f1 = new DiskFileUpload("file3", "file3", "application/json", null, null, 0)) { byte[] bytes = json.getBytes(CharsetUtil.UTF_8); - ByteBuf buf = Unpooled.wrappedBuffer(bytes); - InputStream is = new ByteBufInputStream(buf); - try { + + try (Buffer buf = Helpers.copiedBuffer(bytes); + InputStream is = new BufferInputStream(buf.send())) { f1.setContent(is); assertEquals(json, f1.getString()); assertArrayEquals(bytes, f1.get()); File file = f1.getFile(); assertEquals((long) bytes.length, file.length()); assertArrayEquals(bytes, doReadFile(file, bytes.length)); - } finally { - buf.release(); - is.close(); } - } finally { - f1.delete(); } } @@ -197,28 +179,24 @@ public void testAddContentFromCompositeByteBuf() throws Exception { } private static void testAddContentFromByteBuf0(boolean composite) throws Exception { - DiskFileUpload f1 = new DiskFileUpload("file3", "file3", "application/json", null, null, 0); - try { + try (DiskFileUpload f1 = new DiskFileUpload("file3", "file3", "application/json", null, null, 0)) { byte[] bytes = new byte[4096]; - PlatformDependent.threadLocalRandom().nextBytes(bytes); + ThreadLocalRandom.current().nextBytes(bytes); - final ByteBuf buffer; + final Buffer buffer; if (composite) { - buffer = Unpooled.compositeBuffer() - .addComponent(true, Unpooled.wrappedBuffer(bytes, 0 , bytes.length / 2)) - .addComponent(true, Unpooled.wrappedBuffer(bytes, bytes.length / 2, bytes.length / 2)); + buffer = Helpers.toComposite( + Helpers.copiedBuffer(bytes, 0 , bytes.length / 2), + Helpers.copiedBuffer(bytes, bytes.length / 2, bytes.length / 2)); } else { - buffer = Unpooled.wrappedBuffer(bytes); + buffer = Helpers.copiedBuffer(bytes); } f1.addContent(buffer, true); - ByteBuf buf = f1.getByteBuf(); - assertEquals(buf.readerIndex(), 0); - assertEquals(buf.writerIndex(), bytes.length); - assertArrayEquals(bytes, ByteBufUtil.getBytes(buf)); - } finally { - //release the ByteBuf - f1.delete(); + Buffer buf = f1.getBuffer(); + assertEquals(buf.readerOffset(), 0); + assertEquals(buf.writerOffset(), bytes.length); + assertArrayEquals(bytes, BufferUtil.getBytes(buf)); } } @@ -248,12 +226,11 @@ public void testDelete() throws Exception { byte[] bytes = json.getBytes(CharsetUtil.UTF_8); File tmpFile = null; DiskFileUpload f1 = new DiskFileUpload("file4", "file4", "application/json", null, null, 0); - try { + try (f1) { assertNull(f1.getFile()); - f1.setContent(Unpooled.wrappedBuffer(bytes)); + f1.setContent(Helpers.copiedBuffer(bytes)); assertNotNull(tmpFile = f1.getFile()); } finally { - f1.delete(); assertNull(f1.getFile()); assertNotNull(tmpFile); assertFalse(tmpFile.exists()); @@ -263,16 +240,15 @@ public void testDelete() throws Exception { @Test public void setSetContentFromFileExceptionally() throws Exception { final long maxSize = 4; - DiskFileUpload f1 = new DiskFileUpload("file5", "file5", "application/json", null, null, 0); - f1.setMaxSize(maxSize); - try { - f1.setContent(Unpooled.wrappedBuffer(new byte[(int) maxSize])); + try (DiskFileUpload f1 = new DiskFileUpload("file5", "file5", "application/json", null, null, 0)) { + f1.setMaxSize(maxSize); + f1.setContent(Helpers.copiedBuffer(new byte[(int) maxSize])); File originalFile = f1.getFile(); assertNotNull(originalFile); assertEquals(maxSize, originalFile.length()); assertEquals(maxSize, f1.length()); byte[] bytes = new byte[8]; - PlatformDependent.threadLocalRandom().nextBytes(bytes); + ThreadLocalRandom.current().nextBytes(bytes); File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null); tmpFile.deleteOnExit(); FileOutputStream fos = new FileOutputStream(tmpFile); @@ -290,8 +266,6 @@ public void setSetContentFromFileExceptionally() throws Exception { assertEquals(originalFile, f1.getFile()); assertEquals(maxSize, f1.length()); } - } finally { - f1.delete(); } } } diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpDataTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpDataTest.java index 2bd39e6..63325a5 100644 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpDataTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpDataTest.java @@ -15,10 +15,9 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; +import io.netty5.util.CharsetUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.DefaultBufferAllocators; import org.assertj.core.api.ThrowableAssert; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -30,6 +29,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.nio.charset.Charset; import java.util.Random; import static org.assertj.core.api.Assertions.assertThat; @@ -64,23 +64,23 @@ static void setUp() { @ParameterizedHttpDataTest void testAddContentEmptyBuffer(HttpData httpData) throws IOException { - ByteBuf content = PooledByteBufAllocator.DEFAULT.buffer(); + Buffer content = DefaultBufferAllocators.preferredAllocator().allocate(0); httpData.addContent(content, false); - assertThat(content.refCnt()).isEqualTo(0); + assertThat(content.isAccessible()).isEqualTo(false); } @ParameterizedHttpDataTest void testCompletedFlagPreservedAfterRetainDuplicate(HttpData httpData) throws IOException { - httpData.addContent(Unpooled.wrappedBuffer("foo".getBytes(CharsetUtil.UTF_8)), false); + httpData.addContent(Helpers.copiedBuffer("foo".getBytes(CharsetUtil.UTF_8)), false); assertThat(httpData.isCompleted()).isFalse(); - HttpData duplicate = httpData.retainedDuplicate(); + HttpData duplicate = httpData.replace(httpData.content().split()); assertThat(duplicate.isCompleted()).isFalse(); - assertThat(duplicate.release()).isTrue(); - httpData.addContent(Unpooled.wrappedBuffer("bar".getBytes(CharsetUtil.UTF_8)), true); + duplicate.close(); + httpData.addContent(Helpers.copiedBuffer("bar".getBytes(CharsetUtil.UTF_8)), true); assertThat(httpData.isCompleted()).isTrue(); - duplicate = httpData.retainedDuplicate(); + duplicate = httpData.replace(httpData.content().split()); assertThat(duplicate.isCompleted()).isTrue(); - assertThat(duplicate.release()).isTrue(); + duplicate.close(); } @Test @@ -114,8 +114,158 @@ void testSetContentExceedsMaxSize(final HttpData httpData) { doTestSetContentExceedsSize(httpData, "Size exceed allowed maximum capacity"); } + @Test + void testMemoryAttributeSend() throws IOException { + try (MemoryAttribute mem = new MemoryAttribute("test", 10, Charset.defaultCharset())) { + mem.addContent(Helpers.copiedBuffer("content", Charset.defaultCharset()), false); + mem.setMaxSize(100); + assertThat(mem.isCompleted()).isFalse(); + final MemoryAttribute memSend = (MemoryAttribute) mem.send().receive(); + try (memSend) { + assertThat(mem.isAccessible()).isFalse(); + assertThat(memSend.isAccessible()).isTrue(); + assertThat(memSend.isCompleted()).isFalse(); + assertThat(memSend.getValue()).isEqualTo("content"); + assertThat(memSend.getHttpDataType()).isEqualTo(InterfaceHttpData.HttpDataType.Attribute); + assertThat(memSend.isInMemory()).isTrue(); + assertThat(memSend.getCharset()).isEqualTo(Charset.defaultCharset()); + assertThat(memSend.definedLength()).isEqualTo(10); + assertThat(memSend.getMaxSize()).isEqualTo(100); + } + assertThat(memSend.isAccessible()).isFalse(); + } + } + + @Test + void testMemoryFileUploadSend() throws IOException { + try (MemoryFileUpload mem = new MemoryFileUpload("test", "filename", "text/plain", "BINARY", CharsetUtil.UTF_8, 10)) { + mem.addContent(Helpers.copiedBuffer("content", Charset.defaultCharset()), false); + mem.setMaxSize(100); + assertThat(mem.isCompleted()).isFalse(); + final MemoryFileUpload memSend = (MemoryFileUpload) mem.send().receive(); + try (memSend) { + assertThat(mem.isAccessible()).isFalse(); + assertThat(memSend.isAccessible()).isTrue(); + assertThat(memSend.isCompleted()).isFalse(); + assertThat(memSend.getString()).isEqualTo("content"); + assertThat(memSend.getHttpDataType()).isEqualTo(InterfaceHttpData.HttpDataType.FileUpload); + assertThat(memSend.isInMemory()).isTrue(); + assertThat(memSend.getCharset()).isEqualTo(CharsetUtil.UTF_8); + assertThat(memSend.definedLength()).isEqualTo(10); + assertThat(memSend.getMaxSize()).isEqualTo(100); + assertThat(memSend.getName()).isEqualTo("test"); + assertThat(memSend.getFilename()).isEqualTo("filename"); + assertThat(memSend.getContentType()).isEqualTo("text/plain"); + assertThat(memSend.getContentTransferEncoding()).isEqualTo("BINARY"); + + } + assertThat(memSend.isAccessible()).isFalse(); + } + } + + @Test + void testMixedAttributeSend() throws IOException { + try (MixedAttribute data = new MixedAttribute("test", 10, 100, CharsetUtil.UTF_8, "/tmp", true)) { + data.addContent(Helpers.copiedBuffer("content", Charset.defaultCharset()), false); + data.setMaxSize(1000); + assertThat(data.isCompleted()).isFalse(); + final MixedAttribute send = (MixedAttribute) data.send().receive(); + try (send) { + assertThat(data.isAccessible()).isFalse(); + assertThat(send.isAccessible()).isTrue(); + assertThat(send.isCompleted()).isFalse(); + assertThat(send.getValue()).isEqualTo("content"); + assertThat(send.getHttpDataType()).isEqualTo(InterfaceHttpData.HttpDataType.Attribute); + assertThat(send.isInMemory()).isTrue(); + assertThat(send.getCharset()).isEqualTo(CharsetUtil.UTF_8); + assertThat(send.definedLength()).isEqualTo(10); + assertThat(send.getMaxSize()).isEqualTo(1000); + assertThat(send.limitSize).isEqualTo(100); + assertThat(send.deleteOnExit).isTrue(); + assertThat(send.baseDir).isEqualTo("/tmp"); + } + assertThat(send.isAccessible()).isFalse(); + } + } + + @Test + void testMixedFileUploadSend() throws IOException { + try (MixedFileUpload data = new MixedFileUpload("test", "filename", "text/plain", "BINARY", + CharsetUtil.UTF_8, 10, 100, "/tmp", true)) { + data.addContent(Helpers.copiedBuffer("content", Charset.defaultCharset()), false); + data.setMaxSize(1000); + assertThat(data.isCompleted()).isFalse(); + final MixedFileUpload send = (MixedFileUpload) data.send().receive(); + try (send) { + assertThat(data.isAccessible()).isFalse(); + assertThat(send.isAccessible()).isTrue(); + assertThat(send.isCompleted()).isFalse(); + assertThat(send.getString()).isEqualTo("content"); + assertThat(send.getHttpDataType()).isEqualTo(InterfaceHttpData.HttpDataType.FileUpload); + assertThat(send.isInMemory()).isTrue(); + assertThat(send.getCharset()).isEqualTo(Charset.defaultCharset()); + assertThat(send.definedLength()).isEqualTo(10); + assertThat(send.getMaxSize()).isEqualTo(1000); + assertThat(send.limitSize).isEqualTo(100); + assertThat(send.deleteOnExit).isTrue(); + assertThat(send.baseDir).isEqualTo("/tmp"); + } + assertThat(send.isAccessible()).isFalse(); + } + + } + + @Test + void testDiskAttributeSend() throws IOException { + try (DiskAttribute data = new DiskAttribute("test", 10, "/tmp", true)) { + data.addContent(Helpers.copiedBuffer("content", Charset.defaultCharset()), false); + data.setMaxSize(1000); + assertThat(data.isCompleted()).isFalse(); + final DiskAttribute send = (DiskAttribute) data.send().receive(); + try (send) { + assertThat(data.isAccessible()).isFalse(); + assertThat(send.isAccessible()).isTrue(); + assertThat(send.isCompleted()).isFalse(); + assertThat(send.getString()).isEqualTo("content"); + assertThat(send.getHttpDataType()).isEqualTo(InterfaceHttpData.HttpDataType.Attribute); + assertThat(send.isInMemory()).isFalse(); + assertThat(send.getCharset()).isEqualTo(Charset.defaultCharset()); + assertThat(send.definedLength()).isEqualTo(10); + assertThat(send.getMaxSize()).isEqualTo(1000); + assertThat(send.deleteOnExit()).isTrue(); + assertThat(send.getBaseDirectory()).isEqualTo("/tmp"); + } + assertThat(send.isAccessible()).isFalse(); + } + } + + @Test + void testDiskFileUploadSend() throws IOException { + + try (DiskFileUpload data = new DiskFileUpload("test", "", "text/plain", null, CharsetUtil.UTF_8, 10, "/tmp", true)) { + data.addContent(Helpers.copiedBuffer("content", Charset.defaultCharset()), false); + data.setMaxSize(1000); + assertThat(data.isCompleted()).isFalse(); + final DiskFileUpload send = (DiskFileUpload) data.send().receive(); + try (send) { + assertThat(data.isAccessible()).isFalse(); + assertThat(send.isAccessible()).isTrue(); + assertThat(send.isCompleted()).isFalse(); + assertThat(send.getString()).isEqualTo("content"); + assertThat(send.getHttpDataType()).isEqualTo(InterfaceHttpData.HttpDataType.FileUpload); + assertThat(send.isInMemory()).isFalse(); + assertThat(send.getCharset()).isEqualTo(Charset.defaultCharset()); + assertThat(send.definedLength()).isEqualTo(10); + assertThat(send.getMaxSize()).isEqualTo(1000); + assertThat(send.deleteOnExit()).isTrue(); + assertThat(send.getBaseDirectory()).isEqualTo("/tmp"); + } + assertThat(send.isAccessible()).isFalse(); + } + } + private static void doTestAddContentExceedsSize(final HttpData httpData, String expectedMessage) { - final ByteBuf content = PooledByteBufAllocator.DEFAULT.buffer(); + final Buffer content = DefaultBufferAllocators.preferredAllocator().allocate(0); content.writeBytes(BYTES); assertThatExceptionOfType(IOException.class) @@ -127,12 +277,10 @@ public void call() throws Throwable { } }) .withMessage(expectedMessage); - - assertThat(content.refCnt()).isEqualTo(0); } private static void doTestSetContentExceedsSize(final HttpData httpData, String expectedMessage) { - final ByteBuf content = PooledByteBufAllocator.DEFAULT.buffer(); + final Buffer content = DefaultBufferAllocators.preferredAllocator().allocate(0); content.writeBytes(BYTES); assertThatExceptionOfType(IOException.class) @@ -144,7 +292,5 @@ public void call() throws Throwable { } }) .withMessage(expectedMessage); - - assertThat(content.refCnt()).isEqualTo(0); } } diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostMultiPartRequestDecoderTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostMultiPartRequestDecoderTest.java index 4106074..49a94b2 100644 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostMultiPartRequestDecoderTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostMultiPartRequestDecoderTest.java @@ -15,49 +15,43 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.CharsetUtil; +import io.netty5.util.CharsetUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.handler.codec.http.DefaultFullHttpRequest; +import io.netty5.handler.codec.http.DefaultHttpContent; +import io.netty5.handler.codec.http.DefaultHttpRequest; +import io.netty5.handler.codec.http.FullHttpRequest; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.handler.codec.http.HttpHeaderNames; +import io.netty5.handler.codec.http.HttpMethod; +import io.netty5.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.HttpVersion; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Arrays; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; public class HttpPostMultiPartRequestDecoderTest { @Test public void testDecodeFullHttpRequestWithNoContentTypeHeader() { - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); + FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", DefaultBufferAllocators.preferredAllocator().allocate(0)); try { new HttpPostMultipartRequestDecoder(req); fail("Was expecting an ErrorDataDecoderException"); } catch (HttpPostRequestDecoder.ErrorDataDecoderException expected) { // expected } finally { - assertTrue(req.release()); + req.close(); } } @Test public void testDecodeFullHttpRequestWithInvalidCharset() { - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); + FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", DefaultBufferAllocators.preferredAllocator().allocate(0)); req.headers().set(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=--89421926422648 [; charset=UTF-8]"); @@ -67,7 +61,7 @@ public void testDecodeFullHttpRequestWithInvalidCharset() { } catch (HttpPostRequestDecoder.ErrorDataDecoderException expected) { // expected } finally { - assertTrue(req.release()); + req.close(); } } @@ -81,7 +75,7 @@ public void testDecodeFullHttpRequestWithInvalidPayloadReleaseBuffer() { "--861fbeab-cd20-470c-9609-d40a0f704466--\n"; FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload", - Unpooled.copiedBuffer(content, CharsetUtil.US_ASCII)); + Helpers.copiedBuffer(content, CharsetUtil.US_ASCII)); req.headers().set("content-type", "multipart/form-data; boundary=861fbeab-cd20-470c-9609-d40a0f704466"); req.headers().set("content-length", content.length()); @@ -91,12 +85,12 @@ public void testDecodeFullHttpRequestWithInvalidPayloadReleaseBuffer() { } catch (HttpPostRequestDecoder.ErrorDataDecoderException expected) { // expected } finally { - assertTrue(req.release()); + req.close(); } } @Test - public void testDelimiterExceedLeftSpaceInCurrentBuffer() { + public void testDelimiterExceedLeftSpaceInCurrentBuffer() throws IOException { String delimiter = "--861fbeab-cd20-470c-9609-d40a0f704466"; String suffix = '\n' + delimiter + "--\n"; byte[] bsuffix = suffix.getBytes(CharsetUtil.UTF_8); @@ -115,44 +109,34 @@ public void testDelimiterExceedLeftSpaceInCurrentBuffer() { // Factory using Memory mode HttpDataFactory factory = new DefaultHttpDataFactory(false); HttpPostMultipartRequestDecoder decoder = new HttpPostMultipartRequestDecoder(factory, request); - ByteBuf buf = Unpooled.wrappedBuffer(prefix.getBytes(CharsetUtil.UTF_8)); + Buffer buf = Helpers.copiedBuffer(prefix.getBytes(CharsetUtil.UTF_8)); DefaultHttpContent httpContent = new DefaultHttpContent(buf); decoder.offer(httpContent); assertNotNull((HttpData) decoder.currentPartialHttpData()); - httpContent.release(); + httpContent.close(); // Chunk less than Delimiter size but containing part of delimiter byte[] body = new byte[bytesLastChunk + bsuffix1.length]; Arrays.fill(body, (byte) 2); for (int i = 0; i < bsuffix1.length; i++) { body[bytesLastChunk + i] = bsuffix1[i]; } - ByteBuf content = Unpooled.wrappedBuffer(body); + Buffer content = Helpers.copiedBuffer(body); httpContent = new DefaultHttpContent(content); decoder.offer(httpContent); // Ouf of range before here - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - httpContent.release(); - content = Unpooled.wrappedBuffer(bsuffix2); + assertNotNull(((HttpData) decoder.currentPartialHttpData()).getBuffer()); + httpContent.close(); + content = Helpers.copiedBuffer(bsuffix2); httpContent = new DefaultHttpContent(content); decoder.offer(httpContent); assertNull((HttpData) decoder.currentPartialHttpData()); - httpContent.release(); - decoder.offer(new DefaultLastHttpContent()); + httpContent.close(); + decoder.offer(Helpers.defaultLastHttpContent()); FileUpload data = (FileUpload) decoder.getBodyHttpDatas().get(0); assertEquals(data.length(), bytesLastChunk); assertEquals(true, data.isInMemory()); - InterfaceHttpData[] httpDatas = decoder.getBodyHttpDatas().toArray(new InterfaceHttpData[0]); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(1, httpData.refCnt(), "Before cleanAllHttpData should be 1"); - } factory.cleanAllHttpData(); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(1, httpData.refCnt(), "After cleanAllHttpData should be 1 if in Memory"); - } decoder.destroy(); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(0, httpData.refCnt(), "RefCnt should be 0"); - } } private void commonTestBigFileDelimiterInMiddleChunk(HttpDataFactory factory, boolean inMemory) @@ -179,11 +163,11 @@ private void commonTestBigFileDelimiterInMiddleChunk(HttpDataFactory factory, bo request.headers().set("content-length", prefix.length() + fileSize + suffix.length()); HttpPostMultipartRequestDecoder decoder = new HttpPostMultipartRequestDecoder(factory, request); - ByteBuf buf = Unpooled.wrappedBuffer(prefix.getBytes(CharsetUtil.UTF_8)); + Buffer buf = Helpers.copiedBuffer(prefix.getBytes(CharsetUtil.UTF_8)); DefaultHttpContent httpContent = new DefaultHttpContent(buf); decoder.offer(httpContent); - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - httpContent.release(); + assertNotNull(((HttpData) decoder.currentPartialHttpData()).getBuffer()); + httpContent.close(); byte[] body = new byte[bytesPerChunk]; Arrays.fill(body, (byte) 1); @@ -191,11 +175,11 @@ private void commonTestBigFileDelimiterInMiddleChunk(HttpDataFactory factory, bo body[0] = HttpConstants.CR; body[1] = HttpConstants.LF; for (int i = 0; i < nbChunks; i++) { - ByteBuf content = Unpooled.wrappedBuffer(body, 0, bytesPerChunk); + Buffer content = Helpers.copiedBuffer(body, 0, bytesPerChunk); httpContent = new DefaultHttpContent(content); decoder.offer(httpContent); // **OutOfMemory previously here** - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - httpContent.release(); + assertNotNull(((HttpData) decoder.currentPartialHttpData()).getBuffer()); + httpContent.close(); } byte[] bsuffix1 = suffix1.getBytes(CharsetUtil.UTF_8); @@ -216,45 +200,35 @@ private void commonTestBigFileDelimiterInMiddleChunk(HttpDataFactory factory, bo lastbody[bsuffix1.length + i] = bsuffix1[i]; } - ByteBuf content2 = Unpooled.wrappedBuffer(previousLastbody, 0, previousLastbody.length); + Buffer content2 = Helpers.copiedBuffer(previousLastbody, 0, previousLastbody.length); httpContent = new DefaultHttpContent(content2); decoder.offer(httpContent); - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - httpContent.release(); - content2 = Unpooled.wrappedBuffer(lastbody, 0, lastbody.length); + assertNotNull(((HttpData) decoder.currentPartialHttpData()).getBuffer()); + httpContent.close(); + content2 = Helpers.copiedBuffer(lastbody, 0, lastbody.length); httpContent = new DefaultHttpContent(content2); decoder.offer(httpContent); - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - httpContent.release(); - content2 = Unpooled.wrappedBuffer(suffix2.getBytes(CharsetUtil.UTF_8)); + assertNotNull(((HttpData) decoder.currentPartialHttpData()).getBuffer()); + httpContent.close(); + content2 = Helpers.copiedBuffer(suffix2.getBytes(CharsetUtil.UTF_8)); httpContent = new DefaultHttpContent(content2); decoder.offer(httpContent); assertNull(decoder.currentPartialHttpData()); - httpContent.release(); - decoder.offer(new DefaultLastHttpContent()); + httpContent.close(); + decoder.offer(Helpers.defaultLastHttpContent()); FileUpload data = (FileUpload) decoder.getBodyHttpDatas().get(0); assertEquals(data.length(), fileSize); assertEquals(inMemory, data.isInMemory()); if (data.isInMemory()) { // To be done only if not inMemory: assertEquals(data.get().length, fileSize); - assertFalse(data.getByteBuf().capacity() < 1024 * 1024, + assertFalse(data.getBuffer().capacity() < 1024 * 1024, "Capacity should be higher than 1M"); } assertTrue(decoder.getCurrentAllocatedCapacity() < 1024 * 1024, "Capacity should be less than 1M"); - InterfaceHttpData[] httpDatas = decoder.getBodyHttpDatas().toArray(new InterfaceHttpData[0]); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(1, httpData.refCnt(), "Before cleanAllHttpData should be 1"); - } factory.cleanAllHttpData(); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(inMemory? 1 : 0, httpData.refCnt(), "After cleanAllHttpData should be 1 if in Memory"); - } decoder.destroy(); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(0, httpData.refCnt(), "RefCnt should be 0"); - } } @Test @@ -282,19 +256,19 @@ public void testBIgFileUploadDelimiterInMiddleChunkDecoderMixedFactory() throws } @Test - public void testNotBadReleaseBuffersDuringDecodingDiskFactory() throws IOException { + public void testNotBadReleaseBuffersDuringDecodingDiskFactory() throws Exception { // Using Disk Factory HttpDataFactory factory = new DefaultHttpDataFactory(true); commonNotBadReleaseBuffersDuringDecoding(factory, false); } @Test - public void testNotBadReleaseBuffersDuringDecodingMemoryFactory() throws IOException { + public void testNotBadReleaseBuffersDuringDecodingMemoryFactory() throws Exception { // Using Memory Factory HttpDataFactory factory = new DefaultHttpDataFactory(false); commonNotBadReleaseBuffersDuringDecoding(factory, true); } @Test - public void testNotBadReleaseBuffersDuringDecodingMixedFactory() throws IOException { + public void testNotBadReleaseBuffersDuringDecodingMixedFactory() throws Exception { // Using Mixed Factory HttpDataFactory factory = new DefaultHttpDataFactory(100); commonNotBadReleaseBuffersDuringDecoding(factory, false); @@ -309,7 +283,7 @@ public void testDecodeFullHttpRequestWithOptionalParameters() { "\r\n\u0001\u0002\u0003\u0004\r\n--861fbeab-cd20-470c-9609-d40a0f704466--\r\n\",\n"; FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload", - Unpooled.copiedBuffer(content, CharsetUtil.US_ASCII)); + Helpers.copiedBuffer(content, CharsetUtil.US_ASCII)); req.headers().set("content-type", "multipart/form-data; boundary=861fbeab-cd20-470c-9609-d40a0f704466"); req.headers().set("content-length", content.length()); @@ -320,7 +294,7 @@ public void testDecodeFullHttpRequestWithOptionalParameters() { } private static void commonNotBadReleaseBuffersDuringDecoding(HttpDataFactory factory, boolean inMemory) - throws IOException { + throws Exception { int nbItems = 20; int bytesPerItem = 1000; int maxMemory = 500; @@ -353,23 +327,23 @@ private static void commonNotBadReleaseBuffersDuringDecoding(HttpDataFactory fac for (int i = 0; i < bp2.length; i++) { prefix[bp1.length + 2 + i] = bp2[i]; } - ByteBuf buf = Unpooled.wrappedBuffer(prefix); + Buffer buf = Helpers.copiedBuffer(prefix); DefaultHttpContent httpContent = new DefaultHttpContent(buf); decoder.offer(httpContent); - httpContent.release(); + httpContent.close(); byte[] body = new byte[bytesPerItem]; Arrays.fill(body, (byte) rank); - ByteBuf content = Unpooled.wrappedBuffer(body, 0, bytesPerItem); + Buffer content = Helpers.copiedBuffer(body, 0, bytesPerItem); httpContent = new DefaultHttpContent(content); decoder.offer(httpContent); - httpContent.release(); + httpContent.close(); } byte[] lastbody = suffix.getBytes(CharsetUtil.UTF_8); - ByteBuf content2 = Unpooled.wrappedBuffer(lastbody, 0, lastbody.length); + Buffer content2 = Helpers.copiedBuffer(lastbody, 0, lastbody.length); DefaultHttpContent httpContent = new DefaultHttpContent(content2); decoder.offer(httpContent); - httpContent.release(); - decoder.offer(new DefaultLastHttpContent()); + httpContent.close(); + decoder.offer(Helpers.defaultLastHttpContent()); for (int rank = 0; rank < nbItems; rank++) { FileUpload data = (FileUpload) decoder.getBodyHttpData("image" + (10 + rank)); @@ -380,9 +354,9 @@ private static void commonNotBadReleaseBuffersDuringDecoding(HttpDataFactory fac assertTrue(Arrays.equals(body, data.get())); } // To not be done since will load full file on memory: assertEquals(data.get().length, fileSize); - // Not mandatory since implicitly called during destroy of decoder + // Not mandatory since implicitely called during destroy of decoder for (InterfaceHttpData httpData: decoder.getBodyHttpDatas()) { - httpData.release(); + httpData.close(); factory.removeHttpDataFromClean(request, httpData); } factory.cleanAllHttpData(); @@ -420,11 +394,11 @@ private static void commonTestFileDelimiterLFLastChunk(HttpDataFactory factory, request.headers().set("content-length", prefix.length() + fileSize + suffix.length() + 4); HttpPostMultipartRequestDecoder decoder = new HttpPostMultipartRequestDecoder(factory, request); - ByteBuf buf = Unpooled.wrappedBuffer(prefix.getBytes(CharsetUtil.UTF_8)); + Buffer buf = Helpers.copiedBuffer(prefix.getBytes(CharsetUtil.UTF_8)); DefaultHttpContent httpContent = new DefaultHttpContent(buf); decoder.offer(httpContent); - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - httpContent.release(); + assertNotNull(((HttpData) decoder.currentPartialHttpData()).getBuffer()); + httpContent.close(); byte[] body = new byte[bytesPerChunk]; Arrays.fill(body, (byte) 1); @@ -432,58 +406,49 @@ private static void commonTestFileDelimiterLFLastChunk(HttpDataFactory factory, body[0] = HttpConstants.CR; body[1] = HttpConstants.LF; for (int i = 0; i < nbChunks; i++) { - ByteBuf content = Unpooled.wrappedBuffer(body, 0, bytesPerChunk); + Buffer content = Helpers.copiedBuffer(body, 0, bytesPerChunk); httpContent = new DefaultHttpContent(content); decoder.offer(httpContent); // **OutOfMemory previously here** - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - httpContent.release(); + assertNotNull(((HttpData) decoder.currentPartialHttpData()).getBuffer()); + httpContent.close(); } // Last -2 body = content + CR but no delimiter byte[] previousLastbody = new byte[bytesLastChunk + 1]; Arrays.fill(previousLastbody, (byte) 1); previousLastbody[bytesLastChunk] = HttpConstants.CR; - ByteBuf content2 = Unpooled.wrappedBuffer(previousLastbody, 0, previousLastbody.length); + Buffer content2 = Helpers.copiedBuffer(previousLastbody, 0, previousLastbody.length); httpContent = new DefaultHttpContent(content2); decoder.offer(httpContent); assertNotNull(decoder.currentPartialHttpData()); - httpContent.release(); + httpContent.close(); // Last -1 body = LF+delimiter+CR but no LF - content2 = Unpooled.wrappedBuffer(bsuffixReal, 0, bsuffixReal.length); + content2 = Helpers.copiedBuffer(bsuffixReal, 0, bsuffixReal.length); httpContent = new DefaultHttpContent(content2); decoder.offer(httpContent); assertNull(decoder.currentPartialHttpData()); - httpContent.release(); + httpContent.close(); // Last (LF) - content2 = Unpooled.wrappedBuffer(lastbody, 0, lastbody.length); + content2 = Helpers.copiedBuffer(lastbody, 0, lastbody.length); httpContent = new DefaultHttpContent(content2); decoder.offer(httpContent); assertNull(decoder.currentPartialHttpData()); - httpContent.release(); + httpContent.close(); // End - decoder.offer(new DefaultLastHttpContent()); + decoder.offer(Helpers.defaultLastHttpContent()); FileUpload data = (FileUpload) decoder.getBodyHttpDatas().get(0); assertEquals(data.length(), fileSize); assertEquals(inMemory, data.isInMemory()); if (data.isInMemory()) { // To be done only if not inMemory: assertEquals(data.get().length, fileSize); - assertFalse(data.getByteBuf().capacity() < fileSize, + assertFalse(data.getBuffer().capacity() < fileSize, "Capacity should be at least file size"); } assertTrue(decoder.getCurrentAllocatedCapacity() < fileSize, "Capacity should be less than 1M"); InterfaceHttpData[] httpDatas = decoder.getBodyHttpDatas().toArray(new InterfaceHttpData[0]); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(1, httpData.refCnt(), "Before cleanAllHttpData should be 1"); - } factory.cleanAllHttpData(); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(inMemory? 1 : 0, httpData.refCnt(), "After cleanAllHttpData should be 1 if in Memory"); - } decoder.destroy(); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(0, httpData.refCnt(), "RefCnt should be 0"); - } } @Test diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestDecoderTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestDecoderTest.java index b7dfb83..63bd6f1 100644 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestDecoderTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestDecoderTest.java @@ -15,37 +15,32 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.CharsetUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.BufferAllocator; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.handler.codec.DecoderResult; +import io.netty5.handler.codec.http.DefaultFullHttpRequest; +import io.netty5.handler.codec.http.DefaultHttpContent; +import io.netty5.handler.codec.http.DefaultHttpRequest; +import io.netty5.handler.codec.http.DefaultLastHttpContent; +import io.netty5.handler.codec.http.FullHttpRequest; +import io.netty5.handler.codec.http.HttpHeaderNames; +import io.netty5.handler.codec.http.HttpHeaderValues; +import io.netty5.handler.codec.http.HttpMethod; +import io.netty5.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.HttpVersion; +import io.netty5.handler.codec.http.LastHttpContent; +import io.netty5.util.CharsetUtil; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import java.util.Arrays; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; /** * {@link HttpPostRequestDecoder} test case. @@ -74,7 +69,7 @@ private static void testBinaryStreamUpload(boolean withSpace) throws Exception { final DefaultHttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "http://localhost"); - req.setDecoderResult(DecoderResult.SUCCESS); + req.setDecoderResult(DecoderResult.success()); req.headers().add(HttpHeaderNames.CONTENT_TYPE, contentTypeValue); req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); @@ -93,9 +88,9 @@ private static void testBinaryStreamUpload(boolean withSpace) throws Exception { // Create decoder instance to test. final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - ByteBuf buf = Unpooled.copiedBuffer(body, CharsetUtil.UTF_8); + Buffer buf = Helpers.copiedBuffer(body, CharsetUtil.UTF_8); decoder.offer(new DefaultHttpContent(buf)); - decoder.offer(new DefaultHttpContent(Unpooled.EMPTY_BUFFER)); + decoder.offer(new DefaultHttpContent(DefaultBufferAllocators.preferredAllocator().allocate(0))); // Validate it's enough chunks to decode upload. assertTrue(decoder.hasNext()); @@ -106,9 +101,9 @@ private static void testBinaryStreamUpload(boolean withSpace) throws Exception { // Validate data has been parsed correctly as it was passed into request. assertEquals(data, upload.getString(CharsetUtil.UTF_8), "Invalid decoded data [data=" + data.replaceAll("\r", "\\\\r") + ", upload=" + upload + ']'); - upload.release(); + upload.close(); decoder.destroy(); - buf.release(); + buf.close(); } } @@ -118,9 +113,9 @@ public void testFullHttpRequestUpload() throws Exception { final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); + "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); - req.setDecoderResult(DecoderResult.SUCCESS); + req.setDecoderResult(DecoderResult.success()); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); @@ -136,13 +131,13 @@ public void testFullHttpRequestUpload() throws Exception { data + "\r\n" + "--" + boundary + "--\r\n"; - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); + req.payload().writeBytes(body.getBytes(CharsetUtil.UTF_8)); } // Create decoder instance to test. final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); assertFalse(decoder.getBodyHttpDatas().isEmpty()); decoder.destroy(); - assertTrue(req.release()); + req.close(); } // See https://github.com/netty/netty/issues/2544 @@ -164,8 +159,8 @@ public void testMultipartCodecWithCRasEndOfAttribute() throws Exception { for (int i = 0; i < 4; i++) { final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - req.setDecoderResult(DecoderResult.SUCCESS); + "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); + req.setDecoderResult(DecoderResult.success()); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); final String body = @@ -176,7 +171,7 @@ public void testMultipartCodecWithCRasEndOfAttribute() throws Exception { datas[i] + "\r\n" + "--" + boundary + "--\r\n"; - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); + req.payload().writeBytes(body.getBytes(CharsetUtil.UTF_8)); // Create decoder instance to test. final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); assertFalse(decoder.getBodyHttpDatas().isEmpty()); @@ -189,7 +184,7 @@ public void testMultipartCodecWithCRasEndOfAttribute() throws Exception { assertEquals(datas[i].getBytes(CharsetUtil.UTF_8).length, datar.length); decoder.destroy(); - assertTrue(req.release()); + req.close(); } } @@ -199,9 +194,9 @@ public void testQuotedBoundary() throws Exception { final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); + "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); - req.setDecoderResult(DecoderResult.SUCCESS); + req.setDecoderResult(DecoderResult.success()); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=\"" + boundary + '"'); req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); @@ -217,13 +212,13 @@ public void testQuotedBoundary() throws Exception { data + "\r\n" + "--" + boundary + "--\r\n"; - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); + req.payload().writeBytes(body.getBytes(CharsetUtil.UTF_8)); } // Create decoder instance to test. final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); assertFalse(decoder.getBodyHttpDatas().isEmpty()); decoder.destroy(); - assertTrue(req.release()); + req.close(); } // See https://github.com/netty/netty/issues/1848 @@ -261,9 +256,9 @@ public void testNoZeroOut() throws Exception { int split = 125; - ByteBufAllocator aAlloc = new UnpooledByteBufAllocator(true); - ByteBuf aSmallBuf = aAlloc.heapBuffer(split, split); - ByteBuf aLargeBuf = aAlloc.heapBuffer(aBytes.length - split, aBytes.length - split); + BufferAllocator aAlloc = DefaultBufferAllocators.onHeapAllocator(); + Buffer aSmallBuf = aAlloc.allocate(split).implicitCapacityLimit(split); + Buffer aLargeBuf = aAlloc.allocate(aBytes.length - split).implicitCapacityLimit(aBytes.length - split); aSmallBuf.writeBytes(aBytes, 0, split); aLargeBuf.writeBytes(aBytes, split, aBytes.length - split); @@ -271,7 +266,7 @@ public void testNoZeroOut() throws Exception { aDecoder.offer(new DefaultHttpContent(aSmallBuf)); aDecoder.offer(new DefaultHttpContent(aLargeBuf)); - aDecoder.offer(LastHttpContent.EMPTY_LAST_CONTENT); + aDecoder.offer(Helpers.emptyLastHttpContent()); assertTrue(aDecoder.hasNext(), "Should have a piece of data"); @@ -281,10 +276,10 @@ public void testNoZeroOut() throws Exception { Attribute aAttr = (Attribute) aDecodedData; assertEquals(aData, aAttr.getValue()); - aDecodedData.release(); + aDecodedData.close(); aDecoder.destroy(); - aSmallBuf.release(); - aLargeBuf.release(); + aSmallBuf.close(); + aLargeBuf.close(); } // See https://github.com/netty/netty/issues/2305 @@ -331,10 +326,10 @@ public void testChunkCorrect() throws Exception { byte[] payload3 = payload.substring(firstChunk + middleChunk, firstChunk + middleChunk * 2).getBytes(); byte[] payload4 = payload.substring(firstChunk + middleChunk * 2).getBytes(); - ByteBuf buf1 = Unpooled.directBuffer(payload1.length); - ByteBuf buf2 = Unpooled.directBuffer(payload2.length); - ByteBuf buf3 = Unpooled.directBuffer(payload3.length); - ByteBuf buf4 = Unpooled.directBuffer(payload4.length); + Buffer buf1 = DefaultBufferAllocators.offHeapAllocator().allocate(payload1.length); + Buffer buf2 = DefaultBufferAllocators.offHeapAllocator().allocate(payload2.length); + Buffer buf3 = DefaultBufferAllocators.offHeapAllocator().allocate(payload3.length); + Buffer buf4 = DefaultBufferAllocators.offHeapAllocator().allocate(payload4.length); buf1.writeBytes(payload1); buf2.writeBytes(payload2); @@ -353,10 +348,10 @@ public void testChunkCorrect() throws Exception { assertEquals("794649819", attr.getValue()); decoder.destroy(); - buf1.release(); - buf2.release(); - buf3.release(); - buf4.release(); + buf1.close(); + buf2.close(); + buf3.close(); + buf4.close(); } // See https://github.com/netty/netty/issues/3326 @@ -364,7 +359,7 @@ public void testChunkCorrect() throws Exception { public void testFilenameContainingSemicolon() throws Exception { final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); + "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); // Force to use memory-based data. final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); @@ -378,19 +373,19 @@ public void testFilenameContainingSemicolon() throws Exception { data + "\r\n" + "--" + boundary + "--\r\n"; - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8.name())); + req.payload().writeBytes(body.getBytes(CharsetUtil.UTF_8.name())); // Create decoder instance to test. final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); assertFalse(decoder.getBodyHttpDatas().isEmpty()); decoder.destroy(); - assertTrue(req.release()); + req.close(); } @Test public void testFilenameContainingSemicolon2() throws Exception { final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); + "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); // Force to use memory-based data. final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); @@ -404,7 +399,7 @@ public void testFilenameContainingSemicolon2() throws Exception { data + "\r\n" + "--" + boundary + "--\r\n"; - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8.name())); + req.payload().writeBytes(body.getBytes(CharsetUtil.UTF_8.name())); // Create decoder instance to test. final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); assertFalse(decoder.getBodyHttpDatas().isEmpty()); @@ -413,7 +408,7 @@ public void testFilenameContainingSemicolon2() throws Exception { FileUpload fileUpload = (FileUpload) part1; assertEquals("tmp 0.txt", fileUpload.getFilename()); decoder.destroy(); - assertTrue(req.release()); + req.close(); } @Test @@ -421,9 +416,9 @@ public void testMultipartRequestWithoutContentTypeBody() { final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); + "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); - req.setDecoderResult(DecoderResult.SUCCESS); + req.setDecoderResult(DecoderResult.success()); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); @@ -438,13 +433,13 @@ public void testMultipartRequestWithoutContentTypeBody() { data + "\r\n" + "--" + boundary + "--\r\n"; - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); + req.payload().writeBytes(body.getBytes(CharsetUtil.UTF_8)); } // Create decoder instance to test without any exception. final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); assertFalse(decoder.getBodyHttpDatas().isEmpty()); decoder.destroy(); - assertTrue(req.release()); + req.close(); } @Test @@ -465,7 +460,7 @@ public void testDecodeOtherMimeHeaderFields() throws Exception { final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); + Helpers.copiedBuffer(body.getBytes(Charset.defaultCharset()))); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); @@ -477,14 +472,14 @@ public void testDecodeOtherMimeHeaderFields() throws Exception { byte[] fileBytes = fileUpload.get(); assertTrue(filecontent.equals(new String(fileBytes)), "the filecontent should not be decoded"); decoder.destroy(); - assertTrue(req.release()); + req.close(); } @Test public void testMultipartRequestWithFileInvalidCharset() throws Exception { final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); + "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); // Force to use memory-based data. final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); @@ -498,7 +493,7 @@ public void testMultipartRequestWithFileInvalidCharset() throws Exception { data + "\r\n" + "--" + boundary + "--\r\n"; - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); + req.payload().writeBytes(body.getBytes(CharsetUtil.UTF_8)); // Create decoder instance to test. try { new HttpPostRequestDecoder(inMemoryFactory, req); @@ -506,7 +501,7 @@ public void testMultipartRequestWithFileInvalidCharset() throws Exception { } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { assertTrue(e.getCause() instanceof UnsupportedCharsetException); } finally { - assertTrue(req.release()); + req.close(); } } @@ -514,7 +509,7 @@ public void testMultipartRequestWithFileInvalidCharset() throws Exception { public void testMultipartRequestWithFieldInvalidCharset() throws Exception { final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); + "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); // Force to use memory-based data. final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); @@ -531,7 +526,7 @@ public void testMultipartRequestWithFieldInvalidCharset() throws Exception { "\r\n" + "--" + boundary + "--\r\n"; - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); + req.payload().writeBytes(body.getBytes(CharsetUtil.UTF_8)); // Create decoder instance to test. try { new HttpPostRequestDecoder(inMemoryFactory, req); @@ -539,14 +534,14 @@ public void testMultipartRequestWithFieldInvalidCharset() throws Exception { } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { assertTrue(e.getCause() instanceof UnsupportedCharsetException); } finally { - assertTrue(req.release()); + req.close(); } } @Test public void testFormEncodeIncorrect() throws Exception { LastHttpContent content = new DefaultLastHttpContent( - Unpooled.copiedBuffer("project=netty&=netty&project=netty", CharsetUtil.US_ASCII)); + Helpers.copiedBuffer("project=netty&=netty&project=netty", CharsetUtil.US_ASCII)); DefaultHttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(req); try { @@ -555,7 +550,7 @@ public void testFormEncodeIncorrect() throws Exception { } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { assertTrue(e.getCause() instanceof IllegalArgumentException); } finally { - content.release(); + content.close(); decoder.destroy(); } } @@ -580,7 +575,7 @@ public void testDecodeContentDispositionFieldParameters() throws Exception { final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); + Helpers.copiedBuffer(body.getBytes())); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); @@ -591,7 +586,7 @@ public void testDecodeContentDispositionFieldParameters() throws Exception { FileUpload fileUpload = (FileUpload) part1; assertEquals(filename, fileUpload.getFilename(), "the filename should be decoded"); decoder.destroy(); - assertTrue(req.release()); + req.close(); } // https://github.com/netty/netty/pull/7265 @@ -616,7 +611,7 @@ public void testDecodeWithLanguageContentDispositionFieldParameters() throws Exc final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); + Helpers.copiedBuffer(body.getBytes())); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); @@ -627,7 +622,7 @@ public void testDecodeWithLanguageContentDispositionFieldParameters() throws Exc FileUpload fileUpload = (FileUpload) part1; assertEquals(filename, fileUpload.getFilename(), "the filename should be decoded"); decoder.destroy(); - assertTrue(req.release()); + req.close(); } // https://github.com/netty/netty/pull/7265 @@ -646,7 +641,7 @@ public void testDecodeMalformedNotEncodedContentDispositionFieldParameters() thr final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); + Helpers.copiedBuffer(body.getBytes())); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); @@ -658,7 +653,7 @@ public void testDecodeMalformedNotEncodedContentDispositionFieldParameters() thr } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { assertTrue(e.getCause() instanceof ArrayIndexOutOfBoundsException); } finally { - assertTrue(req.release()); + req.close(); } } @@ -678,7 +673,7 @@ public void testDecodeMalformedBadCharsetContentDispositionFieldParameters() thr final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); + Helpers.copiedBuffer(body.getBytes())); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); @@ -690,7 +685,7 @@ public void testDecodeMalformedBadCharsetContentDispositionFieldParameters() thr } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { assertTrue(e.getCause() instanceof UnsupportedCharsetException); } finally { - assertTrue(req.release()); + req.close(); } } @@ -699,7 +694,7 @@ public void testDecodeMalformedBadCharsetContentDispositionFieldParameters() thr public void testDecodeMalformedEmptyContentTypeFieldParameters() throws Exception { final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); + "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); // Force to use memory-based data. final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); @@ -713,7 +708,7 @@ public void testDecodeMalformedEmptyContentTypeFieldParameters() throws Exceptio data + "\r\n" + "--" + boundary + "--\r\n"; - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8.name())); + req.payload().writeBytes(body.getBytes(CharsetUtil.UTF_8.name())); // Create decoder instance to test. final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); assertFalse(decoder.getBodyHttpDatas().isEmpty()); @@ -722,7 +717,7 @@ public void testDecodeMalformedEmptyContentTypeFieldParameters() throws Exceptio FileUpload fileUpload = (FileUpload) part1; assertEquals("tmp-0.txt", fileUpload.getFilename()); decoder.destroy(); - assertTrue(req.release()); + req.close(); } // https://github.com/netty/netty/issues/8575 @@ -739,7 +734,7 @@ public void testMultipartRequest() throws Exception { "\n" + "test message\n" + "--" + BOUNDARY + "--").getBytes(); - ByteBuf byteBuf = Unpooled.directBuffer(bodyBytes.length); + Buffer byteBuf = DefaultBufferAllocators.offHeapAllocator().allocate(bodyBytes.length); byteBuf.writeBytes(bodyBytes); FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST, "/up", byteBuf); @@ -755,20 +750,20 @@ public void testMultipartRequest() throws Exception { assertEquals(2, decoder.getBodyHttpDatas().size()); Attribute attrMsg = (Attribute) decoder.getBodyHttpData("msg"); - assertTrue(attrMsg.getByteBuf().isDirect()); + assertTrue(attrMsg.getBuffer().isDirect()); assertEquals("test message", attrMsg.getValue()); Attribute attrMsgId = (Attribute) decoder.getBodyHttpData("msg_id"); - assertTrue(attrMsgId.getByteBuf().isDirect()); + assertTrue(attrMsgId.getBuffer().isDirect()); assertEquals("15200", attrMsgId.getValue()); decoder.destroy(); - assertTrue(req.release()); + req.close(); } @Test public void testNotLeak() { final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", - Unpooled.copiedBuffer("a=1&=2&b=3", CharsetUtil.US_ASCII)); + Helpers.copiedBuffer("a=1&=2&b=3", CharsetUtil.US_ASCII)); try { assertThrows(HttpPostRequestDecoder.ErrorDataDecoderException.class, new Executable() { @Override @@ -777,7 +772,7 @@ public void execute() { } }); } finally { - assertTrue(request.release()); + request.close(); } } @@ -786,7 +781,7 @@ public void testNotLeakDirectBufferWhenWrapIllegalArgumentException() { assertThrows(HttpPostRequestDecoder.ErrorDataDecoderException.class, new Executable() { @Override public void execute() { - testNotLeakWhenWrapIllegalArgumentException(Unpooled.directBuffer()); + testNotLeakWhenWrapIllegalArgumentException(DefaultBufferAllocators.offHeapAllocator().allocate(0)); } }); } @@ -796,18 +791,18 @@ public void testNotLeakHeapBufferWhenWrapIllegalArgumentException() { assertThrows(HttpPostRequestDecoder.ErrorDataDecoderException.class, new Executable() { @Override public void execute() throws Throwable { - testNotLeakWhenWrapIllegalArgumentException(Unpooled.buffer()); + testNotLeakWhenWrapIllegalArgumentException(DefaultBufferAllocators.preferredAllocator().allocate(0)); } }); } - private static void testNotLeakWhenWrapIllegalArgumentException(ByteBuf buf) { + private static void testNotLeakWhenWrapIllegalArgumentException(Buffer buf) { buf.writeCharSequence("a=b&foo=%22bar%22&==", CharsetUtil.US_ASCII); FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", buf); try { new HttpPostStandardRequestDecoder(request).destroy(); } finally { - assertTrue(request.release()); + request.close(); } } @@ -845,7 +840,7 @@ public void testDecodeWithLanguageContentDispositionFieldParametersForFix() thro final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); + Helpers.copiedBuffer(body.getBytes())); req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); @@ -857,13 +852,13 @@ public void testDecodeWithLanguageContentDispositionFieldParametersForFix() thro assertEquals(filename, fileUpload.getFilename(), "the filename should be decoded"); decoder.destroy(); - assertTrue(req.release()); + req.close(); } @Test public void testDecodeFullHttpRequestWithUrlEncodedBody() throws Exception { byte[] bodyBytes = "foo=bar&a=b&empty=&city=%3c%22new%22%20york%20city%3e&other_city=los+angeles".getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); + Buffer content = DefaultBufferAllocators.offHeapAllocator().allocate(bodyBytes.length); content.writeBytes(bodyBytes); FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); @@ -874,33 +869,33 @@ public void testDecodeFullHttpRequestWithUrlEncodedBody() throws Exception { assertEquals(5, decoder.getBodyHttpDatas().size()); Attribute attr = (Attribute) decoder.getBodyHttpData("foo"); - assertTrue(attr.getByteBuf().isDirect()); + assertTrue(attr.getBuffer().isDirect()); assertEquals("bar", attr.getValue()); attr = (Attribute) decoder.getBodyHttpData("a"); - assertTrue(attr.getByteBuf().isDirect()); + assertTrue(attr.getBuffer().isDirect()); assertEquals("b", attr.getValue()); attr = (Attribute) decoder.getBodyHttpData("empty"); - assertTrue(attr.getByteBuf().isDirect()); + assertTrue(attr.getBuffer().isDirect()); assertEquals("", attr.getValue()); attr = (Attribute) decoder.getBodyHttpData("city"); - assertTrue(attr.getByteBuf().isDirect()); + assertTrue(attr.getBuffer().isDirect()); assertEquals("<\"new\" york city>", attr.getValue()); attr = (Attribute) decoder.getBodyHttpData("other_city"); - assertTrue(attr.getByteBuf().isDirect()); + assertTrue(attr.getBuffer().isDirect()); assertEquals("los angeles", attr.getValue()); decoder.destroy(); - assertTrue(req.release()); + req.close(); } @Test public void testDecodeFullHttpRequestWithUrlEncodedBodyWithBrokenHexByte0() { byte[] bodyBytes = "foo=bar&a=b&empty=%&city=paris".getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); + Buffer content = DefaultBufferAllocators.offHeapAllocator().allocate(bodyBytes.length); content.writeBytes(bodyBytes); FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); @@ -910,14 +905,14 @@ public void testDecodeFullHttpRequestWithUrlEncodedBodyWithBrokenHexByte0() { } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { assertEquals("Invalid hex byte at index '0' in string: '%'", e.getMessage()); } finally { - assertTrue(req.release()); + req.close(); } } @Test public void testDecodeFullHttpRequestWithUrlEncodedBodyWithBrokenHexByte1() { byte[] bodyBytes = "foo=bar&a=b&empty=%2&city=london".getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); + Buffer content = DefaultBufferAllocators.offHeapAllocator().allocate(bodyBytes.length); content.writeBytes(bodyBytes); FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); @@ -927,46 +922,40 @@ public void testDecodeFullHttpRequestWithUrlEncodedBodyWithBrokenHexByte1() { } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { assertEquals("Invalid hex byte at index '0' in string: '%2'", e.getMessage()); } finally { - assertTrue(req.release()); + req.close(); } } @Test public void testDecodeFullHttpRequestWithUrlEncodedBodyWithInvalidHexNibbleHi() { byte[] bodyBytes = "foo=bar&a=b&empty=%Zc&city=london".getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); + Buffer content = DefaultBufferAllocators.offHeapAllocator().allocate(bodyBytes.length); content.writeBytes(bodyBytes); - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); - try { + try (FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content)) { new HttpPostRequestDecoder(req); fail("Was expecting an ErrorDataDecoderException"); } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { assertEquals("Invalid hex byte at index '0' in string: '%Zc'", e.getMessage()); - } finally { - assertTrue(req.release()); } } @Test public void testDecodeFullHttpRequestWithUrlEncodedBodyWithInvalidHexNibbleLo() { byte[] bodyBytes = "foo=bar&a=b&empty=%2g&city=london".getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); + Buffer content = DefaultBufferAllocators.offHeapAllocator().allocate(bodyBytes.length); content.writeBytes(bodyBytes); - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); - try { + try (FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content)) { new HttpPostRequestDecoder(req); fail("Was expecting an ErrorDataDecoderException"); } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { assertEquals("Invalid hex byte at index '0' in string: '%2g'", e.getMessage()); - } finally { - assertTrue(req.release()); } } @Test - public void testDecodeMultipartRequest() { + public void testDecodeMultipartRequest() throws Exception { byte[] bodyBytes = ("--be38b42a9ad2713f\n" + "content-disposition: form-data; name=\"title\"\n" + "content-length: 10\n" + @@ -980,7 +969,7 @@ public void testDecodeMultipartRequest() { "\n" + "{\"title\":\"Test\"}\n" + "--be38b42a9ad2713f--").getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); + Buffer content = DefaultBufferAllocators.offHeapAllocator().allocate(bodyBytes.length); content.writeBytes(bodyBytes); FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); req.headers().add("Content-Type", "multipart/form-data;boundary=be38b42a9ad2713f"); @@ -991,16 +980,16 @@ public void testDecodeMultipartRequest() { InterfaceHttpData data = decoder.getBodyHttpData("title"); assertTrue(data instanceof MemoryAttribute); assertEquals("bar-stream", ((MemoryAttribute) data).getString()); - assertTrue(data.release()); + data.close(); data = decoder.getBodyHttpData("data"); assertTrue(data instanceof MemoryFileUpload); assertEquals("{\"title\":\"Test\"}", ((MemoryFileUpload) data).getString()); - assertTrue(data.release()); + data.close(); decoder.destroy(); } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { fail("Was not expecting an exception"); } finally { - assertTrue(req.release()); + req.close(); } } @@ -1014,15 +1003,15 @@ void testHttpPostStandardRequestDecoderToDiskNameContainingUnauthorizedChar() th "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaa").getBytes(CharsetUtil.US_ASCII); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); + Buffer content = DefaultBufferAllocators.offHeapAllocator().allocate(bodyBytes.length); content.writeBytes(bodyBytes); FullHttpRequest req = new DefaultFullHttpRequest( - HttpVersion.HTTP_1_1, - HttpMethod.POST, - "/", - content); + HttpVersion.HTTP_1_1, + HttpMethod.POST, + "/", + content); HttpPostStandardRequestDecoder decoder = null; try { decoder = new HttpPostStandardRequestDecoder( @@ -1036,7 +1025,8 @@ void testHttpPostStandardRequestDecoderToDiskNameContainingUnauthorizedChar() th } fail("Was not expecting an exception"); } finally { - assertTrue(req.release()); + req.close(); + assertFalse(req.isAccessible()); } } diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestEncoderTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestEncoderTest.java index 047b6e2..c613b30 100755 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestEncoderTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostRequestEncoderTest.java @@ -15,36 +15,32 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestEncoder.EncoderMode; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestEncoder.ErrorDataEncoderException; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.StringUtil; +import io.netty5.util.CharsetUtil; +import io.netty5.util.internal.StringUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.BufferAllocator; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.handler.codec.http.DefaultFullHttpRequest; +import io.netty5.handler.codec.http.DefaultHttpRequest; +import io.netty5.handler.codec.http.HttpConstants; +import io.netty5.handler.codec.http.HttpContent; +import io.netty5.handler.codec.http.HttpMethod; +import io.netty5.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.HttpVersion; +import io.netty5.handler.codec.http.LastHttpContent; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.File; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_DISPOSITION; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TRANSFER_ENCODING; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import static io.netty5.handler.codec.http.HttpHeaderNames.*; +import static org.junit.jupiter.api.Assertions.*; /** {@link HttpPostRequestEncoder} test case. */ public class HttpPostRequestEncoderTest { @@ -69,7 +65,7 @@ public void testAllowedMethods() throws Exception { private void shouldThrowExceptionIfNotAllowed(HttpMethod method) throws Exception { DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - method, "http://localhost"); + method, "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); File file1 = new File(getClass().getResource("/file-01.txt").toURI()); @@ -102,7 +98,7 @@ private void shouldThrowExceptionIfNotAllowed(HttpMethod method) throws Exceptio @Test public void testSingleFileUploadNoName() throws Exception { DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); + HttpMethod.POST, "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); File file1 = new File(getClass().getResource("/file-01.txt").toURI()); @@ -135,7 +131,7 @@ public void testSingleFileUploadNoName() throws Exception { @Test public void testMultiFileUploadInMixedMode() throws Exception { DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); + HttpMethod.POST, "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); File file1 = new File(getClass().getResource("/file-01.txt").toURI()); @@ -195,7 +191,7 @@ public void testMultiFileUploadInMixedMode() throws Exception { @Test public void testMultiFileUploadInMixedModeNoName() throws Exception { DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); + HttpMethod.POST, "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); File file1 = new File(getClass().getResource("/file-01.txt").toURI()); @@ -245,7 +241,7 @@ public void testMultiFileUploadInMixedModeNoName() throws Exception { @Test public void testSingleFileUploadInHtml5Mode() throws Exception { DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); + HttpMethod.POST, "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); DefaultHttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); @@ -289,7 +285,7 @@ public void testSingleFileUploadInHtml5Mode() throws Exception { @Test public void testMultiFileUploadInHtml5Mode() throws Exception { DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); + HttpMethod.POST, "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); DefaultHttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); @@ -325,7 +321,7 @@ public void testMultiFileUploadInHtml5Mode() throws Exception { @Test public void testHttpPostRequestEncoderSlicedBuffer() throws Exception { DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); + HttpMethod.POST, "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); // add Form attribute @@ -342,13 +338,13 @@ public void testHttpPostRequestEncoderSlicedBuffer() throws Exception { encoder.addBodyFileUpload("myfile", file1, "application/x-zip-compressed", false); encoder.finalizeRequest(); while (! encoder.isEndOfInput()) { - HttpContent httpContent = encoder.readChunk((ByteBufAllocator) null); - ByteBuf content = httpContent.content(); - int refCnt = content.refCnt(); - assertTrue((content.unwrap() == content || content.unwrap() == null) && refCnt == 1 || - content.unwrap() != content && refCnt == 2, - "content: " + content + " content.unwrap(): " + content.unwrap() + " refCnt: " + refCnt); - httpContent.release(); + HttpContent httpContent = encoder.readChunk((BufferAllocator) null); + Buffer content = httpContent.payload(); + // TODO how to test this ? +// assertTrue((content.unwrap() == content || content.unwrap() == null) || +// content.unwrap() != content && refCnt == 2, +// "content: " + content + " content.unwrap(): " + content.unwrap() + " refCnt: " + refCnt); + httpContent.close(); } encoder.cleanFiles(); encoder.close(); @@ -358,20 +354,20 @@ private static String getRequestBody(HttpPostRequestEncoder encoder) throws Exce encoder.finalizeRequest(); List chunks = encoder.multipartHttpDatas; - ByteBuf[] buffers = new ByteBuf[chunks.size()]; + Buffer[] buffers = new Buffer[chunks.size()]; for (int i = 0; i < buffers.length; i++) { InterfaceHttpData data = chunks.get(i); if (data instanceof InternalAttribute) { - buffers[i] = ((InternalAttribute) data).toByteBuf(); + buffers[i] = ((InternalAttribute) data).toBuffer(); } else if (data instanceof HttpData) { - buffers[i] = ((HttpData) data).getByteBuf(); + buffers[i] = ((HttpData) data).getBuffer(); } } - ByteBuf content = Unpooled.wrappedBuffer(buffers); + Buffer content = DefaultBufferAllocators.onHeapAllocator().compose(Stream.of(buffers).map(b -> b.send()).collect(Collectors.toList())); String contentStr = content.toString(CharsetUtil.UTF_8); - content.release(); + content.close(); return contentStr; } @@ -379,7 +375,7 @@ private static String getRequestBody(HttpPostRequestEncoder encoder) throws Exce public void testDataIsMultipleOfChunkSize1() throws Exception { DefaultHttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); + HttpMethod.POST, "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(factory, request, true, HttpConstants.DEFAULT_CHARSET, HttpPostRequestEncoder.EncoderMode.RFC1738); @@ -400,9 +396,9 @@ public void testDataIsMultipleOfChunkSize1() throws Exception { checkNextChunkSize(encoder, 8080); checkNextChunkSize(encoder, 8080); - HttpContent httpContent = encoder.readChunk((ByteBufAllocator) null); + HttpContent httpContent = encoder.readChunk((BufferAllocator) null); assertTrue(httpContent instanceof LastHttpContent, "Expected LastHttpContent is not received"); - httpContent.release(); + httpContent.close(); assertTrue(encoder.isEndOfInput(), "Expected end of input is not receive"); } @@ -410,7 +406,7 @@ public void testDataIsMultipleOfChunkSize1() throws Exception { @Test public void testDataIsMultipleOfChunkSize2() throws Exception { DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); + HttpMethod.POST, "http://localhost", DefaultBufferAllocators.preferredAllocator().allocate(0)); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); int length = 7943; char[] array = new char[length]; @@ -422,9 +418,9 @@ public void testDataIsMultipleOfChunkSize2() throws Exception { checkNextChunkSize(encoder, 8080); - HttpContent httpContent = encoder.readChunk((ByteBufAllocator) null); + HttpContent httpContent = encoder.readChunk((BufferAllocator) null); assertTrue(httpContent instanceof LastHttpContent, "Expected LastHttpContent is not received"); - httpContent.release(); + httpContent.close(); assertTrue(encoder.isEndOfInput(), "Expected end of input is not receive"); } @@ -437,13 +433,13 @@ private static void checkNextChunkSize(HttpPostRequestEncoder encoder, int sizeW int expectedSizeMin = sizeWithoutDelimiter + 2; int expectedSizeMax = sizeWithoutDelimiter + 16; - HttpContent httpContent = encoder.readChunk((ByteBufAllocator) null); + HttpContent httpContent = encoder.readChunk((BufferAllocator) null); - int readable = httpContent.content().readableBytes(); + int readable = httpContent.payload().readableBytes(); boolean expectedSize = readable >= expectedSizeMin && readable <= expectedSizeMax; assertTrue(expectedSize, "Chunk size is not in expected range (" + expectedSizeMin + " - " + expectedSizeMax + "), was: " + readable); - httpContent.release(); + httpContent.close(); } @Test @@ -462,7 +458,7 @@ public void testEncodeChunkedContent() throws Exception { assertNotNull(encoder.finalizeRequest()); while (!encoder.isEndOfInput()) { - encoder.readChunk((ByteBufAllocator) null).release(); + encoder.readChunk((BufferAllocator) null).close(); } assertTrue(encoder.isEndOfInput()); diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostStandardRequestDecoderTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostStandardRequestDecoderTest.java index d4f78df..aae2b37 100644 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostStandardRequestDecoderTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/HttpPostStandardRequestDecoderTest.java @@ -15,15 +15,15 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.CharsetUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.handler.codec.http.DefaultHttpContent; +import io.netty5.handler.codec.http.DefaultHttpRequest; +import io.netty5.handler.codec.http.DefaultLastHttpContent; +import io.netty5.handler.codec.http.HttpMethod; +import io.netty5.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.HttpVersion; +import io.netty5.util.CharsetUtil; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -37,8 +37,8 @@ void testDecodeAttributes() { HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload"); HttpPostStandardRequestDecoder decoder = new HttpPostStandardRequestDecoder(httpDiskDataFactory(), request); - ByteBuf buf = Unpooled.wrappedBuffer(requestBody.getBytes(CharsetUtil.UTF_8)); - DefaultHttpContent httpContent = new DefaultLastHttpContent(buf); + Buffer buf = DefaultBufferAllocators.preferredAllocator().copyOf(requestBody.getBytes(CharsetUtil.UTF_8)); + DefaultLastHttpContent httpContent = new DefaultLastHttpContent(buf); decoder.offer(httpContent); assertEquals(2, decoder.getBodyHttpDatas().size()); @@ -54,8 +54,8 @@ void testDecodeAttributesWithAmpersandPrefixSkipsNullAttribute() { HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload"); HttpPostStandardRequestDecoder decoder = new HttpPostStandardRequestDecoder(httpDiskDataFactory(), request); - ByteBuf buf = Unpooled.wrappedBuffer(requestBody.getBytes(CharsetUtil.UTF_8)); - DefaultHttpContent httpContent = new DefaultLastHttpContent(buf); + Buffer buf = DefaultBufferAllocators.preferredAllocator().copyOf(requestBody.getBytes(CharsetUtil.UTF_8)); + DefaultLastHttpContent httpContent = new DefaultLastHttpContent(buf); decoder.offer(httpContent); assertEquals(1, decoder.getBodyHttpDatas().size()); @@ -70,8 +70,8 @@ void testDecodeZeroAttributesWithAmpersandPrefix() { HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload"); HttpPostStandardRequestDecoder decoder = new HttpPostStandardRequestDecoder(httpDiskDataFactory(), request); - ByteBuf buf = Unpooled.wrappedBuffer(requestBody.getBytes(CharsetUtil.UTF_8)); - DefaultHttpContent httpContent = new DefaultLastHttpContent(buf); + Buffer buf = DefaultBufferAllocators.preferredAllocator().copyOf(requestBody.getBytes(CharsetUtil.UTF_8)); + DefaultLastHttpContent httpContent = new DefaultLastHttpContent(buf); decoder.offer(httpContent); assertEquals(0, decoder.getBodyHttpDatas().size()); diff --git a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/MixedTest.java b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/MixedTest.java index d6540c9..43d25a0 100644 --- a/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/MixedTest.java +++ b/codec-multipart/src/test/java/io/netty/contrib/handler/codec/http/multipart/MixedTest.java @@ -15,8 +15,9 @@ */ package io.netty.contrib.handler.codec.http.multipart; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.buffer.api.DefaultBufferAllocators; +import io.netty5.util.CharsetUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -24,34 +25,38 @@ public class MixedTest { @Test - public void mixedAttributeRefCnt() throws IOException { + public void mixAttributeClosed() throws IOException { MixedAttribute attribute = new MixedAttribute("foo", 100); - Assertions.assertEquals(1, attribute.refCnt()); - attribute.retain(); - Assertions.assertEquals(2, attribute.refCnt()); - - attribute.addContent(Unpooled.wrappedBuffer(new byte[90]), false); - Assertions.assertEquals(2, attribute.refCnt()); - - attribute.addContent(Unpooled.wrappedBuffer(new byte[90]), true); - Assertions.assertEquals(2, attribute.refCnt()); - - attribute.release(2); + byte[] bytes1 = new byte[90]; + Buffer buf1 = DefaultBufferAllocators.onHeapAllocator().allocate(bytes1.length); + buf1.writeBytes(bytes1); + attribute.setContent(buf1); + Assertions.assertTrue(buf1.isAccessible()); + + byte[] bytes2 = new byte[110]; + Buffer buf2 = DefaultBufferAllocators.onHeapAllocator().allocate(bytes2.length); + buf2.writeBytes(bytes2); + attribute.setContent(buf2); // buf1 should be closed because we have changed to Disk. buf2 should be also closed. + Assertions.assertFalse(buf1.isAccessible()); + Assertions.assertFalse(buf2.isAccessible()); + attribute.close(); } @Test - public void mixedFileUploadRefCnt() throws IOException { + public void mixedFileUploadClosed() throws IOException { MixedFileUpload upload = new MixedFileUpload("foo", "foo", "foo", "UTF-8", CharsetUtil.UTF_8, 0, 100); - Assertions.assertEquals(1, upload.refCnt()); - upload.retain(); - Assertions.assertEquals(2, upload.refCnt()); - - upload.addContent(Unpooled.wrappedBuffer(new byte[90]), false); - Assertions.assertEquals(2, upload.refCnt()); - - upload.addContent(Unpooled.wrappedBuffer(new byte[90]), true); - Assertions.assertEquals(2, upload.refCnt()); - - upload.release(2); + byte[] bytes1 = new byte[90]; + Buffer buf1 = DefaultBufferAllocators.onHeapAllocator().allocate(bytes1.length); + buf1.writeBytes(bytes1); + upload.setContent(buf1); + Assertions.assertTrue(buf1.isAccessible()); + + byte[] bytes2 = new byte[110]; + Buffer buf2 = DefaultBufferAllocators.onHeapAllocator().allocate(bytes2.length); + buf2.writeBytes(bytes2); + upload.setContent(buf2); // buf1 should be closed because we have changed to Disk. buf2 should be also closed. + Assertions.assertFalse(buf1.isAccessible()); + Assertions.assertFalse(buf2.isAccessible()); + upload.close(); } } diff --git a/examples/pom.xml b/examples/pom.xml index 5e40f98..06be36e 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -7,10 +7,10 @@ io.netty.contrib netty-codec-multipart-parent - 5.0.0.Final-SNAPSHOT + 5.0.0.Alpha2-SNAPSHOT - netty-template-examples + netty-codec-multipart-examples ${parent.version} @@ -19,10 +19,5 @@ netty-codec-multipart ${project.version} - - io.netty - netty-codec-http - ${netty.version} - - \ No newline at end of file + diff --git a/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadClient.java b/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadClient.java index 82b9170..7a9d1a8 100644 --- a/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadClient.java +++ b/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadClient.java @@ -15,32 +15,31 @@ */ package io.netty.contrib.handler.codec.example.http.multipart; -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.QueryStringEncoder; -import io.netty.handler.codec.http.cookie.ClientCookieEncoder; -import io.netty.handler.codec.http.cookie.DefaultCookie; import io.netty.contrib.handler.codec.http.multipart.DefaultHttpDataFactory; import io.netty.contrib.handler.codec.http.multipart.DiskAttribute; import io.netty.contrib.handler.codec.http.multipart.DiskFileUpload; import io.netty.contrib.handler.codec.http.multipart.HttpDataFactory; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestEncoder; import io.netty.contrib.handler.codec.http.multipart.InterfaceHttpData; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.util.internal.SocketUtils; +import io.netty5.bootstrap.Bootstrap; +import io.netty5.channel.Channel; +import io.netty5.channel.EventLoopGroup; +import io.netty5.channel.MultithreadEventLoopGroup; +import io.netty5.channel.nio.NioHandler; +import io.netty5.channel.socket.nio.NioSocketChannel; +import io.netty5.handler.codec.http.DefaultHttpRequest; +import io.netty5.handler.codec.http.HttpHeaderNames; +import io.netty5.handler.codec.http.HttpHeaderValues; +import io.netty5.handler.codec.http.HttpHeaders; +import io.netty5.handler.codec.http.HttpMethod; +import io.netty5.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.HttpVersion; +import io.netty5.handler.codec.http.QueryStringEncoder; +import io.netty5.handler.codec.http.cookie.ClientCookieEncoder; +import io.netty5.handler.codec.http.cookie.DefaultCookie; +import io.netty5.handler.ssl.SslContext; +import io.netty5.handler.ssl.SslContextBuilder; +import io.netty5.handler.ssl.util.InsecureTrustManagerFactory; import java.io.File; import java.io.FileNotFoundException; @@ -101,7 +100,7 @@ public static void main(String[] args) throws Exception { } // Configure the client. - EventLoopGroup group = new NioEventLoopGroup(); + EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); // setup the factory: here using a mixed memory/disk based on size threshold HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); // Disk if MINSIZE exceed @@ -150,7 +149,7 @@ private static List> formget( Bootstrap bootstrap, String host, int port, String get, URI uriSimple) throws Exception { // XXX /formget // No use of HttpPostRequestEncoder since not a POST - Channel channel = bootstrap.connect(host, port).sync().channel(); + Channel channel = bootstrap.connect(host, port).asStage().get(); // Prepare the HTTP request. QueryStringEncoder encoder = new QueryStringEncoder(get); @@ -190,7 +189,7 @@ private static List> formget( channel.writeAndFlush(request); // Wait for the server to close the connection. - channel.closeFuture().sync(); + channel.closeFuture().asStage().sync(); // convert headers to list return headers.entries(); @@ -207,9 +206,7 @@ private static List formpost( List> headers) throws Exception { // XXX /formpost // Start the connection attempt. - ChannelFuture future = bootstrap.connect(SocketUtils.socketAddress(host, port)); - // Wait until the connection attempt succeeds or fails. - Channel channel = future.sync().channel(); + Channel channel = bootstrap.connect(host, port).asStage().get(); // Prepare the HTTP request. HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString()); @@ -255,7 +252,7 @@ private static List formpost( // bodyRequestEncoder.cleanFiles(); // Wait for the server to close the connection. - channel.closeFuture().sync(); + channel.closeFuture().asStage().sync(); return bodylist; } @@ -267,9 +264,7 @@ private static void formpostmultipart( Iterable> headers, List bodylist) throws Exception { // XXX /formpostmultipart // Start the connection attempt. - ChannelFuture future = bootstrap.connect(SocketUtils.socketAddress(host, port)); - // Wait until the connection attempt succeeds or fails. - Channel channel = future.sync().channel(); + Channel channel = bootstrap.connect(host, port).asStage().get(); // Prepare the HTTP request. HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString()); @@ -302,7 +297,7 @@ private static void formpostmultipart( bodyRequestEncoder.cleanFiles(); // Wait for the server to close the connection. - channel.closeFuture().sync(); + channel.closeFuture().asStage().sync(); } // use to simulate a small TEXTAREA field in a form diff --git a/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadClientHandler.java b/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadClientHandler.java index 5f1ec8f..c6839c7 100644 --- a/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadClientHandler.java +++ b/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadClientHandler.java @@ -15,14 +15,14 @@ */ package io.netty.contrib.handler.codec.example.http.multipart; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.CharsetUtil; +import io.netty5.util.CharsetUtil; +import io.netty5.channel.ChannelHandlerContext; +import io.netty5.channel.SimpleChannelInboundHandler; +import io.netty5.handler.codec.http.HttpContent; +import io.netty5.handler.codec.http.HttpObject; +import io.netty5.handler.codec.http.HttpResponse; +import io.netty5.handler.codec.http.HttpUtil; +import io.netty5.handler.codec.http.LastHttpContent; /** * Handler that just dumps the contents of the response from the server @@ -32,7 +32,7 @@ public class HttpUploadClientHandler extends SimpleChannelInboundHandler { @@ -36,7 +36,7 @@ public void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); if (sslCtx != null) { - pipeline.addLast("ssl", sslCtx.newHandler(ch.alloc())); + pipeline.addLast("ssl", sslCtx.newHandler(ch.bufferAllocator())); } pipeline.addLast("codec", new HttpClientCodec()); diff --git a/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServer.java b/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServer.java index 5c3ad69..a88bf51 100644 --- a/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServer.java +++ b/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServer.java @@ -15,16 +15,17 @@ */ package io.netty.contrib.handler.codec.example.http.multipart; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; +import io.netty5.bootstrap.ServerBootstrap; +import io.netty5.channel.Channel; +import io.netty5.channel.EventLoopGroup; +import io.netty5.channel.MultithreadEventLoopGroup; +import io.netty5.channel.nio.NioHandler; +import io.netty5.channel.socket.nio.NioServerSocketChannel; +import io.netty5.handler.logging.LogLevel; +import io.netty5.handler.logging.LoggingHandler; +import io.netty5.handler.ssl.SslContext; +import io.netty5.handler.ssl.SslContextBuilder; +import io.netty5.handler.ssl.util.SelfSignedCertificate; /** * An HTTP server showing how to use the HTTP multipart package for file uploads and decoding post data. @@ -44,8 +45,9 @@ public static void main(String[] args) throws Exception { sslCtx = null; } - EventLoopGroup bossGroup = new NioEventLoopGroup(1); - EventLoopGroup workerGroup = new NioEventLoopGroup(); + EventLoopGroup bossGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); + EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); + try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup); @@ -53,12 +55,12 @@ public static void main(String[] args) throws Exception { b.handler(new LoggingHandler(LogLevel.INFO)); b.childHandler(new HttpUploadServerInitializer(sslCtx)); - Channel ch = b.bind(PORT).sync().channel(); + Channel ch = b.bind(PORT).asStage().get(); System.err.println("Open your web browser and navigate to " + (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); - ch.closeFuture().sync(); + ch.closeFuture().asStage().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); diff --git a/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServerHandler.java b/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServerHandler.java index e0b3c3b..fe5bf6d 100644 --- a/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServerHandler.java +++ b/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServerHandler.java @@ -15,28 +15,6 @@ */ package io.netty.contrib.handler.codec.example.http.multipart; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.cookie.ServerCookieDecoder; -import io.netty.handler.codec.http.cookie.ServerCookieEncoder; import io.netty.contrib.handler.codec.http.multipart.Attribute; import io.netty.contrib.handler.codec.http.multipart.DefaultHttpDataFactory; import io.netty.contrib.handler.codec.http.multipart.DiskAttribute; @@ -45,11 +23,31 @@ import io.netty.contrib.handler.codec.http.multipart.HttpData; import io.netty.contrib.handler.codec.http.multipart.HttpDataFactory; import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder; -import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException; -import io.netty.contrib.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; import io.netty.contrib.handler.codec.http.multipart.InterfaceHttpData; import io.netty.contrib.handler.codec.http.multipart.InterfaceHttpData.HttpDataType; -import io.netty.util.CharsetUtil; +import io.netty5.util.CharsetUtil; +import io.netty5.buffer.api.Buffer; +import io.netty5.channel.Channel; +import io.netty5.channel.ChannelFutureListeners; +import io.netty5.channel.ChannelHandlerContext; +import io.netty5.channel.SimpleChannelInboundHandler; +import io.netty5.handler.codec.http.DefaultFullHttpResponse; +import io.netty5.handler.codec.http.FullHttpResponse; +import io.netty5.handler.codec.http.HttpContent; +import io.netty5.handler.codec.http.HttpHeaderNames; +import io.netty5.handler.codec.http.HttpHeaderValues; +import io.netty5.handler.codec.http.HttpMethod; +import io.netty5.handler.codec.http.HttpObject; +import io.netty5.handler.codec.http.HttpRequest; +import io.netty5.handler.codec.http.HttpResponseStatus; +import io.netty5.handler.codec.http.HttpUtil; +import io.netty5.handler.codec.http.HttpVersion; +import io.netty5.handler.codec.http.LastHttpContent; +import io.netty5.handler.codec.http.QueryStringDecoder; +import io.netty5.handler.codec.http.cookie.Cookie; +import io.netty5.handler.codec.http.cookie.ServerCookieDecoder; +import io.netty5.handler.codec.http.cookie.ServerCookieEncoder; +import io.netty5.util.concurrent.Future; import java.io.IOException; import java.net.URI; @@ -61,8 +59,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import static io.netty.buffer.Unpooled.*; - public class HttpUploadServerHandler extends SimpleChannelInboundHandler { private static final Logger logger = Logger.getLogger(HttpUploadServerHandler.class.getName()); @@ -89,14 +85,14 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler future = channel.writeAndFlush(response); // Close the connection after the write operation is done if necessary. if (!keepAlive) { - future.addListener(ChannelFutureListener.CLOSE); + future.addListener(ctx, ChannelFutureListeners.CLOSE); } } @@ -421,7 +418,7 @@ private void writeMenu(ChannelHandlerContext ctx) { responseContent.append(""); responseContent.append(""); - ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8); + Buffer buf = ctx.bufferAllocator().copyOf(responseContent.toString(), CharsetUtil.UTF_8); // Build the response object. FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); @@ -438,15 +435,15 @@ private void writeMenu(ChannelHandlerContext ctx) { } // Write the response. - ChannelFuture future = ctx.channel().writeAndFlush(response); + Future future = ctx.channel().writeAndFlush(response); // Close the connection after the write operation is done if necessary. if (!keepAlive) { - future.addListener(ChannelFutureListener.CLOSE); + future.addListener(ctx, ChannelFutureListeners.CLOSE); } } @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + public void channelExceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.log(Level.WARNING, responseContent.toString(), cause); ctx.channel().close(); } diff --git a/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServerInitializer.java b/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServerInitializer.java index 7e09571..b00f56b 100644 --- a/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServerInitializer.java +++ b/examples/src/main/java/io/netty/contrib/handler/codec/example/http/multipart/HttpUploadServerInitializer.java @@ -15,13 +15,13 @@ */ package io.netty.contrib.handler.codec.example.http.multipart; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpContentCompressor; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.ssl.SslContext; +import io.netty5.channel.ChannelInitializer; +import io.netty5.channel.ChannelPipeline; +import io.netty5.channel.socket.SocketChannel; +import io.netty5.handler.codec.http.HttpContentCompressor; +import io.netty5.handler.codec.http.HttpRequestDecoder; +import io.netty5.handler.codec.http.HttpResponseEncoder; +import io.netty5.handler.ssl.SslContext; public class HttpUploadServerInitializer extends ChannelInitializer { @@ -36,7 +36,7 @@ public void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); if (sslCtx != null) { - pipeline.addLast(sslCtx.newHandler(ch.alloc())); + pipeline.addLast(sslCtx.newHandler(ch.bufferAllocator())); } pipeline.addLast(new HttpRequestDecoder()); diff --git a/pom.xml b/pom.xml index 5fd2b6b..dc23016 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ io.netty.contrib netty-codec-multipart-parent - 5.0.0.Final-SNAPSHOT + 5.0.0.Alpha2-SNAPSHOT Netty/Codec/Multipart Parent pom https://netty.io/ @@ -66,8 +66,8 @@ - 4.1.80.Final-SNAPSHOT - 29 + 5.0.0.Alpha5-SNAPSHOT + 30 github @@ -92,7 +92,7 @@ maven-checkstyle-plugin - 3.1.0 + 3.1.2 check-style