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 }