View Javadoc
1   /*
2    * Copyright 2014 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 io.netty.handler.codec.spdy;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  
21  import java.nio.ByteOrder;
22  import java.util.Set;
23  
24  import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
25  
26  /**
27   * Encodes a SPDY Frame into a {@link ByteBuf}.
28   */
29  public class SpdyFrameEncoder {
30  
31      private final int version;
32  
33      /**
34       * Creates a new instance with the specified {@code spdyVersion}.
35       */
36      public SpdyFrameEncoder(SpdyVersion spdyVersion) {
37          if (spdyVersion == null) {
38              throw new NullPointerException("spdyVersion");
39          }
40          version = spdyVersion.getVersion();
41      }
42  
43      private void writeControlFrameHeader(ByteBuf buffer, int type, byte flags, int length) {
44          buffer.writeShort(version | 0x8000);
45          buffer.writeShort(type);
46          buffer.writeByte(flags);
47          buffer.writeMedium(length);
48      }
49  
50      public ByteBuf encodeDataFrame(ByteBufAllocator allocator, int streamId, boolean last, ByteBuf data) {
51          byte flags = last ? SPDY_DATA_FLAG_FIN : 0;
52          int length = data.readableBytes();
53          ByteBuf frame = allocator.ioBuffer(SPDY_HEADER_SIZE + length).order(ByteOrder.BIG_ENDIAN);
54          frame.writeInt(streamId & 0x7FFFFFFF);
55          frame.writeByte(flags);
56          frame.writeMedium(length);
57          frame.writeBytes(data, data.readerIndex(), length);
58          return frame;
59      }
60  
61      public ByteBuf encodeSynStreamFrame(ByteBufAllocator allocator,  int streamId, int associatedToStreamId,
62              byte priority, boolean last, boolean unidirectional, ByteBuf headerBlock) {
63          int headerBlockLength = headerBlock.readableBytes();
64          byte flags = last ? SPDY_FLAG_FIN : 0;
65          if (unidirectional) {
66              flags |= SPDY_FLAG_UNIDIRECTIONAL;
67          }
68          int length = 10 + headerBlockLength;
69          ByteBuf frame = allocator.ioBuffer(SPDY_HEADER_SIZE + length).order(ByteOrder.BIG_ENDIAN);
70          writeControlFrameHeader(frame, SPDY_SYN_STREAM_FRAME, flags, length);
71          frame.writeInt(streamId);
72          frame.writeInt(associatedToStreamId);
73          frame.writeShort((priority & 0xFF) << 13);
74          frame.writeBytes(headerBlock, headerBlock.readerIndex(), headerBlockLength);
75          return frame;
76      }
77  
78      public ByteBuf encodeSynReplyFrame(ByteBufAllocator allocator, int streamId, boolean last, ByteBuf headerBlock) {
79          int headerBlockLength = headerBlock.readableBytes();
80          byte flags = last ? SPDY_FLAG_FIN : 0;
81          int length = 4 + headerBlockLength;
82          ByteBuf frame = allocator.ioBuffer(SPDY_HEADER_SIZE + length).order(ByteOrder.BIG_ENDIAN);
83          writeControlFrameHeader(frame, SPDY_SYN_REPLY_FRAME, flags, length);
84          frame.writeInt(streamId);
85          frame.writeBytes(headerBlock, headerBlock.readerIndex(), headerBlockLength);
86          return frame;
87      }
88  
89      public ByteBuf encodeRstStreamFrame(ByteBufAllocator allocator, int streamId, int statusCode) {
90          byte flags = 0;
91          int length = 8;
92          ByteBuf frame = allocator.ioBuffer(SPDY_HEADER_SIZE + length).order(ByteOrder.BIG_ENDIAN);
93          writeControlFrameHeader(frame, SPDY_RST_STREAM_FRAME, flags, length);
94          frame.writeInt(streamId);
95          frame.writeInt(statusCode);
96          return frame;
97      }
98  
99      public ByteBuf encodeSettingsFrame(ByteBufAllocator allocator, SpdySettingsFrame spdySettingsFrame) {
100         Set<Integer> ids = spdySettingsFrame.ids();
101         int numSettings = ids.size();
102 
103         byte flags = spdySettingsFrame.clearPreviouslyPersistedSettings() ?
104                 SPDY_SETTINGS_CLEAR : 0;
105         int length = 4 + 8 * numSettings;
106         ByteBuf frame = allocator.ioBuffer(SPDY_HEADER_SIZE + length).order(ByteOrder.BIG_ENDIAN);
107         writeControlFrameHeader(frame, SPDY_SETTINGS_FRAME, flags, length);
108         frame.writeInt(numSettings);
109         for (Integer id : ids) {
110             flags = 0;
111             if (spdySettingsFrame.isPersistValue(id)) {
112                 flags |= SPDY_SETTINGS_PERSIST_VALUE;
113             }
114             if (spdySettingsFrame.isPersisted(id)) {
115                 flags |= SPDY_SETTINGS_PERSISTED;
116             }
117             frame.writeByte(flags);
118             frame.writeMedium(id);
119             frame.writeInt(spdySettingsFrame.getValue(id));
120         }
121         return frame;
122     }
123 
124     public ByteBuf encodePingFrame(ByteBufAllocator allocator, int id) {
125         byte flags = 0;
126         int length = 4;
127         ByteBuf frame = allocator.ioBuffer(SPDY_HEADER_SIZE + length).order(ByteOrder.BIG_ENDIAN);
128         writeControlFrameHeader(frame, SPDY_PING_FRAME, flags, length);
129         frame.writeInt(id);
130         return frame;
131     }
132 
133     public ByteBuf encodeGoAwayFrame(ByteBufAllocator allocator, int lastGoodStreamId, int statusCode) {
134         byte flags = 0;
135         int length = 8;
136         ByteBuf frame = allocator.ioBuffer(SPDY_HEADER_SIZE + length).order(ByteOrder.BIG_ENDIAN);
137         writeControlFrameHeader(frame, SPDY_GOAWAY_FRAME, flags, length);
138         frame.writeInt(lastGoodStreamId);
139         frame.writeInt(statusCode);
140         return frame;
141     }
142 
143     public ByteBuf encodeHeadersFrame(ByteBufAllocator allocator, int streamId, boolean last, ByteBuf headerBlock) {
144         int headerBlockLength = headerBlock.readableBytes();
145         byte flags = last ? SPDY_FLAG_FIN : 0;
146         int length = 4 + headerBlockLength;
147         ByteBuf frame = allocator.ioBuffer(SPDY_HEADER_SIZE + length).order(ByteOrder.BIG_ENDIAN);
148         writeControlFrameHeader(frame, SPDY_HEADERS_FRAME, flags, length);
149         frame.writeInt(streamId);
150         frame.writeBytes(headerBlock, headerBlock.readerIndex(), headerBlockLength);
151         return frame;
152     }
153 
154     public ByteBuf encodeWindowUpdateFrame(ByteBufAllocator allocator, int streamId, int deltaWindowSize) {
155         byte flags = 0;
156         int length = 8;
157         ByteBuf frame = allocator.ioBuffer(SPDY_HEADER_SIZE + length).order(ByteOrder.BIG_ENDIAN);
158         writeControlFrameHeader(frame, SPDY_WINDOW_UPDATE_FRAME, flags, length);
159         frame.writeInt(streamId);
160         frame.writeInt(deltaWindowSize);
161         return frame;
162     }
163 }