1 module hunt.http.codec.http.decode.SettingsBodyParser;
2 
3 
4 import hunt.http.codec.http.decode.BodyParser;
5 import hunt.http.codec.http.decode.HeaderParser;
6 import hunt.http.codec.http.decode.Parser;
7 
8 import hunt.http.codec.http.frame.ErrorCode;
9 import hunt.http.codec.http.frame.Flags;
10 import hunt.http.codec.http.frame.SettingsFrame;
11 
12 import hunt.io.ByteBuffer;
13 import hunt.collection.HashMap;
14 import hunt.collection.Map;
15 
16 import hunt.Exceptions;
17 import hunt.logging;
18 import std.format;
19 
20 class SettingsBodyParser :BodyParser {
21 	// 
22 	private State state = State.PREPARE;
23 	private int cursor;
24 	private int length;
25 	private int settingId;
26 	private int settingValue;
27 	private Map!(int, int) settings;
28 
29 	this(HeaderParser headerParser, Parser.Listener listener) {
30 		super(headerParser, listener);
31 	}
32 
33 	protected void reset() {
34 		state = State.PREPARE;
35 		cursor = 0;
36 		length = 0;
37 		settingId = 0;
38 		settingValue = 0;
39 		settings = null;
40 	}
41 
42 	override
43 	protected void emptyBody(ByteBuffer buffer) {
44 		onSettings(new HashMap!(int, int)());
45 	}
46 
47 	override
48 	bool parse(ByteBuffer buffer) {
49 		while (buffer.hasRemaining()) {
50 			switch (state) {
51 			case State.PREPARE: {
52 				// SPEC: wrong streamId is treated as connection error.
53 				if (getStreamId() != 0)
54 					return connectionFailure(buffer, cast(int)ErrorCode.PROTOCOL_ERROR, "invalid_settings_frame");
55 				length = getBodyLength();
56 				settings = new HashMap!(int, int)();
57 				state = State.SETTING_ID;
58 				break;
59 			}
60 			case State.SETTING_ID: {
61 				if (buffer.remaining() >= 2) {
62 					settingId = buffer.get!short() & 0xFF_FF;
63 					state = State.SETTING_VALUE;
64 					length -= 2;
65 					if (length <= 0)
66 						return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_settings_frame");
67 				} else {
68 					cursor = 2;
69 					settingId = 0;
70 					state = State.SETTING_ID_BYTES;
71 				}
72 				break;
73 			}
74 			case State.SETTING_ID_BYTES: {
75 				int currByte = buffer.get() & 0xFF;
76 				--cursor;
77 				settingId += currByte << (8 * cursor);
78 				--length;
79 				if (length <= 0)
80 					return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_settings_frame");
81 				if (cursor == 0) {
82 					state = State.SETTING_VALUE;
83 				}
84 				break;
85 			}
86 			case State.SETTING_VALUE: {
87 				if (buffer.remaining() >= 4) {
88 					settingValue = buffer.get!int();
89 					version(HUNT_HTTP_DEBUG)
90 						tracef(format("setting %d=%d", settingId, settingValue));
91 					settings.put(settingId, settingValue);
92 					state = State.SETTING_ID;
93 					length -= 4;
94 					if (length == 0)
95 						return onSettings(settings);
96 				} else {
97 					cursor = 4;
98 					settingValue = 0;
99 					state = State.SETTING_VALUE_BYTES;
100 				}
101 				break;
102 			}
103 			case State.SETTING_VALUE_BYTES: {
104 				int currByte = buffer.get() & 0xFF;
105 				--cursor;
106 				settingValue += currByte << (8 * cursor);
107 				--length;
108 				if (cursor > 0 && length <= 0)
109 					return connectionFailure(buffer, cast(int)ErrorCode.FRAME_SIZE_ERROR, "invalid_settings_frame");
110 				if (cursor == 0) {
111 					version(HUNT_DEBUG)
112 						tracef(format("setting %d=%d", settingId, settingValue));
113 					settings.put(settingId, settingValue);
114 					state = State.SETTING_ID;
115 					if (length == 0)
116 						return onSettings(settings);
117 				}
118 				break;
119 			}
120 			default: {
121 				throw new IllegalStateException("");
122 			}
123 			}
124 		}
125 		return false;
126 	}
127 
128 	protected bool onSettings(Map!(int, int) settings) {
129 		SettingsFrame frame = new SettingsFrame(settings, hasFlag(Flags.ACK));
130 		reset();
131 		notifySettings(frame);
132 		return true;
133 	}
134 
135 	static SettingsFrame parseBody(ByteBuffer buffer) {
136 		int bodyLength = buffer.remaining();
137 		// AtomicReference!(SettingsFrame) frameRef = new AtomicReference!(SettingsFrame)();
138 		// FIXME: Needing refactor or cleanup -@zxp at 6/28/2018, 11:31:02 AM
139 		// 
140 		SettingsFrame frameRef;
141 
142 		class SettingsBodyParserEx : SettingsBodyParser
143 		{
144 			this() { super(null, null); }
145 
146 			override
147 			protected int getStreamId() {
148 				return 0;
149 			}
150 
151 			override
152 			protected int getBodyLength() {
153 				return bodyLength;
154 			}
155 
156 			override
157 			protected bool onSettings(Map!(int, int) settings) {
158 				// frameRef.set(new SettingsFrame(settings, false));
159 				frameRef = new SettingsFrame(settings, false);
160 				return true;
161 			}
162 
163 			override
164 			protected bool connectionFailure(ByteBuffer buffer, int error, string reason) {
165 				frameRef = null;
166 				return false;
167 			}
168 		}
169 
170 		SettingsBodyParser parser = new SettingsBodyParserEx();
171 		if (bodyLength == 0)
172 			parser.emptyBody(buffer);
173 		else
174 			parser.parse(buffer);
175 		return frameRef;
176 	}
177 
178 	private enum State {
179 		PREPARE, SETTING_ID, SETTING_ID_BYTES, SETTING_VALUE, SETTING_VALUE_BYTES
180 	}
181 }