Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[workspace]
members = ["server", "cache", "core"]
default-members = ["server"]
default-members = ["server"]
resolver = "2"
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ OPTIONS:
```

## Proxy Configuration
```
```yaml
# service configuration
service:
# ip address
Expand All @@ -31,6 +31,12 @@ service:
# dev mode - will enable only terminal logger
dev_mode: true

# ssl configuration (optional)
ssl:
# verify SSL certificates for backend HTTPS connections
# set to false to allow self-signed certificates (not recommended for production)
verify_ssl: true

# inbound paths
inbound:
# match path to group
Expand All @@ -47,14 +53,24 @@ outbound:
timeout: 60
# backend servers for this group
# round robin balancing to all servers
# supports both HTTP and HTTPS backends
servers:
- http://localhost:8080/push
- https://secure.backend.example.com
- group: group_2
servers:
- http://test:8082
- http://test2:8181/test
```

## Features

- **Live Configuration Updates**: Modify `proxy.yaml` and changes are applied automatically without restart
- **Load Balancing**: Round-robin distribution across multiple backend servers
- **HTTPS Support**: Proxy to HTTPS backends with configurable SSL verification
- **Response Caching**: Caches responses based on Cache-Control headers
- **Request Forwarding**: Preserves and adds X-Forwarded-For headers

## Build from source
### Install Rust
```bash
Expand Down
4 changes: 2 additions & 2 deletions cache/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"


[dependencies]
crossbeam = "0.8.0"
crossbeam = "0.8"
anyhow = "1"
actix-web = "3"
actix-web = "4"
blocking-delay-queue = { git = "https://github.com/dejankos/blocking-delay-queue" }
4 changes: 2 additions & 2 deletions cache/src/expiring_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;

use std::time::{Duration, Instant};

use actix_web::http::{HeaderMap, StatusCode};
use actix_web::http::{StatusCode, header::HeaderMap};
use actix_web::web::Bytes;
use blocking_delay_queue::{BlockingDelayQueue, DelayItem};
use crossbeam::sync::{ShardedLock, ShardedLockWriteGuard};
Expand Down Expand Up @@ -75,7 +75,7 @@ impl ResponseCache {

#[cfg(test)]
mod tests {
use actix_web::http::{HeaderMap, StatusCode};
use actix_web::http::{StatusCode, header::HeaderMap};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
Expand Down
6 changes: 6 additions & 0 deletions config/proxy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ service:
# dev mode - will enable only terminal logger
dev_mode: true

# ssl configuration (optional)
ssl:
# verify SSL certificates for backend HTTPS connections
# set to false to allow self-signed certificates (not recommended for production)
verify_ssl: true

# inbound paths
inbound:
# match path to group
Expand Down
17 changes: 9 additions & 8 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
simplelog = "0.7.6"
log = "0.4.8"
simplelog = "0.12"
log = "0.4"
anyhow = "1"
crossbeam = "0.8.0"
crossbeam = "0.8"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
url = "2.2.1"
notify = "4.0.15"
regex = "1.4.4"
actix-web = { version = "3", features = ["openssl"] }
serde_yaml = "0.9"
url = "2"
notify = "6.1"
regex = "1"
actix-web = { version = "4", features = ["openssl"] }
awc = { version = "3", features = ["openssl"] }
openssl = "0.10"

cache = { path = "../cache" }
35 changes: 22 additions & 13 deletions core/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::{Path, PathBuf};
use std::path::Path;
use std::sync::Arc;

use std::time::Duration;
Expand All @@ -24,6 +24,16 @@ pub struct Service {
pub dev_mode: bool,
}

#[derive(Debug, Deserialize, Clone)]
pub struct SslConfig {
#[serde(default = "default_ssl_verify")]
pub verify_ssl: bool,
}

fn default_ssl_verify() -> bool {
true
}

impl Default for Service {
fn default() -> Self {
Service {
Expand All @@ -39,6 +49,8 @@ impl Default for Service {
#[derive(Debug, Deserialize)]
pub struct ProxyProperties {
pub service: Service,
#[serde(default)]
pub ssl: Option<SslConfig>,
pub inbound: Vec<Inbound>,
pub outbound: Vec<Outbound>,
}
Expand Down Expand Up @@ -73,18 +85,6 @@ pub struct Group {
pub timeout: Duration,
}

trait FileName {
fn file_name_to_str(&self) -> &str;
}

impl FileName for &PathBuf {
fn file_name_to_str(&self) -> &str {
self.file_name()
.as_ref()
.and_then(|os_str| os_str.to_str())
.unwrap_or("")
}
}

impl Configuration {
pub fn new<P>(path: P) -> Result<Self>
Expand Down Expand Up @@ -119,6 +119,15 @@ impl Configuration {
.clone()
}

pub fn ssl_config(&self) -> Option<SslConfig> {
self.proxy_config
.read()
.expect("proxy config read lock poisoned!")
.props
.ssl
.clone()
}

fn interested(&self, file_name: &str) -> bool {
CONFIG_FILE == file_name
}
Expand Down
35 changes: 23 additions & 12 deletions core/src/file_watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::time::Duration;
use anyhow::Result;
use crossbeam::sync::ShardedLock;
use log::error;
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};

use crate::task::spawn;

Expand Down Expand Up @@ -57,24 +57,35 @@ impl FileWatcher {

fn run_event_loop(path: &Path, listeners: Listeners) -> Result<()> {
let (tx, rx) = channel();
let mut watcher = watcher(tx, Duration::from_secs(5))?;

let mut watcher = RecommendedWatcher::new(
move |res: Result<Event, notify::Error>| {
if let Ok(event) = res {
let _ = tx.send(event);
}
},
Config::default().with_poll_interval(Duration::from_secs(5)),
)?;

watcher.watch(path, RecursiveMode::NonRecursive)?;

// Keep watcher alive in the loop
loop {
match rx.recv() {
Ok(event) => match event {
DebouncedEvent::Write(ref p) => listeners
.read()
.expect("listener mutex poisoned!")
.iter()
.for_each(|l| l.notify_file_changed(p)),
DebouncedEvent::Error(e, o) => {
error!("Path {:?} watch error {}.", o, e);
Ok(event) => {
if matches!(event.kind, EventKind::Modify(_)) {
for path in event.paths {
listeners
.read()
.expect("listener mutex poisoned!")
.iter()
.for_each(|l| l.notify_file_changed(&path));
}
}
_ => {}
},
}
Err(e) => {
error!("Error receiving file events - stopping file watch! {}", e);
drop(watcher); // Explicitly drop watcher before breaking
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod yaml_utils;
mod task;

pub use self::balancer::Balancer;
pub use self::config::Configuration;
pub use self::config::{Configuration, SslConfig};
pub use self::file_watcher::FileWatcher;
pub use self::log::init_logger;
pub use self::proxy::Proxy;
4 changes: 2 additions & 2 deletions core/src/log.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use log::LevelFilter;
use simplelog::{ConfigBuilder, TermLogger, TerminalMode, ThreadLogMode, WriteLogger};
use simplelog::{ColorChoice, ConfigBuilder, TermLogger, TerminalMode, ThreadLogMode, WriteLogger};
use std::fs::File;

pub fn init_logger(log_path: Option<String>, dev_mode: bool) {
Expand All @@ -8,7 +8,7 @@ pub fn init_logger(log_path: Option<String>, dev_mode: bool) {
.build();

if log_path.is_none() || dev_mode {
TermLogger::init(LevelFilter::Info, cfg, TerminalMode::Mixed)
TermLogger::init(LevelFilter::Info, cfg, TerminalMode::Mixed, ColorChoice::Auto)
.expect("Failed to init term logger");
} else {
let log_file =
Expand Down
Loading