From 59dab76f317239ed6c9e10df72e1c41dab4adc02 Mon Sep 17 00:00:00 2001 From: rudy2steiner Date: Wed, 17 Mar 2021 21:22:41 +0800 Subject: [PATCH 1/4] Per stream data mime codec Signed-off-by: rudy2steiner --- .../rsocket/metadata/MimeMetadataCodec.java | 150 ++++++++++++++++++ .../metadata/MimeMetadataCodecTest.java | 55 +++++++ 2 files changed, 205 insertions(+) create mode 100644 rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java create mode 100644 rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java diff --git a/rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java b/rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java new file mode 100644 index 000000000..e5475fe2c --- /dev/null +++ b/rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java @@ -0,0 +1,150 @@ +/* + * Copyright 2015-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.rsocket.metadata; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.CompositeByteBuf; +import io.netty.util.CharsetUtil; +import java.util.ArrayList; +import java.util.List; + +/** + * + * A flyweight class that can be used to encode/decode stream data mime types. + *

For more on the above metadata formats, see the corresponding Per stream + * data mime types definition + * + **/ +public class MimeMetadataCodec { + static final int STREAM_METADATA_KNOWN_MASK = 0x80; // 1000 0000 + static final byte STREAM_METADATA_LENGTH_MASK = 0x7F; // 0111 1111 + + private MimeMetadataCodec() {} + + /** + * made up of a single byte: this represents an encoded mime id, which can be further + * decoded using {@link #decode(ByteBuf)} + **/ + public static ByteBuf encodeWellKnowMime( + ByteBufAllocator allocator, WellKnownMimeType mimeType) { + return allocator.buffer(1, 1) + .writeByte(mimeType.getIdentifier() | STREAM_METADATA_KNOWN_MASK); + } + + /** + * Encode {@link WellKnownMimeType} or custom mime type into a newly allocated {@link ByteBuf} + * @param allocator the {@link ByteBufAllocator} to use to create the buffer. + * @param mimeType mime type + * @return the encoded mime + **/ + public static ByteBuf encode( + ByteBufAllocator allocator, + String mimeType) { + if (mimeType == null || mimeType.length() == 0) { + throw new IllegalArgumentException("Mime type null or length is zero"); + } + WellKnownMimeType wkn = WellKnownMimeType.fromString(mimeType); + if (wkn == WellKnownMimeType.UNPARSEABLE_MIME_TYPE) { + return encodeCustomMime(allocator,mimeType); + }else { + return encodeWellKnowMime(allocator,wkn); + } + } + + /** + * Encode multiple {@link WellKnownMimeType} or custom mime type into a newly allocated {@link ByteBuf} + * @param allocator the {@link ByteBufAllocator} to use to create the buffer. + * @param mimeTypes mime types + * @return the encoded mimes + **/ + public static ByteBuf encode( + ByteBufAllocator allocator, + List mimeTypes) { + if (mimeTypes == null || mimeTypes.size() == 0) { + throw new IllegalArgumentException("Mime types empty"); + } + CompositeByteBuf compositeMimeByteBuf = allocator.compositeBuffer(); + for (String mimeType : mimeTypes) { + compositeMimeByteBuf.addComponents(true, encode(allocator, mimeType)); + } + return compositeMimeByteBuf; + } + + /** + * Encode a custom mime type into a newly allocated {@link ByteBuf}. + * + *

This larger representation encodes the mime type representation's length on a single byte, + * then the representation itself + * @param allocator the {@link ByteBufAllocator} to use to create the buffer. + * @param customMime a custom mime type to encode. + * @return the encoded mime + */ + public static ByteBuf encodeCustomMime( + ByteBufAllocator allocator, String customMime) { + ByteBuf mime = allocator.buffer(1 + customMime.length()); + // reserve 1 byte for the customMime length + // /!\ careful not to read that first byte, which is random at this point + int writerIndexInitial = mime.writerIndex(); + mime.writerIndex(writerIndexInitial + 1); + + // write the custom mime in UTF8 but validate it is all ASCII-compatible + // (which produces the right result since ASCII chars are still encoded on 1 byte in UTF8) + int customMimeLength = ByteBufUtil.writeUtf8(mime, customMime); + if (!ByteBufUtil.isText( + mime, mime.readerIndex() + 1, customMimeLength, CharsetUtil.US_ASCII)) { + mime.release(); + throw new IllegalArgumentException("custom mime type must be US_ASCII characters only"); + } + if (customMimeLength < 1 || customMimeLength > 128) { + mime.release(); + throw new IllegalArgumentException( + "custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128"); + } + mime.markWriterIndex(); + // go back to beginning and write the length + // encoded length is one less than actual length, since 0 is never a valid length, which gives + // wider representation range + mime.writerIndex(writerIndexInitial); + mime.writeByte(customMimeLength - 1); + + // go back to post-mime type + mime.resetWriterIndex(); + return mime; + } + + /** + * Decode mimes from a {@link ByteBuf} that contains at least enough bytes for one mime. + * @return decoded mime types + **/ + public static List decode(ByteBuf buf) { + List mimes = new ArrayList<>(); + while(buf.isReadable()){ + byte mimeIdOrLength = buf.readByte(); + if ((mimeIdOrLength & STREAM_METADATA_KNOWN_MASK) == STREAM_METADATA_KNOWN_MASK) { + byte mimeIdentifier = (byte)( mimeIdOrLength & STREAM_METADATA_LENGTH_MASK); + mimes.add(WellKnownMimeType.fromIdentifier(mimeIdentifier).toString()); + } + else{ + int mimeLen = Byte.toUnsignedInt(mimeIdOrLength) + 1; + mimes.add(buf.readCharSequence(mimeLen, CharsetUtil.US_ASCII).toString()); + } + } + return mimes; + } +} diff --git a/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java b/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java new file mode 100644 index 000000000..457ffb1da --- /dev/null +++ b/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2015-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.rsocket.metadata; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import org.assertj.core.util.Lists; +import org.junit.Assert; +import org.junit.Test; +import java.util.List; + +public class MimeMetadataCodecTest { + + @Test + public void customMime() { + String customMime = "aaa/bb"; + ByteBuf byteBuf = MimeMetadataCodec.encodeCustomMime(ByteBufAllocator.DEFAULT, customMime); + List mimes = MimeMetadataCodec.decode(byteBuf); + Assert.assertTrue(mimes.size() == 1); + Assert.assertEquals(customMime, mimes.get(0)); + } + + @Test + public void wellKnowMime() { + WellKnownMimeType wellKnownMimeType = WellKnownMimeType.APPLICATION_HESSIAN; + ByteBuf byteBuf = MimeMetadataCodec.encodeWellKnowMime(ByteBufAllocator.DEFAULT, wellKnownMimeType); + List mimes = MimeMetadataCodec.decode(byteBuf); + Assert.assertTrue(mimes.size() == 1); + Assert.assertEquals(wellKnownMimeType, WellKnownMimeType.fromString(mimes.get(0))); + } + + @Test + public void multipleAndMixTypeMime() { + List mimes = Lists.newArrayList("aaa/bbb", WellKnownMimeType.APPLICATION_HESSIAN.getString()); + ByteBuf byteBuf = MimeMetadataCodec.encode(ByteBufAllocator.DEFAULT, mimes); + List decodedMimes = MimeMetadataCodec.decode(byteBuf); + Assert.assertTrue(mimes.size() == 2); + Assert.assertTrue(mimes.containsAll(decodedMimes)); + Assert.assertTrue(decodedMimes.containsAll(mimes)); + } + +} From 72ff46f119ae28870cd771b578ef28c16a6f4cf0 Mon Sep 17 00:00:00 2001 From: rudy2steiner Date: Wed, 17 Mar 2021 21:51:06 +0800 Subject: [PATCH 2/4] checkstyle Signed-off-by: rudy2steiner --- .../rsocket/metadata/MimeMetadataCodec.java | 213 +++++++++--------- .../metadata/MimeMetadataCodecTest.java | 55 ++--- 2 files changed, 132 insertions(+), 136 deletions(-) diff --git a/rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java b/rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java index e5475fe2c..51150bb1e 100644 --- a/rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java +++ b/rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java @@ -24,127 +24,122 @@ import java.util.List; /** + * A flyweight class that can be used to encode/decode stream data mime types. * - * A flyweight class that can be used to encode/decode stream data mime types. *

For more on the above metadata formats, see the corresponding Per stream - * data mime types definition - * - **/ + * href="https://github.com/rsocket/rsocket/blob/master/Extensions/PerStreamDataMimeTypesDefinition.md">Per + * stream data mime types definition + */ public class MimeMetadataCodec { - static final int STREAM_METADATA_KNOWN_MASK = 0x80; // 1000 0000 - static final byte STREAM_METADATA_LENGTH_MASK = 0x7F; // 0111 1111 + static final int STREAM_METADATA_KNOWN_MASK = 0x80; // 1000 0000 + static final byte STREAM_METADATA_LENGTH_MASK = 0x7F; // 0111 1111 - private MimeMetadataCodec() {} + private MimeMetadataCodec() {} - /** - * made up of a single byte: this represents an encoded mime id, which can be further - * decoded using {@link #decode(ByteBuf)} - **/ - public static ByteBuf encodeWellKnowMime( - ByteBufAllocator allocator, WellKnownMimeType mimeType) { - return allocator.buffer(1, 1) - .writeByte(mimeType.getIdentifier() | STREAM_METADATA_KNOWN_MASK); - } + /** + * made up of a single byte: this represents an encoded mime id, which can be further decoded + * using {@link #decode(ByteBuf)} + */ + public static ByteBuf encodeWellKnowMime(ByteBufAllocator allocator, WellKnownMimeType mimeType) { + return allocator.buffer(1, 1).writeByte(mimeType.getIdentifier() | STREAM_METADATA_KNOWN_MASK); + } - /** - * Encode {@link WellKnownMimeType} or custom mime type into a newly allocated {@link ByteBuf} - * @param allocator the {@link ByteBufAllocator} to use to create the buffer. - * @param mimeType mime type - * @return the encoded mime - **/ - public static ByteBuf encode( - ByteBufAllocator allocator, - String mimeType) { - if (mimeType == null || mimeType.length() == 0) { - throw new IllegalArgumentException("Mime type null or length is zero"); - } - WellKnownMimeType wkn = WellKnownMimeType.fromString(mimeType); - if (wkn == WellKnownMimeType.UNPARSEABLE_MIME_TYPE) { - return encodeCustomMime(allocator,mimeType); - }else { - return encodeWellKnowMime(allocator,wkn); - } + /** + * Encode {@link WellKnownMimeType} or custom mime type into a newly allocated {@link ByteBuf} + * + * @param allocator the {@link ByteBufAllocator} to use to create the buffer. + * @param mimeType mime type + * @return the encoded mime + */ + public static ByteBuf encode(ByteBufAllocator allocator, String mimeType) { + if (mimeType == null || mimeType.length() == 0) { + throw new IllegalArgumentException("Mime type null or length is zero"); } - - /** - * Encode multiple {@link WellKnownMimeType} or custom mime type into a newly allocated {@link ByteBuf} - * @param allocator the {@link ByteBufAllocator} to use to create the buffer. - * @param mimeTypes mime types - * @return the encoded mimes - **/ - public static ByteBuf encode( - ByteBufAllocator allocator, - List mimeTypes) { - if (mimeTypes == null || mimeTypes.size() == 0) { - throw new IllegalArgumentException("Mime types empty"); - } - CompositeByteBuf compositeMimeByteBuf = allocator.compositeBuffer(); - for (String mimeType : mimeTypes) { - compositeMimeByteBuf.addComponents(true, encode(allocator, mimeType)); - } - return compositeMimeByteBuf; + WellKnownMimeType wkn = WellKnownMimeType.fromString(mimeType); + if (wkn == WellKnownMimeType.UNPARSEABLE_MIME_TYPE) { + return encodeCustomMime(allocator, mimeType); + } else { + return encodeWellKnowMime(allocator, wkn); } + } - /** - * Encode a custom mime type into a newly allocated {@link ByteBuf}. - * - *

This larger representation encodes the mime type representation's length on a single byte, - * then the representation itself - * @param allocator the {@link ByteBufAllocator} to use to create the buffer. - * @param customMime a custom mime type to encode. - * @return the encoded mime - */ - public static ByteBuf encodeCustomMime( - ByteBufAllocator allocator, String customMime) { - ByteBuf mime = allocator.buffer(1 + customMime.length()); - // reserve 1 byte for the customMime length - // /!\ careful not to read that first byte, which is random at this point - int writerIndexInitial = mime.writerIndex(); - mime.writerIndex(writerIndexInitial + 1); + /** + * Encode multiple {@link WellKnownMimeType} or custom mime type into a newly allocated {@link + * ByteBuf} + * + * @param allocator the {@link ByteBufAllocator} to use to create the buffer. + * @param mimeTypes mime types + * @return the encoded mimes + */ + public static ByteBuf encode(ByteBufAllocator allocator, List mimeTypes) { + if (mimeTypes == null || mimeTypes.size() == 0) { + throw new IllegalArgumentException("Mime types empty"); + } + CompositeByteBuf compositeMimeByteBuf = allocator.compositeBuffer(); + for (String mimeType : mimeTypes) { + compositeMimeByteBuf.addComponents(true, encode(allocator, mimeType)); + } + return compositeMimeByteBuf; + } - // write the custom mime in UTF8 but validate it is all ASCII-compatible - // (which produces the right result since ASCII chars are still encoded on 1 byte in UTF8) - int customMimeLength = ByteBufUtil.writeUtf8(mime, customMime); - if (!ByteBufUtil.isText( - mime, mime.readerIndex() + 1, customMimeLength, CharsetUtil.US_ASCII)) { - mime.release(); - throw new IllegalArgumentException("custom mime type must be US_ASCII characters only"); - } - if (customMimeLength < 1 || customMimeLength > 128) { - mime.release(); - throw new IllegalArgumentException( - "custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128"); - } - mime.markWriterIndex(); - // go back to beginning and write the length - // encoded length is one less than actual length, since 0 is never a valid length, which gives - // wider representation range - mime.writerIndex(writerIndexInitial); - mime.writeByte(customMimeLength - 1); + /** + * Encode a custom mime type into a newly allocated {@link ByteBuf}. + * + *

This larger representation encodes the mime type representation's length on a single byte, + * then the representation itself + * + * @param allocator the {@link ByteBufAllocator} to use to create the buffer. + * @param customMime a custom mime type to encode. + * @return the encoded mime + */ + public static ByteBuf encodeCustomMime(ByteBufAllocator allocator, String customMime) { + ByteBuf mime = allocator.buffer(1 + customMime.length()); + // reserve 1 byte for the customMime length + // /!\ careful not to read that first byte, which is random at this point + int writerIndexInitial = mime.writerIndex(); + mime.writerIndex(writerIndexInitial + 1); - // go back to post-mime type - mime.resetWriterIndex(); - return mime; + // write the custom mime in UTF8 but validate it is all ASCII-compatible + // (which produces the right result since ASCII chars are still encoded on 1 byte in UTF8) + int customMimeLength = ByteBufUtil.writeUtf8(mime, customMime); + if (!ByteBufUtil.isText(mime, mime.readerIndex() + 1, customMimeLength, CharsetUtil.US_ASCII)) { + mime.release(); + throw new IllegalArgumentException("custom mime type must be US_ASCII characters only"); } + if (customMimeLength < 1 || customMimeLength > 128) { + mime.release(); + throw new IllegalArgumentException( + "custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128"); + } + mime.markWriterIndex(); + // go back to beginning and write the length + // encoded length is one less than actual length, since 0 is never a valid length, which gives + // wider representation range + mime.writerIndex(writerIndexInitial); + mime.writeByte(customMimeLength - 1); + + // go back to post-mime type + mime.resetWriterIndex(); + return mime; + } - /** - * Decode mimes from a {@link ByteBuf} that contains at least enough bytes for one mime. - * @return decoded mime types - **/ - public static List decode(ByteBuf buf) { - List mimes = new ArrayList<>(); - while(buf.isReadable()){ - byte mimeIdOrLength = buf.readByte(); - if ((mimeIdOrLength & STREAM_METADATA_KNOWN_MASK) == STREAM_METADATA_KNOWN_MASK) { - byte mimeIdentifier = (byte)( mimeIdOrLength & STREAM_METADATA_LENGTH_MASK); - mimes.add(WellKnownMimeType.fromIdentifier(mimeIdentifier).toString()); - } - else{ - int mimeLen = Byte.toUnsignedInt(mimeIdOrLength) + 1; - mimes.add(buf.readCharSequence(mimeLen, CharsetUtil.US_ASCII).toString()); - } - } - return mimes; + /** + * Decode mimes from a {@link ByteBuf} that contains at least enough bytes for one mime. + * + * @return decoded mime types + */ + public static List decode(ByteBuf buf) { + List mimes = new ArrayList<>(); + while (buf.isReadable()) { + byte mimeIdOrLength = buf.readByte(); + if ((mimeIdOrLength & STREAM_METADATA_KNOWN_MASK) == STREAM_METADATA_KNOWN_MASK) { + byte mimeIdentifier = (byte) (mimeIdOrLength & STREAM_METADATA_LENGTH_MASK); + mimes.add(WellKnownMimeType.fromIdentifier(mimeIdentifier).toString()); + } else { + int mimeLen = Byte.toUnsignedInt(mimeIdOrLength) + 1; + mimes.add(buf.readCharSequence(mimeLen, CharsetUtil.US_ASCII).toString()); + } } + return mimes; + } } diff --git a/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java b/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java index 457ffb1da..30c8ece44 100644 --- a/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java +++ b/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java @@ -17,39 +17,40 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; +import java.util.List; import org.assertj.core.util.Lists; import org.junit.Assert; import org.junit.Test; -import java.util.List; public class MimeMetadataCodecTest { - @Test - public void customMime() { - String customMime = "aaa/bb"; - ByteBuf byteBuf = MimeMetadataCodec.encodeCustomMime(ByteBufAllocator.DEFAULT, customMime); - List mimes = MimeMetadataCodec.decode(byteBuf); - Assert.assertTrue(mimes.size() == 1); - Assert.assertEquals(customMime, mimes.get(0)); - } - - @Test - public void wellKnowMime() { - WellKnownMimeType wellKnownMimeType = WellKnownMimeType.APPLICATION_HESSIAN; - ByteBuf byteBuf = MimeMetadataCodec.encodeWellKnowMime(ByteBufAllocator.DEFAULT, wellKnownMimeType); - List mimes = MimeMetadataCodec.decode(byteBuf); - Assert.assertTrue(mimes.size() == 1); - Assert.assertEquals(wellKnownMimeType, WellKnownMimeType.fromString(mimes.get(0))); - } + @Test + public void customMime() { + String customMime = "aaa/bb"; + ByteBuf byteBuf = MimeMetadataCodec.encodeCustomMime(ByteBufAllocator.DEFAULT, customMime); + List mimes = MimeMetadataCodec.decode(byteBuf); + Assert.assertTrue(mimes.size() == 1); + Assert.assertEquals(customMime, mimes.get(0)); + } - @Test - public void multipleAndMixTypeMime() { - List mimes = Lists.newArrayList("aaa/bbb", WellKnownMimeType.APPLICATION_HESSIAN.getString()); - ByteBuf byteBuf = MimeMetadataCodec.encode(ByteBufAllocator.DEFAULT, mimes); - List decodedMimes = MimeMetadataCodec.decode(byteBuf); - Assert.assertTrue(mimes.size() == 2); - Assert.assertTrue(mimes.containsAll(decodedMimes)); - Assert.assertTrue(decodedMimes.containsAll(mimes)); - } + @Test + public void wellKnowMime() { + WellKnownMimeType wellKnownMimeType = WellKnownMimeType.APPLICATION_HESSIAN; + ByteBuf byteBuf = + MimeMetadataCodec.encodeWellKnowMime(ByteBufAllocator.DEFAULT, wellKnownMimeType); + List mimes = MimeMetadataCodec.decode(byteBuf); + Assert.assertTrue(mimes.size() == 1); + Assert.assertEquals(wellKnownMimeType, WellKnownMimeType.fromString(mimes.get(0))); + } + @Test + public void multipleAndMixTypeMime() { + List mimes = + Lists.newArrayList("aaa/bbb", WellKnownMimeType.APPLICATION_HESSIAN.getString()); + ByteBuf byteBuf = MimeMetadataCodec.encode(ByteBufAllocator.DEFAULT, mimes); + List decodedMimes = MimeMetadataCodec.decode(byteBuf); + Assert.assertTrue(mimes.size() == 2); + Assert.assertTrue(mimes.containsAll(decodedMimes)); + Assert.assertTrue(decodedMimes.containsAll(mimes)); + } } From 0a422f97d3bf7341b7011aa23ad1af59a81644b5 Mon Sep 17 00:00:00 2001 From: rudy2steiner Date: Fri, 19 Mar 2021 23:45:04 +0800 Subject: [PATCH 3/4] more acurrate comment and naming Signed-off-by: rudy2steiner --- ...aCodec.java => MimeTypeMetadataCodec.java} | 62 ++++++++++--------- .../metadata/MimeMetadataCodecTest.java | 37 +++++------ 2 files changed, 49 insertions(+), 50 deletions(-) rename rsocket-core/src/main/java/io/rsocket/metadata/{MimeMetadataCodec.java => MimeTypeMetadataCodec.java} (72%) diff --git a/rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java b/rsocket-core/src/main/java/io/rsocket/metadata/MimeTypeMetadataCodec.java similarity index 72% rename from rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java rename to rsocket-core/src/main/java/io/rsocket/metadata/MimeTypeMetadataCodec.java index 51150bb1e..3c1b44a1f 100644 --- a/rsocket-core/src/main/java/io/rsocket/metadata/MimeMetadataCodec.java +++ b/rsocket-core/src/main/java/io/rsocket/metadata/MimeTypeMetadataCodec.java @@ -24,32 +24,35 @@ import java.util.List; /** - * A flyweight class that can be used to encode/decode stream data mime types. + * Provides support for encoding and/or decoding the per-stream MIME type to use for payload data. * - *

For more on the above metadata formats, see the corresponding Per - * stream data mime types definition + *

For more on the format of the metadata, see the + * Stream Data MIME Types extension specification. + * + * @since 1.1.1 */ -public class MimeMetadataCodec { - static final int STREAM_METADATA_KNOWN_MASK = 0x80; // 1000 0000 - static final byte STREAM_METADATA_LENGTH_MASK = 0x7F; // 0111 1111 +public class MimeTypeMetadataCodec { + private static final int STREAM_METADATA_KNOWN_MASK = 0x80; // 1000 0000 + private static final byte STREAM_METADATA_LENGTH_MASK = 0x7F; // 0111 1111 - private MimeMetadataCodec() {} + private MimeTypeMetadataCodec() {} /** - * made up of a single byte: this represents an encoded mime id, which can be further decoded - * using {@link #decode(ByteBuf)} + * Encode a {@link WellKnownMimeType} into a newly allocated {@link ByteBuf} and this can then be + * decoded using {@link #decode(ByteBuf)}. */ - public static ByteBuf encodeWellKnowMime(ByteBufAllocator allocator, WellKnownMimeType mimeType) { + public static ByteBuf encode(ByteBufAllocator allocator, WellKnownMimeType mimeType) { return allocator.buffer(1, 1).writeByte(mimeType.getIdentifier() | STREAM_METADATA_KNOWN_MASK); } /** - * Encode {@link WellKnownMimeType} or custom mime type into a newly allocated {@link ByteBuf} + * Encode either a {@link WellKnownMimeType} or a custom mime type into a newly allocated {@link + * ByteBuf}. * - * @param allocator the {@link ByteBufAllocator} to use to create the buffer. + * @param allocator the {@link ByteBufAllocator} to use to create the buffer * @param mimeType mime type - * @return the encoded mime + * @return the encoded mime type */ public static ByteBuf encode(ByteBufAllocator allocator, String mimeType) { if (mimeType == null || mimeType.length() == 0) { @@ -59,17 +62,17 @@ public static ByteBuf encode(ByteBufAllocator allocator, String mimeType) { if (wkn == WellKnownMimeType.UNPARSEABLE_MIME_TYPE) { return encodeCustomMime(allocator, mimeType); } else { - return encodeWellKnowMime(allocator, wkn); + return encode(allocator, wkn); } } /** * Encode multiple {@link WellKnownMimeType} or custom mime type into a newly allocated {@link - * ByteBuf} + * ByteBuf}. * - * @param allocator the {@link ByteBufAllocator} to use to create the buffer. + * @param allocator the {@link ByteBufAllocator} to use to create the buffer * @param mimeTypes mime types - * @return the encoded mimes + * @return the encoded mime types */ public static ByteBuf encode(ByteBufAllocator allocator, List mimeTypes) { if (mimeTypes == null || mimeTypes.size() == 0) { @@ -86,18 +89,17 @@ public static ByteBuf encode(ByteBufAllocator allocator, List mimeTypes) * Encode a custom mime type into a newly allocated {@link ByteBuf}. * *

This larger representation encodes the mime type representation's length on a single byte, - * then the representation itself + * then the representation itself. * - * @param allocator the {@link ByteBufAllocator} to use to create the buffer. - * @param customMime a custom mime type to encode. + * @param allocator the {@link ByteBufAllocator} to use to create the buffer + * @param customMime a custom mime type to encode * @return the encoded mime */ - public static ByteBuf encodeCustomMime(ByteBufAllocator allocator, String customMime) { + private static ByteBuf encodeCustomMime(ByteBufAllocator allocator, String customMime) { ByteBuf mime = allocator.buffer(1 + customMime.length()); // reserve 1 byte for the customMime length // /!\ careful not to read that first byte, which is random at this point - int writerIndexInitial = mime.writerIndex(); - mime.writerIndex(writerIndexInitial + 1); + mime.writerIndex(1); // write the custom mime in UTF8 but validate it is all ASCII-compatible // (which produces the right result since ASCII chars are still encoded on 1 byte in UTF8) @@ -115,7 +117,7 @@ public static ByteBuf encodeCustomMime(ByteBufAllocator allocator, String custom // go back to beginning and write the length // encoded length is one less than actual length, since 0 is never a valid length, which gives // wider representation range - mime.writerIndex(writerIndexInitial); + mime.writerIndex(0); mime.writeByte(customMimeLength - 1); // go back to post-mime type @@ -124,22 +126,22 @@ public static ByteBuf encodeCustomMime(ByteBufAllocator allocator, String custom } /** - * Decode mimes from a {@link ByteBuf} that contains at least enough bytes for one mime. + * Decode mime types from a {@link ByteBuf} that contains at least enough bytes for one mime type. * * @return decoded mime types */ public static List decode(ByteBuf buf) { - List mimes = new ArrayList<>(); + List mimeTypes = new ArrayList<>(); while (buf.isReadable()) { byte mimeIdOrLength = buf.readByte(); if ((mimeIdOrLength & STREAM_METADATA_KNOWN_MASK) == STREAM_METADATA_KNOWN_MASK) { byte mimeIdentifier = (byte) (mimeIdOrLength & STREAM_METADATA_LENGTH_MASK); - mimes.add(WellKnownMimeType.fromIdentifier(mimeIdentifier).toString()); + mimeTypes.add(WellKnownMimeType.fromIdentifier(mimeIdentifier).toString()); } else { int mimeLen = Byte.toUnsignedInt(mimeIdOrLength) + 1; - mimes.add(buf.readCharSequence(mimeLen, CharsetUtil.US_ASCII).toString()); + mimeTypes.add(buf.readCharSequence(mimeLen, CharsetUtil.US_ASCII).toString()); } } - return mimes; + return mimeTypes; } } diff --git a/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java b/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java index 30c8ece44..01bc11ab2 100644 --- a/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java +++ b/rsocket-core/src/test/java/io/rsocket/metadata/MimeMetadataCodecTest.java @@ -18,39 +18,36 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import java.util.List; +import org.assertj.core.api.Assertions; import org.assertj.core.util.Lists; -import org.junit.Assert; import org.junit.Test; public class MimeMetadataCodecTest { @Test - public void customMime() { - String customMime = "aaa/bb"; - ByteBuf byteBuf = MimeMetadataCodec.encodeCustomMime(ByteBufAllocator.DEFAULT, customMime); - List mimes = MimeMetadataCodec.decode(byteBuf); - Assert.assertTrue(mimes.size() == 1); - Assert.assertEquals(customMime, mimes.get(0)); + public void customMimeType() { + String customMimeType = "aaa/bb"; + ByteBuf byteBuf = MimeTypeMetadataCodec.encode(ByteBufAllocator.DEFAULT, customMimeType); + List mimeTypes = MimeTypeMetadataCodec.decode(byteBuf); + Assertions.assertThat(mimeTypes.size()).isEqualTo(1); + Assertions.assertThat(customMimeType).isEqualTo(mimeTypes.get(0)); } @Test - public void wellKnowMime() { + public void wellKnowMimeType() { WellKnownMimeType wellKnownMimeType = WellKnownMimeType.APPLICATION_HESSIAN; - ByteBuf byteBuf = - MimeMetadataCodec.encodeWellKnowMime(ByteBufAllocator.DEFAULT, wellKnownMimeType); - List mimes = MimeMetadataCodec.decode(byteBuf); - Assert.assertTrue(mimes.size() == 1); - Assert.assertEquals(wellKnownMimeType, WellKnownMimeType.fromString(mimes.get(0))); + ByteBuf byteBuf = MimeTypeMetadataCodec.encode(ByteBufAllocator.DEFAULT, wellKnownMimeType); + List mimes = MimeTypeMetadataCodec.decode(byteBuf); + Assertions.assertThat(mimes.size()).isEqualTo(1); + Assertions.assertThat(wellKnownMimeType).isEqualTo(WellKnownMimeType.fromString(mimes.get(0))); } @Test - public void multipleAndMixTypeMime() { - List mimes = + public void multipleAndMixMimeType() { + List mimeTypes = Lists.newArrayList("aaa/bbb", WellKnownMimeType.APPLICATION_HESSIAN.getString()); - ByteBuf byteBuf = MimeMetadataCodec.encode(ByteBufAllocator.DEFAULT, mimes); - List decodedMimes = MimeMetadataCodec.decode(byteBuf); - Assert.assertTrue(mimes.size() == 2); - Assert.assertTrue(mimes.containsAll(decodedMimes)); - Assert.assertTrue(decodedMimes.containsAll(mimes)); + ByteBuf byteBuf = MimeTypeMetadataCodec.encode(ByteBufAllocator.DEFAULT, mimeTypes); + List decodedMimeTypes = MimeTypeMetadataCodec.decode(byteBuf); + Assertions.assertThat(decodedMimeTypes).isEqualTo(mimeTypes); } } From 682e3555383ff96fb889f35f08d177d0e1252221 Mon Sep 17 00:00:00 2001 From: rudy2steiner Date: Fri, 19 Mar 2021 23:55:00 +0800 Subject: [PATCH 4/4] rename encodeCustomMime method Signed-off-by: rudy2steiner --- .../io/rsocket/metadata/MimeTypeMetadataCodec.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rsocket-core/src/main/java/io/rsocket/metadata/MimeTypeMetadataCodec.java b/rsocket-core/src/main/java/io/rsocket/metadata/MimeTypeMetadataCodec.java index 3c1b44a1f..16203e9f5 100644 --- a/rsocket-core/src/main/java/io/rsocket/metadata/MimeTypeMetadataCodec.java +++ b/rsocket-core/src/main/java/io/rsocket/metadata/MimeTypeMetadataCodec.java @@ -60,7 +60,7 @@ public static ByteBuf encode(ByteBufAllocator allocator, String mimeType) { } WellKnownMimeType wkn = WellKnownMimeType.fromString(mimeType); if (wkn == WellKnownMimeType.UNPARSEABLE_MIME_TYPE) { - return encodeCustomMime(allocator, mimeType); + return encodeCustomMimeType(allocator, mimeType); } else { return encode(allocator, wkn); } @@ -92,18 +92,18 @@ public static ByteBuf encode(ByteBufAllocator allocator, List mimeTypes) * then the representation itself. * * @param allocator the {@link ByteBufAllocator} to use to create the buffer - * @param customMime a custom mime type to encode - * @return the encoded mime + * @param customMimeType a custom mime type to encode + * @return the encoded mime type */ - private static ByteBuf encodeCustomMime(ByteBufAllocator allocator, String customMime) { - ByteBuf mime = allocator.buffer(1 + customMime.length()); + private static ByteBuf encodeCustomMimeType(ByteBufAllocator allocator, String customMimeType) { + ByteBuf mime = allocator.buffer(1 + customMimeType.length()); // reserve 1 byte for the customMime length // /!\ careful not to read that first byte, which is random at this point mime.writerIndex(1); // write the custom mime in UTF8 but validate it is all ASCII-compatible // (which produces the right result since ASCII chars are still encoded on 1 byte in UTF8) - int customMimeLength = ByteBufUtil.writeUtf8(mime, customMime); + int customMimeLength = ByteBufUtil.writeUtf8(mime, customMimeType); if (!ByteBufUtil.isText(mime, mime.readerIndex() + 1, customMimeLength, CharsetUtil.US_ASCII)) { mime.release(); throw new IllegalArgumentException("custom mime type must be US_ASCII characters only");