src/java.net.http/share/classes/jdk/internal/net/http/frame/FramesEncoder.java
branchhttp-client-branch
changeset 56092 fd85b2bf2b0d
parent 56089 42208b2f224e
child 56451 9585061fdb04
equal deleted inserted replaced
56091:aedd6133e7a0 56092:fd85b2bf2b0d
       
     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 
       
    41     public FramesEncoder() {
       
    42     }
       
    43 
       
    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 
       
    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 
       
    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 
       
    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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   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 
       
   282     private ByteBuffer getBuffer(int capacity) {
       
   283         return ByteBuffer.allocate(capacity);
       
   284     }
       
   285 
       
   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 }