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.client;
25 import dakka.vibe.server;
26 import vibe.http.common : HTTPResponse, HTTPRequest, HTTPVersion, HTTPMethod;
27 import vibe.http.server : HTTPServerRequest, SessionOption;
28 
29 class DakkaHTTPResponse : HTTPResponse {
30 	private {
31 		HTTPReqResp reqresp_;
32 	}
33 
34 	this(HTTPReqResp reqresp) {
35 		reqresp_ = reqresp;
36 	}
37 
38 	void writeBody(ubyte[] data, string content_type = null) { reqresp_.response_writeBody(data, content_type); }
39 	void writeVoidBody() { reqresp_.response_writeVoidBody(); }
40 	void redirect(string url, int status = HTTPStatus.Found) { reqresp_.response_redirect(url, status); }
41 	DakkaCookie setCookie(string name, string value, string path = "/") { return DakkaCookie(reqresp_, name, value, path); }
42 	DakkaSession startSession(string path = "/", SessionOption options = SessionOption.httpOnly) { return DakkaSession(reqresp_, path, cast(size_t)options); }
43 	void terminateSession() { reqresp_.response_terminateSession(); }
44 }
45 
46 struct DakkaCookie {
47 	private {
48 		HTTPReqResp reqresp_;
49 		string m_name;
50 
51 		string m_value;
52 		string m_domain;
53 		string m_path;
54 		string m_expires;
55 		long m_maxAge;
56 	}
57 
58 	this(HTTPReqResp reqresp, string m_name, string value, string path) {
59 		reqresp_ = reqresp;
60 		this.m_name = m_name;
61 		this.m_value = value;
62 		this.m_path = path;
63 		set();
64 	}
65 
66 	@property string name() const { return m_name; }
67 
68 	@property void value(string value) { m_value = value; set(); }
69 	@property string value() const { return m_value; }
70 	
71 	@property void domain(string value) { m_domain = value; set(); }
72 	@property string domain() const { return m_domain; }
73 	
74 	@property void path(string value) { m_path = value; set(); }
75 	@property string path() const { return m_path; }
76 	
77 	@property void expires(string value) { m_expires = value; set(); }
78 	@property string expires() const { return m_expires; }
79 	
80 	@property void maxAge(long value) { m_maxAge = value; set(); }
81 	@property long maxAge() const { return m_maxAge; }
82 
83 	void set() { reqresp_.response_setCookie(m_name, m_value, m_path, m_maxAge, m_expires, m_domain); }
84 }
85 
86 final struct DakkaSession {
87 	private {
88 		string id_;
89 		HTTPReqResp reqresp_;
90 	}
91 
92 	this(HTTPReqResp reqresp, string path, size_t options) {
93 		reqresp_ = reqresp;
94 		reqresp.response_startSession(path, options);
95 		id_ =  reqresp.session_id;
96 	}
97 
98 	bool opCast() { return !reqresp_.session_isnull; }
99 	@property string id() const { return id_; }
100 	bool isKeySet(string key) { return reqresp_.session_isKeySet(key); }
101 
102 	T get(T)(string key, lazy T def_value = T.init) {
103 		import std.conv : to;
104 
105 		if (isKeySet(key))
106 			static if (is(T == string))
107 				return reqresp_.session_get(key);
108 			else
109 				return to!T(reqresp_.session_get(key));
110 		else
111 			return def_value;
112 	}
113 
114 	void set(T)(string key, T value) {
115 		import std.conv : to;
116 		static if (is(T == string))
117 			reqresp_.session_set(key, value);
118 		else
119 			reqresp_.session_set(key, to!string(value));
120 	}
121 
122 	int opApply(int delegate(string key, string value) del) {
123 		foreach(key; reqresp_.session_keys)
124 			if( auto ret = del(key, reqresp_.session_get(key)) != 0 )
125 				return ret;
126 		return 0;
127 	}
128 
129 	string opIndex(string name) { return reqresp_.session_get(name); }
130 	void opIndexAssign(string value, string name) { set(name, value); }
131 }
132 
133 class DakkaHTTPRequest : HTTPRequest {
134 	HTTPServerRequest requestImpl;
135 	alias requestImpl this;
136 
137 	this(HTTPReqResp reqresp) {
138 		auto data = reqresp.request;
139         import std.datetime : SysTime;
140         requestImpl = new HTTPServerRequest(SysTime.fromISOExtString(data.timeCreated), data.port);
141 
142 		mixin(settingFromType!(RequestData, "data")(11));
143 		foreach(k, v; data.headers) {
144 			headers[k] = v;
145 			requestImpl.headers[k] = v;
146 		}
147 		foreach(k, v; data.query) {
148 			requestImpl.query[k] = v;
149 		}
150 		foreach(k, v; data.form) {
151 			requestImpl.form[k] = v;
152 		}
153 	}
154 }
155 
156 private {
157 	pure string settingFromType(T, string name)(size_t max = size_t.max) {
158 		enum T t = T.init;
159 		string ret;
160 		foreach(i, id; __traits(allMembers, T)) {
161 			if (id != "opAssign" && i < max)
162 				ret ~= "static if (__traits(hasMember, typeof(this), \""~ id ~ "\"))" ~ id ~ " = " ~ name ~ "." ~ id ~ ";";
163 		}
164 		return ret;
165 	}
166 }