wx_work/server/
server.rs

1use std::time::{SystemTime, UNIX_EPOCH};
2
3use actix_web::{web, App as ActixApp, Error, HttpResponse, HttpServer};
4use futures::StreamExt;
5use log::{info, warn};
6use serde::Deserialize;
7
8use super::crypto::Crypto;
9use super::{App, RecvMessage};
10
11pub struct Builder<T: App> {
12    app: T,
13    token: String,
14    encoding_aes_key: String,
15    port: Option<u16>, // optional, default is 12349
16}
17
18pub struct Server<T: App> {
19    app: T,
20    crypto: Crypto,
21    port: u16,
22}
23
24impl<T: App> Builder<T> {
25    pub fn new(app: T, token: impl ToString, encoding_aes_key: impl ToString) -> Self {
26        Builder {
27            app,
28            token: token.to_string(),
29            encoding_aes_key: encoding_aes_key.to_string(),
30            port: None,
31        }
32    }
33
34    pub fn port(mut self, p: u16) -> Self {
35        self.port = Some(p);
36        self
37    }
38
39    pub fn build(self) -> anyhow::Result<Server<T>> {
40        let app = self.app;
41        let crypto = Crypto::new(self.token, self.encoding_aes_key)?;
42        let port = self.port.unwrap_or(12349);
43        let s = Server { app, crypto, port };
44        Ok(s)
45    }
46}
47
48impl<T: App> Server<T> {
49    // caller should provide a tokio runtime
50    // https://github.com/actix/actix-web/issues/1283
51    pub async fn run(self) -> std::io::Result<()> {
52        std::thread::spawn(move || run(self).expect("start server failed"));
53        let ret = futures::future::pending().await;
54        Ok(ret)
55    }
56}
57
58// TODO remove this when https://github.com/actix/actix-net/pull/266#issuecomment-808939487 is released
59#[actix_web::main]
60async fn run<T: App>(s: Server<T>) -> std::io::Result<()> {
61    let server = web::Data::new(s);
62    let addr = format!("0.0.0.0:{}", server.port);
63    HttpServer::new(move || {
64        ActixApp::new()
65            .app_data(server.clone())
66            .route("/", web::get().to(validate::<T>))
67            .route("/", web::post().to(recv::<T>))
68    })
69    .bind(addr)?
70    .run()
71    .await?;
72    Ok(())
73}
74
75#[derive(Debug, Deserialize)]
76struct ValidateParams {
77    msg_signature: String,
78    timestamp: u64,
79    nonce: u64,
80    echostr: String,
81}
82
83#[derive(Debug, Deserialize)]
84struct RecvParams {
85    msg_signature: String,
86    timestamp: u64,
87    nonce: u64,
88}
89
90async fn validate<T: App>(
91    info: web::Query<ValidateParams>,
92    server: web::Data<Server<T>>,
93) -> HttpResponse {
94    info!("validate request: params: {:?}", info);
95
96    let crypto = &server.crypto;
97    let payload = match crypto.decrypt(&info.echostr) {
98        Ok(d) => d,
99        Err(e) => {
100            warn!("decrypt validate message failed, reason: {}", e);
101            return HttpResponse::BadRequest().finish();
102        }
103    };
104
105    HttpResponse::Ok().body(payload.data)
106}
107
108async fn recv<T: App>(
109    info: web::Query<RecvParams>,
110    mut body: web::Payload,
111    server: web::Data<Server<T>>,
112) -> Result<HttpResponse, Error> {
113    info!("receive request: params: {:?}", info);
114
115    let mut bytes = web::BytesMut::new();
116    while let Some(item) = body.next().await {
117        bytes.extend_from_slice(&item?);
118    }
119
120    let crypto = &server.crypto;
121    let msg = match RecvMessage::parse(
122        &bytes,
123        &crypto,
124        info.timestamp,
125        info.nonce,
126        &info.msg_signature,
127    ) {
128        Ok(d) => d,
129        Err(e) => {
130            warn!("parse message failed, reason: {}", e);
131            return Ok(HttpResponse::BadRequest().finish());
132        }
133    };
134
135    match server.app.handle(msg).await {
136        Some(m) => {
137            let msg = m
138                .serialize(current_timestamp(), gen_nonce(), crypto)
139                .unwrap();
140            Ok(HttpResponse::Ok().body(msg))
141        }
142        None => Ok(HttpResponse::Ok().finish()),
143    }
144}
145
146///////////////////////////// helper functions ///////////////////////////////////////////////
147
148#[inline]
149fn current_timestamp() -> u64 {
150    SystemTime::now()
151        .duration_since(UNIX_EPOCH)
152        .unwrap()
153        .as_secs()
154}
155
156#[inline]
157fn gen_nonce() -> u64 {
158    rand::random()
159}