Cargo fmt

This commit is contained in:
Gregory Schier
2026-05-08 12:03:34 -07:00
parent 19ed8c2f0d
commit b0b282535f
25 changed files with 199 additions and 252 deletions

View File

@@ -25,11 +25,7 @@ pub struct ActionMetadata {
}
fn default_hotkey(mac: &str, other: &str) -> Option<String> {
if cfg!(target_os = "macos") {
Some(mac.into())
} else {
Some(other.into())
}
if cfg!(target_os = "macos") { Some(mac.into()) } else { Some(other.into()) }
}
/// All global actions with their metadata, used by `list_actions` RPC.

View File

@@ -14,10 +14,8 @@ pub struct ProxyQueryManager {
impl ProxyQueryManager {
pub fn new(db_path: &Path) -> Self {
let manager = SqliteConnectionManager::file(db_path);
let pool = Pool::builder()
.max_size(5)
.build(manager)
.expect("Failed to create proxy DB pool");
let pool =
Pool::builder().max_size(5).build(manager).expect("Failed to create proxy DB pool");
run_migrations(&pool, &MIGRATIONS).expect("Failed to run proxy DB migrations");
Self { pool }
}

View File

@@ -2,18 +2,18 @@ pub mod actions;
pub mod db;
pub mod models;
use crate::actions::{ActionInvocation, ActionMetadata, GlobalAction};
use crate::db::ProxyQueryManager;
use crate::models::{HttpExchange, ModelPayload, ProxyHeader};
use log::warn;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use std::sync::Mutex;
use log::warn;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use yaak_database::{ModelChangeEvent, UpdateSource};
use yaak_proxy::{CapturedRequest, ProxyEvent, ProxyHandle, RequestState};
use yaak_rpc::{RpcError, RpcEventEmitter, define_rpc};
use crate::actions::{ActionInvocation, ActionMetadata, GlobalAction};
use crate::db::ProxyQueryManager;
use crate::models::{HttpExchange, ModelPayload, ProxyHeader};
// -- Context --
@@ -25,11 +25,7 @@ pub struct ProxyCtx {
impl ProxyCtx {
pub fn new(db_path: &Path, events: RpcEventEmitter) -> Self {
Self {
handle: Mutex::new(None),
db: ProxyQueryManager::new(db_path),
events,
}
Self { handle: Mutex::new(None), db: ProxyQueryManager::new(db_path), events }
}
}
@@ -88,17 +84,15 @@ fn execute_action(ctx: &ProxyCtx, invocation: ActionInvocation) -> Result<bool,
match invocation {
ActionInvocation::Global { action } => match action {
GlobalAction::ProxyStart => {
let mut handle = ctx
.handle
.lock()
.map_err(|_| RpcError { message: "lock poisoned".into() })?;
let mut handle =
ctx.handle.lock().map_err(|_| RpcError { message: "lock poisoned".into() })?;
if handle.is_some() {
return Ok(true); // already running
}
let mut proxy_handle = yaak_proxy::start_proxy(9090)
.map_err(|e| RpcError { message: e })?;
let mut proxy_handle =
yaak_proxy::start_proxy(9090).map_err(|e| RpcError { message: e })?;
if let Some(event_rx) = proxy_handle.take_event_rx() {
let db = ctx.db.clone();
@@ -107,49 +101,43 @@ fn execute_action(ctx: &ProxyCtx, invocation: ActionInvocation) -> Result<bool,
}
*handle = Some(proxy_handle);
ctx.events.emit("proxy_state_changed", &ProxyStatePayload {
state: ProxyState::Running,
});
ctx.events
.emit("proxy_state_changed", &ProxyStatePayload { state: ProxyState::Running });
Ok(true)
}
GlobalAction::ProxyStop => {
let mut handle = ctx
.handle
.lock()
.map_err(|_| RpcError { message: "lock poisoned".into() })?;
let mut handle =
ctx.handle.lock().map_err(|_| RpcError { message: "lock poisoned".into() })?;
handle.take();
ctx.events.emit("proxy_state_changed", &ProxyStatePayload {
state: ProxyState::Stopped,
});
ctx.events
.emit("proxy_state_changed", &ProxyStatePayload { state: ProxyState::Stopped });
Ok(true)
}
},
}
}
fn get_proxy_state(ctx: &ProxyCtx, _req: GetProxyStateRequest) -> Result<GetProxyStateResponse, RpcError> {
let handle = ctx
.handle
.lock()
.map_err(|_| RpcError { message: "lock poisoned".into() })?;
let state = if handle.is_some() {
ProxyState::Running
} else {
ProxyState::Stopped
};
fn get_proxy_state(
ctx: &ProxyCtx,
_req: GetProxyStateRequest,
) -> Result<GetProxyStateResponse, RpcError> {
let handle = ctx.handle.lock().map_err(|_| RpcError { message: "lock poisoned".into() })?;
let state = if handle.is_some() { ProxyState::Running } else { ProxyState::Stopped };
Ok(GetProxyStateResponse { state })
}
fn list_actions(_ctx: &ProxyCtx, _req: ListActionsRequest) -> Result<ListActionsResponse, RpcError> {
Ok(ListActionsResponse {
actions: crate::actions::all_global_actions(),
})
fn list_actions(
_ctx: &ProxyCtx,
_req: ListActionsRequest,
) -> Result<ListActionsResponse, RpcError> {
Ok(ListActionsResponse { actions: crate::actions::all_global_actions() })
}
fn list_models(ctx: &ProxyCtx, _req: ListModelsRequest) -> Result<ListModelsResponse, RpcError> {
ctx.db.with_conn(|db| {
Ok(ListModelsResponse {
http_exchanges: db.find_all::<HttpExchange>()
http_exchanges: db
.find_all::<HttpExchange>()
.map_err(|e| RpcError { message: e.to_string() })?,
})
})
@@ -157,28 +145,35 @@ fn list_models(ctx: &ProxyCtx, _req: ListModelsRequest) -> Result<ListModelsResp
// -- Event loop --
fn run_event_loop(rx: std::sync::mpsc::Receiver<ProxyEvent>, db: ProxyQueryManager, events: RpcEventEmitter) {
fn run_event_loop(
rx: std::sync::mpsc::Receiver<ProxyEvent>,
db: ProxyQueryManager,
events: RpcEventEmitter,
) {
let mut in_flight: HashMap<u64, CapturedRequest> = HashMap::new();
while let Ok(event) = rx.recv() {
match event {
ProxyEvent::RequestStart { id, method, url, http_version } => {
in_flight.insert(id, CapturedRequest {
in_flight.insert(
id,
method,
url,
http_version,
status: None,
elapsed_ms: None,
remote_http_version: None,
request_headers: vec![],
request_body: None,
response_headers: vec![],
response_body: None,
response_body_size: 0,
state: RequestState::Sending,
error: None,
});
CapturedRequest {
id,
method,
url,
http_version,
status: None,
elapsed_ms: None,
remote_http_version: None,
request_headers: vec![],
request_body: None,
response_headers: vec![],
response_body: None,
response_body_size: 0,
state: RequestState::Sending,
error: None,
},
);
}
ProxyEvent::RequestHeader { id, name, value } => {
if let Some(r) = in_flight.get_mut(&id) {
@@ -230,28 +225,30 @@ fn write_entry(db: &ProxyQueryManager, events: &RpcEventEmitter, r: &CapturedReq
let entry = HttpExchange {
url: r.url.clone(),
method: r.method.clone(),
req_headers: r.request_headers.iter()
req_headers: r
.request_headers
.iter()
.map(|(n, v)| ProxyHeader { name: n.clone(), value: v.clone() })
.collect(),
req_body: r.request_body.clone(),
res_status: r.status.map(|s| s as i32),
res_headers: r.response_headers.iter()
res_headers: r
.response_headers
.iter()
.map(|(n, v)| ProxyHeader { name: n.clone(), value: v.clone() })
.collect(),
res_body: r.response_body.clone(),
error: r.error.clone(),
..Default::default()
};
db.with_conn(|ctx| {
match ctx.upsert(&entry, &UpdateSource::Background) {
Ok((saved, created)) => {
events.emit("model_write", &ModelPayload {
model: saved,
change: ModelChangeEvent::Upsert { created },
});
}
Err(e) => warn!("Failed to write proxy entry: {e}"),
db.with_conn(|ctx| match ctx.upsert(&entry, &UpdateSource::Background) {
Ok((saved, created)) => {
events.emit(
"model_write",
&ModelPayload { model: saved, change: ModelChangeEvent::Upsert { created } },
);
}
Err(e) => warn!("Failed to write proxy entry: {e}"),
});
}

View File

@@ -3,7 +3,10 @@ use rusqlite::Row;
use sea_query::{IntoColumnRef, IntoIden, IntoTableRef, Order, SimpleExpr, enum_def};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use yaak_database::{ModelChangeEvent, Result as DbResult, UpdateSource, UpsertModelInfo, generate_prefixed_id, upsert_date};
use yaak_database::{
ModelChangeEvent, Result as DbResult, UpdateSource, UpsertModelInfo, generate_prefixed_id,
upsert_date,
};
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase")]

View File

@@ -122,9 +122,7 @@ fn setup_window_menu<R: Runtime>(win: &WebviewWindow<R>) -> Result<()> {
}
// Commands for development
"dev.reset_size" => webview_window
.set_size(LogicalSize::new(1100.0, 600.0))
.unwrap(),
"dev.reset_size" => webview_window.set_size(LogicalSize::new(1100.0, 600.0)).unwrap(),
"dev.reset_size_16x9" => {
let width = webview_window.outer_size().unwrap().width;
let height = width * 9 / 16;
@@ -1507,7 +1505,6 @@ async fn cmd_reload_plugins<R: Runtime>(
Ok(errors)
}
#[tauri::command]
async fn cmd_plugin_info<R: Runtime>(
id: &str,
@@ -1580,7 +1577,14 @@ async fn cmd_new_child_window(
inner_size: (f64, f64),
) -> YaakResult<()> {
let use_native_titlebar = parent_window.app_handle().db().get_settings().use_native_titlebar;
let win = yaak_window::window::create_child_window(&parent_window, url, label, title, inner_size, use_native_titlebar)?;
let win = yaak_window::window::create_child_window(
&parent_window,
url,
label,
title,
inner_size,
use_native_titlebar,
)?;
setup_window_menu(&win)?;
Ok(())
}
@@ -1878,7 +1882,11 @@ pub fn run() {
match event {
RunEvent::Ready => {
let use_native_titlebar = app_handle.db().get_settings().use_native_titlebar;
if let Ok(win) = yaak_window::window::create_main_window(app_handle, "/", use_native_titlebar) {
if let Ok(win) = yaak_window::window::create_main_window(
app_handle,
"/",
use_native_titlebar,
) {
let _ = setup_window_menu(&win);
}
let h = app_handle.clone();

View File

@@ -3,7 +3,6 @@ use crate::http_request::send_http_request_with_context;
use crate::models_ext::BlobManagerExt;
use crate::models_ext::QueryManagerExt;
use crate::render::{render_grpc_request, render_http_request, render_json_value};
use yaak_window::window::{CreateWindowConfig, create_window};
use crate::{
call_frontend, cookie_jar_from_window, environment_from_window, get_window_from_plugin_context,
workspace_from_window,
@@ -36,6 +35,7 @@ use yaak_plugins::plugin_handle::PluginHandle;
use yaak_plugins::template_callback::PluginTemplateCallback;
use yaak_tauri_utils::window::WorkspaceWindowTrait;
use yaak_templates::{RenderErrorBehavior, RenderOptions};
use yaak_window::window::{CreateWindowConfig, create_window};
pub(crate) async fn handle_plugin_event<R: Runtime>(
app_handle: &AppHandle<R>,

View File

@@ -1,6 +1,6 @@
use log::{error, info, warn};
use tauri::{Emitter, Manager, RunEvent, State, WebviewWindow};
use tauri::Runtime;
use tauri::{Emitter, Manager, RunEvent, State, WebviewWindow};
use yaak_proxy_lib::ProxyCtx;
use yaak_rpc::{RpcEventEmitter, RpcRouter};
use yaak_window::window::CreateWindowConfig;

View File

@@ -109,19 +109,16 @@ fn position_traffic_lights(ns_window_handle: UnsafeWindowHandle, x: f64, y: f64,
// we've modified it. This avoids the height growing on repeated calls.
use std::sync::OnceLock;
static DEFAULT_TITLEBAR_HEIGHT: OnceLock<f64> = OnceLock::new();
let default_height =
*DEFAULT_TITLEBAR_HEIGHT.get_or_init(|| NSView::frame(title_bar_container_view).size.height);
let default_height = *DEFAULT_TITLEBAR_HEIGHT
.get_or_init(|| NSView::frame(title_bar_container_view).size.height);
// On pre-Tahoe, button_height + y is larger than the default title bar
// height, so the resize works as before. On Tahoe (26+), the default is
// already 32px and button_height + y = 32, so nothing changes. In that
// case, add TITLEBAR_EXTRA_HEIGHT extra pixels to push the buttons down.
let desired = button_height + y;
let title_bar_frame_height = if desired > default_height {
desired
} else {
default_height + TITLEBAR_EXTRA_HEIGHT
};
let title_bar_frame_height =
if desired > default_height { desired } else { default_height + TITLEBAR_EXTRA_HEIGHT };
let mut title_bar_rect = NSView::frame(title_bar_container_view);
title_bar_rect.size.height = title_bar_frame_height;

View File

@@ -65,8 +65,7 @@ impl<'a> DbContext<'a> {
.cond_where(Expr::col(col).eq(value))
.build_rusqlite(SqliteQueryBuilder);
let mut stmt = self.conn.prepare(sql.as_str()).expect("Failed to prepare query");
stmt.query_row(&*params.as_params(), M::from_row)
.ok()
stmt.query_row(&*params.as_params(), M::from_row).ok()
}
pub fn find_all<M>(&self) -> Result<Vec<M>>
@@ -126,9 +125,8 @@ impl<'a> DbContext<'a> {
let other_values = model.clone().insert_values(source)?;
let mut column_vec = vec![id_iden.clone()];
let mut value_vec = vec![
if id_val.is_empty() { M::generate_id().into() } else { id_val.into() },
];
let mut value_vec =
vec![if id_val.is_empty() { M::generate_id().into() } else { id_val.into() }];
for (col, val) in other_values {
value_vec.push(val.into());

View File

@@ -55,8 +55,7 @@ pub fn run_migrations(pool: &Pool<SqliteConnectionManager>, dir: &Dir<'_>) -> Re
continue;
}
let sql =
entry.as_file().unwrap().contents_utf8().expect("Failed to read migration file");
let sql = entry.as_file().unwrap().contents_utf8().expect("Failed to read migration file");
info!("Applying migration: {}", filename);
let conn = pool.get()?;

View File

@@ -10,10 +10,10 @@ pub fn generate_id() -> String {
pub fn generate_id_of_length(n: usize) -> String {
let alphabet: [char; 57] = [
'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A',
'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z',
'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C',
'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z',
];
nanoid!(n, &alphabet)

View File

@@ -3,7 +3,8 @@ use std::collections::HashMap;
use std::sync::mpsc;
/// Type-erased handler function: takes context + JSON payload, returns JSON or error.
type HandlerFn<Ctx> = Box<dyn Fn(&Ctx, serde_json::Value) -> Result<serde_json::Value, RpcError> + Send + Sync>;
type HandlerFn<Ctx> =
Box<dyn Fn(&Ctx, serde_json::Value) -> Result<serde_json::Value, RpcError> + Send + Sync>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcError {
@@ -57,9 +58,7 @@ pub struct RpcRouter<Ctx> {
impl<Ctx> RpcRouter<Ctx> {
pub fn new() -> Self {
Self {
handlers: HashMap::new(),
}
Self { handlers: HashMap::new() }
}
/// Register a handler for a command name.
@@ -77,23 +76,15 @@ impl<Ctx> RpcRouter<Ctx> {
) -> Result<serde_json::Value, RpcError> {
match self.handlers.get(cmd) {
Some(handler) => handler(ctx, payload),
None => Err(RpcError {
message: format!("unknown command: {cmd}"),
}),
None => Err(RpcError { message: format!("unknown command: {cmd}") }),
}
}
/// Handle a full `RpcRequest`, returning an `RpcResponse`.
pub fn handle(&self, req: RpcRequest, ctx: &Ctx) -> RpcResponse {
match self.dispatch(&req.cmd, req.payload, ctx) {
Ok(payload) => RpcResponse::Success {
id: req.id,
payload,
},
Err(e) => RpcResponse::Error {
id: req.id,
error: e.message,
},
Ok(payload) => RpcResponse::Success { id: req.id, payload },
Err(e) => RpcResponse::Error { id: req.id, error: e.message },
}
}

View File

@@ -304,7 +304,10 @@ async fn build_binary_body(
}))
}
fn build_text_body(body: &BTreeMap<String, serde_json::Value>, body_type: &str) -> Option<SendableBodyWithMeta> {
fn build_text_body(
body: &BTreeMap<String, serde_json::Value>,
body_type: &str,
) -> Option<SendableBodyWithMeta> {
let text = get_str_map(body, "text");
if text.is_empty() {
return None;

View File

@@ -16,8 +16,8 @@ use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::str::FromStr;
use ts_rs::TS;
use yaak_database::{Result as DbResult, UpdateSource};
pub use yaak_database::{UpsertModelInfo, upsert_date};
use yaak_database::{UpdateSource, Result as DbResult};
#[macro_export]
macro_rules! impl_model {
@@ -2526,4 +2526,3 @@ impl AnyModel {
}
}
}

View File

@@ -1,5 +1,5 @@
use crate::connection_or_tx::ConnectionOrTx;
use crate::client_db::ClientDb;
use crate::connection_or_tx::ConnectionOrTx;
use crate::error::Result;
use crate::models::{
Environment, EnvironmentIden, Folder, FolderIden, GrpcRequest, GrpcRequestIden, HttpRequest,

View File

@@ -16,7 +16,10 @@ impl<'a> ClientDb<'a> {
.add(Expr::col(PluginKeyValueIden::Key).eq(key)),
)
.build_rusqlite(SqliteQueryBuilder);
self.conn().resolve().query_row(sql.as_str(), &*params.as_params(), |row| row.try_into()).ok()
self.conn()
.resolve()
.query_row(sql.as_str(), &*params.as_params(), |row| row.try_into())
.ok()
}
pub fn set_plugin_key_value(

View File

@@ -10,7 +10,9 @@ use std::collections::BTreeMap;
use ts_rs::TS;
use yaak_core::WorkspaceContext;
pub use yaak_database::{ModelChangeEvent, generate_id, generate_id_of_length, generate_prefixed_id};
pub use yaak_database::{
ModelChangeEvent, generate_id, generate_id_of_length, generate_prefixed_id,
};
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase")]

View File

@@ -79,10 +79,9 @@ where
let len = data.len();
self.bytes_count += len as u64;
self.chunks.push(data.clone());
let _ = self.event_tx.send(ProxyEvent::ResponseBodyChunk {
id: self.request_id,
bytes: len,
});
let _ = self
.event_tx
.send(ProxyEvent::ResponseBodyChunk { id: self.request_id, bytes: len });
}
Poll::Ready(Some(Ok(frame)))
}

View File

@@ -18,23 +18,14 @@ impl CertificateAuthority {
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
params.key_usages.push(KeyUsagePurpose::KeyCertSign);
params.key_usages.push(KeyUsagePurpose::CrlSign);
params
.distinguished_name
.push(rcgen::DnType::CommonName, "Debug Proxy CA");
params
.distinguished_name
.push(rcgen::DnType::OrganizationName, "Debug Proxy");
params.distinguished_name.push(rcgen::DnType::CommonName, "Debug Proxy CA");
params.distinguished_name.push(rcgen::DnType::OrganizationName, "Debug Proxy");
let key = KeyPair::generate()?;
let ca_cert = params.self_signed(&key)?;
let ca_cert_der = ca_cert.der().clone();
Ok(Self {
ca_cert,
ca_cert_der,
ca_key: key,
cache: Mutex::new(HashMap::new()),
})
Ok(Self { ca_cert, ca_cert_der, ca_key: key, cache: Mutex::new(HashMap::new()) })
}
pub fn ca_pem(&self) -> String {
@@ -53,9 +44,7 @@ impl CertificateAuthority {
}
let mut params = CertificateParams::new(vec![domain.to_string()])?;
params
.distinguished_name
.push(rcgen::DnType::CommonName, domain);
params.distinguished_name.push(rcgen::DnType::CommonName, domain);
let leaf_key = KeyPair::generate()?;
let leaf_cert = params.signed_by(&leaf_key, &self.ca_cert, &self.ca_key)?;
@@ -63,20 +52,18 @@ impl CertificateAuthority {
let cert_der = leaf_cert.der().clone();
let key_der = leaf_key.serialize_der();
let mut config = ServerConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
.with_safe_default_protocol_versions()?
.with_no_client_auth()
.with_single_cert(
vec![cert_der, self.ca_cert_der.clone()],
PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_der)),
)?;
let mut config =
ServerConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
.with_safe_default_protocol_versions()?
.with_no_client_auth()
.with_single_cert(
vec![cert_der, self.ca_cert_der.clone()],
PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_der)),
)?;
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
let config = Arc::new(config);
self.cache
.lock()
.unwrap()
.insert(domain.to_string(), config.clone());
self.cache.lock().unwrap().insert(domain.to_string(), config.clone());
Ok(config)
}
}

View File

@@ -1,5 +1,5 @@
use std::sync::mpsc as std_mpsc;
use std::sync::Arc;
use std::sync::mpsc as std_mpsc;
use hyper::server::conn::http1;
use hyper::service::service_fn;

View File

@@ -4,9 +4,9 @@ mod connection;
mod request;
use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use std::sync::mpsc as std_mpsc;
use std::sync::Arc;
use cert::CertificateAuthority;
use tokio::net::TcpListener;
@@ -27,7 +27,11 @@ pub enum ProxyEvent {
http_version: String,
},
/// A request header sent to the upstream server.
RequestHeader { id: u64, name: String, value: String },
RequestHeader {
id: u64,
name: String,
value: String,
},
/// The full request body (buffered before forwarding).
RequestBody { id: u64, body: Vec<u8> },
/// Response headers received from upstream.
@@ -38,7 +42,11 @@ pub enum ProxyEvent {
elapsed_ms: u64,
},
/// A response header received from the upstream server.
ResponseHeader { id: u64, name: String, value: String },
ResponseHeader {
id: u64,
name: String,
value: String,
},
/// A chunk of the response body was received (emitted per-frame).
ResponseBodyChunk { id: u64, bytes: usize },
/// The response body stream has completed.

View File

@@ -63,10 +63,7 @@ fn emit_request_events(
});
}
if let Some(body) = body {
let _ = tx.send(ProxyEvent::RequestBody {
id,
body: body.clone(),
});
let _ = tx.send(ProxyEvent::RequestBody { id, body: body.clone() });
}
}
@@ -123,22 +120,13 @@ async fn handle_http(
let http_version = version_str(req.version());
let start = Instant::now();
let _ = event_tx.send(ProxyEvent::RequestStart {
id,
method,
url: uri.clone(),
http_version,
});
let _ = event_tx.send(ProxyEvent::RequestStart { id, method, url: uri.clone(), http_version });
let client: Client<_, Full<Bytes>> = Client::builder(TokioExecutor::new()).build_http();
let (parts, body) = req.into_parts();
let body_bytes = body.collect().await?.to_bytes();
let request_body = if body_bytes.is_empty() {
None
} else {
Some(body_bytes.to_vec())
};
let request_body = if body_bytes.is_empty() { None } else { Some(body_bytes.to_vec()) };
emit_request_events(&event_tx, id, &parts.headers, &request_body);
let outgoing_req = Request::from_parts(parts, Full::new(body_bytes));
@@ -148,16 +136,10 @@ async fn handle_http(
emit_response_events(&event_tx, id, &resp, &start);
let (parts, body) = resp.into_parts();
Ok(Response::from_parts(
parts,
measured_incoming(body, id, start, event_tx),
))
Ok(Response::from_parts(parts, measured_incoming(body, id, start, event_tx)))
}
Err(e) => {
let _ = event_tx.send(ProxyEvent::Error {
id,
error: e.to_string(),
});
let _ = event_tx.send(ProxyEvent::Error { id, error: e.to_string() });
Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
}
}
@@ -168,11 +150,7 @@ async fn handle_connect(
event_tx: std_mpsc::Sender<ProxyEvent>,
ca: Arc<CertificateAuthority>,
) -> Result<Response<BoxBody>, Box<dyn std::error::Error + Send + Sync>> {
let authority = req
.uri()
.authority()
.map(|a| a.to_string())
.unwrap_or_default();
let authority = req.uri().authority().map(|a| a.to_string()).unwrap_or_default();
let (host, port) = parse_host_port(&authority);
let server_config = ca.server_config(&host)?;
@@ -189,10 +167,7 @@ async fn handle_connect(
}
};
let tls_stream = match acceptor
.accept(hyper_util::rt::TokioIo::new(upgraded))
.await
{
let tls_stream = match acceptor.accept(hyper_util::rt::TokioIo::new(upgraded)).await {
Ok(s) => s,
Err(e) => {
eprintln!("TLS accept failed for {host}: {e}");
@@ -203,10 +178,7 @@ async fn handle_connect(
let tx = event_tx.clone();
let host_for_requests = host.clone();
let mut builder = auto::Builder::new(TokioExecutor::new());
builder
.http1()
.preserve_header_case(true)
.title_case_headers(true);
builder.http1().preserve_header_case(true).title_case_headers(true);
if let Err(e) = builder
.serve_connection_with_upgrades(
hyper_util::rt::TokioIo::new(tls_stream),
@@ -271,20 +243,12 @@ async fn forward_https(
let id = REQUEST_ID.fetch_add(1, Ordering::Relaxed);
let method = req.method().to_string();
let http_version = version_str(req.version());
let path = req
.uri()
.path_and_query()
.map(|pq| pq.to_string())
.unwrap_or_else(|| "/".into());
let path = req.uri().path_and_query().map(|pq| pq.to_string()).unwrap_or_else(|| "/".into());
let uri_str = format!("https://{host}{path}");
let start = Instant::now();
let _ = event_tx.send(ProxyEvent::RequestStart {
id,
method,
url: uri_str.clone(),
http_version,
});
let _ =
event_tx.send(ProxyEvent::RequestStart { id, method, url: uri_str.clone(), http_version });
// Connect to upstream with TLS
let tcp_stream = TcpStream::connect(target_addr).await?;
@@ -305,18 +269,13 @@ async fn forward_https(
let server_name = ServerName::try_from(host.to_string())?;
let tls_stream = connector.connect(server_name, tcp_stream).await?;
let negotiated_h2 = tls_stream
.get_ref()
.1
.alpn_protocol()
.map_or(false, |p| p == b"h2");
let negotiated_h2 = tls_stream.get_ref().1.alpn_protocol().map_or(false, |p| p == b"h2");
let io = hyper_util::rt::TokioIo::new(tls_stream);
let mut sender = if negotiated_h2 {
let (sender, conn) = hyper::client::conn::http2::Builder::new(TokioExecutor::new())
.handshake(io)
.await?;
let (sender, conn) =
hyper::client::conn::http2::Builder::new(TokioExecutor::new()).handshake(io).await?;
tokio::spawn(async move {
if let Err(e) = conn.await {
eprintln!("Upstream h2 connection error: {e}");
@@ -340,11 +299,7 @@ async fn forward_https(
// Capture request metadata
let (mut parts, body) = req.into_parts();
let body_bytes = body.collect().await?.to_bytes();
let request_body = if body_bytes.is_empty() {
None
} else {
Some(body_bytes.to_vec())
};
let request_body = if body_bytes.is_empty() { None } else { Some(body_bytes.to_vec()) };
emit_request_events(&event_tx, id, &parts.headers, &request_body);
if negotiated_h2 {
@@ -365,16 +320,10 @@ async fn forward_https(
emit_response_events(&event_tx, id, &resp, &start);
let (parts, body) = resp.into_parts();
Ok(Response::from_parts(
parts,
measured_incoming(body, id, start, event_tx),
))
Ok(Response::from_parts(parts, measured_incoming(body, id, start, event_tx)))
}
Err(e) => {
let _ = event_tx.send(ProxyEvent::Error {
id,
error: e.to_string(),
});
let _ = event_tx.send(ProxyEvent::Error { id, error: e.to_string() });
Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
}
}

View File

@@ -1,9 +1,9 @@
pub mod error;
pub mod escape;
pub mod format_json;
pub mod strip_json_comments;
pub mod parser;
pub mod renderer;
pub mod strip_json_comments;
pub mod wasm;
pub use parser::*;

View File

@@ -113,11 +113,8 @@ pub fn strip_json_comments(text: &str) -> String {
}
// Remove lines that are now empty (were comment-only lines)
let result = result
.lines()
.filter(|line| !line.trim().is_empty())
.collect::<Vec<&str>>()
.join("\n");
let result =
result.lines().filter(|line| !line.trim().is_empty()).collect::<Vec<&str>>().join("\n");
// Remove trailing commas before } or ]
strip_trailing_commas(&result)
@@ -192,10 +189,12 @@ mod tests {
#[test]
fn test_trailing_line_comment() {
assert_eq!(
strip_json_comments(r#"{
strip_json_comments(
r#"{
"foo": "bar", // this is a comment
"baz": 123
}"#),
}"#
),
r#"{
"foo": "bar",
"baz": 123
@@ -206,10 +205,12 @@ mod tests {
#[test]
fn test_whole_line_comment() {
assert_eq!(
strip_json_comments(r#"{
strip_json_comments(
r#"{
// this is a comment
"foo": "bar"
}"#),
}"#
),
r#"{
"foo": "bar"
}"#
@@ -219,9 +220,11 @@ mod tests {
#[test]
fn test_inline_block_comment() {
assert_eq!(
strip_json_comments(r#"{
strip_json_comments(
r#"{
"foo": /* a comment */ "bar"
}"#),
}"#
),
r#"{
"foo": "bar"
}"#
@@ -231,10 +234,12 @@ mod tests {
#[test]
fn test_whole_line_block_comment() {
assert_eq!(
strip_json_comments(r#"{
strip_json_comments(
r#"{
/* a comment */
"foo": "bar"
}"#),
}"#
),
r#"{
"foo": "bar"
}"#
@@ -244,12 +249,14 @@ mod tests {
#[test]
fn test_multiline_block_comment() {
assert_eq!(
strip_json_comments(r#"{
strip_json_comments(
r#"{
/**
* Hello World!
*/
"foo": "bar"
}"#),
}"#
),
r#"{
"foo": "bar"
}"#
@@ -276,12 +283,14 @@ mod tests {
#[test]
fn test_multiple_comments() {
assert_eq!(
strip_json_comments(r#"{
strip_json_comments(
r#"{
// first comment
"foo": "bar", // trailing
/* block */
"baz": 123
}"#),
}"#
),
r#"{
"foo": "bar",
"baz": 123
@@ -292,10 +301,12 @@ mod tests {
#[test]
fn test_trailing_comma_after_comment_removed() {
assert_eq!(
strip_json_comments(r#"{
strip_json_comments(
r#"{
"a": "aaa",
// "b": "bbb"
}"#),
}"#
),
r#"{
"a": "aaa"
}"#
@@ -304,10 +315,7 @@ mod tests {
#[test]
fn test_trailing_comma_in_array() {
assert_eq!(
strip_json_comments(r#"[1, 2, /* 3 */]"#),
r#"[1, 2]"#
);
assert_eq!(strip_json_comments(r#"[1, 2, /* 3 */]"#), r#"[1, 2]"#);
}
#[test]

View File

@@ -2,7 +2,9 @@ use log::info;
use serde_json::Value;
use std::collections::BTreeMap;
use yaak_http::path_placeholders::apply_path_placeholders;
use yaak_models::models::{Environment, GrpcRequest, HttpRequest, HttpRequestHeader, HttpUrlParameter};
use yaak_models::models::{
Environment, GrpcRequest, HttpRequest, HttpRequestHeader, HttpUrlParameter,
};
use yaak_models::render::make_vars_hashmap;
use yaak_templates::{RenderOptions, TemplateCallback, parse_and_render, render_json_value_raw};