1 module hunt.http.codec.http.decode.PingBodyParser;
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.Flags;
11 import hunt.http.codec.http.frame.PingFrame;
12 
13 import hunt.Exceptions;
14 
15 /**
16 */
17 class PingBodyParser :BodyParser {
18 	private State state = State.PREPARE;
19 	private int cursor;
20 	private byte[] payload;
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 		payload = null;
30 	}
31 
32 	override
33 	bool parse(ByteBuffer buffer) {
34 		while (buffer.hasRemaining()) {
35 			switch (state) {
36 			case State.PREPARE: {
37 				// SPEC: wrong streamId is treated as connection error.
38 				if (getStreamId() != 0)
39 					return connectionFailure(buffer, cast(int)ErrorCode.PROTOCOL_ERROR, "invalid_ping_frame");
40 				// SPEC: wrong body length is treated as connection error.
41 				if (getBodyLength() != 8)
42 					return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_ping_frame");
43 				state = State.PAYLOAD;
44 				break;
45 			}
46 			case State.PAYLOAD: {
47 				payload = new byte[8];
48 				if (buffer.remaining() >= 8) {
49 					buffer.get(payload);
50 					return onPing(payload);
51 				} else {
52 					state = State.PAYLOAD_BYTES;
53 					cursor = 8;
54 				}
55 				break;
56 			}
57 			case State.PAYLOAD_BYTES: {
58 				payload[8 - cursor] = buffer.get();
59 				--cursor;
60 				if (cursor == 0)
61 					return onPing(payload);
62 				break;
63 			}
64 			default: {
65 				throw new IllegalStateException("");
66 			}
67 			}
68 		}
69 		return false;
70 	}
71 
72 	private bool onPing(byte[] payload) {
73 		PingFrame frame = new PingFrame(payload, hasFlag(Flags.ACK));
74 		reset();
75 		notifyPing(frame);
76 		return true;
77 	}
78 
79 	private enum State {
80 		PREPARE, PAYLOAD, PAYLOAD_BYTES
81 	}
82 }