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 }