1 module hunt.http.HttpRequest;
2 
3 import hunt.http.HttpField;
4 import hunt.http.HttpFields;
5 import hunt.http.HttpHeader;
6 import hunt.http.HttpMetaData;
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.format;
21 import std.range;
22 
23 
24 version(WITH_HUNT_TRACE) {
25     import hunt.trace.Tracer;
26 }
27 
28 /**
29  * 
30  */
31 class HttpRequest : HttpMetaData {
32     private string _method;
33     private HttpURI _uri;
34     private Object[string] _attributes;
35 
36     this(HttpFields fields) {
37         this("", null, HttpVersion.Null, fields);
38     }
39 
40     // this(string method, HttpURI uri, HttpVersion ver, HttpFields fields) {
41     //     this(method, uri, ver, fields, long.min);
42     // }
43 
44 
45     this(string method, string scheme, string host, int port, string uri, 
46             HttpVersion ver, HttpFields fields, long contentLength=long.min) {
47         this(method, new HttpURI(scheme, host, port, uri), ver, fields, contentLength);
48     }
49 
50     this(HttpRequest request) {
51         this(request.getMethod(), new HttpURI(request.getURI()), request.getHttpVersion(), 
52             new HttpFields(request.getFields()), request.getContentLength());
53     }
54 
55     this(string method, HttpURI uri, HttpVersion ver, HttpFields fields, long contentLength=long.min) {
56         super(ver, fields, contentLength);
57         _method = method;
58         _uri = uri;
59         
60     }
61 
62     override void recycle() {
63         super.recycle();
64         _method = null;
65         if (_uri !is null)
66             _uri.clear();
67     }
68 
69     override bool isRequest() {
70         return true;
71     }
72     
73     /**
74      * Checks whether the request is secure or not.
75      *
76      * This method can read the client protocol from the "X-Forwarded-Proto" header
77      * when trusted proxies were set via "setTrustedProxies()".
78      *
79      * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http".
80      *
81      * @return bool
82      */
83     bool isHttps() {
84         // FIXME: Needing refactor or cleanup -@zhangxueping at 2020-04-16T11:28:15+08:00
85         // 
86         string scheme = _uri.getScheme();
87         return scheme == HttpScheme.HTTPS || scheme == HttpScheme.WSS;
88     }
89 
90     /**
91     * @return the HTTP method
92     */
93     string getMethod() {
94         return _method;
95     }
96 
97     /**
98     * @param method the HTTP method to set
99     */
100     void setMethod(string method) {
101         _method = method;
102     }
103 
104     /**
105     * @return the HTTP URI
106     */
107     HttpURI getURI() {
108         return _uri;
109     }
110 
111     /**
112     * @return the HTTP URI in string form
113     */
114     string getURIString() {
115         return _uri is null ? null : _uri.toString();
116     }
117 
118     /**
119     * @param uri the HTTP URI to set
120     */
121     void setURI(HttpURI uri) {
122         _uri = uri;
123     }
124 
125     bool headerExists(HttpHeader header) {
126         return getFields().containsKey(header.asString());
127     }
128 
129     bool headerExists(string key) {
130         return getFields().containsKey(key);
131     }
132 
133     bool isChunked() {
134         string transferEncoding = getFields().get(HttpHeader.TRANSFER_ENCODING);
135         return HttpHeaderValue.CHUNKED.asString() == transferEncoding
136                 || (getHttpVersion() == HttpVersion.HTTP_2 && getContentLength() < 0);
137     }
138 
139     deprecated("Using getAttribute instead.")
140     Object getAttachment() {
141         return getAttribute("_attachment");
142     }
143 
144     deprecated("Using setAttribute instead.")
145     void setAttachment(Object attachment) {
146         // this.attachment = attachment;
147         setAttribute("_attachment", attachment);
148     }
149 
150     /**
151      * Returns the value of the user-defined attribute of this connection.
152      *
153      * @param key the key of the attribute
154      * @return <tt>null</tt> if there is no attribute with the specified key
155      */
156     Object getAttribute(string key) {
157         return getAttribute(key, null);
158     }
159 
160     /**
161      * Returns the value of user defined attribute associated with the
162      * specified key.  If there's no such attribute, the specified default
163      * value is associated with the specified key, and the default value is
164      * returned.  This method is same with the following code except that the
165      * operation is performed atomically.
166      * <pre>
167      * if (containsAttribute(key)) {
168      *     return getAttribute(key);
169      * } else {
170      *     setAttribute(key, defaultValue);
171      *     return defaultValue;
172      * }
173      * </pre>
174      * 
175      * @param key the key of the attribute we want to retreive
176      * @param defaultValue the default value of the attribute
177      * @return The retrieved attribute or <tt>null</tt> if not found
178      */
179     Object getAttribute(string key, Object defaultValue) {
180         return _attributes.get(key, defaultValue);
181     }
182 
183     /**
184      * Sets a user-defined attribute.
185      *
186      * @param key the key of the attribute
187      * @param value the value of the attribute
188      * @return The old value of the attribute.  <tt>null</tt> if it is new.
189      */
190     Object setAttribute(string key, Object value) {
191         auto itemPtr = key in _attributes;
192 		Object oldValue = null;
193         if(itemPtr !is null) {
194             oldValue = *itemPtr;
195         }
196         _attributes[key] = value;
197 		return oldValue;
198     }
199 
200     /**
201      * Removes a user-defined attribute with the specified key.
202      *
203      * @param key The key of the attribute we want to remove
204      * @return The old value of the attribute.  <tt>null</tt> if not found.
205      */
206     Object removeAttribute(string key) {
207         auto itemPtr = key in _attributes;
208         if(itemPtr is null) {
209             return null;
210         } else {
211             Object oldValue = *itemPtr;
212             _attributes.remove(key);
213             return oldValue;
214         }
215     }
216 
217     /**
218      * @param key The key of the attribute we are looking for in the connection 
219      * @return <tt>true</tt> if this connection contains the attribute with
220      * the specified <tt>key</tt>.
221      */
222     bool containsAttribute(string key) {
223         auto itemPtr = key in _attributes;
224         return itemPtr !is null;
225     }
226 
227     /**
228      * @return the set of keys of all user-defined attributes.
229      */
230     string[] getAttributeKeys() {
231         return _attributes.keys();
232     }
233 
234     override string toString() {
235         HttpFields fields = getFields();
236         return format("%s{u=%s,%s,h=%d,cl=%d}",
237                 getMethod(), getURI(), getHttpVersion(), fields is null ? -1 : fields.size(), getContentLength());
238     }
239 
240 version(WITH_HUNT_TRACE) {
241     Tracer tracer;
242 }
243 
244 }