1 module hunt.http.codec.http.decode.PriorityBodyParser; 2 3 import hunt.io.ByteBuffer; 4 5 import hunt.http.codec.http.decode.BodyParser; 6 import hunt.http.codec.http.decode.HeaderParser; 7 import hunt.http.codec.http.decode.Parser; 8 9 import hunt.http.codec.http.frame.ErrorCode; 10 import hunt.http.codec.http.frame.PriorityFrame; 11 12 import hunt.Exceptions; 13 14 /** 15 */ 16 class PriorityBodyParser :BodyParser { 17 private State state = State.PREPARE; 18 private int cursor; 19 private bool exclusive; 20 private int parentStreamId; 21 22 this(HeaderParser headerParser, Parser.Listener listener) { 23 super(headerParser, listener); 24 } 25 26 private void reset() { 27 state = State.PREPARE; 28 cursor = 0; 29 exclusive = false; 30 parentStreamId = 0; 31 } 32 33 override 34 bool parse(ByteBuffer buffer) { 35 while (buffer.hasRemaining()) { 36 switch (state) { 37 case State.PREPARE: { 38 // SPEC: wrong streamId is treated as connection error. 39 if (getStreamId() == 0) 40 return connectionFailure(buffer, cast(int)ErrorCode.PROTOCOL_ERROR, "invalid_priority_frame"); 41 int length = getBodyLength(); 42 if (length != 5) 43 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_priority_frame"); 44 state = State.EXCLUSIVE; 45 break; 46 } 47 case State.EXCLUSIVE: { 48 // We must only peek the first byte and not advance the buffer 49 // because the 31 least significant bits represent the stream 50 // id. 51 int currByte = buffer.get(buffer.position()); 52 exclusive = (currByte & 0x80) == 0x80; 53 state = State.PARENT_STREAM_ID; 54 break; 55 } 56 case State.PARENT_STREAM_ID: { 57 if (buffer.remaining() >= 4) { 58 parentStreamId = buffer.get!int(); 59 parentStreamId &= 0x7F_FF_FF_FF; 60 state = State.WEIGHT; 61 } else { 62 state = State.PARENT_STREAM_ID_BYTES; 63 cursor = 4; 64 } 65 break; 66 } 67 case State.PARENT_STREAM_ID_BYTES: { 68 int currByte = buffer.get() & 0xFF; 69 --cursor; 70 parentStreamId += currByte << (8 * cursor); 71 if (cursor == 0) { 72 parentStreamId &= 0x7F_FF_FF_FF; 73 state = State.WEIGHT; 74 } 75 break; 76 } 77 case State.WEIGHT: { 78 // SPEC: stream cannot depend on itself. 79 if (getStreamId() == parentStreamId) 80 return connectionFailure(buffer, cast(int)ErrorCode.PROTOCOL_ERROR, "invalid_priority_frame"); 81 82 int weight = (buffer.get() & 0xFF) + 1; 83 return onPriority(parentStreamId, weight, exclusive); 84 } 85 default: { 86 throw new IllegalStateException(""); 87 } 88 } 89 } 90 return false; 91 } 92 93 private bool onPriority(int parentStreamId, int weight, bool exclusive) { 94 PriorityFrame frame = new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive); 95 reset(); 96 notifyPriority(frame); 97 return true; 98 } 99 100 private enum State { 101 PREPARE, EXCLUSIVE, PARENT_STREAM_ID, PARENT_STREAM_ID_BYTES, WEIGHT 102 } 103 }