1 module hunt.http.codec.http.decode.ContinuationBodyParser;
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.HeaderBlockFragments;
8 import hunt.http.codec.http.decode.HeaderParser;
9 import hunt.http.codec.http.decode.Parser;
10 
11 import hunt.http.codec.http.frame.ErrorCode;
12 import hunt.http.codec.http.frame.Flags;
13 import hunt.http.codec.http.frame.HeadersFrame;
14 
15 import hunt.http.HttpMetaData;
16 
17 import hunt.Exceptions;
18 
19 /**
20 */
21 class ContinuationBodyParser :BodyParser {
22 	private HeaderBlockParser headerBlockParser;
23 	private HeaderBlockFragments headerBlockFragments;
24 	private State state = State.PREPARE;
25 	private int length;
26 
27 	this(HeaderParser headerParser, Parser.Listener listener,
28 			HeaderBlockParser headerBlockParser, HeaderBlockFragments headerBlockFragments) {
29 		super(headerParser, listener);
30 		this.headerBlockParser = headerBlockParser;
31 		this.headerBlockFragments = headerBlockFragments;
32 	}
33 
34 	override
35 	protected void emptyBody(ByteBuffer buffer) {
36 		if (hasFlag(Flags.END_HEADERS))
37 			onHeaders();
38 	}
39 
40 	override
41 	bool parse(ByteBuffer buffer) {
42 		while (buffer.hasRemaining()) {
43 			switch (state) {
44 			case State.PREPARE: {
45 				// SPEC: wrong streamId is treated as connection error.
46 				if (getStreamId() == 0)
47 					return connectionFailure(buffer, cast(int)ErrorCode.PROTOCOL_ERROR, "invalid_continuation_frame");
48 
49 				if (getStreamId() != headerBlockFragments.getStreamId())
50 					return connectionFailure(buffer, cast(int)ErrorCode.PROTOCOL_ERROR, "invalid_continuation_stream");
51 
52 				length = getBodyLength();
53 				state = State.FRAGMENT;
54 				break;
55 			}
56 			case State.FRAGMENT: {
57 				int remaining = buffer.remaining();
58 				if (remaining < length) {
59 					headerBlockFragments.storeFragment(buffer, remaining, false);
60 					length -= remaining;
61 					break;
62 				} else {
63 					bool last = hasFlag(Flags.END_HEADERS);
64 					headerBlockFragments.storeFragment(buffer, length, last);
65 					reset();
66 					if (last)
67 						onHeaders();
68 					return true;
69 				}
70 			}
71 			default: {
72 				throw new IllegalStateException("");
73 			}
74 			}
75 		}
76 		return false;
77 	}
78 
79 	private void onHeaders() {
80 		ByteBuffer headerBlock = headerBlockFragments.complete();
81 		HttpMetaData metaData = headerBlockParser.parse(headerBlock, headerBlock.remaining());
82 		HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, headerBlockFragments.getPriorityFrame(),
83 				headerBlockFragments.isEndStream());
84 		notifyHeaders(frame);
85 	}
86 
87 	private void reset() {
88 		state = State.PREPARE;
89 		length = 0;
90 	}
91 
92 	private enum State {
93 		PREPARE, FRAGMENT
94 	}
95 }