]> git.proxmox.com Git - proxmox-backup.git/blob - src/api_schema/router.rs
60d00e4cde1d811426aad2741eca8c03a52719bb
[proxmox-backup.git] / src / api_schema / router.rs
1 use failure::*;
2
3 use crate::api_schema::*;
4 use serde_json::{Value};
5 use std::collections::HashMap;
6 use std::sync::Arc;
7 use std::fmt;
8
9 use hyper::{Body, Response, StatusCode};
10 use hyper::rt::Future;
11 use hyper::http::request::Parts;
12
13 pub type BoxFut = Box<Future<Item = Response<Body>, Error = failure::Error> + Send>;
14
15 /// Abstract Interface for API methods to interact with the environment
16 pub trait RpcEnvironment {
17
18 /// Use this to pass additional result data. It is up to the environment
19 /// how the data is used.
20 fn set_result_attrib(&mut self, name: &str, value: Value);
21
22 /// Query additional result data.
23 fn get_result_attrib(&self, name: &str) -> Option<&Value>;
24
25 /// The environment type
26 fn env_type(&self) -> RpcEnvironmentType;
27
28 /// Set user name
29 fn set_user(&mut self, user: Option<String>);
30
31 /// Get user name
32 fn get_user(&self) -> Option<String>;
33 }
34
35
36 /// Environment Type
37 ///
38 /// We use this to enumerate the different environment types. Some methods
39 /// needs to do different things when started from the command line interface,
40 /// or when executed from a privileged server running as root.
41 #[derive(PartialEq, Copy, Clone)]
42 pub enum RpcEnvironmentType {
43 /// Command started from command line
44 CLI,
45 /// Access from public accessible server
46 PUBLIC,
47 /// Access from privileged server (run as root)
48 PRIVILEGED,
49 }
50
51 #[derive(Debug, Fail)]
52 pub struct HttpError {
53 pub code: StatusCode,
54 pub message: String,
55 }
56
57 impl HttpError {
58 pub fn new(code: StatusCode, message: String) -> Self {
59 HttpError { code, message }
60 }
61 }
62
63 impl fmt::Display for HttpError {
64 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65 write!(f, "Error {}: {}", self.code, self.message)
66 }
67 }
68
69 macro_rules! http_err {
70 ($status:ident, $msg:expr) => {{
71 Error::from(HttpError::new(StatusCode::$status, $msg))
72 }}
73 }
74
75 type ApiHandlerFn = fn(Value, &ApiMethod, &mut dyn RpcEnvironment) -> Result<Value, Error>;
76
77 type ApiAsyncHandlerFn = fn(Parts, Body, Value, &ApiAsyncMethod, &mut dyn RpcEnvironment) -> Result<BoxFut, Error>;
78
79 /// This struct defines synchronous API call which returns the restulkt as json `Value`
80 pub struct ApiMethod {
81 /// The protected flag indicates that the provides function should be forwarded
82 /// to the deaemon running in priviledged mode.
83 pub protected: bool,
84 /// This flag indicates that the provided method may change the local timezone, so the server
85 /// should do a tzset afterwards
86 pub reload_timezone: bool,
87 /// Parameter type Schema
88 pub parameters: ObjectSchema,
89 /// Return type Schema
90 pub returns: Arc<Schema>,
91 /// Handler function
92 pub handler: ApiHandlerFn,
93 }
94
95 impl ApiMethod {
96
97 pub fn new(handler: ApiHandlerFn, parameters: ObjectSchema) -> Self {
98 Self {
99 parameters,
100 handler,
101 returns: Arc::new(Schema::Null),
102 protected: false,
103 reload_timezone: false,
104 }
105 }
106
107 pub fn returns<S: Into<Arc<Schema>>>(mut self, schema: S) -> Self {
108
109 self.returns = schema.into();
110
111 self
112 }
113
114 pub fn protected(mut self, protected: bool) -> Self {
115
116 self.protected = protected;
117
118 self
119 }
120
121 pub fn reload_timezone(mut self, reload_timezone: bool) -> Self {
122
123 self.reload_timezone = reload_timezone;
124
125 self
126 }
127 }
128
129 pub struct ApiAsyncMethod {
130 pub parameters: ObjectSchema,
131 pub returns: Arc<Schema>,
132 pub handler: ApiAsyncHandlerFn,
133 }
134
135 impl ApiAsyncMethod {
136
137 pub fn new(handler: ApiAsyncHandlerFn, parameters: ObjectSchema) -> Self {
138 Self {
139 parameters,
140 handler,
141 returns: Arc::new(Schema::Null),
142 }
143 }
144
145 pub fn returns<S: Into<Arc<Schema>>>(mut self, schema: S) -> Self {
146
147 self.returns = schema.into();
148
149 self
150 }
151 }
152
153 pub enum SubRoute {
154 None,
155 Hash(HashMap<String, Router>),
156 MatchAll { router: Box<Router>, param_name: String },
157 }
158
159 pub enum MethodDefinition {
160 None,
161 Simple(ApiMethod),
162 Async(ApiAsyncMethod),
163 }
164
165 pub struct Router {
166 pub get: MethodDefinition,
167 pub put: MethodDefinition,
168 pub post: MethodDefinition,
169 pub delete: MethodDefinition,
170 pub subroute: SubRoute,
171 }
172
173 impl Router {
174
175 pub fn new() -> Self {
176 Self {
177 get: MethodDefinition::None,
178 put: MethodDefinition::None,
179 post: MethodDefinition::None,
180 delete: MethodDefinition::None,
181 subroute: SubRoute::None
182 }
183 }
184
185 pub fn subdir<S: Into<String>>(mut self, subdir: S, router: Router) -> Self {
186 if let SubRoute::None = self.subroute {
187 self.subroute = SubRoute::Hash(HashMap::new());
188 }
189 match self.subroute {
190 SubRoute::Hash(ref mut map) => {
191 map.insert(subdir.into(), router);
192 }
193 _ => panic!("unexpected subroute type"),
194 }
195 self
196 }
197
198 pub fn subdirs(mut self, map: HashMap<String, Router>) -> Self {
199 self.subroute = SubRoute::Hash(map);
200 self
201 }
202
203 pub fn match_all<S: Into<String>>(mut self, param_name: S, router: Router) -> Self {
204 if let SubRoute::None = self.subroute {
205 self.subroute = SubRoute::MatchAll { router: Box::new(router), param_name: param_name.into() };
206 } else {
207 panic!("unexpected subroute type");
208 }
209 self
210 }
211
212 pub fn get(mut self, m: ApiMethod) -> Self {
213 self.get = MethodDefinition::Simple(m);
214 self
215 }
216
217 pub fn put(mut self, m: ApiMethod) -> Self {
218 self.put = MethodDefinition::Simple(m);
219 self
220 }
221
222 pub fn post(mut self, m: ApiMethod) -> Self {
223 self.post = MethodDefinition::Simple(m);
224 self
225 }
226
227 pub fn upload(mut self, m: ApiAsyncMethod) -> Self {
228 self.post = MethodDefinition::Async(m);
229 self
230 }
231
232 pub fn download(mut self, m: ApiAsyncMethod) -> Self {
233 self.get = MethodDefinition::Async(m);
234 self
235 }
236
237
238 pub fn delete(mut self, m: ApiMethod) -> Self {
239 self.delete = MethodDefinition::Simple(m);
240 self
241 }
242
243 pub fn find_route(&self, components: &[&str], uri_param: &mut HashMap<String, String>) -> Option<&Router> {
244
245 if components.len() == 0 { return Some(self); };
246
247 let (dir, rest) = (components[0], &components[1..]);
248
249 match self.subroute {
250 SubRoute::None => {},
251 SubRoute::Hash(ref dirmap) => {
252 if let Some(ref router) = dirmap.get(dir) {
253 println!("FOUND SUBDIR {}", dir);
254 return router.find_route(rest, uri_param);
255 }
256 }
257 SubRoute::MatchAll { ref router, ref param_name } => {
258 println!("URI PARAM {} = {}", param_name, dir); // fixme: store somewhere
259 uri_param.insert(param_name.clone(), dir.into());
260 return router.find_route(rest, uri_param);
261 },
262 }
263
264 None
265 }
266 }