* Updated Clap
* Removed http-to-https-redirect server
This commit is contained in:
Piyush मिश्रः 2023-06-16 17:47:27 +05:30
parent 91fdc5576d
commit b8be7e468d
6 changed files with 126 additions and 297 deletions

70
Cargo.lock generated
View File

@ -363,17 +363,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "actix-web-middleware-redirect-https"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ce1464786203c29120f6d1e4dcdc9c3975506e70b89c3a56c5389f265256dc6"
dependencies = [
"actix-service",
"actix-web",
"futures",
]
[[package]] [[package]]
name = "actix_derive" name = "actix_derive"
version = "0.5.0" version = "0.5.0"
@ -409,6 +398,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "anyhow"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
dependencies = [
"backtrace",
]
[[package]] [[package]]
name = "ascii" name = "ascii"
version = "0.9.3" version = "0.9.3"
@ -623,26 +621,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.0.10" version = "4.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375" checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e"
dependencies = [ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
"clap_derive", "clap_derive",
"indexmap", "clap_lex",
"lazy_static", "once_cell",
"os_str_bytes",
"strsim", "strsim",
"termcolor", "termcolor",
"textwrap",
] ]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "3.0.6" version = "4.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "517358c28fcef6607bf6f76108e02afad7e82297d132a6b846dcc1fc3efcd153" checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
dependencies = [ dependencies = [
"heck 0.4.0", "heck 0.4.0",
"proc-macro-error", "proc-macro-error",
@ -651,6 +647,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "clap_lex"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646"
dependencies = [
"os_str_bytes",
]
[[package]] [[package]]
name = "combine" name = "combine"
version = "3.8.1" version = "3.8.1"
@ -1292,7 +1297,7 @@ dependencies = [
"actix-ratelimit", "actix-ratelimit",
"actix-web", "actix-web",
"actix-web-actors", "actix-web-actors",
"actix-web-middleware-redirect-https", "anyhow",
"base64 0.13.0", "base64 0.13.0",
"clap", "clap",
"env_logger", "env_logger",
@ -1456,9 +1461,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.9.0" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
@ -1498,9 +1503,6 @@ name = "os_str_bytes"
version = "6.0.0" version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
@ -1635,11 +1637,11 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.36" version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
dependencies = [ dependencies = [
"unicode-xid", "unicode-ident",
] ]
[[package]] [[package]]
@ -2144,12 +2146,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "textwrap"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.30" version = "1.0.30"
@ -2407,6 +2403,12 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
version = "0.1.19" version = "0.1.19"

View File

@ -19,11 +19,10 @@ actix-web-actors = "3"
actix-broker = "0.3" actix-broker = "0.3"
actix-files = "0.5" actix-files = "0.5"
actix-ratelimit = "0.3" actix-ratelimit = "0.3"
actix-web-middleware-redirect-https = "3.0.1"
env_logger = "0.9" env_logger = "0.9"
openssl = "0.10" openssl = "0.10"
clap = { version = "3.0", features = ["derive"] } clap = { version = "4", features = ["derive"] }
lazy_static = "1.4" lazy_static = "1.4"
serde = "1.0" serde = "1.0"
@ -36,3 +35,4 @@ base64 = "0.13"
log = "0.4.17" log = "0.4.17"
rustls = "0.18.0" rustls = "0.18.0"
rustls-pemfile = "1.0.2" rustls-pemfile = "1.0.2"
anyhow = { version = "1.0.71", features = ["backtrace"] }

View File

@ -1,7 +1,10 @@
{ {
"bind_address": "Address to start server. (For example: localhost:8000)",
"static_dir_path": "Directory to static html files to serve",
"logger_pattern": "https://docs.rs/actix-web/latest/actix_web/middleware/struct.Logger.html (For example: %t [%a] %s %{User-Agent}i %r)",
"salt": "PUT SOME TEXT FOR SALT", "salt": "PUT SOME TEXT FOR SALT",
"tenor_key": "GET API KEY FROM https://tenor.com AND PASTE HERE", "tenor_key": "GET API KEY FROM https://tenor.com AND PASTE HERE",
"ssl_cert": "", "allow_ssl": false,
"ssl_key": "", "ssl_cert": "Path to ssl certificate",
"logger_pattern": "%t [%a] %s %{User-Agent}i %r" "ssl_key": "Path to ssl key"
} }

View File

@ -15,170 +15,45 @@
along with Lupt. If not, see <https://www.gnu.org/licenses/> along with Lupt. If not, see <https://www.gnu.org/licenses/>
*/ */
use clap::{ErrorKind as ClapErrorKind, IntoApp, Parser}; use clap::Parser;
// use clap::{App, Arg}; use serde::Deserialize;
use serde::{Deserialize, Serialize}; use std::{fs::File, io::BufReader, path::PathBuf};
use std::{
fs::File,
io::{BufReader, ErrorKind as IOErrorKind},
path::PathBuf,
};
#[derive(Parser)] #[derive(Parser)]
#[clap(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]
pub(crate) struct Config { pub(crate) struct Args {
/// Path of directory with index.html
#[clap(short, long, parse(from_os_str), value_name = "FILE")]
pub(crate) static_path: PathBuf,
/// Address to bind for server
#[clap(short, long, value_name = "ADDRESS")]
pub(crate) bind_address: String,
/// Port to bind for http server
#[clap(short, long, value_name = "PORT")]
pub(crate) port: String,
/// Port to bind for https (ssl) server
#[clap(short = 'o', long, value_name = "PORT")]
pub(crate) port_ssl: Option<String>,
/// Path to config file /// Path to config file
#[clap(short, long, parse(from_os_str), value_name = "FILE")] #[clap(short, long, value_name = "FILE")]
pub(crate) config_file: PathBuf, pub(crate) config_file: PathBuf,
} }
#[derive(Serialize, Deserialize)] #[derive(Deserialize)]
pub(crate) struct ConfigFile { pub(crate) struct ConfigFile {
pub(crate) bind_address: String,
pub(crate) static_dir_path: PathBuf,
pub(crate) logger_pattern: String,
pub(crate) salt: String, pub(crate) salt: String,
pub(crate) tenor_key: Option<String>, pub(crate) tenor_key: Option<String>,
pub(crate) allow_ssl: Option<bool>,
pub(crate) ssl_cert: Option<String>, pub(crate) ssl_cert: Option<String>,
pub(crate) ssl_key: Option<String>, pub(crate) ssl_key: Option<String>,
pub(crate) logger_pattern: String,
} }
const HELP_CONFIG_FILE: &'static str = "Config File is corrupt. pub(crate) fn generate() -> ConfigFile {
let args: Args = Args::parse();
Config file must have following fields let config_file = File::open(&args.config_file)
- salt: Salt for hashing .map_err(|e| anyhow!(e))
- tenor_key: Key of tenor gif api .expect("Failed to open config file!");
- ssl_cert: Path to certificate of ssl let reader = BufReader::new(config_file);
- ssl_key: Path to private key of ssl let json: ConfigFile = serde_json::from_reader(reader)
- logger_pattern: Pattern to make log according to Actix Logger"; .map_err(|e| anyhow!(e))
.expect("Failed to open config file!");
pub(crate) fn generate() -> (Config, ConfigFile) { json
let config: Config = Config::parse();
let config_file = File::open(&config.config_file);
if let Err(e) = config_file {
let mut app = Config::into_app();
match e.kind() {
IOErrorKind::NotFound => app
.error(ClapErrorKind::InvalidValue, "Error: Config file is missing")
.exit(),
_ => app
.error(
ClapErrorKind::InvalidValue,
format!("Error(Config File): {}", e.to_string()),
)
.exit(),
}
} }
let reader = BufReader::new(config_file.unwrap()); lazy_static! {
let json: ConfigFile = match serde_json::from_reader(reader) { pub(crate) static ref CONFIG: ConfigFile = generate();
Ok(read) => read,
Err(e) => {
let mut app = Config::into_app();
app.error(
ClapErrorKind::InvalidValue,
format!(
"Error(Config File): {}\n\n{}",
e.to_string(),
HELP_CONFIG_FILE
),
)
.exit();
} }
};
(config, json)
}
// impl Config {
// pub fn new() -> Self {
// let matches = App::new("Lupt (लुप्त)")
// .version(env!("CARGO_PKG_VERSION"))
// .author(env!("CARGO_PKG_AUTHORS"))
// .about(env!("CARGO_PKG_DESCRIPTION"))
// .arg(
// Arg::with_name("bind_address")
// .short("a")
// .long("bind_address")
// .value_name("ADDRESS")
// .help("Address to bind for server")
// .required(true)
// .takes_value(true),
// )
// .arg(
// Arg::with_name("port")
// .short("p")
// .long("port")
// .value_name("PORT")
// .help("Port to bind for server")
// .required(true)
// .takes_value(true),
// )
// .arg(
// Arg::with_name("port_x")
// .short("x")
// .long("port_x")
// .value_name("PORT")
// .help("Port to bind for http if ssl is enabled to redirect to https")
// .required(false)
// .takes_value(true),
// )
// .arg(
// Arg::with_name("static_path")
// .short("s")
// .long("static_path")
// .value_name("DIR")
// .help("Path of directory with index.html")
// .required(true)
// .takes_value(true),
// )
// .arg(
// Arg::with_name("config")
// .short("c")
// .long("config")
// .value_name("FILE")
// .help("Path to config file")
// .required(true)
// .takes_value(true),
// )
// .get_matches();
// let conf = matches.value_of("config").unwrap().to_owned();
// let conf = std::fs::read_to_string(conf).expect("Failed to read config");
// let config = serde_json::from_str::<ConfigFile>(&conf).expect(
// r"
// Config File is corrupt.
// Config file must have following fields
// - salt: Salt for hashing
// - tenor_key: Key of tenor gif api
// - ssl_cert: Path to certificate of ssl
// - ssl_key: Path to private key of ssl
// - logger_pattern: Pattern to make log according to Actix Logger
// ",
// );
// Config {
// static_path: matches.value_of("static_path").unwrap().to_owned(),
// bind_address: matches.value_of("bind_address").unwrap().to_owned(),
// port: matches.value_of("port").unwrap().to_owned(),
// port_x: matches.value_of("port_x").unwrap_or("").to_owned(),
// config,
// }
// }
// }

View File

@ -27,6 +27,9 @@
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
#[macro_use]
extern crate anyhow;
use actix_files as fs; use actix_files as fs;
use actix_ratelimit::{MemoryStore, MemoryStoreActor, RateLimiter}; use actix_ratelimit::{MemoryStore, MemoryStoreActor, RateLimiter};
use actix_web::{ use actix_web::{
@ -35,10 +38,9 @@ use actix_web::{
web, App, Error, HttpRequest, HttpResponse, HttpServer, web, App, Error, HttpRequest, HttpResponse, HttpServer,
}; };
use actix_web_actors::ws; use actix_web_actors::ws;
use log::error; use config::CONFIG;
// use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslConnector, SslFiletype, SslMethod};
use rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig}; use rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig};
use std::{fs::File, sync::RwLock}; use std::fs::File;
use ws_sansad::WsSansad; use ws_sansad::WsSansad;
mod broker_messages; mod broker_messages;
@ -48,147 +50,53 @@ mod errors;
mod validator; mod validator;
mod ws_sansad; mod ws_sansad;
lazy_static! {
pub static ref SALT: RwLock<String> = RwLock::new("".to_owned());
pub static ref TENOR_API_KEY: RwLock<Option<String>> = RwLock::new(None);
}
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "info"); std::env::set_var("RUST_LOG", "info");
env_logger::init(); env_logger::init();
let (config, config_file) = config::generate();
error!("Hello");
*SALT.write().unwrap() = config_file.salt;
if let Some(key) = config_file.tenor_key { lazy_static::initialize(&CONFIG);
*TENOR_API_KEY.write().unwrap() = Some(key);
}
let rustls_server_config = if config_file.ssl_key.is_some() && config_file.ssl_cert.is_some() { let main_server = HttpServer::new(move || {
gen_rustls_server_config(config_file.ssl_key.unwrap(), config_file.ssl_cert.unwrap())
} else {
None
};
let logger_pattern = config_file.logger_pattern;
let static_path = config.static_path;
let server = HttpServer::new(move || {
let mut app = App::new() let mut app = App::new()
.wrap( .wrap(
RateLimiter::new(MemoryStoreActor::from(MemoryStore::new().clone()).start()) RateLimiter::new(MemoryStoreActor::from(MemoryStore::new().clone()).start())
.with_interval(std::time::Duration::from_secs(60)) .with_interval(std::time::Duration::from_secs(60))
.with_max_requests(200), .with_max_requests(200),
) )
.wrap(Logger::new(&logger_pattern)) .wrap(Logger::new(&CONFIG.logger_pattern))
.service(web::resource("/ws/").route(web::get().to(ws_index))); .service(web::resource("/ws/").route(web::get().to(ws_index)));
if TENOR_API_KEY.read().unwrap().is_some() { if let Some(key) = &CONFIG.tenor_key {
if key.len() > 0 {
app = app app = app
.service(web::resource("/gif/{pos}/").route(web::get().to(gif))) .service(web::resource("/gif/{pos}/").route(web::get().to(gif)))
.service(web::resource("/gif/{pos}/{query}").route(web::get().to(gif))); .service(web::resource("/gif/{pos}/{query}").route(web::get().to(gif)));
} }
}
app = app.service(fs::Files::new("/", &static_path).index_file("index.html")); app = app.service(fs::Files::new("/", &CONFIG.static_dir_path).index_file("index.html"));
app app
}); });
if rustls_server_config.is_some() && config.port_ssl.is_some() { let main_server = if CONFIG.allow_ssl.unwrap_or(false) {
let port = config.port.clone(); main_server
let port_ssl = config.port_ssl.clone().unwrap();
let redirect_server = HttpServer::new(move || {
App::new()
.wrap(
RateLimiter::new(MemoryStoreActor::from(MemoryStore::new().clone()).start())
.with_interval(std::time::Duration::from_secs(60))
.with_max_requests(100),
)
.wrap(
actix_web_middleware_redirect_https::RedirectHTTPS::with_replacements(&[(
port.clone(),
port_ssl.clone(),
)]),
)
.route(
"/",
web::get().to(|| {
HttpResponse::Ok()
.content_type("text/plain")
.body("Always HTTPS on non-default ports!")
}),
)
})
.bind(format!("{}:{}", config.bind_address, config.port))?
.run();
let sc = rustls_server_config.unwrap();
let server = server
.bind_rustls( .bind_rustls(
format!("{}:{}", config.bind_address, config.port_ssl.unwrap()), &CONFIG.bind_address,
sc, gen_rustls_server_config(
CONFIG.ssl_key.clone().unwrap(),
CONFIG.ssl_cert.clone().unwrap(),
),
)? )?
.run();
tokio::try_join!(redirect_server, server)?;
} else {
server
.bind(format!("{}:{}", config.bind_address, config.port))?
.run() .run()
.await?;
}
Ok(())
}
async fn ws_index(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
ws::start(WsSansad::new(), &req, stream)
}
async fn gif(req: HttpRequest) -> Result<HttpResponse, Error> {
let name = req.match_info().get("query").unwrap_or("");
let mut pos = req.match_info().get("pos").unwrap_or("");
if pos == "_" {
pos = ""
}
let client = Client::builder()
.connector(Connector::new().finish())
.finish();
let tenor_key = TENOR_API_KEY.read().unwrap();
let key = match &*tenor_key {
Some(a) => a.as_str(),
None => panic!("No api key!"),
};
let url = if name != "" {
format!(
"https://tenor.googleapis.com/v2/search?q={}&key={}&limit=20&media_filter=tinygif&pos={}",
name.replace(" ", "+"),
key,
pos
)
} else { } else {
format!( main_server.bind(&CONFIG.bind_address)?.run()
"https://tenor.googleapis.com/v2/featured?key={}&limit=20&media_filter=tinygif&pos={}",
key, pos
)
}; };
let response = client main_server.await
.get(url)
.header("User-Agent", "actix-web/3.0")
.send()
.await?
.body()
.await?;
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(response))
} }
fn gen_rustls_server_config(key: String, cert: String) -> Option<ServerConfig> { fn gen_rustls_server_config(key: String, cert: String) -> ServerConfig {
if key != "" && cert != "" {
let mut br = std::io::BufReader::new(File::open(cert).unwrap()); let mut br = std::io::BufReader::new(File::open(cert).unwrap());
let certs = rustls_pemfile::certs(&mut br) let certs = rustls_pemfile::certs(&mut br)
.unwrap() .unwrap()
@ -208,8 +116,49 @@ fn gen_rustls_server_config(key: String, cert: String) -> Option<ServerConfig> {
let mut config = ServerConfig::new(NoClientAuth::new()); let mut config = ServerConfig::new(NoClientAuth::new());
config.set_single_cert(certs, private_key).unwrap(); config.set_single_cert(certs, private_key).unwrap();
Some(config) config
}
async fn ws_index(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
ws::start(WsSansad::new(), &req, stream)
}
async fn gif(req: HttpRequest) -> Result<HttpResponse, Error> {
let name = req.match_info().get("query").unwrap_or("");
let mut pos = req.match_info().get("pos").unwrap_or("");
if pos == "_" {
pos = ""
}
let client = Client::builder()
.connector(Connector::new().finish())
.finish();
let tenor_key = CONFIG.tenor_key.clone().unwrap();
let url = if name != "" {
format!(
"https://tenor.googleapis.com/v2/search?q={}&key={}&limit=20&media_filter=tinygif&pos={}",
name.replace(" ", "+"),
tenor_key,
pos
)
} else { } else {
None format!(
} "https://tenor.googleapis.com/v2/featured?key={}&limit=20&media_filter=tinygif&pos={}",
tenor_key, pos
)
};
let response = client
.get(url)
.header("User-Agent", "actix-web/3.0")
.send()
.await?
.body()
.await?;
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(response))
} }

View File

@ -16,8 +16,8 @@
*/ */
use super::*; use super::*;
use crate::config::CONFIG;
use sha2::{Digest, Sha224}; use sha2::{Digest, Sha224};
impl WsSansad { impl WsSansad {
/// Request to join to kaksh /// Request to join to kaksh
pub async fn join_kaksh(&mut self, val: Value) { pub async fn join_kaksh(&mut self, val: Value) {
@ -55,7 +55,7 @@ impl WsSansad {
return; return;
} }
let mut hasher = Sha224::new(); let mut hasher = Sha224::new();
hasher.update(format!("{}{}", kunjika, crate::SALT.read().unwrap()).as_bytes()); hasher.update(format!("{}{}", kunjika, CONFIG.salt).as_bytes());
let kunjika = base64::encode(hasher.finalize())[..8].to_owned(); let kunjika = base64::encode(hasher.finalize())[..8].to_owned();
// Name // Name
@ -160,7 +160,7 @@ impl WsSansad {
return; return;
} }
let mut hasher = Sha224::new(); let mut hasher = Sha224::new();
hasher.update(format!("{}{}", kunjika, crate::SALT.read().unwrap()).as_bytes()); hasher.update(format!("{}{}", kunjika, &CONFIG.salt).as_bytes());
let kunjika = base64::encode(hasher.finalize())[..8].to_owned(); let kunjika = base64::encode(hasher.finalize())[..8].to_owned();
// Name // Name