1 module hunt.http.codec.http.decode.PushPromiseBodyParser; 2 3 import hunt.io.ByteBuffer; 4 5 import hunt.http.codec.http.decode.BodyParser; 6 import hunt.http.codec.http.decode.HeaderBlockParser; 7 import hunt.http.codec.http.decode.HeaderParser; 8 import hunt.http.codec.http.decode.Parser; 9 10 import hunt.http.codec.http.frame.ErrorCode; 11 import hunt.http.codec.http.frame.Flags; 12 import hunt.http.codec.http.frame.PushPromiseFrame; 13 import hunt.http.HttpMetaData; 14 15 import hunt.Exceptions; 16 17 import std.algorithm; 18 19 /** 20 */ 21 class PushPromiseBodyParser :BodyParser { 22 private HeaderBlockParser headerBlockParser; 23 private State state = State.PREPARE; 24 private int cursor; 25 private int length; 26 private int paddingLength; 27 private int streamId; 28 29 this(HeaderParser headerParser, Parser.Listener listener, 30 HeaderBlockParser headerBlockParser) { 31 super(headerParser, listener); 32 this.headerBlockParser = headerBlockParser; 33 } 34 35 private void reset() { 36 state = State.PREPARE; 37 cursor = 0; 38 length = 0; 39 paddingLength = 0; 40 streamId = 0; 41 } 42 43 override 44 bool parse(ByteBuffer buffer) { 45 bool loop = false; 46 while (buffer.hasRemaining() || loop) { 47 switch (state) { 48 case State.PREPARE: { 49 // SPEC: wrong streamId is treated as connection error. 50 if (getStreamId() == 0) 51 return connectionFailure(buffer, cast(int)ErrorCode.PROTOCOL_ERROR, "invalid_push_promise_frame"); 52 53 // For now we don't support PUSH_PROMISE frames that don't have 54 // END_HEADERS. 55 if (!hasFlag(Flags.END_HEADERS)) 56 return connectionFailure(buffer, cast(int)ErrorCode.INTERNAL_ERROR, "unsupported_push_promise_frame"); 57 58 length = getBodyLength(); 59 60 if (isPadding()) { 61 state = State.PADDING_LENGTH; 62 } else { 63 state = State.STREAM_ID; 64 } 65 break; 66 } 67 case State.PADDING_LENGTH: { 68 paddingLength = buffer.get() & 0xFF; 69 --length; 70 length -= paddingLength; 71 state = State.STREAM_ID; 72 if (length < 4) 73 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_push_promise_frame"); 74 break; 75 } 76 case State.STREAM_ID: { 77 if (buffer.remaining() >= 4) { 78 streamId = buffer.get!int(); 79 streamId &= 0x7F_FF_FF_FF; 80 length -= 4; 81 state = State.HEADERS; 82 loop = length == 0; 83 } else { 84 state = State.STREAM_ID_BYTES; 85 cursor = 4; 86 } 87 break; 88 } 89 case State.STREAM_ID_BYTES: { 90 int currByte = buffer.get() & 0xFF; 91 --cursor; 92 streamId += currByte << (8 * cursor); 93 --length; 94 if (cursor > 0 && length <= 0) 95 return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_push_promise_frame"); 96 if (cursor == 0) { 97 streamId &= 0x7F_FF_FF_FF; 98 state = State.HEADERS; 99 loop = length == 0; 100 } 101 break; 102 } 103 case State.HEADERS: { 104 HttpMetaData metaData = headerBlockParser.parse(buffer, length); 105 if (metaData !is null) { 106 state = State.PADDING; 107 loop = paddingLength == 0; 108 onPushPromise(streamId, metaData); 109 } 110 break; 111 } 112 case State.PADDING: { 113 int size = std.algorithm.min(buffer.remaining(), paddingLength); 114 buffer.position(buffer.position() + size); 115 paddingLength -= size; 116 if (paddingLength == 0) { 117 reset(); 118 return true; 119 } 120 break; 121 } 122 default: { 123 throw new IllegalStateException(""); 124 } 125 } 126 } 127 return false; 128 } 129 130 private void onPushPromise(int streamId, HttpMetaData metaData) { 131 PushPromiseFrame frame = new PushPromiseFrame(getStreamId(), streamId, metaData); 132 notifyPushPromise(frame); 133 } 134 135 private enum State { 136 PREPARE, PADDING_LENGTH, STREAM_ID, STREAM_ID_BYTES, HEADERS, PADDING 137 } 138 }