1 module hunt.http.HttpMetaData; 2 3 import hunt.http.HttpBody; 4 import hunt.http.HttpHeader; 5 import hunt.http.HttpField; 6 import hunt.http.HttpFields; 7 import hunt.http.HttpScheme; 8 import hunt.http.HttpVersion; 9 10 import hunt.collection; 11 import hunt.Functions; 12 import hunt.Exceptions; 13 import hunt.logging; 14 import hunt.net.util.HttpURI; 15 import hunt.text.Common; 16 import hunt.util.StringBuilder; 17 import hunt.util.Common; 18 19 import std.ascii; 20 import std.conv; 21 import std.format; 22 import std.range; 23 24 deprecated("Using HttpMetaData instead.") 25 alias MetaData = HttpMetaData; 26 27 // from org.apache.hc.core5.http; 28 // HttpMessage : MessageHeaders 29 // 30 31 /** 32 * 33 */ 34 class HttpMetaData : Iterable!HttpField { 35 36 enum string ConntentTypeHeader = HttpHeader.CONTENT_TYPE.toString(); 37 enum string ConntentLengthHeader = HttpHeader.CONTENT_TYPE.toString(); 38 39 private HttpVersion _httpVersion; 40 private HttpFields _fields; 41 private HttpBody _body; 42 private long _contentLength; 43 protected string _contentType; 44 private Supplier!HttpFields _trailers; 45 46 this(HttpVersion ver, HttpFields fields) { 47 this(ver, fields, long.min); 48 } 49 50 this(HttpVersion ver, HttpFields fields, long contentLength) { 51 version(HUNT_HTTP_DEBUG) { 52 if(contentLength>0) { 53 tracef("version: %s", ver.toString()); 54 tracef("contentLength: %d", contentLength); 55 if(fields !is null) { 56 trace(fields.toString()); 57 } 58 } 59 } 60 // assert(fields !is null); 61 _httpVersion = ver; 62 _fields = fields; 63 _contentLength = contentLength; 64 } 65 66 protected void recycle() { 67 _httpVersion = HttpVersion.Null; 68 if (_fields !is null) 69 _fields.clear(); 70 _contentLength = long.min; 71 } 72 73 bool isRequest() { 74 return false; 75 } 76 77 bool isResponse() { 78 return false; 79 } 80 81 bool haveBody() { 82 return _body !is null; 83 } 84 85 /** 86 * @deprecated use {@link #getHttpVersion()} instead 87 */ 88 // deprecated("") 89 // HttpVersion getVersion() { 90 // return getHttpVersion(); 91 // } 92 93 /** 94 * @return the HTTP version of this HttpMetaData object 95 */ 96 HttpVersion getHttpVersion() { 97 return _httpVersion; 98 } 99 100 /** 101 * @param httpVersion the HTTP version to set 102 */ 103 void setHttpVersion(HttpVersion httpVersion) { 104 _httpVersion = httpVersion; 105 } 106 107 /** 108 * @return the HTTP fields of this HttpMetaData object 109 */ 110 HttpFields getFields() { 111 return _fields; 112 } 113 114 Supplier!HttpFields getTrailerSupplier() { 115 return _trailers; 116 } 117 118 void setTrailerSupplier(Supplier!HttpFields trailers) { 119 _trailers = trailers; 120 } 121 122 /** 123 * Returns a non-null value if this response was passed to {@link Callback#onResponse} or returned 124 * from {@link Call#execute()}. Response bodies must be {@linkplain ResponseBody closed} and may 125 * be consumed only once. 126 * 127 * <p>This always returns null on responses returned from {@link #cacheResponse}, {@link 128 * #networkResponse}, and {@link #priorResponse()}. 129 */ 130 HttpBody getBody() { 131 if(_body is null) { 132 throw new Exception("The body is NOT set yet."); 133 } 134 return _body; 135 } 136 137 HttpMetaData setBody(HttpBody b) { 138 if(b !is null) { 139 HttpFields fields = getFields(); 140 if(!fields.contains(HttpHeader.CONTENT_TYPE)) { 141 fields.put(HttpHeader.CONTENT_TYPE, b.contentType); 142 } else { 143 version(HUNT_HTTP_DEBUG) { 144 string existedType = fields.get(HttpHeader.CONTENT_TYPE); 145 string newType = b.contentType(); 146 147 version(HUNT_HTTP_DEBUG) { 148 if(existedType != newType) { 149 warningf("content-type collision, old: %s, new: %s", existedType, newType); 150 } 151 } 152 153 if(fields.contains(HttpHeader.CONTENT_LENGTH)) { 154 auto len = fields.get(HttpHeader.CONTENT_LENGTH); 155 tracef("content-type: %s, content-length: %s", existedType, len); 156 } else { 157 tracef("content-type: %s", existedType); 158 } 159 } 160 } 161 162 fields.put(HttpHeader.CONTENT_LENGTH, b.contentLength.to!string()); 163 } 164 _body = b; 165 166 return this; 167 } 168 169 /** 170 * @return the content length if available, otherwise {@link Long#MIN_VALUE} 171 */ 172 long getContentLength() { 173 if (_contentLength == long.min || _contentLength == -1) { 174 if (_fields !is null) { 175 HttpField field = _fields.getField(HttpHeader.CONTENT_LENGTH); 176 _contentLength = field is null ? -1 : field.getLongValue(); 177 } 178 } 179 version(HUNT_HTTP_DEBUG_MORE) tracef("contentLength=%d", _contentLength); 180 return _contentLength; 181 } 182 183 string getContentType() { 184 if (_contentType.empty()) { 185 if (_fields !is null) { 186 HttpField field = _fields.getField(HttpHeader.CONTENT_TYPE); 187 _contentType = field is null ? "" : field.getValue(); 188 } 189 } 190 return _contentType; 191 } 192 193 string[] headers(string name) { 194 HttpFields fs = getFields(); 195 if(fs !is null) 196 return fs.getValuesList(name); 197 else 198 return null; 199 } 200 201 string[] headers(HttpHeader header) { 202 return headers(header.toString()); 203 } 204 205 string header(string name) { 206 return getFields().get(name); 207 } 208 209 string header(HttpHeader h) { 210 return getFields().get(h); 211 } 212 213 // string header(string name, string defaultValue) { 214 // HttpFields fs = getFields(); 215 // string result = fs.get(name); 216 // return result.empty ? defaultValue : result; 217 // } 218 219 HttpFields headers() { 220 return getFields(); 221 } 222 223 /** 224 * @return an iterator over the HTTP fields 225 * @see #getFields() 226 */ 227 InputRange!HttpField iterator() { 228 return _fields is null ? inputRangeObject(new HttpField[0]) : _fields.iterator(); 229 } 230 231 232 int opApply(scope int delegate(ref HttpField) dg) { 233 int result = 0; 234 foreach(HttpField v; _fields) 235 { 236 result = dg(v); 237 if(result != 0) return result; 238 } 239 return result; 240 } 241 242 override string toString() { 243 StringBuilder sb = new StringBuilder(); 244 foreach (HttpField field ; _fields) 245 sb.append(field.toString()).append(std.ascii.newline); 246 return sb.toString(); 247 } 248 249 250 alias withBody = setBody; 251 252 } 253