| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- // Simulates direct scans by keeping a list of bridges blocked by the censor
- use hyper::{
- body::{self, Bytes},
- header::HeaderValue,
- server::conn::AddrStream,
- service::{make_service_fn, service_fn},
- Body, Method, Request, Response, Server, StatusCode,
- };
- use serde_json::json;
- use std::{
- collections::{HashMap, HashSet},
- convert::Infallible,
- net::SocketAddr,
- time::Duration,
- };
- use tokio::{
- spawn,
- sync::{mpsc, oneshot},
- time::sleep,
- };
- async fn serve_direct_scan_api(
- // We have to serialize our HashMap keys as Strings instead of [u8; 20]s,
- // so it's easier to just use Strings throughout.
- blocked_bridges: &mut HashSet<String>,
- guessed_bridges: &mut HashMap<String, HashSet<String>>,
- req: Request<Body>,
- ) -> Result<Response<Body>, Infallible> {
- match req.method() {
- &Method::OPTIONS => Ok(Response::builder()
- .header("Access-Control-Allow-Origin", HeaderValue::from_static("*"))
- .header("Access-Control-Allow-Headers", "accept, content-type")
- .header("Access-Control-Allow-Methods", "POST")
- .status(200)
- .body(Body::from("Allow POST"))
- .unwrap()),
- _ => match req.uri().path() {
- "/add_blocked_bridges" => Ok::<_, Infallible>({
- let bytes = body::to_bytes(req.into_body()).await.unwrap();
- add_blocked_bridges(blocked_bridges, bytes)
- }),
- "/verify_blocked_bridges" => Ok::<_, Infallible>({
- let bytes = body::to_bytes(req.into_body()).await.unwrap();
- verify_blocked_bridges(blocked_bridges, guessed_bridges, bytes)
- }),
- // Get the bridges Troll Patrol guessed were blocked today
- "/get_guessed_bridges" => Ok::<_, Infallible>({
- prepare_header(serde_json::to_string(&guessed_bridges).unwrap())
- }),
- // Reset guessed bridges at the beginning of the day
- "/reset_guessed_bridges" => Ok::<_, Infallible>({
- *guessed_bridges = HashMap::<String, HashSet<String>>::new();
- prepare_header("OK".to_string())
- }),
- _ => Ok::<_, Infallible>({
- Response::builder()
- .status(StatusCode::NOT_FOUND)
- .body(Body::from("Not found"))
- .unwrap()
- }),
- },
- }
- }
- pub async fn server() {
- let (context_tx, context_rx) = mpsc::channel(32);
- let request_tx = context_tx.clone();
- spawn(async move { create_context_manager(context_rx).await });
- let addr = SocketAddr::from(([127, 0, 0, 1], 8006));
- let make_svc = make_service_fn(move |_conn: &AddrStream| {
- let request_tx = request_tx.clone();
- let service = service_fn(move |req| {
- let request_tx = request_tx.clone();
- let (response_tx, response_rx) = oneshot::channel();
- let cmd = Command::Request {
- req,
- sender: response_tx,
- };
- async move {
- request_tx.send(cmd).await.unwrap();
- response_rx.await.unwrap()
- }
- });
- async move { Ok::<_, Infallible>(service) }
- });
- let server = Server::bind(&addr).serve(make_svc);
- println!("Listening on localhost:8006");
- if let Err(e) = server.await {
- eprintln!("server error: {}", e);
- }
- }
- async fn create_context_manager(context_rx: mpsc::Receiver<Command>) {
- tokio::select! {
- create_context = context_manager(context_rx) => create_context,
- }
- }
- async fn context_manager(mut context_rx: mpsc::Receiver<Command>) {
- let mut blocked_bridges = HashSet::<String>::new();
- let mut guessed_bridges = HashMap::<String, HashSet<String>>::new();
- while let Some(cmd) = context_rx.recv().await {
- use Command::*;
- match cmd {
- Request { req, sender } => {
- let response =
- serve_direct_scan_api(&mut blocked_bridges, &mut guessed_bridges, req).await;
- if let Err(e) = sender.send(response) {
- eprintln!("Server Response Error: {:?}", e);
- }
- sleep(Duration::from_millis(1)).await;
- }
- }
- }
- }
- #[derive(Debug)]
- enum Command {
- Request {
- req: Request<Body>,
- sender: oneshot::Sender<Result<Response<Body>, Infallible>>,
- },
- }
- fn add_blocked_bridges(blocked_bridges: &mut HashSet<String>, request: Bytes) -> Response<Body> {
- let new_blocked_bridges: HashSet<String> = match serde_json::from_slice(&request) {
- Ok(req) => req,
- Err(e) => {
- let response = json!({"error": e.to_string()});
- let val = serde_json::to_string(&response).unwrap();
- return prepare_header(val);
- }
- };
- blocked_bridges.extend(new_blocked_bridges);
- prepare_header("OK".to_string())
- }
- fn verify_blocked_bridges(
- blocked_bridges: &mut HashSet<String>,
- guessed_bridges: &mut HashMap<String, HashSet<String>>,
- request: Bytes,
- ) -> Response<Body> {
- let mut verified_blocked_bridges = HashMap::<String, HashSet<String>>::new();
- let possibly_blocked_bridges: HashMap<String, HashSet<String>> =
- match serde_json::from_slice(&request) {
- Ok(req) => req,
- Err(e) => {
- let response = json!({"error": e.to_string()});
- let val = serde_json::to_string(&response).unwrap();
- return prepare_header(val);
- }
- };
- *guessed_bridges = possibly_blocked_bridges.clone();
- for (fpr, countries) in possibly_blocked_bridges {
- if blocked_bridges.contains(&fpr) {
- verified_blocked_bridges.insert(fpr, countries);
- }
- }
- prepare_header(serde_json::to_string(&verified_blocked_bridges).unwrap())
- }
- // Prepare HTTP Response for successful Server Request
- fn prepare_header(response: String) -> Response<Body> {
- let mut resp = Response::new(Body::from(response));
- resp.headers_mut()
- .insert("Access-Control-Allow-Origin", HeaderValue::from_static("*"));
- resp
- }
|