1 module hunt.http.codec.http.decode.HeadersBodyParser; 2 3 import hunt.http.codec.http.decode.BodyParser; 4 import hunt.http.codec.http.decode.HeaderParser; 5 import hunt.http.codec.http.decode.HeaderBlockFragments; 6 import hunt.http.codec.http.decode.HeaderBlockParser; 7 import hunt.http.codec.http.decode.Parser; 8 9 import hunt.http.codec.http.frame.ErrorCode; 10 import hunt.http.codec.http.frame.Flags; 11 import hunt.http.codec.http.frame.HeadersFrame; 12 import hunt.http.codec.http.frame.PriorityFrame; 13 import hunt.http.HttpMetaData; 14 15 import hunt.io.BufferUtils; 16 import hunt.io.ByteBuffer; 17 18 import hunt.Exceptions; 19 20 import std.algorithm; 21 22 /** 23 */ 24 class HeadersBodyParser :BodyParser { 25 private HeaderBlockParser headerBlockParser; 26 private HeaderBlockFragments headerBlockFragments; 27 private State state = State.PREPARE; 28 private int cursor; 29 private int length; 30 private int paddingLength; 31 private bool exclusive; 32 private int parentStreamId; 33 private int weight; 34 35 this(HeaderParser headerParser, Parser.Listener listener, HeaderBlockParser headerBlockParser, 36 HeaderBlockFragments headerBlockFragments) { 37 super(headerParser, listener); 38 this.headerBlockParser = headerBlockParser; 39 this.headerBlockFragments = headerBlockFragments; 40 } 41 42 private void reset() { 43 state = State.PREPARE; 44 cursor = 0; 45 length = 0; 46 paddingLength = 0; 47 exclusive = false; 48 parentStreamId = 0; 49 weight = 0; 50 } 51 52 override 53 protected void emptyBody(ByteBuffer buffer) { 54 if (hasFlag(Flags.END_HEADERS)) { 55 HttpMetaData metaData = headerBlockParser.parse(BufferUtils.EMPTY_BUFFER, 0); 56 onHeaders(0, 0, false, metaData); 57 } else { 58 headerBlockFragments.setStreamId(getStreamId()); 59 headerBlockFragments.setEndStream(isEndStream()); 60 if (hasFlag(Flags.PRIORITY)) 61 connectionFailure(buffer, cast(int)ErrorCode.PROTOCOL_ERROR, "invalid_headers_priority_frame"); 62 } 63 } 64 65 override 66 bool parse(ByteBuffer buffer) { 67 bool loop = false; 68 while (buffer.hasRemaining() || loop) { 69 switch (state) { 70 case State.PREPARE: { 71 // SPEC: wrong streamId is treated as connection error. 72 if (getStreamId() == 0) 73 return connectionFailure(buffer, cast(int)ErrorCode.PROTOCOL_ERROR, "invalid_headers_frame"); 74 75 length = getBodyLength(); 76 77 if (isPadding()) { 78 state = State.PADDING_LENGTH; 79 } else if (hasFlag(Flags.PRIORITY)) { 80 state = State.EXCLUSIVE; 81 } else { 82 state = State.HEADERS; 83 } 84 break; 85 } 86 case State.PADDING_LENGTH: { 87 paddingLength = buffer.get() & 0xFF; 88 --length; 89 length -= paddingLength; 90 state = hasFlag(Flags.PRIORITY) ? State.EXCLUSIVE : State.HEADERS; 91 loop = length == 0; 92 if (length < 0) 93 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_headers_frame_padding"); 94 break; 95 } 96 case State.EXCLUSIVE: { 97 // We must only peek the first byte and not advance the buffer 98 // because the 31 least significant bits represent the stream 99 // id. 100 int currByte = buffer.get(buffer.position()); 101 exclusive = (currByte & 0x80) == 0x80; 102 state = State.PARENT_STREAM_ID; 103 break; 104 } 105 case State.PARENT_STREAM_ID: { 106 if (buffer.remaining() >= 4) { 107 parentStreamId = buffer.get!int(); 108 parentStreamId &= 0x7F_FF_FF_FF; 109 length -= 4; 110 state = State.WEIGHT; 111 if (length < 1) 112 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_headers_frame"); 113 } else { 114 state = State.PARENT_STREAM_ID_BYTES; 115 cursor = 4; 116 } 117 break; 118 } 119 case State.PARENT_STREAM_ID_BYTES: { 120 int currByte = buffer.get() & 0xFF; 121 --cursor; 122 parentStreamId += currByte << (8 * cursor); 123 --length; 124 if (cursor > 0 && length <= 0) 125 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_headers_frame"); 126 if (cursor == 0) { 127 parentStreamId &= 0x7F_FF_FF_FF; 128 state = State.WEIGHT; 129 if (length < 1) 130 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_headers_frame"); 131 } 132 break; 133 } 134 case State.WEIGHT: { 135 weight = (buffer.get() & 0xFF) + 1; 136 --length; 137 state = State.HEADERS; 138 loop = length == 0; 139 break; 140 } 141 case State.HEADERS: { 142 if (hasFlag(Flags.END_HEADERS)) { 143 HttpMetaData metaData = headerBlockParser.parse(buffer, length); 144 if (metaData !is null) { 145 state = State.PADDING; 146 loop = paddingLength == 0; 147 onHeaders(parentStreamId, weight, exclusive, metaData); 148 } 149 } else { 150 int remaining = buffer.remaining(); 151 if (remaining < length) { 152 headerBlockFragments.storeFragment(buffer, remaining, false); 153 length -= remaining; 154 } else { 155 headerBlockFragments.setStreamId(getStreamId()); 156 headerBlockFragments.setEndStream(isEndStream()); 157 if (hasFlag(Flags.PRIORITY)) 158 headerBlockFragments.setPriorityFrame( 159 new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive)); 160 headerBlockFragments.storeFragment(buffer, length, false); 161 state = State.PADDING; 162 loop = paddingLength == 0; 163 } 164 } 165 break; 166 } 167 case State.PADDING: { 168 int size = std.algorithm.min(buffer.remaining(), paddingLength); 169 buffer.position(buffer.position() + size); 170 paddingLength -= size; 171 if (paddingLength == 0) { 172 reset(); 173 return true; 174 } 175 break; 176 } 177 default: { 178 throw new IllegalStateException(""); 179 } 180 } 181 } 182 return false; 183 } 184 185 private void onHeaders(int parentStreamId, int weight, bool exclusive, HttpMetaData metaData) { 186 PriorityFrame priorityFrame = null; 187 if (hasFlag(Flags.PRIORITY)) 188 priorityFrame = new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive); 189 HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, priorityFrame, isEndStream()); 190 notifyHeaders(frame); 191 } 192 193 private enum State { 194 PREPARE, PADDING_LENGTH, EXCLUSIVE, PARENT_STREAM_ID, PARENT_STREAM_ID_BYTES, WEIGHT, HEADERS, PADDING 195 } 196 }