]> git.proxmox.com Git - proxmox-backup.git/blame - src/main.rs
limit allowed body size
[proxmox-backup.git] / src / main.rs
CommitLineData
763220ce 1extern crate apitest;
d8d978eb 2
6d77fb40 3use failure::*;
ef1f0e65 4use std::collections::HashMap;
d11f14f7 5
c819ec8d 6//use std::collections::HashMap;
504b3597 7use lazy_static::lazy_static;
28e47cea 8
504b3597 9//use apitest::json_schema::*;
886e5ce8 10use apitest::api_info::*;
6d77fb40 11use apitest::json_schema::*;
886e5ce8 12
504b3597 13//use serde_derive::{Serialize, Deserialize};
805aec15
DM
14use serde_json::{json, Value};
15
16use tokio::prelude::*;
17use tokio::timer::Delay;
22f0adf2 18
c819ec8d 19//use hyper::body::Payload;
b82472c0 20use hyper::http::request::Parts;
28e47cea 21use hyper::{Method, Body, Request, Response, Server, StatusCode};
b82472c0
DM
22use hyper::rt::{Future, Stream};
23use hyper::service::service_fn;
3cdec2a0 24use hyper::header;
b82472c0 25
805aec15
DM
26use futures::future::*;
27
28use std::time::{Duration, Instant};
b82472c0 29
260c1ee8 30type BoxFut = Box<Future<Item = Response<Body>, Error = failure::Error> + Send>;
d8d978eb 31
260c1ee8 32macro_rules! error_response {
28e47cea
DM
33 ($status:ident, $msg:expr) => {{
34 let mut resp = Response::new(Body::from($msg));
35 *resp.status_mut() = StatusCode::$status;
260c1ee8 36 resp
b82472c0
DM
37 }}
38}
260c1ee8 39
b82472c0
DM
40macro_rules! http_error_future {
41 ($status:ident, $msg:expr) => {{
260c1ee8 42 let resp = error_response!($status, $msg);
805aec15 43 return Box::new(ok(resp));
28e47cea
DM
44 }}
45}
46
805aec15 47fn get_request_parameters_async<'a>(
c819ec8d
DM
48 info: &'a ApiMethod,
49 parts: Parts,
50 req_body: Body,
805aec15 51) -> Box<Future<Item = Value, Error = failure::Error> + Send + 'a>
b82472c0 52{
cec9f02e 53 let resp = req_body
260c1ee8 54 .map_err(|err| format_err!("Promlems reading request body: {}", err))
cec9f02e
DM
55 .fold(Vec::new(), |mut acc, chunk| {
56 if acc.len() + chunk.len() < 64*1024 { //fimxe: max request body size?
57 acc.extend_from_slice(&*chunk);
58 ok(acc)
59 } else {
60 err(format_err!("Request body too large"))
61 }
62 })
260c1ee8 63 .and_then(move |body| {
b82472c0 64
260c1ee8 65 let bytes = String::from_utf8(body.to_vec())?; // why copy??
b82472c0 66
260c1ee8 67 println!("GOT BODY {:?}", bytes);
b82472c0 68
260c1ee8 69 let mut test_required = true;
b82472c0 70
260c1ee8 71 let mut params = json!({});
c819ec8d 72
260c1ee8
DM
73 if bytes.len() > 0 {
74 params = parse_query_string(&bytes, &info.parameters, true)?;
75 test_required = false;
76 }
c819ec8d 77
260c1ee8
DM
78 if let Some(query_str) = parts.uri.query() {
79 let query_params = parse_query_string(query_str, &info.parameters, test_required)?;
c819ec8d 80
260c1ee8
DM
81 for (k, v) in query_params.as_object().unwrap() {
82 params[k] = v.clone(); // fixme: why clone()??
83 }
b82472c0 84 }
b82472c0 85
805aec15
DM
86 println!("GOT PARAMS {}", params);
87 Ok(params)
88 });
89
90 Box::new(resp)
91}
92
93fn handle_async_api_request<'a>(
94 info: &'a ApiMethod,
95 parts: Parts,
96 req_body: Body,
97) -> Box<Future<Item = Response<Body>, Error = failure::Error> + Send + 'a>
98{
99 let params = get_request_parameters_async(info, parts, req_body);
100
101 let resp = params
102 .and_then(move |params| {
103
104 println!("GOT PARAMS {}", params);
105
106 /*
107 let when = Instant::now() + Duration::from_millis(3000);
108 let task = Delay::new(when).then(|_| {
109 println!("A LAZY TASK");
110 ok(())
111 });
112
113 tokio::spawn(task);
114 */
115
116 (info.async_handler)(params, info)
117 });
118
119 Box::new(resp)
120}
121
122fn handle_sync_api_request<'a>(
123 info: &'a ApiMethod,
124 parts: Parts,
125 req_body: Body,
126) -> Box<Future<Item = Response<Body>, Error = failure::Error> + Send + 'a>
127{
128 let params = get_request_parameters_async(info, parts, req_body);
129
130 let resp = params
131 .and_then(move |params| {
132
260c1ee8
DM
133 println!("GOT PARAMS {}", params);
134
805aec15
DM
135 /*
136 let when = Instant::now() + Duration::from_millis(3000);
137 let task = Delay::new(when).then(|_| {
138 println!("A LAZY TASK");
139 ok(())
140 });
141
142 tokio::spawn(task);
143 */
144
260c1ee8 145 let res = (info.handler)(params, info)?;
c819ec8d 146
260c1ee8 147 Ok(res)
c819ec8d 148
805aec15
DM
149 }).then(|result| {
150 match result {
151 Ok(ref value) => {
152 let json_str = value.to_string();
c819ec8d 153
805aec15
DM
154 Ok(Response::builder()
155 .status(StatusCode::OK)
3cdec2a0 156 .header(header::CONTENT_TYPE, "application/json")
805aec15
DM
157 .body(Body::from(json_str))?)
158 }
159 Err(err) => Ok(error_response!(BAD_REQUEST, err.to_string()))
260c1ee8 160 }
805aec15 161 });
b82472c0
DM
162
163 Box::new(resp)
164}
165
166fn handle_request(req: Request<Body>) -> BoxFut {
167
168 let (parts, body) = req.into_parts();
28e47cea 169
b82472c0
DM
170 let method = parts.method.clone();
171 let path = parts.uri.path();
886e5ce8 172
28e47cea
DM
173 let components: Vec<&str> = path.split('/').filter(|x| !x.is_empty()).collect();
174 let comp_len = components.len();
886e5ce8
DM
175
176 println!("REQUEST {} {}", method, path);
28e47cea
DM
177 println!("COMPO {:?}", components);
178
179 if comp_len >= 1 && components[0] == "api3" {
180 println!("GOT API REQUEST");
181 if comp_len >= 2 {
182 let format = components[1];
183 if format != "json" {
6639c14b 184 http_error_future!(BAD_REQUEST, format!("Unsupported output format '{}'.", format))
28e47cea
DM
185 }
186
0dde2f04 187 if let Some(info) = ROUTER.find_method(&components[2..]) {
28e47cea
DM
188 println!("FOUND INFO");
189 let api_method_opt = match method {
b82472c0
DM
190 Method::GET => &info.get,
191 Method::PUT => &info.put,
192 Method::POST => &info.post,
193 Method::DELETE => &info.delete,
0dde2f04 194 _ => &None,
28e47cea 195 };
bfcba4fd 196 let api_method = match api_method_opt {
28e47cea 197 Some(m) => m,
6639c14b 198 _ => http_error_future!(NOT_FOUND, format!("No such method '{}'.", method)),
28e47cea
DM
199 };
200
4beaacb6 201 // fixme: handle auth
28e47cea 202
805aec15
DM
203 //return handle_sync_api_request(api_method, parts, body);
204 return handle_async_api_request(api_method, parts, body);
b82472c0 205
28e47cea 206 } else {
6639c14b 207 http_error_future!(NOT_FOUND, "Path not found.");
28e47cea
DM
208 }
209 }
210 }
886e5ce8 211
805aec15 212 Box::new(ok(Response::new(Body::from("RETURN WEB GUI\n"))))
886e5ce8
DM
213}
214
ef1f0e65
DM
215// add default dirs which includes jquery and bootstrap
216// my $base = '/usr/share/libpve-http-server-perl';
217// add_dirs($self->{dirs}, '/css/' => "$base/css/");
218// add_dirs($self->{dirs}, '/js/' => "$base/js/");
219// add_dirs($self->{dirs}, '/fonts/' => "$base/fonts/");
220
221use std::io;
222use std::fs::{self, DirEntry};
223use std::path::{Path, PathBuf};
224
225fn add_dirs(cache: &mut HashMap<String, PathBuf>, alias: String, path: &Path) -> Result<(), Error> {
226
227 if path.is_dir() {
228 for direntry in fs::read_dir(path)? {
229 let entry = direntry?;
230 let entry_path = entry.path();
231 let file_type = entry.file_type()?;
232 if let Some(file_name) = entry_path.file_name() {
233 let newalias = alias.clone() + &String::from(file_name.to_string_lossy()); // fixme
234 if file_type.is_dir() {
235 add_dirs(cache, newalias, entry_path.as_path())?;
236 } else if file_type.is_file() {
237 cache.insert(newalias, entry_path);
238 }
239 }
240 }
241 }
242 Ok(())
243 }
244
245fn initialize_directory_cache() -> HashMap<String, PathBuf> {
246
247 let mut basedirs = HashMap::new();
248
249 basedirs.insert("novnc", Path::new("/usr/share/novnc-pve"));
250 basedirs.insert("extjs", Path::new("/usr/share/javascript/extjs"));
251 basedirs.insert("fontawesome", Path::new("/usr/share/fonts-font-awesome"));
252 basedirs.insert("xtermjs", Path::new("/usr/share/pve-xtermjs"));
253 basedirs.insert("widgettoolkit", Path::new("/usr/share/javascript/proxmox-widget-toolkit"));
254
255 let mut cache = HashMap::new();
256
257 add_dirs(&mut cache, "/pve2/ext6/".into(), basedirs["extjs"]);
258
259 cache
260}
261
262lazy_static!{
263 static ref CACHED_DIRS: HashMap<String, PathBuf> = initialize_directory_cache();
264}
265
504b3597 266lazy_static!{
0dde2f04 267 static ref ROUTER: MethodInfo = apitest::api3::router();
504b3597
DM
268}
269
d8d978eb
DM
270fn main() {
271 println!("Fast Static Type Definitions 1");
272
ef1f0e65
DM
273 let mut count = 0;
274 for (k, v) in CACHED_DIRS.iter() {
275 println!("DIRCACHE: {:?} => {:?}", k, v);
276 count += 1;
277 }
278 println!("Dircache contains {} entries.", count);
279
886e5ce8
DM
280 let addr = ([127, 0, 0, 1], 8007).into();
281
282 let new_svc = || {
260c1ee8
DM
283 service_fn(|req| {
284 // clumsy way to convert failure::Error to Response
285 handle_request(req).then(|result| -> Result<Response<Body>, String> {
286 match result {
287 Ok(res) => Ok(res),
6639c14b 288 Err(err) => Ok(error_response!(BAD_REQUEST, err.to_string())),
260c1ee8
DM
289 }
290 })
291 })
886e5ce8
DM
292 };
293
294 let server = Server::bind(&addr)
295 .serve(new_svc)
296 .map_err(|e| eprintln!("server error: {}", e));
297
298 // Run this server for... forever!
299 hyper::rt::run(server);
d8d978eb 300}