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