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
pub use facebook_webhook;
use std::{convert::Infallible, error, sync::Arc};
use bytes::Bytes;
use facebook_webhook::{
event_notifications::{self, PassBackCallbackFn, SIGNATURE_HEADER_NAME},
verification_requests::{self, Query},
};
use warp::{
http::{Response, StatusCode},
hyper::Body,
Filter,
};
pub trait Context: Send + Sync + Clone {
fn get_verify_token(&self, app_id: u64) -> Result<String, Box<dyn error::Error + Send + Sync>>;
fn get_app_secret(&self, app_id: u64) -> Result<String, Box<dyn error::Error + Send + Sync>>;
}
pub fn handle<C: Context>(
path_prefix: String,
ctx: C,
callback: PassBackCallbackFn<'static, C>,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
verification_requests_filter(path_prefix.clone(), ctx.clone()).or(event_notifications_filter(
path_prefix,
ctx,
Arc::new(callback),
))
}
fn verification_requests_filter<C: Context>(
path_prefix: String,
ctx: C,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
warp::path(path_prefix)
.and(warp::path::param::<u64>())
.and(warp::path::end())
.and(warp::get())
.and(warp::query::<Query>())
.and_then(move |app_id, query| {
let ctx = ctx.clone();
async move {
let part: Result<Result<Response<Body>, warp::http::Error>, Infallible> = {
match ctx.get_verify_token(app_id) {
Ok(verify_token) => {
let res =
verification_requests::pass_back_with_query(query, &verify_token);
Ok(Response::builder()
.status(res.status_code)
.body(res.body.into()))
}
Err(err) => Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(err.to_string().into())),
}
};
part
}
})
}
fn event_notifications_filter<C: Context>(
path_prefix: String,
ctx: C,
callback: Arc<PassBackCallbackFn<'static, C>>,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
warp::path(path_prefix)
.and(warp::path::param::<u64>())
.and(warp::path::end())
.and(warp::post())
.and(warp::header::<String>(SIGNATURE_HEADER_NAME))
.and(warp::body::content_length_limit(1024 * 32).and(warp::body::bytes()))
.and_then(
move |app_id, signature_header_value: String, request_body_bytes: Bytes| {
let ctx = ctx.clone();
let callback = callback.clone();
async move {
let part: Result<Result<Response<Body>, warp::http::Error>, Infallible> = {
match ctx.get_app_secret(app_id) {
Ok(app_secret) => {
let res = event_notifications::pass_back(
signature_header_value.as_bytes(),
&request_body_bytes[..],
&app_secret,
ctx,
callback,
)
.await;
Ok(Response::builder()
.status(res.status_code)
.body(res.body.into()))
}
Err(err) => Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(err.to_string().into())),
}
};
part
}
},
)
}