1 //
2 // ========================================================================
3 // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.http2.parser;
20
21 import java.nio.ByteBuffer;
22
23 import org.eclipse.jetty.http2.frames.Frame;
24
25 /**
26 * <p>The parser for the frame header of HTTP/2 frames.</p>
27 *
28 * @see Parser
29 */
30 public class HeaderParser
31 {
32 private State state = State.LENGTH;
33 private int cursor;
34
35 private int length;
36 private int type;
37 private int flags;
38 private int streamId;
39
40 protected void reset()
41 {
42 state = State.LENGTH;
43 cursor = 0;
44
45 length = 0;
46 type = 0;
47 flags = 0;
48 streamId = 0;
49 }
50
51 /**
52 * <p>Parses the header bytes in the given {@code buffer}; only the header
53 * bytes are consumed, therefore when this method returns, the buffer may
54 * contain unconsumed bytes.</p>
55 *
56 * @param buffer the buffer to parse
57 * @return true if the whole header bytes were parsed, false if not enough
58 * header bytes were present in the buffer
59 */
60 public boolean parse(ByteBuffer buffer)
61 {
62 while (buffer.hasRemaining())
63 {
64 switch (state)
65 {
66 case LENGTH:
67 {
68 int octet = buffer.get() & 0xFF;
69 length = (length << 8) + octet;
70 if (++cursor == 3)
71 {
72 length &= Frame.MAX_MAX_LENGTH;
73 state = State.TYPE;
74 }
75 break;
76 }
77 case TYPE:
78 {
79 type = buffer.get() & 0xFF;
80 state = State.FLAGS;
81 break;
82 }
83 case FLAGS:
84 {
85 flags = buffer.get() & 0xFF;
86 state = State.STREAM_ID;
87 break;
88 }
89 case STREAM_ID:
90 {
91 if (buffer.remaining() >= 4)
92 {
93 streamId = buffer.getInt();
94 // Most significant bit MUST be ignored as per specification.
95 streamId &= 0x7F_FF_FF_FF;
96 return true;
97 }
98 else
99 {
100 state = State.STREAM_ID_BYTES;
101 cursor = 4;
102 }
103 break;
104 }
105 case STREAM_ID_BYTES:
106 {
107 int currByte = buffer.get() & 0xFF;
108 --cursor;
109 streamId += currByte << (8 * cursor);
110 if (cursor == 0)
111 {
112 // Most significant bit MUST be ignored as per specification.
113 streamId &= 0x7F_FF_FF_FF;
114 return true;
115 }
116 break;
117 }
118 default:
119 {
120 throw new IllegalStateException();
121 }
122 }
123 }
124 return false;
125 }
126
127 public int getLength()
128 {
129 return length;
130 }
131
132 public int getFrameType()
133 {
134 return type;
135 }
136
137 public boolean hasFlag(int bit)
138 {
139 return (flags & bit) == bit;
140 }
141
142 public int getStreamId()
143 {
144 return streamId;
145 }
146
147 private enum State
148 {
149 LENGTH, TYPE, FLAGS, STREAM_ID, STREAM_ID_BYTES
150 }
151 }