1 /*
   2  * Copyright (c) 2015, 2016, 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 java.net.http;
  27 
  28 import java.io.IOException;
  29 import java.nio.ByteBuffer;
  30 
  31 /**
  32  * When sending a frame, the length field must be set in sub-class
  33  * by calling computeLength()
  34  */
  35 abstract class Http2Frame {
  36 
  37     int length = -1;
  38     int type;
  39     int streamid;
  40     int flags;
  41 
  42     // called when reading in only
  43     void initCommon(int length, int type, int streamid, int flags) {
  44         this.length = length;
  45         this.type = type;
  46         this.streamid = streamid;
  47         this.flags = flags;
  48     }
  49 
  50     public int length() {
  51         return length;
  52     }
  53 
  54     public int type() {
  55         return type;
  56     }
  57 
  58     public int streamid() {
  59         return streamid;
  60     }
  61 
  62     public void setFlag(int flag) {
  63         flags |= flag;
  64     }
  65 
  66     public void setFlags(int flags) {
  67         this.flags = flags;
  68     }
  69 
  70     public int getFlags() {
  71         return flags;
  72     }
  73 
  74     public boolean getFlag(int flag) {
  75         return (flags & flag) != 0;
  76     }
  77 
  78     public void clearFlag(int flag) {
  79         flags &= 0xffffffff ^ flag;
  80     }
  81 
  82     public void streamid(int streamid) {
  83         this.streamid = streamid;
  84     }
  85 
  86     abstract void readIncomingImpl(ByteBufferConsumer bc) throws IOException;
  87 
  88     /**
  89      * assume given array contains at least one complete frame.
  90      */
  91     static Http2Frame readIncoming(ByteBufferConsumer bc) throws IOException {
  92         int x = bc.getInt();
  93         int length = x >> 8;
  94         int type = x & 0xff;
  95         int flags = bc.getByte();
  96         int streamid = bc.getInt();
  97         Http2Frame f = null;
  98         switch (type) {
  99           case DataFrame.TYPE:
 100             f = new DataFrame();
 101             break;
 102           case HeadersFrame.TYPE:
 103             f = new HeadersFrame();
 104             break;
 105           case ContinuationFrame.TYPE:
 106             f = new ContinuationFrame();
 107             break;
 108           case ResetFrame.TYPE:
 109             f = new ResetFrame();
 110             break;
 111           case PriorityFrame.TYPE:
 112             f = new PriorityFrame();
 113             break;
 114           case SettingsFrame.TYPE:
 115             f = new SettingsFrame();
 116             break;
 117           case GoAwayFrame.TYPE:
 118             f = new GoAwayFrame();
 119             break;
 120           case PingFrame.TYPE:
 121             f = new PingFrame();
 122             break;
 123           case PushPromiseFrame.TYPE:
 124             f = new PushPromiseFrame();
 125             break;
 126           case WindowUpdateFrame.TYPE:
 127             f = new WindowUpdateFrame();
 128             break;
 129           default:
 130             String msg = Integer.toString(type);
 131             throw new IOException("unknown frame type " + msg);
 132         }
 133         f.initCommon(length, type, streamid, flags);
 134         f.readIncomingImpl(bc);
 135         return f;
 136     }
 137 
 138     public String typeAsString() {
 139         return asString(this.type);
 140     }
 141 
 142     public static String asString(int type) {
 143         switch (type) {
 144           case DataFrame.TYPE:
 145             return "DATA";
 146           case HeadersFrame.TYPE:
 147             return "HEADERS";
 148           case ContinuationFrame.TYPE:
 149             return "CONTINUATION";
 150           case ResetFrame.TYPE:
 151             return "RESET";
 152           case PriorityFrame.TYPE:
 153             return "PRIORITY";
 154           case SettingsFrame.TYPE:
 155             return "SETTINGS";
 156           case GoAwayFrame.TYPE:
 157             return "GOAWAY";
 158           case PingFrame.TYPE:
 159             return "PING";
 160           case PushPromiseFrame.TYPE:
 161             return "PUSH_PROMISE";
 162           case WindowUpdateFrame.TYPE:
 163             return "WINDOW_UPDATE";
 164           default:
 165             return "UNKNOWN";
 166         }
 167     }
 168 
 169     @Override
 170     public String toString() {
 171         StringBuilder sb = new StringBuilder();
 172         sb.append(typeAsString())
 173                 .append(": length=")
 174                 .append(Integer.toString(length))
 175                 .append(", streamid=")
 176                 .append(streamid)
 177                 .append(", flags=");
 178 
 179         int f = flags;
 180         int i = 0;
 181         if (f == 0) {
 182             sb.append("0 ");
 183         } else {
 184             while (f != 0) {
 185                 if ((f & 1) == 1) {
 186                     sb.append(flagAsString(1 << i))
 187                       .append(' ');
 188                 }
 189                 f = f >> 1;
 190                 i++;
 191             }
 192         }
 193         return sb.toString();
 194     }
 195 
 196     // Override
 197     String flagAsString(int f) {
 198         return "unknown";
 199     }
 200 
 201     abstract void computeLength();
 202 
 203     void writeOutgoing(ByteBufferGenerator bg) {
 204         if (length == -1) {
 205             throw new InternalError("Length not set on outgoing frame");
 206         }
 207         ByteBuffer buf = bg.getBuffer(9);
 208         int x = (length << 8) + type;
 209         buf.putInt(x);
 210         buf.put((byte)flags);
 211         buf.putInt(streamid);
 212     }
 213 }