1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2014 Richard Andrew Cattermole
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 module dakka.vibe.server;
25 import dakka.base.defs;
26 import vibe.http.server : HTTPServerRequest, HTTPServerResponse, SessionOption;
27 import vibe.http.common : HTTPVersion, HTTPMethod, CookieValueMap;
28 import vibe.http.session : Session;
29 import vibe.http.status : HTTPStatus;
30 import vibe.inet.webform : FormFields;
31 import vibe.inet.message : InetHeaderMap;
32 import vibe.data.json : Json;
33 import std.datetime : SysTime;
34 
35 final struct RequestData {
36 	//HTTPRequest
37 	HTTPVersion httpVersion;
38 	HTTPMethod method;
39 	string requestURL;
40 
41 	//HTTPServerRequest
42 	string peer;
43 	bool ssl;
44 	string path;
45 	string username;
46 	string password;
47 	string queryString;
48 	CookieValueMap cookies;
49 	string[string] params;
50 
51 	// not included by arg copy
52 
53 	//HTTPRequest
54 	string[string] headers;
55 
56 	//HTTPServerRequest
57 	string[string] query;
58 	string[string] form;
59 
60 	ushort port;
61 	string timeCreated;
62 }
63 
64 class HTTPReqResp : Actor {
65 	import cerealed.attrs;
66 
67 	@NoCereal private __gshared {
68 		HTTPServerRequest request_;
69 		HTTPServerResponse response_;
70 		Session session_;
71 		bool reassigned;
72 	}
73 
74 	@DakkaLocalOnly
75 	void assignData(HTTPServerRequest request, HTTPServerResponse response, Session session) {
76 		synchronized {
77 			reassigned = true;
78 			request_ = request;
79 			response_ = response;
80 			session_ = session;
81 		}
82 	}
83 
84 	@NoCereal {
85 		@property {
86 			bool hasBeenReassigned() {
87 				synchronized {
88 					if (reassigned) {
89 						reassigned = false;
90 						return true;
91 					} else {
92 						return false;
93 					}
94 				}
95 			}
96 
97 			/*
98 			 * Request 
99 			 */
100 
101 			RequestData request() {
102 				import vibe.utils.array : FixedAppender;
103 
104 				synchronized {
105 					string[string] headers;
106 					foreach(key, value; request_.headers)
107 						headers[key] = value;
108 					string[string] query;
109 					foreach(key, value; request_.query)
110 						headers[key] = value;
111 					string[string] form;
112 					foreach(key, value; request_.form)
113 						headers[key] = value;
114 					ushort port = request_.clientAddress.port;
115                     return mixin("RequestData(" ~ argsFromNames!(RequestData, "request_")(11) ~ ", headers, query, form, port, request_.timeCreated.toISOExtString)");
116 				}
117 			}
118 
119 			@DakkaLocalOnly {
120 				import dakka.vibe.client : DakkaHTTPRequest, DakkaHTTPResponse;
121 				DakkaHTTPRequest client_request() { return new DakkaHTTPRequest(this); }
122 				DakkaHTTPResponse client_response() { return new DakkaHTTPResponse(this); }
123 			}
124 		}
125 
126 		/*
127 		 * Response
128 		 */
129 
130 		void response_writeBody(ubyte[] data, string content_type = null) {
131 			synchronized
132 				response_.writeBody(data, content_type);
133 		}
134 
135 		void response_writeVoidBody() {
136 			synchronized
137 				response_.writeVoidBody();
138 		}
139 
140 		void response_redirect(string url, int status = HTTPStatus.Found) {
141 			synchronized
142 				response_.redirect(url, status);
143 		}
144 
145 		void response_setCookie(string name, string value, string path = "/", long maxAge = long.min, string expires = null, string domain=null) {
146 			synchronized {
147 				auto cookie = response_.setCookie(name, value, path);
148 				if (maxAge != long.min)
149 					cookie.maxAge = maxAge;
150 				if (expires !is null)
151 					cookie.expires = expires;
152 				if (domain !is null)
153 					cookie.domain = domain;
154 			}
155 
156 		}
157 
158 		void response_startSession(string path = "/", size_t options = SessionOption.httpOnly) {
159 			synchronized
160 				session_ = response_.startSession(path, cast(SessionOption)options);
161 		}
162 
163 		void response_terminateSession() {
164 			synchronized
165 				response_.terminateSession();
166 		}
167 
168 		/*
169 		 * Session
170 		 */
171 
172 		bool session_isnull() {
173 			synchronized
174 				return session_.id is null;
175 		}
176 
177 		string session_id() {
178 			synchronized {
179 				assert(session_.id !is null, "Session is currently null. Cannot get id from it.");
180 				return session_.id;
181 			}
182 		}
183 
184 		bool session_isKeySet(string name) {
185 			synchronized {
186 				assert(session_.id !is null, "Session is currently null. Cannot get id from it.");
187 				return session_.isKeySet(name);
188 			}
189 		}
190 
191 		void session_set(string key, string value) {
192 			synchronized {
193 				assert(session_.id !is null, "Session is currently null. Cannot get id from it.");
194 				session_.set(key, value);
195 			}
196 		}
197 
198 		string session_get(string key) {
199 			synchronized	{
200 				assert(session_.id !is null, "Session is currently null. Cannot get id from it.");
201 				return session_.get!string(key);
202 			}
203 		}
204 
205 		string[] session_keys() {
206 			synchronized {
207 				assert(session_.id !is null, "Session is currently null. Cannot get id from it.");
208 				string[] ret;
209 				foreach(k, v; session_) {
210 					ret ~= k;
211 				}
212 				return ret;
213 			}
214 		}
215 	}
216 }
217 
218 private {
219 	pure string argsFromNames(T, string name)(size_t max = size_t.max) {
220 		enum T t = T.init;
221 		string ret;
222 		foreach(i, id; __traits(allMembers, T)) {
223 			if (id != "opAssign" && i < max)
224 				ret ~= name ~ "." ~ id ~ ",";
225 		}
226 		ret.length--;
227 		return ret;
228 	}
229 }