View Javadoc

    1   /*
    2    * Copyright 2012 The Netty Project
    3    *
    4    * The Netty Project licenses this file to you under the Apache License,
    5    * version 2.0 (the "License"); you may not use this file except in compliance
    6    * with the License. You may obtain a copy of the License at:
    7    *
    8    *   http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    13   * License for the specific language governing permissions and limitations
    14   * under the License.
    15   */
    16  package org.jboss.netty.handler.codec.spdy;
    17
    18  import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
    19
    20  import java.nio.ByteOrder;
    21  import java.util.Set;
    22
    23  import org.jboss.netty.buffer.ChannelBuffer;
    24  import org.jboss.netty.buffer.ChannelBuffers;
    25  import org.jboss.netty.channel.Channel;
    26  import org.jboss.netty.channel.ChannelDownstreamHandler;
    27  import org.jboss.netty.channel.ChannelEvent;
    28  import org.jboss.netty.channel.ChannelHandlerContext;
    29  import org.jboss.netty.channel.ChannelStateEvent;
    30  import org.jboss.netty.channel.Channels;
    31  import org.jboss.netty.channel.MessageEvent;
    32
    33  /**
    34   * Encodes a SPDY Data or Control Frame into a {@link ChannelBuffer}.
    35   */
    36  public class SpdyFrameEncoder implements ChannelDownstreamHandler {
    37
    38      private final int version;
    39      private volatile boolean finished;
    40      private final SpdyHeaderBlockCompressor headerBlockCompressor;
    41
    42      /**
    43       * Creates a new instance with the default {@code version (2)},
    44       * {@code compressionLevel (6)}, {@code windowBits (15)},
    45       * and {@code memLevel (8)}.
    46       */
    47      @Deprecated
    48      public SpdyFrameEncoder() {
    49          this(2, 6, 15, 8);
    50      }
    51
    52      /**
    53       * Creates a new instance with the specified {@code version} and the
    54       * default {@code compressionLevel (6)}, {@code windowBits (15)},
    55       * and {@code memLevel (8)}.
    56       */
    57      public SpdyFrameEncoder(int version) {
    58          this(version, 6, 15, 8);
    59      }
    60
    61      /**
    62       * Creates a new instance with the default {@code version (2)} and the
    63       * specified parameters.
    64       */
    65      @Deprecated
    66      public SpdyFrameEncoder(int compressionLevel, int windowBits, int memLevel) {
    67          this(2, compressionLevel, windowBits, memLevel);
    68      }
    69
    70      /**
    71       * Creates a new instance with the specified parameters.
    72       */
    73      public SpdyFrameEncoder(int version, int compressionLevel, int windowBits, int memLevel) {
    74          if (version < SPDY_MIN_VERSION || version > SPDY_MAX_VERSION) {
    75              throw new IllegalArgumentException(
    76                      "unknown version: " + version);
    77          }
    78          this.version = version;
    79          headerBlockCompressor = SpdyHeaderBlockCompressor.newInstance(
    80                  version, compressionLevel, windowBits, memLevel);
    81      }
    82
    83      public void handleDownstream(
    84              final ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
    85          if (evt instanceof ChannelStateEvent) {
    86              ChannelStateEvent e = (ChannelStateEvent) evt;
    87              switch (e.getState()) {
    88              case OPEN:
    89              case CONNECTED:
    90              case BOUND:
    91                  if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) {
    92                      synchronized (headerBlockCompressor) {
    93                          finished = true;
    94                          headerBlockCompressor.end();
    95                      }
    96                  }
    97              }
    98          }
    99
    100         if (!(evt instanceof MessageEvent)) {
    101             ctx.sendDownstream(evt);
    102             return;
    103         }
    104
    105         final MessageEvent e = (MessageEvent) evt;
    106         Object msg = e.getMessage();
    107
    108         if (msg instanceof SpdyDataFrame) {
    109
    110             SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
    111             ChannelBuffer data = spdyDataFrame.getData();
    112             byte flags = spdyDataFrame.isLast() ? SPDY_DATA_FLAG_FIN : 0;
    113             ChannelBuffer header = ChannelBuffers.buffer(
    114                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE);
    115             header.writeInt(spdyDataFrame.getStreamId() & 0x7FFFFFFF);
    116             header.writeByte(flags);
    117             header.writeMedium(data.readableBytes());
    118             ChannelBuffer frame = ChannelBuffers.wrappedBuffer(header, data);
    119             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
    120             return;
    121
    122         } else if (msg instanceof SpdySynStreamFrame) {
    123
    124             synchronized (headerBlockCompressor) {
    125                 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
    126                 ChannelBuffer data = compressHeaderBlock(
    127                         encodeHeaderBlock(version, spdySynStreamFrame));
    128                 byte flags = spdySynStreamFrame.isLast() ? SPDY_FLAG_FIN : 0;
    129                 if (spdySynStreamFrame.isUnidirectional()) {
    130                     flags |= SPDY_FLAG_UNIDIRECTIONAL;
    131                 }
    132                 int headerBlockLength = data.readableBytes();
    133                 int length;
    134                 if (version < 3) {
    135                     length = headerBlockLength == 0 ? 12 : 10 + headerBlockLength;
    136                 } else {
    137                     length = 10 + headerBlockLength;
    138                 }
    139                 ChannelBuffer frame = ChannelBuffers.buffer(
    140                         ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 12);
    141                 frame.writeShort(version | 0x8000);
    142                 frame.writeShort(SPDY_SYN_STREAM_FRAME);
    143                 frame.writeByte(flags);
    144                 frame.writeMedium(length);
    145                 frame.writeInt(spdySynStreamFrame.getStreamId());
    146                 frame.writeInt(spdySynStreamFrame.getAssociatedToStreamId());
    147                 if (version < 3) {
    148                     // Restrict priorities for SPDY/2 to between 0 and 3
    149                     byte priority = spdySynStreamFrame.getPriority();
    150                     if (priority > 3) {
    151                         priority = 3;
    152                     }
    153                     frame.writeShort((priority & 0xFF) << 14);
    154                 } else {
    155                     frame.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
    156                 }
    157                 if (version < 3 && data.readableBytes() == 0) {
    158                     frame.writeShort(0);
    159                 }
    160                 // Writes of compressed data must occur in order
    161                 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
    162                 e.getChannel().getPipeline().execute(new Runnable() {
    163                     public void run() {
    164                         Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
    165                     }
    166                 });
    167             }
    168             return;
    169
    170         } else if (msg instanceof SpdySynReplyFrame) {
    171
    172             synchronized (headerBlockCompressor) {
    173                 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
    174                 ChannelBuffer data = compressHeaderBlock(
    175                         encodeHeaderBlock(version, spdySynReplyFrame));
    176                 byte flags = spdySynReplyFrame.isLast() ? SPDY_FLAG_FIN : 0;
    177                 int headerBlockLength = data.readableBytes();
    178                 int length;
    179                 if (version < 3) {
    180                     length = headerBlockLength == 0 ? 8 : 6 + headerBlockLength;
    181                 } else {
    182                     length = 4 + headerBlockLength;
    183                 }
    184                 ChannelBuffer frame = ChannelBuffers.buffer(
    185                         ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
    186                 frame.writeShort(version | 0x8000);
    187                 frame.writeShort(SPDY_SYN_REPLY_FRAME);
    188                 frame.writeByte(flags);
    189                 frame.writeMedium(length);
    190                 frame.writeInt(spdySynReplyFrame.getStreamId());
    191                 if (version < 3) {
    192                     if (data.readableBytes() == 0) {
    193                         frame.writeInt(0);
    194                     } else {
    195                         frame.writeShort(0);
    196                     }
    197                 }
    198                 // Writes of compressed data must occur in order
    199                 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
    200                 e.getChannel().getPipeline().execute(new Runnable() {
    201                     public void run() {
    202                         Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
    203                     }
    204                 });
    205             }
    206             return;
    207
    208         } else if (msg instanceof SpdyRstStreamFrame) {
    209
    210             SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
    211             ChannelBuffer frame = ChannelBuffers.buffer(
    212                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
    213             frame.writeShort(version | 0x8000);
    214             frame.writeShort(SPDY_RST_STREAM_FRAME);
    215             frame.writeInt(8);
    216             frame.writeInt(spdyRstStreamFrame.getStreamId());
    217             frame.writeInt(spdyRstStreamFrame.getStatus().getCode());
    218             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
    219             return;
    220
    221         } else if (msg instanceof SpdySettingsFrame) {
    222
    223             SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
    224             byte flags = spdySettingsFrame.clearPreviouslyPersistedSettings() ?
    225                 SPDY_SETTINGS_CLEAR : 0;
    226             Set<Integer> IDs = spdySettingsFrame.getIds();
    227             int numEntries = IDs.size();
    228             int length = 4 + numEntries * 8;
    229             ChannelBuffer frame = ChannelBuffers.buffer(
    230                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
    231             frame.writeShort(version | 0x8000);
    232             frame.writeShort(SPDY_SETTINGS_FRAME);
    233             frame.writeByte(flags);
    234             frame.writeMedium(length);
    235             frame.writeInt(numEntries);
    236             for (Integer ID: IDs) {
    237                 int id = ID.intValue();
    238                 byte ID_flags = (byte) 0;
    239                 if (spdySettingsFrame.isPersistValue(id)) {
    240                     ID_flags |= SPDY_SETTINGS_PERSIST_VALUE;
    241                 }
    242                 if (spdySettingsFrame.isPersisted(id)) {
    243                     ID_flags |= SPDY_SETTINGS_PERSISTED;
    244                 }
    245                 if (version < 3) {
    246                     // Chromium Issue 79156
    247                     // SPDY setting ids are not written in network byte order
    248                     // Write id assuming the architecture is little endian
    249                     frame.writeByte(id & 0xFF);
    250                     frame.writeByte(id >>  8 & 0xFF);
    251                     frame.writeByte(id >> 16 & 0xFF);
    252                     frame.writeByte(ID_flags);
    253                 } else {
    254                     frame.writeByte(ID_flags);
    255                     frame.writeMedium(id);
    256                 }
    257                 frame.writeInt(spdySettingsFrame.getValue(id));
    258             }
    259             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
    260             return;
    261
    262         } else if (msg instanceof SpdyNoOpFrame) {
    263
    264             ChannelBuffer frame = ChannelBuffers.buffer(
    265                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE);
    266             frame.writeShort(version | 0x8000);
    267             frame.writeShort(SPDY_NOOP_FRAME);
    268             frame.writeInt(0);
    269             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
    270             return;
    271
    272         } else if (msg instanceof SpdyPingFrame) {
    273
    274             SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
    275             ChannelBuffer frame = ChannelBuffers.buffer(
    276                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 4);
    277             frame.writeShort(version | 0x8000);
    278             frame.writeShort(SPDY_PING_FRAME);
    279             frame.writeInt(4);
    280             frame.writeInt(spdyPingFrame.getId());
    281             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
    282             return;
    283
    284         } else if (msg instanceof SpdyGoAwayFrame) {
    285
    286             SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
    287             int length = version < 3 ? 4 : 8;
    288             ChannelBuffer frame = ChannelBuffers.buffer(
    289                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
    290             frame.writeShort(version | 0x8000);
    291             frame.writeShort(SPDY_GOAWAY_FRAME);
    292             frame.writeInt(length);
    293             frame.writeInt(spdyGoAwayFrame.getLastGoodStreamId());
    294             if (version >= 3) {
    295                 frame.writeInt(spdyGoAwayFrame.getStatus().getCode());
    296             }
    297             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
    298             return;
    299
    300         } else if (msg instanceof SpdyHeadersFrame) {
    301
    302             synchronized (headerBlockCompressor) {
    303                 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
    304                 ChannelBuffer data = compressHeaderBlock(
    305                         encodeHeaderBlock(version, spdyHeadersFrame));
    306                 byte flags = spdyHeadersFrame.isLast() ? SPDY_FLAG_FIN : 0;
    307                 int headerBlockLength = data.readableBytes();
    308                 int length;
    309                 if (version < 3) {
    310                     length = headerBlockLength == 0 ? 4 : 6 + headerBlockLength;
    311                 } else {
    312                     length = 4 + headerBlockLength;
    313                 }
    314                 ChannelBuffer frame = ChannelBuffers.buffer(
    315                         ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
    316                 frame.writeShort(version | 0x8000);
    317                 frame.writeShort(SPDY_HEADERS_FRAME);
    318                 frame.writeByte(flags);
    319                 frame.writeMedium(length);
    320                 frame.writeInt(spdyHeadersFrame.getStreamId());
    321                 if (version < 3 && data.readableBytes() != 0) {
    322                     frame.writeShort(0);
    323                 }
    324                 // Writes of compressed data must occur in order
    325                 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
    326                 e.getChannel().getPipeline().execute(new Runnable() {
    327                     public void run() {
    328                         Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
    329                     }
    330                 });
    331             }
    332             return;
    333
    334         } else if (msg instanceof SpdyWindowUpdateFrame) {
    335
    336             SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
    337             ChannelBuffer frame = ChannelBuffers.buffer(
    338                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
    339             frame.writeShort(version | 0x8000);
    340             frame.writeShort(SPDY_WINDOW_UPDATE_FRAME);
    341             frame.writeInt(8);
    342             frame.writeInt(spdyWindowUpdateFrame.getStreamId());
    343             frame.writeInt(spdyWindowUpdateFrame.getDeltaWindowSize());
    344             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
    345             return;
    346         }
    347
    348         // Unknown message type
    349         ctx.sendDownstream(evt);
    350     }
    351
    352     private static void writeLengthField(int version, ChannelBuffer buffer, int length) {
    353         if (version < 3) {
    354             buffer.writeShort(length);
    355         } else {
    356             buffer.writeInt(length);
    357         }
    358     }
    359
    360     private static void setLengthField(int version, ChannelBuffer buffer, int writerIndex, int length) {
    361         if (version < 3) {
    362             buffer.setShort(writerIndex, length);
    363         } else {
    364             buffer.setInt(writerIndex, length);
    365         }
    366     }
    367
    368     private static ChannelBuffer encodeHeaderBlock(int version, SpdyHeaderBlock headerFrame)
    369             throws Exception {
    370         Set<String> names = headerFrame.getHeaderNames();
    371         int numHeaders = names.size();
    372         if (numHeaders == 0) {
    373             return ChannelBuffers.EMPTY_BUFFER;
    374         }
    375         if (numHeaders > SPDY_MAX_NV_LENGTH) {
    376             throw new IllegalArgumentException(
    377                     "header block contains too many headers");
    378         }
    379         ChannelBuffer headerBlock = ChannelBuffers.dynamicBuffer(
    380                 ByteOrder.BIG_ENDIAN, 256);
    381         writeLengthField(version, headerBlock, numHeaders);
    382         for (String name: names) {
    383             byte[] nameBytes = name.getBytes("UTF-8");
    384             writeLengthField(version, headerBlock, nameBytes.length);
    385             headerBlock.writeBytes(nameBytes);
    386             int savedIndex = headerBlock.writerIndex();
    387             int valueLength = 0;
    388             writeLengthField(version, headerBlock, valueLength);
    389             for (String value: headerFrame.getHeaders(name)) {
    390                 byte[] valueBytes = value.getBytes("UTF-8");
    391                 headerBlock.writeBytes(valueBytes);
    392                 headerBlock.writeByte(0);
    393                 valueLength += valueBytes.length + 1;
    394             }
    395             valueLength --;
    396             if (valueLength > SPDY_MAX_NV_LENGTH) {
    397                 throw new IllegalArgumentException(
    398                         "header exceeds allowable length: " + name);
    399             }
    400             setLengthField(version, headerBlock, savedIndex, valueLength);
    401             headerBlock.writerIndex(headerBlock.writerIndex() - 1);
    402         }
    403         return headerBlock;
    404     }
    405
    406     // always called while synchronized on headerBlockCompressor
    407     private ChannelBuffer compressHeaderBlock(ChannelBuffer uncompressed)
    408             throws Exception {
    409         if (uncompressed.readableBytes() == 0) {
    410             return ChannelBuffers.EMPTY_BUFFER;
    411         }
    412         ChannelBuffer compressed = ChannelBuffers.dynamicBuffer();
    413         if (!finished) {
    414             headerBlockCompressor.setInput(uncompressed);
    415             headerBlockCompressor.encode(compressed);
    416         }
    417         return compressed;
    418     }
    419 }