1 /*
2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package jdk.internal.net.http.frame;
27 
28 import java.nio.ByteBuffer;
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 /**
33  * Frames Encoder
34  *
35  * Encode framed into ByteBuffers.
36  * The class is stateless.
37  */
38 public class FramesEncoder {
39 
40 
FramesEncoder()41     public FramesEncoder() {
42     }
43 
encodeFrames(List<HeaderFrame> frames)44     public List<ByteBuffer> encodeFrames(List<HeaderFrame> frames) {
45         List<ByteBuffer> bufs = new ArrayList<>(frames.size() * 2);
46         for (HeaderFrame f : frames) {
47             bufs.addAll(encodeFrame(f));
48         }
49         return bufs;
50     }
51 
encodeConnectionPreface(byte[] preface, SettingsFrame frame)52     public ByteBuffer encodeConnectionPreface(byte[] preface, SettingsFrame frame) {
53         final int length = frame.length();
54         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length + preface.length);
55         buf.put(preface);
56         putSettingsFrame(buf, frame, length);
57         buf.flip();
58         return buf;
59     }
60 
encodeFrame(Http2Frame frame)61     public List<ByteBuffer> encodeFrame(Http2Frame frame) {
62         switch (frame.type()) {
63             case DataFrame.TYPE:
64                 return encodeDataFrame((DataFrame) frame);
65             case HeadersFrame.TYPE:
66                 return encodeHeadersFrame((HeadersFrame) frame);
67             case PriorityFrame.TYPE:
68                 return encodePriorityFrame((PriorityFrame) frame);
69             case ResetFrame.TYPE:
70                 return encodeResetFrame((ResetFrame) frame);
71             case SettingsFrame.TYPE:
72                 return encodeSettingsFrame((SettingsFrame) frame);
73             case PushPromiseFrame.TYPE:
74                 return encodePushPromiseFrame((PushPromiseFrame) frame);
75             case PingFrame.TYPE:
76                 return encodePingFrame((PingFrame) frame);
77             case GoAwayFrame.TYPE:
78                 return encodeGoAwayFrame((GoAwayFrame) frame);
79             case WindowUpdateFrame.TYPE:
80                 return encodeWindowUpdateFrame((WindowUpdateFrame) frame);
81             case ContinuationFrame.TYPE:
82                 return encodeContinuationFrame((ContinuationFrame) frame);
83             default:
84                 throw new UnsupportedOperationException("Not supported frame "+frame.type()+" ("+frame.getClass().getName()+")");
85         }
86     }
87 
88     private static final int NO_FLAGS = 0;
89     private static final int ZERO_STREAM = 0;
90 
encodeDataFrame(DataFrame frame)91     private List<ByteBuffer> encodeDataFrame(DataFrame frame) {
92         // non-zero stream
93         assert frame.streamid() != 0;
94         ByteBuffer buf = encodeDataFrameStart(frame);
95         if (frame.getFlag(DataFrame.PADDED)) {
96             return joinWithPadding(buf, frame.getData(), frame.getPadLength());
97         } else {
98             return join(buf, frame.getData());
99         }
100     }
101 
encodeDataFrameStart(DataFrame frame)102     private ByteBuffer encodeDataFrameStart(DataFrame frame) {
103         boolean isPadded = frame.getFlag(DataFrame.PADDED);
104         final int length = frame.getDataLength() + (isPadded ? (frame.getPadLength() + 1) : 0);
105         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 1 : 0));
106         putHeader(buf, length, DataFrame.TYPE, frame.getFlags(), frame.streamid());
107         if (isPadded) {
108             buf.put((byte) frame.getPadLength());
109         }
110         buf.flip();
111         return buf;
112     }
113 
encodeHeadersFrame(HeadersFrame frame)114     private List<ByteBuffer> encodeHeadersFrame(HeadersFrame frame) {
115         // non-zero stream
116         assert frame.streamid() != 0;
117         ByteBuffer buf = encodeHeadersFrameStart(frame);
118         if (frame.getFlag(HeadersFrame.PADDED)) {
119             return joinWithPadding(buf, frame.getHeaderBlock(), frame.getPadLength());
120         } else {
121             return join(buf, frame.getHeaderBlock());
122         }
123     }
124 
encodeHeadersFrameStart(HeadersFrame frame)125     private ByteBuffer encodeHeadersFrameStart(HeadersFrame frame) {
126         boolean isPadded = frame.getFlag(HeadersFrame.PADDED);
127         boolean hasPriority = frame.getFlag(HeadersFrame.PRIORITY);
128         final int length = frame.getHeaderLength() + (isPadded ? (frame.getPadLength() + 1) : 0) + (hasPriority ? 5 : 0);
129         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 1 : 0) + (hasPriority ? 5 : 0));
130         putHeader(buf, length, HeadersFrame.TYPE, frame.getFlags(), frame.streamid());
131         if (isPadded) {
132             buf.put((byte) frame.getPadLength());
133         }
134         if (hasPriority) {
135             putPriority(buf, frame.getExclusive(), frame.getStreamDependency(), frame.getWeight());
136         }
137         buf.flip();
138         return buf;
139     }
140 
encodePriorityFrame(PriorityFrame frame)141     private List<ByteBuffer> encodePriorityFrame(PriorityFrame frame) {
142         // non-zero stream; no flags
143         assert frame.streamid() != 0;
144         final int length = 5;
145         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
146         putHeader(buf, length, PriorityFrame.TYPE, NO_FLAGS, frame.streamid());
147         putPriority(buf, frame.exclusive(), frame.streamDependency(), frame.weight());
148         buf.flip();
149         return List.of(buf);
150     }
151 
encodeResetFrame(ResetFrame frame)152     private List<ByteBuffer> encodeResetFrame(ResetFrame frame) {
153         // non-zero stream; no flags
154         assert frame.streamid() != 0;
155         final int length = 4;
156         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
157         putHeader(buf, length, ResetFrame.TYPE, NO_FLAGS, frame.streamid());
158         buf.putInt(frame.getErrorCode());
159         buf.flip();
160         return List.of(buf);
161     }
162 
encodeSettingsFrame(SettingsFrame frame)163     private List<ByteBuffer> encodeSettingsFrame(SettingsFrame frame) {
164         // only zero stream
165         assert frame.streamid() == 0;
166         final int length = frame.length();
167         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
168         putSettingsFrame(buf, frame, length);
169         buf.flip();
170         return List.of(buf);
171     }
172 
encodePushPromiseFrame(PushPromiseFrame frame)173     private List<ByteBuffer> encodePushPromiseFrame(PushPromiseFrame frame) {
174         // non-zero stream
175         assert frame.streamid() != 0;
176         boolean isPadded = frame.getFlag(PushPromiseFrame.PADDED);
177         final int length = frame.getHeaderLength() + (isPadded ? 5 : 4);
178         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 5 : 4));
179         putHeader(buf, length, PushPromiseFrame.TYPE, frame.getFlags(), frame.streamid());
180         if (isPadded) {
181             buf.put((byte) frame.getPadLength());
182         }
183         buf.putInt(frame.getPromisedStream());
184         buf.flip();
185 
186         if (frame.getFlag(PushPromiseFrame.PADDED)) {
187             return joinWithPadding(buf, frame.getHeaderBlock(), frame.getPadLength());
188         } else {
189             return join(buf, frame.getHeaderBlock());
190         }
191     }
192 
encodePingFrame(PingFrame frame)193     private List<ByteBuffer> encodePingFrame(PingFrame frame) {
194         // only zero stream
195         assert frame.streamid() == 0;
196         final int length = 8;
197         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
198         putHeader(buf, length, PingFrame.TYPE, frame.getFlags(), ZERO_STREAM);
199         buf.put(frame.getData());
200         buf.flip();
201         return List.of(buf);
202     }
203 
encodeGoAwayFrame(GoAwayFrame frame)204     private List<ByteBuffer> encodeGoAwayFrame(GoAwayFrame frame) {
205         // only zero stream; no flags
206         assert frame.streamid() == 0;
207         byte[] debugData = frame.getDebugData();
208         final int length = 8 + debugData.length;
209         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
210         putHeader(buf, length, GoAwayFrame.TYPE, NO_FLAGS, ZERO_STREAM);
211         buf.putInt(frame.getLastStream());
212         buf.putInt(frame.getErrorCode());
213         if (debugData.length > 0) {
214             buf.put(debugData);
215         }
216         buf.flip();
217         return List.of(buf);
218     }
219 
encodeWindowUpdateFrame(WindowUpdateFrame frame)220     private List<ByteBuffer> encodeWindowUpdateFrame(WindowUpdateFrame frame) {
221         // any stream; no flags
222         final int length = 4;
223         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
224         putHeader(buf, length, WindowUpdateFrame.TYPE, NO_FLAGS, frame.streamid);
225         buf.putInt(frame.getUpdate());
226         buf.flip();
227         return List.of(buf);
228     }
229 
encodeContinuationFrame(ContinuationFrame frame)230     private List<ByteBuffer> encodeContinuationFrame(ContinuationFrame frame) {
231         // non-zero stream;
232         assert frame.streamid() != 0;
233         final int length = frame.getHeaderLength();
234         ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE);
235         putHeader(buf, length, ContinuationFrame.TYPE, frame.getFlags(), frame.streamid());
236         buf.flip();
237         return join(buf, frame.getHeaderBlock());
238     }
239 
joinWithPadding(ByteBuffer buf, List<ByteBuffer> data, int padLength)240     private List<ByteBuffer> joinWithPadding(ByteBuffer buf, List<ByteBuffer> data, int padLength) {
241         int len = data.size();
242         if (len == 0) return List.of(buf, getPadding(padLength));
243         else if (len == 1) return List.of(buf, data.get(0), getPadding(padLength));
244         else if (len == 2) return List.of(buf, data.get(0), data.get(1), getPadding(padLength));
245         List<ByteBuffer> res = new ArrayList<>(len+2);
246         res.add(buf);
247         res.addAll(data);
248         res.add(getPadding(padLength));
249         return res;
250     }
251 
join(ByteBuffer buf, List<ByteBuffer> data)252     private List<ByteBuffer> join(ByteBuffer buf, List<ByteBuffer> data) {
253         int len = data.size();
254         if (len == 0) return List.of(buf);
255         else if (len == 1) return List.of(buf, data.get(0));
256         else if (len == 2) return List.of(buf, data.get(0), data.get(1));
257         List<ByteBuffer> joined = new ArrayList<>(len + 1);
258         joined.add(buf);
259         joined.addAll(data);
260         return joined;
261     }
262 
putSettingsFrame(ByteBuffer buf, SettingsFrame frame, int length)263     private void putSettingsFrame(ByteBuffer buf, SettingsFrame frame, int length) {
264         // only zero stream;
265         assert frame.streamid() == 0;
266         putHeader(buf, length, SettingsFrame.TYPE, frame.getFlags(), ZERO_STREAM);
267         frame.toByteBuffer(buf);
268     }
269 
putHeader(ByteBuffer buf, int length, int type, int flags, int streamId)270     private void putHeader(ByteBuffer buf, int length, int type, int flags, int streamId) {
271         int x = (length << 8) + type;
272         buf.putInt(x);
273         buf.put((byte) flags);
274         buf.putInt(streamId);
275     }
276 
putPriority(ByteBuffer buf, boolean exclusive, int streamDependency, int weight)277     private void putPriority(ByteBuffer buf, boolean exclusive, int streamDependency, int weight) {
278         buf.putInt(exclusive ? (1 << 31) + streamDependency : streamDependency);
279         buf.put((byte) weight);
280     }
281 
getBuffer(int capacity)282     private ByteBuffer getBuffer(int capacity) {
283         return ByteBuffer.allocate(capacity);
284     }
285 
getPadding(int length)286     public ByteBuffer getPadding(int length) {
287         if (length > 255) {
288             throw new IllegalArgumentException("Padding too big");
289         }
290         return ByteBuffer.allocate(length); // zeroed!
291     }
292 
293 }
294