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 }