1 module hunt.http.codec.http.decode.GoAwayBodyParser; 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.GoAwayFrame; 11 12 import hunt.Exceptions; 13 14 /** 15 */ 16 class GoAwayBodyParser :BodyParser { 17 private State state = State.PREPARE; 18 private int cursor; 19 private int length; 20 private int lastStreamId; 21 private int error; 22 private byte[] payload; 23 24 this(HeaderParser headerParser, Parser.Listener listener) { 25 super(headerParser, listener); 26 } 27 28 private void reset() { 29 state = State.PREPARE; 30 cursor = 0; 31 length = 0; 32 lastStreamId = 0; 33 error = 0; 34 payload = null; 35 } 36 37 override 38 bool parse(ByteBuffer buffer) { 39 while (buffer.hasRemaining()) { 40 switch (state) { 41 case State.PREPARE: { 42 state = State.LAST_STREAM_ID; 43 length = getBodyLength(); 44 break; 45 } 46 case State.LAST_STREAM_ID: { 47 if (buffer.remaining() >= 4) { 48 lastStreamId = buffer.get!int(); 49 lastStreamId &= 0x7F_FF_FF_FF; 50 state = State.ERROR; 51 length -= 4; 52 if (length <= 0) 53 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_go_away_frame"); 54 } else { 55 state = State.LAST_STREAM_ID_BYTES; 56 cursor = 4; 57 } 58 break; 59 } 60 case State.LAST_STREAM_ID_BYTES: { 61 int currByte = buffer.get() & 0xFF; 62 --cursor; 63 lastStreamId += currByte << (8 * cursor); 64 --length; 65 if (cursor > 0 && length <= 0) 66 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_go_away_frame"); 67 if (cursor == 0) { 68 lastStreamId &= 0x7F_FF_FF_FF; 69 state = State.ERROR; 70 if (length == 0) 71 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_go_away_frame"); 72 } 73 break; 74 } 75 case State.ERROR: { 76 if (buffer.remaining() >= 4) { 77 error = buffer.get!int(); 78 state = State.PAYLOAD; 79 length -= 4; 80 if (length < 0) 81 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_go_away_frame"); 82 if (length == 0) 83 return onGoAway(lastStreamId, error, null); 84 } else { 85 state = State.ERROR_BYTES; 86 cursor = 4; 87 } 88 break; 89 } 90 case State.ERROR_BYTES: { 91 int currByte = buffer.get() & 0xFF; 92 --cursor; 93 error += currByte << (8 * cursor); 94 --length; 95 if (cursor > 0 && length <= 0) 96 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_go_away_frame"); 97 if (cursor == 0) { 98 state = State.PAYLOAD; 99 if (length == 0) 100 return onGoAway(lastStreamId, error, null); 101 } 102 break; 103 } 104 case State.PAYLOAD: { 105 payload = new byte[length]; 106 if (buffer.remaining() >= length) { 107 buffer.get(payload); 108 return onGoAway(lastStreamId, error, payload); 109 } else { 110 state = State.PAYLOAD_BYTES; 111 cursor = length; 112 } 113 break; 114 } 115 case State.PAYLOAD_BYTES: { 116 payload[payload.length - cursor] = buffer.get(); 117 --cursor; 118 if (cursor == 0) 119 return onGoAway(lastStreamId, error, payload); 120 break; 121 } 122 default: { 123 throw new IllegalStateException(""); 124 } 125 } 126 } 127 return false; 128 } 129 130 private bool onGoAway(int lastStreamId, int error, byte[] payload) { 131 GoAwayFrame frame = new GoAwayFrame(lastStreamId, error, payload); 132 reset(); 133 notifyGoAway(frame); 134 return true; 135 } 136 137 private enum State { 138 PREPARE, LAST_STREAM_ID, LAST_STREAM_ID_BYTES, ERROR, ERROR_BYTES, PAYLOAD, PAYLOAD_BYTES 139 } 140 }