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 }