1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#![deny(missing_docs)]
use async_trait::async_trait;
use js_sys::{Function, Reflect};
use std::{convert::Into, rc::Rc, sync::Mutex};
use wasm_bindgen::JsValue;
mod error;
pub use error::Error;
mod method;
pub use method::Method;
mod request;
pub use request::Request;
mod response;
pub use response::Response;
mod context;
pub use context::Context;
pub(crate) mod js_values;
use logging::{log, prelude::*};
pub(crate) use service_logging as logging;
#[async_trait(?Send)]
pub trait Runnable {
async fn run(&self, lq: Rc<Mutex<logging::LogQueue>>);
}
#[async_trait(?Send)]
pub trait Handler<E> {
async fn handle(&self, ctx: &mut Context) -> Result<(), E>;
}
pub async fn service_request<E>(
req: JsValue,
logger: Box<dyn logging::Logger>,
handler: Box<dyn Handler<E>>,
) -> Result<JsValue, JsValue>
where
E: ToString,
{
use js_sys::{Map, Object};
let map = Map::from(req);
let req = Request::from_js(&map)?;
let js_event = Object::from(check_defined(
map.get(&JsValue::from_str("event")),
"missing event",
)?);
let mut ctx = Context::new(req);
let response = match handler.handle(&mut ctx).await {
Ok(_) => {
let resp = ctx.take_response();
let promise = deferred_promise(ctx.take_logs(), ctx.take_tasks(), logger);
let wait_until =
Function::from(Reflect::get(&js_event, &JsValue::from_str("waitUntil"))?);
wait_until.call1(&js_event, &promise)?;
resp
}
Err(e) => {
log!(ctx, logging::Severity::Error, _:"handler",
url: ctx.url().path(),
method: ctx.method(),
error: e);
ctx.response()
.status(500)
.text("Sorry, unexpected internal error");
ctx.take_response()
}
};
Ok(response.into_js())
}
fn deferred_promise(
logs: Vec<logging::LogEntry>,
tasks: Vec<Box<dyn Runnable + std::panic::UnwindSafe>>,
logger: Box<dyn logging::Logger>,
) -> js_sys::Promise {
wasm_bindgen_futures::future_to_promise(async move {
let _ = logging::send_logs(logs, &logger).await;
let lq = Rc::new(Mutex::new(logging::LogQueue::default()));
for t in tasks.into_iter() {
t.run(lq.clone()).await;
}
let mut lock_queue = lq.lock().unwrap();
let _ = logging::send_logs(lock_queue.take(), &logger).await;
Ok(JsValue::undefined())
})
}
fn check_defined(v: JsValue, msg: &str) -> Result<JsValue, JsValue> {
if v.is_undefined() {
return Err(JsValue::from_str(msg.into()));
}
Ok(v)
}