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