11/*
2- * Copyright 2015-2018 the original author or authors.
2+ * Copyright 2015-2021 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
2424import java .util .List ;
2525
2626/**
27- * Provides support for encoding and/or decoding the per-stream MIME type to use for payload data.
27+ * Provides support for encoding and decoding the per-stream MIME type to use for payload data.
2828 *
2929 * <p>For more on the format of the metadata, see the <a
3030 * href="https://github.com/rsocket/rsocket/blob/master/Extensions/PerStreamDataMimeTypesDefinition.md">
3333 * @since 1.1.1
3434 */
3535public class MimeTypeMetadataCodec {
36+
3637 private static final int STREAM_METADATA_KNOWN_MASK = 0x80 ; // 1000 0000
38+
3739 private static final byte STREAM_METADATA_LENGTH_MASK = 0x7F ; // 0111 1111
3840
3941 private MimeTypeMetadataCodec () {}
4042
4143 /**
42- * Encode a {@link WellKnownMimeType} into a newly allocated {@link ByteBuf} and this can then be
43- * decoded using {@link #decode(ByteBuf)}.
44+ * Encode a {@link WellKnownMimeType} into a newly allocated single byte {@link ByteBuf}.
45+ *
46+ * @param allocator the allocator to create the buffer with
47+ * @param mimeType well-known MIME type to encode
48+ * @return the resulting buffer
4449 */
4550 public static ByteBuf encode (ByteBufAllocator allocator , WellKnownMimeType mimeType ) {
4651 return allocator .buffer (1 , 1 ).writeByte (mimeType .getIdentifier () | STREAM_METADATA_KNOWN_MASK );
4752 }
4853
4954 /**
50- * Encode either a {@link WellKnownMimeType} or a custom mime type into a newly allocated {@link
51- * ByteBuf}.
55+ * Encode the given MIME type into a newly allocated {@link ByteBuf}.
5256 *
53- * @param allocator the {@link ByteBufAllocator} to use to create the buffer
54- * @param mimeType mime type
55- * @return the encoded mime type
57+ * @param allocator the allocator to create the buffer with
58+ * @param mimeType MIME type to encode
59+ * @return the resulting buffer
5660 */
5761 public static ByteBuf encode (ByteBufAllocator allocator , String mimeType ) {
5862 if (mimeType == null || mimeType .length () == 0 ) {
59- throw new IllegalArgumentException ("Mime type null or length is zero " );
63+ throw new IllegalArgumentException ("MIME type is required " );
6064 }
6165 WellKnownMimeType wkn = WellKnownMimeType .fromString (mimeType );
6266 if (wkn == WellKnownMimeType .UNPARSEABLE_MIME_TYPE ) {
@@ -67,79 +71,65 @@ public static ByteBuf encode(ByteBufAllocator allocator, String mimeType) {
6771 }
6872
6973 /**
70- * Encode multiple {@link WellKnownMimeType} or custom mime type into a newly allocated {@link
71- * ByteBuf}.
74+ * Encode multiple MIME types into a newly allocated {@link ByteBuf}.
7275 *
73- * @param allocator the {@link ByteBufAllocator} to use to create the buffer
74- * @param mimeTypes mime types
75- * @return the encoded mime types
76+ * @param allocator the allocator to create the buffer with
77+ * @param mimeTypes MIME types to encode
78+ * @return the resulting buffer
7679 */
7780 public static ByteBuf encode (ByteBufAllocator allocator , List <String > mimeTypes ) {
7881 if (mimeTypes == null || mimeTypes .size () == 0 ) {
79- throw new IllegalArgumentException ("Mime types empty " );
82+ throw new IllegalArgumentException ("No MIME types provided " );
8083 }
81- CompositeByteBuf compositeMimeByteBuf = allocator .compositeBuffer ();
84+ CompositeByteBuf compositeByteBuf = allocator .compositeBuffer ();
8285 for (String mimeType : mimeTypes ) {
83- compositeMimeByteBuf .addComponents (true , encode (allocator , mimeType ));
86+ ByteBuf byteBuf = encode (allocator , mimeType );
87+ compositeByteBuf .addComponents (true , byteBuf );
8488 }
85- return compositeMimeByteBuf ;
89+ return compositeByteBuf ;
8690 }
8791
88- /**
89- * Encode a custom mime type into a newly allocated {@link ByteBuf}.
90- *
91- * <p>This larger representation encodes the mime type representation's length on a single byte,
92- * then the representation itself.
93- *
94- * @param allocator the {@link ByteBufAllocator} to use to create the buffer
95- * @param customMimeType a custom mime type to encode
96- * @return the encoded mime type
97- */
9892 private static ByteBuf encodeCustomMimeType (ByteBufAllocator allocator , String customMimeType ) {
99- ByteBuf mime = allocator .buffer (1 + customMimeType .length ());
100- // reserve 1 byte for the customMime length
101- // /!\ careful not to read that first byte, which is random at this point
102- mime . writerIndex ( 1 );
93+ ByteBuf byteBuf = allocator .buffer (1 + customMimeType .length ());
94+
95+ byteBuf . writerIndex ( 1 );
96+ int length = ByteBufUtil . writeUtf8 ( byteBuf , customMimeType );
10397
104- // write the custom mime in UTF8 but validate it is all ASCII-compatible
105- // (which produces the right result since ASCII chars are still encoded on 1 byte in UTF8)
106- int customMimeLength = ByteBufUtil .writeUtf8 (mime , customMimeType );
107- if (!ByteBufUtil .isText (mime , mime .readerIndex () + 1 , customMimeLength , CharsetUtil .US_ASCII )) {
108- mime .release ();
109- throw new IllegalArgumentException ("custom mime type must be US_ASCII characters only" );
98+ if (!ByteBufUtil .isText (byteBuf , 1 , length , CharsetUtil .US_ASCII )) {
99+ byteBuf .release ();
100+ throw new IllegalArgumentException ("MIME type must be ASCII characters only" );
110101 }
111- if (customMimeLength < 1 || customMimeLength > 128 ) {
112- mime .release ();
102+
103+ if (length < 1 || length > 128 ) {
104+ byteBuf .release ();
113105 throw new IllegalArgumentException (
114- "custom mime type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128" );
106+ "MIME type must have a strictly positive length that fits on 7 unsigned bits, ie 1-128" );
115107 }
116- mime .markWriterIndex ();
117- // go back to beginning and write the length
118- // encoded length is one less than actual length, since 0 is never a valid length, which gives
119- // wider representation range
120- mime .writerIndex (0 );
121- mime .writeByte (customMimeLength - 1 );
122108
123- // go back to post-mime type
124- mime .resetWriterIndex ();
125- return mime ;
109+ byteBuf .markWriterIndex ();
110+ byteBuf .writerIndex (0 );
111+ byteBuf .writeByte (length - 1 );
112+ byteBuf .resetWriterIndex ();
113+
114+ return byteBuf ;
126115 }
127116
128117 /**
129- * Decode mime types from a {@link ByteBuf} that contains at least enough bytes for one mime type .
118+ * Decode the per-stream MIME type metadata encoded in the given {@link ByteBuf} .
130119 *
131- * @return decoded mime types
120+ * @return the decoded MIME types
132121 */
133- public static List <String > decode (ByteBuf buf ) {
122+ public static List <String > decode (ByteBuf byteBuf ) {
134123 List <String > mimeTypes = new ArrayList <>();
135- while (buf .isReadable ()) {
136- byte mimeIdOrLength = buf .readByte ();
137- if ((mimeIdOrLength & STREAM_METADATA_KNOWN_MASK ) == STREAM_METADATA_KNOWN_MASK ) {
138- byte mimeIdentifier = (byte ) (mimeIdOrLength & STREAM_METADATA_LENGTH_MASK );
139- mimeTypes .add (WellKnownMimeType .fromIdentifier (mimeIdentifier ).toString ());
124+ while (byteBuf .isReadable ()) {
125+ byte idOrLength = byteBuf .readByte ();
126+ if ((idOrLength & STREAM_METADATA_KNOWN_MASK ) == STREAM_METADATA_KNOWN_MASK ) {
127+ byte id = (byte ) (idOrLength & STREAM_METADATA_LENGTH_MASK );
128+ WellKnownMimeType wellKnownMimeType = WellKnownMimeType .fromIdentifier (id );
129+ mimeTypes .add (wellKnownMimeType .toString ());
140130 } else {
141- int mimeLen = Byte .toUnsignedInt (mimeIdOrLength ) + 1 ;
142- mimeTypes .add (buf .readCharSequence (mimeLen , CharsetUtil .US_ASCII ).toString ());
131+ int length = Byte .toUnsignedInt (idOrLength ) + 1 ;
132+ mimeTypes .add (byteBuf .readCharSequence (length , CharsetUtil .US_ASCII ).toString ());
143133 }
144134 }
145135 return mimeTypes ;
0 commit comments