Files
qiming/qiming-rcoder/crates/rcoder-proxy/src/lib.rs
2026-06-01 13:54:52 +08:00

256 lines
6.8 KiB
Rust

//! Pingora 反向代理库
//!
//! 这个库提供了一个基于端口参数的反向代理服务,可以通过 URL 参数中的端口信息
//! 将请求代理到对应的后端服务。主要用于 Docker 容器中统一端口访问多个前端应用。
//!
//! # 特性
//!
//! - **端口路由**: 支持通过查询参数 (`?port=3000`) 或路径 (`/proxy/3000/path`) 指定目标端口
//! - **动态后端管理**: 运行时添加、移除和管理后端服务
//! - **高性能**: 基于 Axum 和 Reqwest 的异步处理
//! - **灵活配置**: 支持命令行参数和配置文件
//! - **完整日志**: 详细的操作日志和错误处理
//!
//! # 快速开始
//!
//! ```rust,ignore
//! use rcoder_proxy::{ProxyConfig, ProxyServer};
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let config = ProxyConfig {
//! listen_port: 8080,
//! default_backend_port: 3000,
//! backend_host: "127.0.0.1".to_string(),
//! port_param: "port".to_string(),
//! config_file: None,
//! verbose: false,
//! };
//!
//! let server = ProxyServer::new(config);
//! server.start().await?;
//!
//! Ok(())
//! }
//! ```
//!
//! # 使用方式
//!
//! ## 查询参数方式
//!
//! ```bash
//! curl "http://localhost:8080?port=3000"
//! curl "http://localhost:8080/api/data?port=3001&format=json"
//! ```
//!
//! ## 路径方式
//!
//! ```bash
//! curl "http://localhost:8080/proxy/3000/"
//! curl "http://localhost:8080/proxy/3001/api/users"
//! ```
//!
//! ## 默认端口
//!
//! ```bash
//! curl "http://localhost:8080/" # 自动路由到默认端口 3000
//! ```
// 导入模块
pub mod config;
pub mod pingora_server;
pub mod router;
pub mod server;
pub mod service;
pub mod vnc_resolver;
// 重新导出公共接口
pub use config::ProxyConfig;
pub use pingora_server::PingoraServerManager;
pub use router::{RouteType, create_router, get_routes_documentation};
pub use server::{PingoraServerRunner, ProxyServer, ProxyServerBuilder};
pub use service::{PingoraProxyService, PortProxyService}; // PortProxyService 是别名
pub use vnc_resolver::{
DynVncBackendResolver, VncBackendInfo, VncBackendResolver, VncResolveError,
};
// 库级别的常量和类型
pub const DEFAULT_PORT: u16 = 8080;
pub const DEFAULT_BACKEND_PORT: u16 = 3000;
pub const DEFAULT_BACKEND_HOST: &str = "127.0.0.1";
pub const DEFAULT_PORT_PARAM: &str = "port";
/// 库版本信息
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
/// 库的主要特性
pub mod features {
/// 支持查询参数端口提取
pub const QUERY_PARAM_ROUTING: bool = true;
/// 支持路径端口提取
pub const PATH_ROUTING: bool = true;
/// 支持动态后端管理
pub const DYNAMIC_BACKENDS: bool = true;
/// 支持配置文件
pub const CONFIG_FILE_SUPPORT: bool = true;
/// 支持详细日志
pub const VERBOSE_LOGGING: bool = true;
}
/// 便捷函数:创建默认配置
pub fn default_config() -> ProxyConfig {
ProxyConfig::default()
}
/// 便捷函数:创建指定端口的配置
pub fn config_with_port(port: u16) -> ProxyConfig {
ProxyConfig::with_listen_port(port)
}
/// 便捷函数:创建默认代理服务器
pub fn default_server() -> ProxyServer {
ProxyServer::default()
}
/// 便捷函数:创建指定端口的代理服务器
pub fn server_with_port(port: u16) -> ProxyServer {
ProxyServer::with_listen_port(port)
}
/// 便捷函数:创建自定义配置的代理服务器
pub fn server_with_config<F>(config_fn: F) -> ProxyServer
where
F: FnOnce(ProxyConfig) -> ProxyConfig,
{
let config = config_fn(default_config());
ProxyServer::new(config)
}
/// 快速启动代理服务器
///
/// # 参数
///
/// * `listen_port` - 监听端口
/// * `default_backend_port` - 默认后端端口
/// * `backend_host` - 后端主机地址
///
/// # 示例
///
/// ```rust,ignore
/// use rcoder_proxy::quick_start;
///
/// #[tokio::main]
/// async fn main() -> anyhow::Result<()> {
/// quick_start(8080, 3000, "127.0.0.1").await?;
/// Ok(())
/// }
/// ```
pub async fn quick_start(
listen_port: u16,
default_backend_port: u16,
backend_host: &str,
) -> anyhow::Result<()> {
let config = ProxyConfig {
listen_port,
default_backend_port,
backend_host: backend_host.to_string(),
port_param: DEFAULT_PORT_PARAM.to_string(),
config_file: None,
verbose: false,
};
let server = ProxyServer::new(config);
server.start().await
}
/// Proxy error type
#[derive(Debug, thiserror::Error)]
pub enum ProxyError {
#[error("config error: {0}")]
Config(String),
#[error("network error: {0}")]
Network(String),
#[error("backend error: {0}")]
Backend(String),
#[error("port extraction error: {0}")]
PortExtraction(String),
#[error("request handling error: {0}")]
RequestHandling(String),
}
impl From<anyhow::Error> for ProxyError {
fn from(err: anyhow::Error) -> Self {
ProxyError::RequestHandling(err.to_string())
}
}
/// 代理结果类型
pub type ProxyResult<T> = Result<T, ProxyError>;
#[cfg(test)]
mod integration_tests {
use super::*;
#[test]
fn test_library_constants() {
assert_eq!(DEFAULT_PORT, 8080);
assert_eq!(DEFAULT_BACKEND_PORT, 3000);
assert_eq!(DEFAULT_BACKEND_HOST, "127.0.0.1");
assert_eq!(DEFAULT_PORT_PARAM, "port");
assert!(!VERSION.is_empty());
}
#[test]
fn test_convenience_functions() {
// 测试默认配置
let config = default_config();
assert_eq!(config.listen_port, 8080);
// 测试指定端口配置
let config = config_with_port(9090);
assert_eq!(config.listen_port, 9090);
// 测试默认服务器
let server = default_server();
assert_eq!(server.listen_port(), 8080);
// 测试指定端口服务器
let server = server_with_port(9090);
assert_eq!(server.listen_port(), 9090);
// 测试自定义配置服务器
let server = server_with_config(|mut config| {
config.listen_port = 8080;
config.backend_host = "localhost".to_string();
config
});
assert_eq!(server.config().backend_host, "localhost");
}
#[test]
fn test_features() {
assert!(features::QUERY_PARAM_ROUTING);
assert!(features::PATH_ROUTING);
assert!(features::DYNAMIC_BACKENDS);
assert!(features::CONFIG_FILE_SUPPORT);
assert!(features::VERBOSE_LOGGING);
}
#[test]
fn test_proxy_error() {
let error = ProxyError::Config("Invalid port".to_string());
assert!(error.to_string().contains("配置错误"));
let error = ProxyError::Network("Connection failed".to_string());
assert!(error.to_string().contains("网络错误"));
}
}