1 module hunt.http.codec.http.encode.HeadersGenerator;
2 
3 import hunt.collection;
4 
5 import hunt.http.codec.http.frame.Flags;
6 import hunt.http.codec.http.frame.Frame;
7 import hunt.http.codec.http.frame.FrameType;
8 import hunt.http.codec.http.frame.HeadersFrame;
9 import hunt.http.codec.http.frame.PriorityFrame;
10 import hunt.http.codec.http.hpack.HpackEncoder;
11 import hunt.http.HttpMetaData;
12 
13 import hunt.http.codec.http.encode.FrameGenerator;
14 import hunt.http.codec.http.encode.HeaderGenerator;
15 import hunt.http.codec.http.encode.PriorityGenerator;
16 
17 import hunt.Exceptions;
18 import std.conv;
19 
20 /**
21 */
22 class HeadersGenerator :FrameGenerator {
23 	private HpackEncoder encoder;
24 	private int maxHeaderBlockFragment;
25 	private PriorityGenerator priorityGenerator;
26 
27 	this(HeaderGenerator headerGenerator, HpackEncoder encoder) {
28 		this(headerGenerator, encoder, 0);
29 	}
30 
31 	this(HeaderGenerator headerGenerator, HpackEncoder encoder, int maxHeaderBlockFragment) {
32 		super(headerGenerator);
33 		this.encoder = encoder;
34 		this.maxHeaderBlockFragment = maxHeaderBlockFragment;
35 		this.priorityGenerator = new PriorityGenerator(headerGenerator);
36 	}
37 
38 	override
39 	List!(ByteBuffer) generate(Frame frame) {
40 		HeadersFrame headersFrame = cast(HeadersFrame) frame;
41 		return generateHeaders(headersFrame.getStreamId(), headersFrame.getMetaData(), headersFrame.getPriority(),
42 				headersFrame.isEndStream());
43 	}
44 
45 	List!(ByteBuffer) generateHeaders(int streamId, HttpMetaData metaData, PriorityFrame priority,
46 			bool endStream) {
47 		List!(ByteBuffer) list = new LinkedList!(ByteBuffer)();
48 		if (streamId < 0)
49 			throw new IllegalArgumentException("Invalid stream id: " ~ streamId.to!string());
50 
51 		int flags = Flags.NONE;
52 
53 		if (priority !is null)
54 			flags = Flags.PRIORITY;
55 
56 		int maxFrameSize = getMaxFrameSize();
57 		ByteBuffer hpacked = BufferUtils.allocate(maxFrameSize);
58 		BufferUtils.clearToFill(hpacked);
59 		encoder.encode(hpacked, metaData);
60 		int hpackedLength = hpacked.position();
61 		BufferUtils.flipToFlush(hpacked, 0);
62 
63 		// Split into CONTINUATION frames if necessary.
64 		if (maxHeaderBlockFragment > 0 && hpackedLength > maxHeaderBlockFragment) {
65 			if (endStream)
66 				flags |= Flags.END_STREAM;
67 
68 			int length = maxHeaderBlockFragment;
69 			if (priority !is null)
70 				length += PriorityFrame.PRIORITY_LENGTH;
71 
72 			ByteBuffer header = generateHeader(FrameType.HEADERS, length, flags, streamId);
73 			generatePriority(header, priority);
74 			BufferUtils.flipToFlush(header, 0);
75 			list.add(header);
76 
77 			hpacked.limit(maxHeaderBlockFragment);
78 			list.add(hpacked.slice());
79 
80 			int position = maxHeaderBlockFragment;
81 			int limit = position + maxHeaderBlockFragment;
82 			while (limit < hpackedLength) {
83 				hpacked.position(position).limit(limit);
84 				header = generateHeader(FrameType.CONTINUATION, maxHeaderBlockFragment, Flags.NONE, streamId);
85 				BufferUtils.flipToFlush(header, 0);
86 				list.add(header);
87 				list.add(hpacked.slice());
88 				position += maxHeaderBlockFragment;
89 				limit += maxHeaderBlockFragment;
90 			}
91 
92 			hpacked.position(position).limit(hpackedLength);
93 			header = generateHeader(FrameType.CONTINUATION, hpacked.remaining(), Flags.END_HEADERS, streamId);
94 			BufferUtils.flipToFlush(header, 0);
95 			list.add(header);
96 			list.add(hpacked);
97 		} else {
98 			flags |= Flags.END_HEADERS;
99 			if (endStream)
100 				flags |= Flags.END_STREAM;
101 
102 			int length = hpackedLength;
103 			if (priority !is null)
104 				length += PriorityFrame.PRIORITY_LENGTH;
105 
106 			ByteBuffer header = generateHeader(FrameType.HEADERS, length, flags, streamId);
107 			generatePriority(header, priority);
108 			BufferUtils.flipToFlush(header, 0);
109 			list.add(header);
110 			list.add(hpacked);
111 		}
112 		return list;
113 	}
114 
115 	private void generatePriority(ByteBuffer header, PriorityFrame priority) {
116 		if (priority !is null) {
117 			priorityGenerator.generatePriorityBody(header, priority.getStreamId(), priority.getParentStreamId(),
118 					priority.getWeight(), priority.isExclusive());
119 		}
120 	}
121 }