1 /*
2  * Hunt - A high-level D Programming Language Web framework that encourages rapid development and clean, pragmatic design.
3  *
4  * Copyright (C) 2015-2019, HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.framework.http.Response;
13 
14 import hunt.framework.http.HttpErrorResponseHandler;
15 import hunt.http.HttpStatus;
16 import hunt.http.server;
17 import hunt.io.ByteBuffer;
18 import hunt.serialization.JsonSerializer;
19 import hunt.logging.ConsoleLogger;
20 
21 import std.conv;
22 import std.json;
23 import std.range;
24 import std.traits;
25 
26 /**
27  * 
28  */
29 class Response {
30     protected HttpServerResponse _response;
31     private bool _bodySet = false;
32 
33     this() {
34         _response = new HttpServerResponse();
35     }
36 
37     this(string content) {
38         HttpBody hb = HttpBody.create(content);
39         this(hb);
40     }
41 
42     this(HttpBody bodyContent) {
43         this(new HttpServerResponse(bodyContent));
44     }
45 
46     this(HttpServerResponse response) {
47         this._response = response;
48     }
49 
50     HttpServerResponse httpResponse() {
51         return _response;
52     }
53 
54     HttpFields getFields() {
55         return _response.getFields();
56     }
57 
58     Response header(T)(string header, T value) {
59         getFields().put(header, value);
60         return this;
61     }
62 
63     Response header(T)(HttpHeader header, T value) {
64         getFields().put(header, value);
65         return this;
66     }
67 
68     Response headers(T = string)(T[string] headers) {
69         foreach (string k, T v; headers)
70             getFields().add(k, v);
71         return this;
72     }
73 
74     Response setRestContent(T)(T content) {
75         if(_bodySet) {
76             version(HUNT_DEBUG) warning("The body is set again.");
77         }
78 
79         string contentType = MimeType.APPLICATION_JSON_VALUE;
80 
81         static if(is(T : ByteBuffer)) {
82             HttpBody hb = HttpBody.create(contentType, content);
83         } else static if(isSomeString!T) {
84             HttpBody hb = HttpBody.create(contentType, content);
85         } else {
86             JSONValue js = JsonSerializer.toJson(content);
87             string c = js.toString();
88             HttpBody hb = HttpBody.create(contentType, c);
89         }
90         _response.setBody(hb);
91         _bodySet = true;
92         return this;        
93     }
94 
95     /**
96      * Sets the response content.
97      *
98      * @return this
99      */
100     Response setContent(T)(T content, string contentType = MimeType.TEXT_HTML_VALUE) {
101         if(_bodySet) {
102             version(HUNT_DEBUG) warning("The body is set again.");
103         }
104 
105         static if(is(T : ByteBuffer)) {
106             HttpBody hb = HttpBody.create(contentType, content);
107         } else static if(isSomeString!T) {
108             HttpBody hb = HttpBody.create(contentType, content);
109         } else static if(is(T : K[], K) && is(Unqual!K == ubyte)) {
110             HttpBody hb = HttpBody.create(contentType, content);
111         } else {
112             string c = content.to!string();
113             HttpBody hb = HttpBody.create(contentType, c);
114         }
115         _response.setBody(hb);
116         _bodySet = true;
117         return this;
118     }
119 
120     Response setHtmlContent(string content) {
121         setContent(content, MimeType.TEXT_HTML_UTF_8.toString());
122         return this;
123     }
124 
125 
126     ///set http status code eg. 404 200
127     Response setStatus(int status) {
128         _response.setStatus(status);
129         return this;
130     }
131 
132     int status() @property {
133         return _response.getStatus();
134     }
135 
136 
137     Response setReason(string reason) {
138         _response.setReason(reason);
139         return this;
140     }
141 
142     ///download file 
143     Response download(string filename, ubyte[] file, string content_type = "binary/octet-stream") {
144         header(HttpHeader.CONTENT_TYPE, content_type);
145         header(HttpHeader.CONTENT_DISPOSITION,
146                 "attachment; filename=" ~ filename ~ "; size=" ~ (file.length.to!string));
147         setContent(file);
148 
149         return this;
150     }
151 
152     /**
153      * Add a cookie to the response.
154      *
155      * @param  Cookie cookie
156      * @return this
157      */
158     Response withCookie(Cookie cookie) {
159         _response.withCookie(cookie);
160         return this;
161     }
162 
163 //     /// the session store implementation.
164 //     @property HttpSession session() {
165 //         return _request.session();
166 //     }
167 
168 //     /// ditto
169 //     // @property void session(HttpSession se) {
170 //     //     _session = se;
171 //     // }
172 
173 
174 //     // void redirect(string url, bool is301 = false)
175 //     // {
176 //     //     if (_isDone)
177 //     //         return;
178 
179 //     //     setStatus((is301 ? 301 : 302));
180 //     //     setHeader(HttpHeader.LOCATION, url);
181 
182 //     //     connectionClose();
183 //     //     done();
184 //     // }
185 
186     void do404(string body_ = "", string contentype = "text/html;charset=UTF-8") {
187         doError(404, body_, contentype);
188     }
189 
190     void do403(string body_ = "", string contentype = "text/html;charset=UTF-8") {
191         doError(403, body_, contentype);
192     }
193 
194     void doError(ushort code, string body_ = "", string contentype = "text/html;charset=UTF-8") {
195 
196         setStatus(code);
197         getFields().put(HttpHeader.CONTENT_TYPE, contentype);
198         setContent(errorPageHtml(code, body_));
199     }
200 
201     void doError(ushort code, Throwable exception, string contentype = "text/html;charset=UTF-8") {
202 
203         setStatus(code);
204         getFields().put(HttpHeader.CONTENT_TYPE, contentype);
205 
206         version(HUNT_DEBUG) {
207             setContent(errorPageWithStack(code, "<pre>" ~ exception.toString() ~ "/<pre>"));
208         } else {
209             setContent(errorPageWithStack(code, exception.msg));
210         }
211     }    
212 
213     void setHttpError(ushort code) {
214         this.setStatus(code);
215         this.setContent(errorPageHtml(code));
216     }
217 
218     
219     alias setHeader = header;
220     alias withHeaders = headers;
221     alias withContent = setContent;
222 }