1 module hunt.http.codec.http.hpack.MetaDataBuilder;
2 
3 import hunt.http.codec.http.hpack.AuthorityHttpField;
4 import hunt.http.codec.http.model.BadMessageException;
5 import hunt.http.codec.http.model.HostPortHttpField;
6 import hunt.http.codec.http.model.HttpField;
7 import hunt.http.codec.http.model.HttpFields;
8 import hunt.http.codec.http.model.HttpHeader;
9 import hunt.http.codec.http.model.HttpScheme;
10 import hunt.http.codec.http.model.HttpStatus;
11 import hunt.http.codec.http.model.HttpVersion;
12 import hunt.http.codec.http.model.MetaData;
13 import hunt.http.codec.http.model.StaticTableHttpField;
14 
15 import hunt.lang.exception;
16 
17 import std.algorithm;
18 import std.array;
19 import std.conv;
20 
21 import hunt.logging;
22 
23 /**
24 */
25 class MetaDataBuilder {
26 	private int _maxSize;
27 	private int _size;
28 	private int _status;
29 	private string _method;
30 	private string _scheme;
31 	private HostPortHttpField _authority;
32 	private string _path;
33 	private long _contentLength = long.min;
34 	private HttpFields _fields;
35 
36 	/**
37 	 * @param maxHeadersSize
38 	 *            The maximum size of the headers, expressed as total name and
39 	 *            value characters.
40 	 */
41 	this(int maxHeadersSize) {
42 		_maxSize = maxHeadersSize;
43 		_fields = new HttpFields(10);
44 	}
45 
46 	/**
47 	 * Get the maxSize.
48 	 * 
49 	 * @return the maxSize
50 	 */
51 	int getMaxSize() {
52 		return _maxSize;
53 	}
54 
55 	/**
56 	 * Get the size.
57 	 * 
58 	 * @return the current size in bytes
59 	 */
60 	int getSize() {
61 		return _size;
62 	}
63 
64 	void emit(HttpField field) {
65 		HttpHeader header = field.getHeader();
66 		string name = field.getName();
67 		string value = field.getValue();
68 		int field_size = cast(int)(name.length + (value == null ? 0 : value.length));
69 		_size += field_size + 32;
70 		if (_size > _maxSize)
71 			throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,
72 					"Header size " ~ to!string(_size) ~ ">" ~ to!string(_maxSize));
73 
74 		string fieldTypeName = typeof(field).stringof;
75 		// trace("fieldTypeName: ", fieldTypeName);
76 		if (fieldTypeName.startsWith("StaticTableHttpField")) {
77 			if(header == HttpHeader.C_STATUS){
78 				StaticTableHttpField!int staticField = cast(StaticTableHttpField!int) field;
79 				_status = staticField.getStaticValue();
80 			}
81 			else if(header == HttpHeader.C_METHOD){
82 				_method = value;
83 			}
84 			else if(header == HttpHeader.C_SCHEME){
85 				StaticTableHttpField!string staticField = cast(StaticTableHttpField!string) field;
86 				_scheme = staticField.getStaticValue();
87 			}
88 			else
89 				throw new IllegalArgumentException(name);
90 
91 		} else if (header != HttpHeader.Null) {
92 			if(header == HttpHeader.C_STATUS)
93 				_status = field.getIntValue();
94 			else if(header == HttpHeader.C_METHOD)
95 				_method = value;
96 			else if(header == HttpHeader.C_SCHEME){
97 				if (value != null)
98 					_scheme = value; // HttpScheme.CACHE[value];
99 			}
100 			else if(header == HttpHeader.C_AUTHORITY) {
101 				if (typeid(field) == typeid(HostPortHttpField))
102 					_authority = cast(HostPortHttpField) field;
103 				else if (value != null)
104 					_authority = new AuthorityHttpField(value);
105 			}
106 			else if(header == HttpHeader.HOST){
107 				// :authority fields must come first. If we have one, ignore the
108 				// host header as far as authority goes.
109 				if (_authority is null) {
110 					if (typeid(field) == typeid(HostPortHttpField))
111 						_authority = cast(HostPortHttpField) field;
112 					else if (value != null)
113 						_authority = new AuthorityHttpField(value);
114 				}
115 				_fields.add(field);
116 			}
117 			else if(header == HttpHeader.C_PATH)
118 				_path = value;
119 			else if(header == HttpHeader.CONTENT_LENGTH) {
120 				_contentLength = field.getLongValue();
121 				_fields.add(field);
122 			}
123 			else
124 			{
125 				if (name[0] != ':')
126 					_fields.add(field);
127 			}
128 		} else {
129 			if (name[0] != ':')
130 				_fields.add(field);
131 		}
132 	}
133 
134 	MetaData build() {
135 		try {
136 			HttpFields fields = _fields;
137 			_fields = new HttpFields(std.algorithm.max(10, fields.size() + 5));
138 
139 			if (!_method.empty)
140 				return new HttpRequest(_method, _scheme, _authority, _path, HttpVersion.HTTP_2, fields,
141 						_contentLength);
142 			if (_status != 0)
143 				return new HttpResponse(HttpVersion.HTTP_2, _status, fields, _contentLength);
144 			return new MetaData(HttpVersion.HTTP_2, fields, _contentLength);
145 		} finally {
146 			_status = 0;
147 			_method = null;
148 			_scheme = null;
149 			_authority = null;
150 			_path = null;
151 			_size = 0;
152 			_contentLength = long.min;
153 		}
154 	}
155 
156 	/**
157 	 * Check that the max size will not be exceeded.
158 	 * 
159 	 * @param length
160 	 *            the length
161 	 * @param huffman
162 	 *            the huffman name
163 	 */
164 	void checkSize(int length, bool huffman) {
165 		// Apply a huffman fudge factor
166 		if (huffman)
167 			length = (length * 4) / 3;
168 		if ((_size + length) > _maxSize)
169 			throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,
170 					"Header size " ~ to!string(_size + length) ~ ">" ~ to!string(_maxSize));
171 	}
172 }