extra_infos_server.rs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. use crate::extra_info::ExtraInfo;
  2. use hyper::{
  3. body::{self, Bytes},
  4. header::HeaderValue,
  5. server::conn::AddrStream,
  6. service::{make_service_fn, service_fn},
  7. Body, Method, Request, Response, Server,
  8. };
  9. use serde_json::json;
  10. use std::{collections::HashSet, convert::Infallible, net::SocketAddr, time::Duration};
  11. use tokio::{
  12. spawn,
  13. sync::{mpsc, oneshot},
  14. time::sleep,
  15. };
  16. async fn serve_extra_infos(
  17. extra_infos_pages: &mut Vec<String>,
  18. req: Request<Body>,
  19. ) -> Result<Response<Body>, Infallible> {
  20. match req.method() {
  21. &Method::OPTIONS => Ok(Response::builder()
  22. .header("Access-Control-Allow-Origin", HeaderValue::from_static("*"))
  23. .header("Access-Control-Allow-Headers", "accept, content-type")
  24. .header("Access-Control-Allow-Methods", "POST")
  25. .status(200)
  26. .body(Body::from("Allow POST"))
  27. .unwrap()),
  28. _ => match req.uri().path() {
  29. "/" => Ok::<_, Infallible>(serve_index(&extra_infos_pages)),
  30. "/add" => Ok::<_, Infallible>({
  31. let bytes = body::to_bytes(req.into_body()).await.unwrap();
  32. add_extra_infos(extra_infos_pages, bytes)
  33. }),
  34. path => Ok::<_, Infallible>({
  35. // Serve the requested file
  36. serve_extra_infos_file(&extra_infos_pages, path)
  37. }),
  38. },
  39. }
  40. }
  41. pub async fn server() {
  42. let (context_tx, context_rx) = mpsc::channel(32);
  43. let request_tx = context_tx.clone();
  44. spawn(async move { create_context_manager(context_rx).await });
  45. let addr = SocketAddr::from(([127, 0, 0, 1], 8004));
  46. let make_svc = make_service_fn(move |_conn: &AddrStream| {
  47. let request_tx = request_tx.clone();
  48. let service = service_fn(move |req| {
  49. let request_tx = request_tx.clone();
  50. let (response_tx, response_rx) = oneshot::channel();
  51. let cmd = Command::Request {
  52. req,
  53. sender: response_tx,
  54. };
  55. async move {
  56. request_tx.send(cmd).await.unwrap();
  57. response_rx.await.unwrap()
  58. }
  59. });
  60. async move { Ok::<_, Infallible>(service) }
  61. });
  62. let server = Server::bind(&addr).serve(make_svc);
  63. println!("Listening on localhost:8004");
  64. if let Err(e) = server.await {
  65. eprintln!("server error: {}", e);
  66. }
  67. }
  68. async fn create_context_manager(context_rx: mpsc::Receiver<Command>) {
  69. tokio::select! {
  70. create_context = context_manager(context_rx) => create_context,
  71. }
  72. }
  73. async fn context_manager(mut context_rx: mpsc::Receiver<Command>) {
  74. let mut extra_infos_pages = Vec::<String>::new();
  75. while let Some(cmd) = context_rx.recv().await {
  76. use Command::*;
  77. match cmd {
  78. Request { req, sender } => {
  79. let response = serve_extra_infos(&mut extra_infos_pages, req).await;
  80. if let Err(e) = sender.send(response) {
  81. eprintln!("Server Response Error: {:?}", e);
  82. }
  83. sleep(Duration::from_millis(1)).await;
  84. }
  85. }
  86. }
  87. }
  88. #[derive(Debug)]
  89. enum Command {
  90. Request {
  91. req: Request<Body>,
  92. sender: oneshot::Sender<Result<Response<Body>, Infallible>>,
  93. },
  94. }
  95. fn add_extra_infos(extra_infos_pages: &mut Vec<String>, request: Bytes) -> Response<Body> {
  96. let extra_infos: HashSet<ExtraInfo> = match serde_json::from_slice(&request) {
  97. Ok(req) => req,
  98. Err(e) => {
  99. let response = json!({"error": e.to_string()});
  100. let val = serde_json::to_string(&response).unwrap();
  101. return prepare_header(val);
  102. }
  103. };
  104. let mut extra_infos_file = String::new();
  105. for extra_info in extra_infos {
  106. extra_infos_file.push_str(extra_info.to_string().as_str());
  107. }
  108. if extra_infos_file.len() > 0 {
  109. extra_infos_pages.push(extra_infos_file);
  110. }
  111. prepare_header("OK".to_string())
  112. }
  113. fn serve_index(extra_infos_pages: &Vec<String>) -> Response<Body> {
  114. let mut body_str = String::new();
  115. for i in 0..extra_infos_pages.len() {
  116. body_str
  117. .push_str(format!("<a href=\"{}-extra-infos\">{}-extra-infos</a>\n", i, i).as_str());
  118. }
  119. prepare_header(body_str)
  120. }
  121. fn serve_extra_infos_file(extra_infos_pages: &Vec<String>, path: &str) -> Response<Body> {
  122. if path.ends_with("-extra-infos") {
  123. if let Ok(index) = &path[1..(path.len() - "-extra-infos".len())].parse::<usize>() {
  124. if extra_infos_pages.len() > *index {
  125. return prepare_header(extra_infos_pages[*index].clone());
  126. }
  127. }
  128. }
  129. prepare_header("Not a valid file".to_string())
  130. }
  131. // Prepare HTTP Response for successful Server Request
  132. fn prepare_header(response: String) -> Response<Body> {
  133. let mut resp = Response::new(Body::from(response));
  134. resp.headers_mut()
  135. .insert("Access-Control-Allow-Origin", HeaderValue::from_static("*"));
  136. resp
  137. }