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

View File

@ -19,11 +19,10 @@ actix-web-actors = "3"
actix-broker = "0.3"
actix-files = "0.5"
actix-ratelimit = "0.3"
actix-web-middleware-redirect-https = "3.0.1"
env_logger = "0.9"
openssl = "0.10"
clap = { version = "3.0", features = ["derive"] }
clap = { version = "4", features = ["derive"] }
lazy_static = "1.4"
serde = "1.0"
@ -36,3 +35,4 @@ base64 = "0.13"
log = "0.4.17"
rustls = "0.18.0"
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",
"tenor_key": "GET API KEY FROM https://tenor.com AND PASTE HERE",
"ssl_cert": "",
"ssl_key": "",
"logger_pattern": "%t [%a] %s %{User-Agent}i %r"
"allow_ssl": false,
"ssl_cert": "Path to ssl certificate",
"ssl_key": "Path to ssl key"
}

View File

@ -15,170 +15,45 @@
along with Lupt. If not, see <https://www.gnu.org/licenses/>
*/
use clap::{ErrorKind as ClapErrorKind, IntoApp, Parser};
// use clap::{App, Arg};
use serde::{Deserialize, Serialize};
use std::{
fs::File,
io::{BufReader, ErrorKind as IOErrorKind},
path::PathBuf,
};
use clap::Parser;
use serde::Deserialize;
use std::{fs::File, io::BufReader, path::PathBuf};
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
pub(crate) struct Config {
/// 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>,
pub(crate) struct Args {
/// 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,
}
#[derive(Serialize, Deserialize)]
#[derive(Deserialize)]
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) tenor_key: Option<String>,
pub(crate) allow_ssl: Option<bool>,
pub(crate) ssl_cert: Option<String>,
pub(crate) ssl_key: Option<String>,
pub(crate) logger_pattern: String,
}
const HELP_CONFIG_FILE: &'static str = "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";
pub(crate) fn generate() -> (Config, ConfigFile) {
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(),
}
pub(crate) fn generate() -> ConfigFile {
let args: Args = Args::parse();
let config_file = File::open(&args.config_file)
.map_err(|e| anyhow!(e))
.expect("Failed to open config file!");
let reader = BufReader::new(config_file);
let json: ConfigFile = serde_json::from_reader(reader)
.map_err(|e| anyhow!(e))
.expect("Failed to open config file!");
json
}
let reader = BufReader::new(config_file.unwrap());
let json: ConfigFile = match serde_json::from_reader(reader) {
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();
lazy_static! {
pub(crate) static ref CONFIG: ConfigFile = generate();
}
};
(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]
extern crate lazy_static;
#[macro_use]
extern crate anyhow;
use actix_files as fs;
use actix_ratelimit::{MemoryStore, MemoryStoreActor, RateLimiter};
use actix_web::{
@ -35,10 +38,9 @@ use actix_web::{
web, App, Error, HttpRequest, HttpResponse, HttpServer,
};
use actix_web_actors::ws;
use log::error;
// use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslConnector, SslFiletype, SslMethod};
use config::CONFIG;
use rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig};
use std::{fs::File, sync::RwLock};
use std::fs::File;
use ws_sansad::WsSansad;
mod broker_messages;
@ -48,147 +50,53 @@ mod errors;
mod validator;
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]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "info");
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 {
*TENOR_API_KEY.write().unwrap() = Some(key);
}
lazy_static::initialize(&CONFIG);
let rustls_server_config = if config_file.ssl_key.is_some() && config_file.ssl_cert.is_some() {
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 main_server = HttpServer::new(move || {
let mut app = App::new()
.wrap(
RateLimiter::new(MemoryStoreActor::from(MemoryStore::new().clone()).start())
.with_interval(std::time::Duration::from_secs(60))
.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)));
if TENOR_API_KEY.read().unwrap().is_some() {
if let Some(key) = &CONFIG.tenor_key {
if key.len() > 0 {
app = app
.service(web::resource("/gif/{pos}/").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
});
if rustls_server_config.is_some() && config.port_ssl.is_some() {
let port = config.port.clone();
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
let main_server = if CONFIG.allow_ssl.unwrap_or(false) {
main_server
.bind_rustls(
format!("{}:{}", config.bind_address, config.port_ssl.unwrap()),
sc,
&CONFIG.bind_address,
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()
.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 {
format!(
"https://tenor.googleapis.com/v2/featured?key={}&limit=20&media_filter=tinygif&pos={}",
key, pos
)
main_server.bind(&CONFIG.bind_address)?.run()
};
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))
main_server.await
}
fn gen_rustls_server_config(key: String, cert: String) -> Option<ServerConfig> {
if key != "" && cert != "" {
fn gen_rustls_server_config(key: String, cert: String) -> ServerConfig {
let mut br = std::io::BufReader::new(File::open(cert).unwrap());
let certs = rustls_pemfile::certs(&mut br)
.unwrap()
@ -208,8 +116,49 @@ fn gen_rustls_server_config(key: String, cert: String) -> Option<ServerConfig> {
let mut config = ServerConfig::new(NoClientAuth::new());
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 {
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 crate::config::CONFIG;
use sha2::{Digest, Sha224};
impl WsSansad {
/// Request to join to kaksh
pub async fn join_kaksh(&mut self, val: Value) {
@ -55,7 +55,7 @@ impl WsSansad {
return;
}
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();
// Name
@ -160,7 +160,7 @@ impl WsSansad {
return;
}
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();
// Name