mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-05-17 21:27:05 +02:00
Cargo fmt
This commit is contained in:
@@ -25,11 +25,7 @@ pub struct ActionMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_hotkey(mac: &str, other: &str) -> Option<String> {
|
fn default_hotkey(mac: &str, other: &str) -> Option<String> {
|
||||||
if cfg!(target_os = "macos") {
|
if cfg!(target_os = "macos") { Some(mac.into()) } else { Some(other.into()) }
|
||||||
Some(mac.into())
|
|
||||||
} else {
|
|
||||||
Some(other.into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All global actions with their metadata, used by `list_actions` RPC.
|
/// All global actions with their metadata, used by `list_actions` RPC.
|
||||||
|
|||||||
@@ -14,10 +14,8 @@ pub struct ProxyQueryManager {
|
|||||||
impl ProxyQueryManager {
|
impl ProxyQueryManager {
|
||||||
pub fn new(db_path: &Path) -> Self {
|
pub fn new(db_path: &Path) -> Self {
|
||||||
let manager = SqliteConnectionManager::file(db_path);
|
let manager = SqliteConnectionManager::file(db_path);
|
||||||
let pool = Pool::builder()
|
let pool =
|
||||||
.max_size(5)
|
Pool::builder().max_size(5).build(manager).expect("Failed to create proxy DB pool");
|
||||||
.build(manager)
|
|
||||||
.expect("Failed to create proxy DB pool");
|
|
||||||
run_migrations(&pool, &MIGRATIONS).expect("Failed to run proxy DB migrations");
|
run_migrations(&pool, &MIGRATIONS).expect("Failed to run proxy DB migrations");
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,18 @@ pub mod actions;
|
|||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod models;
|
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::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use log::warn;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
use yaak_database::{ModelChangeEvent, UpdateSource};
|
use yaak_database::{ModelChangeEvent, UpdateSource};
|
||||||
use yaak_proxy::{CapturedRequest, ProxyEvent, ProxyHandle, RequestState};
|
use yaak_proxy::{CapturedRequest, ProxyEvent, ProxyHandle, RequestState};
|
||||||
use yaak_rpc::{RpcError, RpcEventEmitter, define_rpc};
|
use yaak_rpc::{RpcError, RpcEventEmitter, define_rpc};
|
||||||
use crate::actions::{ActionInvocation, ActionMetadata, GlobalAction};
|
|
||||||
use crate::db::ProxyQueryManager;
|
|
||||||
use crate::models::{HttpExchange, ModelPayload, ProxyHeader};
|
|
||||||
|
|
||||||
// -- Context --
|
// -- Context --
|
||||||
|
|
||||||
@@ -25,11 +25,7 @@ pub struct ProxyCtx {
|
|||||||
|
|
||||||
impl ProxyCtx {
|
impl ProxyCtx {
|
||||||
pub fn new(db_path: &Path, events: RpcEventEmitter) -> Self {
|
pub fn new(db_path: &Path, events: RpcEventEmitter) -> Self {
|
||||||
Self {
|
Self { handle: Mutex::new(None), db: ProxyQueryManager::new(db_path), events }
|
||||||
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 {
|
match invocation {
|
||||||
ActionInvocation::Global { action } => match action {
|
ActionInvocation::Global { action } => match action {
|
||||||
GlobalAction::ProxyStart => {
|
GlobalAction::ProxyStart => {
|
||||||
let mut handle = ctx
|
let mut handle =
|
||||||
.handle
|
ctx.handle.lock().map_err(|_| RpcError { message: "lock poisoned".into() })?;
|
||||||
.lock()
|
|
||||||
.map_err(|_| RpcError { message: "lock poisoned".into() })?;
|
|
||||||
|
|
||||||
if handle.is_some() {
|
if handle.is_some() {
|
||||||
return Ok(true); // already running
|
return Ok(true); // already running
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut proxy_handle = yaak_proxy::start_proxy(9090)
|
let mut proxy_handle =
|
||||||
.map_err(|e| RpcError { message: e })?;
|
yaak_proxy::start_proxy(9090).map_err(|e| RpcError { message: e })?;
|
||||||
|
|
||||||
if let Some(event_rx) = proxy_handle.take_event_rx() {
|
if let Some(event_rx) = proxy_handle.take_event_rx() {
|
||||||
let db = ctx.db.clone();
|
let db = ctx.db.clone();
|
||||||
@@ -107,49 +101,43 @@ fn execute_action(ctx: &ProxyCtx, invocation: ActionInvocation) -> Result<bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*handle = Some(proxy_handle);
|
*handle = Some(proxy_handle);
|
||||||
ctx.events.emit("proxy_state_changed", &ProxyStatePayload {
|
ctx.events
|
||||||
state: ProxyState::Running,
|
.emit("proxy_state_changed", &ProxyStatePayload { state: ProxyState::Running });
|
||||||
});
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
GlobalAction::ProxyStop => {
|
GlobalAction::ProxyStop => {
|
||||||
let mut handle = ctx
|
let mut handle =
|
||||||
.handle
|
ctx.handle.lock().map_err(|_| RpcError { message: "lock poisoned".into() })?;
|
||||||
.lock()
|
|
||||||
.map_err(|_| RpcError { message: "lock poisoned".into() })?;
|
|
||||||
handle.take();
|
handle.take();
|
||||||
ctx.events.emit("proxy_state_changed", &ProxyStatePayload {
|
ctx.events
|
||||||
state: ProxyState::Stopped,
|
.emit("proxy_state_changed", &ProxyStatePayload { state: ProxyState::Stopped });
|
||||||
});
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_proxy_state(ctx: &ProxyCtx, _req: GetProxyStateRequest) -> Result<GetProxyStateResponse, RpcError> {
|
fn get_proxy_state(
|
||||||
let handle = ctx
|
ctx: &ProxyCtx,
|
||||||
.handle
|
_req: GetProxyStateRequest,
|
||||||
.lock()
|
) -> Result<GetProxyStateResponse, RpcError> {
|
||||||
.map_err(|_| RpcError { message: "lock poisoned".into() })?;
|
let handle = ctx.handle.lock().map_err(|_| RpcError { message: "lock poisoned".into() })?;
|
||||||
let state = if handle.is_some() {
|
let state = if handle.is_some() { ProxyState::Running } else { ProxyState::Stopped };
|
||||||
ProxyState::Running
|
|
||||||
} else {
|
|
||||||
ProxyState::Stopped
|
|
||||||
};
|
|
||||||
Ok(GetProxyStateResponse { state })
|
Ok(GetProxyStateResponse { state })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_actions(_ctx: &ProxyCtx, _req: ListActionsRequest) -> Result<ListActionsResponse, RpcError> {
|
fn list_actions(
|
||||||
Ok(ListActionsResponse {
|
_ctx: &ProxyCtx,
|
||||||
actions: crate::actions::all_global_actions(),
|
_req: ListActionsRequest,
|
||||||
})
|
) -> Result<ListActionsResponse, RpcError> {
|
||||||
|
Ok(ListActionsResponse { actions: crate::actions::all_global_actions() })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_models(ctx: &ProxyCtx, _req: ListModelsRequest) -> Result<ListModelsResponse, RpcError> {
|
fn list_models(ctx: &ProxyCtx, _req: ListModelsRequest) -> Result<ListModelsResponse, RpcError> {
|
||||||
ctx.db.with_conn(|db| {
|
ctx.db.with_conn(|db| {
|
||||||
Ok(ListModelsResponse {
|
Ok(ListModelsResponse {
|
||||||
http_exchanges: db.find_all::<HttpExchange>()
|
http_exchanges: db
|
||||||
|
.find_all::<HttpExchange>()
|
||||||
.map_err(|e| RpcError { message: e.to_string() })?,
|
.map_err(|e| RpcError { message: e.to_string() })?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -157,28 +145,35 @@ fn list_models(ctx: &ProxyCtx, _req: ListModelsRequest) -> Result<ListModelsResp
|
|||||||
|
|
||||||
// -- Event loop --
|
// -- 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();
|
let mut in_flight: HashMap<u64, CapturedRequest> = HashMap::new();
|
||||||
|
|
||||||
while let Ok(event) = rx.recv() {
|
while let Ok(event) = rx.recv() {
|
||||||
match event {
|
match event {
|
||||||
ProxyEvent::RequestStart { id, method, url, http_version } => {
|
ProxyEvent::RequestStart { id, method, url, http_version } => {
|
||||||
in_flight.insert(id, CapturedRequest {
|
in_flight.insert(
|
||||||
id,
|
id,
|
||||||
method,
|
CapturedRequest {
|
||||||
url,
|
id,
|
||||||
http_version,
|
method,
|
||||||
status: None,
|
url,
|
||||||
elapsed_ms: None,
|
http_version,
|
||||||
remote_http_version: None,
|
status: None,
|
||||||
request_headers: vec![],
|
elapsed_ms: None,
|
||||||
request_body: None,
|
remote_http_version: None,
|
||||||
response_headers: vec![],
|
request_headers: vec![],
|
||||||
response_body: None,
|
request_body: None,
|
||||||
response_body_size: 0,
|
response_headers: vec![],
|
||||||
state: RequestState::Sending,
|
response_body: None,
|
||||||
error: None,
|
response_body_size: 0,
|
||||||
});
|
state: RequestState::Sending,
|
||||||
|
error: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ProxyEvent::RequestHeader { id, name, value } => {
|
ProxyEvent::RequestHeader { id, name, value } => {
|
||||||
if let Some(r) = in_flight.get_mut(&id) {
|
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 {
|
let entry = HttpExchange {
|
||||||
url: r.url.clone(),
|
url: r.url.clone(),
|
||||||
method: r.method.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() })
|
.map(|(n, v)| ProxyHeader { name: n.clone(), value: v.clone() })
|
||||||
.collect(),
|
.collect(),
|
||||||
req_body: r.request_body.clone(),
|
req_body: r.request_body.clone(),
|
||||||
res_status: r.status.map(|s| s as i32),
|
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() })
|
.map(|(n, v)| ProxyHeader { name: n.clone(), value: v.clone() })
|
||||||
.collect(),
|
.collect(),
|
||||||
res_body: r.response_body.clone(),
|
res_body: r.response_body.clone(),
|
||||||
error: r.error.clone(),
|
error: r.error.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
db.with_conn(|ctx| {
|
db.with_conn(|ctx| match ctx.upsert(&entry, &UpdateSource::Background) {
|
||||||
match ctx.upsert(&entry, &UpdateSource::Background) {
|
Ok((saved, created)) => {
|
||||||
Ok((saved, created)) => {
|
events.emit(
|
||||||
events.emit("model_write", &ModelPayload {
|
"model_write",
|
||||||
model: saved,
|
&ModelPayload { model: saved, change: ModelChangeEvent::Upsert { created } },
|
||||||
change: ModelChangeEvent::Upsert { created },
|
);
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(e) => warn!("Failed to write proxy entry: {e}"),
|
|
||||||
}
|
}
|
||||||
|
Err(e) => warn!("Failed to write proxy entry: {e}"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ use rusqlite::Row;
|
|||||||
use sea_query::{IntoColumnRef, IntoIden, IntoTableRef, Order, SimpleExpr, enum_def};
|
use sea_query::{IntoColumnRef, IntoIden, IntoTableRef, Order, SimpleExpr, enum_def};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::TS;
|
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)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|||||||
@@ -122,9 +122,7 @@ fn setup_window_menu<R: Runtime>(win: &WebviewWindow<R>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Commands for development
|
// Commands for development
|
||||||
"dev.reset_size" => webview_window
|
"dev.reset_size" => webview_window.set_size(LogicalSize::new(1100.0, 600.0)).unwrap(),
|
||||||
.set_size(LogicalSize::new(1100.0, 600.0))
|
|
||||||
.unwrap(),
|
|
||||||
"dev.reset_size_16x9" => {
|
"dev.reset_size_16x9" => {
|
||||||
let width = webview_window.outer_size().unwrap().width;
|
let width = webview_window.outer_size().unwrap().width;
|
||||||
let height = width * 9 / 16;
|
let height = width * 9 / 16;
|
||||||
@@ -1507,7 +1505,6 @@ async fn cmd_reload_plugins<R: Runtime>(
|
|||||||
Ok(errors)
|
Ok(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn cmd_plugin_info<R: Runtime>(
|
async fn cmd_plugin_info<R: Runtime>(
|
||||||
id: &str,
|
id: &str,
|
||||||
@@ -1580,7 +1577,14 @@ async fn cmd_new_child_window(
|
|||||||
inner_size: (f64, f64),
|
inner_size: (f64, f64),
|
||||||
) -> YaakResult<()> {
|
) -> YaakResult<()> {
|
||||||
let use_native_titlebar = parent_window.app_handle().db().get_settings().use_native_titlebar;
|
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)?;
|
setup_window_menu(&win)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1878,7 +1882,11 @@ pub fn run() {
|
|||||||
match event {
|
match event {
|
||||||
RunEvent::Ready => {
|
RunEvent::Ready => {
|
||||||
let use_native_titlebar = app_handle.db().get_settings().use_native_titlebar;
|
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 _ = setup_window_menu(&win);
|
||||||
}
|
}
|
||||||
let h = app_handle.clone();
|
let h = app_handle.clone();
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use crate::http_request::send_http_request_with_context;
|
|||||||
use crate::models_ext::BlobManagerExt;
|
use crate::models_ext::BlobManagerExt;
|
||||||
use crate::models_ext::QueryManagerExt;
|
use crate::models_ext::QueryManagerExt;
|
||||||
use crate::render::{render_grpc_request, render_http_request, render_json_value};
|
use crate::render::{render_grpc_request, render_http_request, render_json_value};
|
||||||
use yaak_window::window::{CreateWindowConfig, create_window};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
call_frontend, cookie_jar_from_window, environment_from_window, get_window_from_plugin_context,
|
call_frontend, cookie_jar_from_window, environment_from_window, get_window_from_plugin_context,
|
||||||
workspace_from_window,
|
workspace_from_window,
|
||||||
@@ -36,6 +35,7 @@ use yaak_plugins::plugin_handle::PluginHandle;
|
|||||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||||
use yaak_tauri_utils::window::WorkspaceWindowTrait;
|
use yaak_tauri_utils::window::WorkspaceWindowTrait;
|
||||||
use yaak_templates::{RenderErrorBehavior, RenderOptions};
|
use yaak_templates::{RenderErrorBehavior, RenderOptions};
|
||||||
|
use yaak_window::window::{CreateWindowConfig, create_window};
|
||||||
|
|
||||||
pub(crate) async fn handle_plugin_event<R: Runtime>(
|
pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||||
app_handle: &AppHandle<R>,
|
app_handle: &AppHandle<R>,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use tauri::{Emitter, Manager, RunEvent, State, WebviewWindow};
|
|
||||||
use tauri::Runtime;
|
use tauri::Runtime;
|
||||||
|
use tauri::{Emitter, Manager, RunEvent, State, WebviewWindow};
|
||||||
use yaak_proxy_lib::ProxyCtx;
|
use yaak_proxy_lib::ProxyCtx;
|
||||||
use yaak_rpc::{RpcEventEmitter, RpcRouter};
|
use yaak_rpc::{RpcEventEmitter, RpcRouter};
|
||||||
use yaak_window::window::CreateWindowConfig;
|
use yaak_window::window::CreateWindowConfig;
|
||||||
|
|||||||
@@ -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.
|
// we've modified it. This avoids the height growing on repeated calls.
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
static DEFAULT_TITLEBAR_HEIGHT: OnceLock<f64> = OnceLock::new();
|
static DEFAULT_TITLEBAR_HEIGHT: OnceLock<f64> = OnceLock::new();
|
||||||
let default_height =
|
let default_height = *DEFAULT_TITLEBAR_HEIGHT
|
||||||
*DEFAULT_TITLEBAR_HEIGHT.get_or_init(|| NSView::frame(title_bar_container_view).size.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
|
// 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
|
// 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
|
// already 32px and button_height + y = 32, so nothing changes. In that
|
||||||
// case, add TITLEBAR_EXTRA_HEIGHT extra pixels to push the buttons down.
|
// case, add TITLEBAR_EXTRA_HEIGHT extra pixels to push the buttons down.
|
||||||
let desired = button_height + y;
|
let desired = button_height + y;
|
||||||
let title_bar_frame_height = if desired > default_height {
|
let title_bar_frame_height =
|
||||||
desired
|
if desired > default_height { desired } else { default_height + TITLEBAR_EXTRA_HEIGHT };
|
||||||
} else {
|
|
||||||
default_height + TITLEBAR_EXTRA_HEIGHT
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut title_bar_rect = NSView::frame(title_bar_container_view);
|
let mut title_bar_rect = NSView::frame(title_bar_container_view);
|
||||||
title_bar_rect.size.height = title_bar_frame_height;
|
title_bar_rect.size.height = title_bar_frame_height;
|
||||||
|
|||||||
@@ -65,8 +65,7 @@ impl<'a> DbContext<'a> {
|
|||||||
.cond_where(Expr::col(col).eq(value))
|
.cond_where(Expr::col(col).eq(value))
|
||||||
.build_rusqlite(SqliteQueryBuilder);
|
.build_rusqlite(SqliteQueryBuilder);
|
||||||
let mut stmt = self.conn.prepare(sql.as_str()).expect("Failed to prepare query");
|
let mut stmt = self.conn.prepare(sql.as_str()).expect("Failed to prepare query");
|
||||||
stmt.query_row(&*params.as_params(), M::from_row)
|
stmt.query_row(&*params.as_params(), M::from_row).ok()
|
||||||
.ok()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_all<M>(&self) -> Result<Vec<M>>
|
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 other_values = model.clone().insert_values(source)?;
|
||||||
|
|
||||||
let mut column_vec = vec![id_iden.clone()];
|
let mut column_vec = vec![id_iden.clone()];
|
||||||
let mut value_vec = vec![
|
let mut value_vec =
|
||||||
if id_val.is_empty() { M::generate_id().into() } else { id_val.into() },
|
vec![if id_val.is_empty() { M::generate_id().into() } else { id_val.into() }];
|
||||||
];
|
|
||||||
|
|
||||||
for (col, val) in other_values {
|
for (col, val) in other_values {
|
||||||
value_vec.push(val.into());
|
value_vec.push(val.into());
|
||||||
|
|||||||
@@ -55,8 +55,7 @@ pub fn run_migrations(pool: &Pool<SqliteConnectionManager>, dir: &Dir<'_>) -> Re
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sql =
|
let sql = entry.as_file().unwrap().contents_utf8().expect("Failed to read migration file");
|
||||||
entry.as_file().unwrap().contents_utf8().expect("Failed to read migration file");
|
|
||||||
|
|
||||||
info!("Applying migration: {}", filename);
|
info!("Applying migration: {}", filename);
|
||||||
let conn = pool.get()?;
|
let conn = pool.get()?;
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ pub fn generate_id() -> String {
|
|||||||
|
|
||||||
pub fn generate_id_of_length(n: usize) -> String {
|
pub fn generate_id_of_length(n: usize) -> String {
|
||||||
let alphabet: [char; 57] = [
|
let alphabet: [char; 57] = [
|
||||||
'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
|
'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||||
'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A',
|
'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C',
|
||||||
'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T',
|
'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||||
'U', 'V', 'W', 'X', 'Y', 'Z',
|
'X', 'Y', 'Z',
|
||||||
];
|
];
|
||||||
|
|
||||||
nanoid!(n, &alphabet)
|
nanoid!(n, &alphabet)
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ use std::collections::HashMap;
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
/// Type-erased handler function: takes context + JSON payload, returns JSON or error.
|
/// 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)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct RpcError {
|
pub struct RpcError {
|
||||||
@@ -57,9 +58,7 @@ pub struct RpcRouter<Ctx> {
|
|||||||
|
|
||||||
impl<Ctx> RpcRouter<Ctx> {
|
impl<Ctx> RpcRouter<Ctx> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self { handlers: HashMap::new() }
|
||||||
handlers: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a handler for a command name.
|
/// Register a handler for a command name.
|
||||||
@@ -77,23 +76,15 @@ impl<Ctx> RpcRouter<Ctx> {
|
|||||||
) -> Result<serde_json::Value, RpcError> {
|
) -> Result<serde_json::Value, RpcError> {
|
||||||
match self.handlers.get(cmd) {
|
match self.handlers.get(cmd) {
|
||||||
Some(handler) => handler(ctx, payload),
|
Some(handler) => handler(ctx, payload),
|
||||||
None => Err(RpcError {
|
None => Err(RpcError { message: format!("unknown command: {cmd}") }),
|
||||||
message: format!("unknown command: {cmd}"),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a full `RpcRequest`, returning an `RpcResponse`.
|
/// Handle a full `RpcRequest`, returning an `RpcResponse`.
|
||||||
pub fn handle(&self, req: RpcRequest, ctx: &Ctx) -> RpcResponse {
|
pub fn handle(&self, req: RpcRequest, ctx: &Ctx) -> RpcResponse {
|
||||||
match self.dispatch(&req.cmd, req.payload, ctx) {
|
match self.dispatch(&req.cmd, req.payload, ctx) {
|
||||||
Ok(payload) => RpcResponse::Success {
|
Ok(payload) => RpcResponse::Success { id: req.id, payload },
|
||||||
id: req.id,
|
Err(e) => RpcResponse::Error { id: req.id, error: e.message },
|
||||||
payload,
|
|
||||||
},
|
|
||||||
Err(e) => RpcResponse::Error {
|
|
||||||
id: req.id,
|
|
||||||
error: e.message,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
let text = get_str_map(body, "text");
|
||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ use std::collections::HashMap;
|
|||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
use yaak_database::{Result as DbResult, UpdateSource};
|
||||||
pub use yaak_database::{UpsertModelInfo, upsert_date};
|
pub use yaak_database::{UpsertModelInfo, upsert_date};
|
||||||
use yaak_database::{UpdateSource, Result as DbResult};
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_model {
|
macro_rules! impl_model {
|
||||||
@@ -2526,4 +2526,3 @@ impl AnyModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::connection_or_tx::ConnectionOrTx;
|
|
||||||
use crate::client_db::ClientDb;
|
use crate::client_db::ClientDb;
|
||||||
|
use crate::connection_or_tx::ConnectionOrTx;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
Environment, EnvironmentIden, Folder, FolderIden, GrpcRequest, GrpcRequestIden, HttpRequest,
|
Environment, EnvironmentIden, Folder, FolderIden, GrpcRequest, GrpcRequestIden, HttpRequest,
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ impl<'a> ClientDb<'a> {
|
|||||||
.add(Expr::col(PluginKeyValueIden::Key).eq(key)),
|
.add(Expr::col(PluginKeyValueIden::Key).eq(key)),
|
||||||
)
|
)
|
||||||
.build_rusqlite(SqliteQueryBuilder);
|
.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(
|
pub fn set_plugin_key_value(
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ use std::collections::BTreeMap;
|
|||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
use yaak_core::WorkspaceContext;
|
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)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|||||||
@@ -79,10 +79,9 @@ where
|
|||||||
let len = data.len();
|
let len = data.len();
|
||||||
self.bytes_count += len as u64;
|
self.bytes_count += len as u64;
|
||||||
self.chunks.push(data.clone());
|
self.chunks.push(data.clone());
|
||||||
let _ = self.event_tx.send(ProxyEvent::ResponseBodyChunk {
|
let _ = self
|
||||||
id: self.request_id,
|
.event_tx
|
||||||
bytes: len,
|
.send(ProxyEvent::ResponseBodyChunk { id: self.request_id, bytes: len });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Poll::Ready(Some(Ok(frame)))
|
Poll::Ready(Some(Ok(frame)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,23 +18,14 @@ impl CertificateAuthority {
|
|||||||
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
|
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
|
||||||
params.key_usages.push(KeyUsagePurpose::KeyCertSign);
|
params.key_usages.push(KeyUsagePurpose::KeyCertSign);
|
||||||
params.key_usages.push(KeyUsagePurpose::CrlSign);
|
params.key_usages.push(KeyUsagePurpose::CrlSign);
|
||||||
params
|
params.distinguished_name.push(rcgen::DnType::CommonName, "Debug Proxy CA");
|
||||||
.distinguished_name
|
params.distinguished_name.push(rcgen::DnType::OrganizationName, "Debug Proxy");
|
||||||
.push(rcgen::DnType::CommonName, "Debug Proxy CA");
|
|
||||||
params
|
|
||||||
.distinguished_name
|
|
||||||
.push(rcgen::DnType::OrganizationName, "Debug Proxy");
|
|
||||||
|
|
||||||
let key = KeyPair::generate()?;
|
let key = KeyPair::generate()?;
|
||||||
let ca_cert = params.self_signed(&key)?;
|
let ca_cert = params.self_signed(&key)?;
|
||||||
let ca_cert_der = ca_cert.der().clone();
|
let ca_cert_der = ca_cert.der().clone();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self { ca_cert, ca_cert_der, ca_key: key, cache: Mutex::new(HashMap::new()) })
|
||||||
ca_cert,
|
|
||||||
ca_cert_der,
|
|
||||||
ca_key: key,
|
|
||||||
cache: Mutex::new(HashMap::new()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ca_pem(&self) -> String {
|
pub fn ca_pem(&self) -> String {
|
||||||
@@ -53,9 +44,7 @@ impl CertificateAuthority {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut params = CertificateParams::new(vec![domain.to_string()])?;
|
let mut params = CertificateParams::new(vec![domain.to_string()])?;
|
||||||
params
|
params.distinguished_name.push(rcgen::DnType::CommonName, domain);
|
||||||
.distinguished_name
|
|
||||||
.push(rcgen::DnType::CommonName, domain);
|
|
||||||
|
|
||||||
let leaf_key = KeyPair::generate()?;
|
let leaf_key = KeyPair::generate()?;
|
||||||
let leaf_cert = params.signed_by(&leaf_key, &self.ca_cert, &self.ca_key)?;
|
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 cert_der = leaf_cert.der().clone();
|
||||||
let key_der = leaf_key.serialize_der();
|
let key_der = leaf_key.serialize_der();
|
||||||
|
|
||||||
let mut config = ServerConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
|
let mut config =
|
||||||
.with_safe_default_protocol_versions()?
|
ServerConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
|
||||||
.with_no_client_auth()
|
.with_safe_default_protocol_versions()?
|
||||||
.with_single_cert(
|
.with_no_client_auth()
|
||||||
vec![cert_der, self.ca_cert_der.clone()],
|
.with_single_cert(
|
||||||
PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_der)),
|
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()];
|
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||||
|
|
||||||
let config = Arc::new(config);
|
let config = Arc::new(config);
|
||||||
self.cache
|
self.cache.lock().unwrap().insert(domain.to_string(), config.clone());
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.insert(domain.to_string(), config.clone());
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::sync::mpsc as std_mpsc;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::mpsc as std_mpsc;
|
||||||
|
|
||||||
use hyper::server::conn::http1;
|
use hyper::server::conn::http1;
|
||||||
use hyper::service::service_fn;
|
use hyper::service::service_fn;
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ mod connection;
|
|||||||
mod request;
|
mod request;
|
||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::AtomicU64;
|
use std::sync::atomic::AtomicU64;
|
||||||
use std::sync::mpsc as std_mpsc;
|
use std::sync::mpsc as std_mpsc;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use cert::CertificateAuthority;
|
use cert::CertificateAuthority;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
@@ -27,7 +27,11 @@ pub enum ProxyEvent {
|
|||||||
http_version: String,
|
http_version: String,
|
||||||
},
|
},
|
||||||
/// A request header sent to the upstream server.
|
/// 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).
|
/// The full request body (buffered before forwarding).
|
||||||
RequestBody { id: u64, body: Vec<u8> },
|
RequestBody { id: u64, body: Vec<u8> },
|
||||||
/// Response headers received from upstream.
|
/// Response headers received from upstream.
|
||||||
@@ -38,7 +42,11 @@ pub enum ProxyEvent {
|
|||||||
elapsed_ms: u64,
|
elapsed_ms: u64,
|
||||||
},
|
},
|
||||||
/// A response header received from the upstream server.
|
/// 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).
|
/// A chunk of the response body was received (emitted per-frame).
|
||||||
ResponseBodyChunk { id: u64, bytes: usize },
|
ResponseBodyChunk { id: u64, bytes: usize },
|
||||||
/// The response body stream has completed.
|
/// The response body stream has completed.
|
||||||
|
|||||||
@@ -63,10 +63,7 @@ fn emit_request_events(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if let Some(body) = body {
|
if let Some(body) = body {
|
||||||
let _ = tx.send(ProxyEvent::RequestBody {
|
let _ = tx.send(ProxyEvent::RequestBody { id, body: body.clone() });
|
||||||
id,
|
|
||||||
body: body.clone(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,22 +120,13 @@ async fn handle_http(
|
|||||||
let http_version = version_str(req.version());
|
let http_version = version_str(req.version());
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
let _ = event_tx.send(ProxyEvent::RequestStart {
|
let _ = event_tx.send(ProxyEvent::RequestStart { id, method, url: uri.clone(), http_version });
|
||||||
id,
|
|
||||||
method,
|
|
||||||
url: uri.clone(),
|
|
||||||
http_version,
|
|
||||||
});
|
|
||||||
|
|
||||||
let client: Client<_, Full<Bytes>> = Client::builder(TokioExecutor::new()).build_http();
|
let client: Client<_, Full<Bytes>> = Client::builder(TokioExecutor::new()).build_http();
|
||||||
|
|
||||||
let (parts, body) = req.into_parts();
|
let (parts, body) = req.into_parts();
|
||||||
let body_bytes = body.collect().await?.to_bytes();
|
let body_bytes = body.collect().await?.to_bytes();
|
||||||
let request_body = if body_bytes.is_empty() {
|
let request_body = if body_bytes.is_empty() { None } else { Some(body_bytes.to_vec()) };
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(body_bytes.to_vec())
|
|
||||||
};
|
|
||||||
emit_request_events(&event_tx, id, &parts.headers, &request_body);
|
emit_request_events(&event_tx, id, &parts.headers, &request_body);
|
||||||
|
|
||||||
let outgoing_req = Request::from_parts(parts, Full::new(body_bytes));
|
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);
|
emit_response_events(&event_tx, id, &resp, &start);
|
||||||
|
|
||||||
let (parts, body) = resp.into_parts();
|
let (parts, body) = resp.into_parts();
|
||||||
Ok(Response::from_parts(
|
Ok(Response::from_parts(parts, measured_incoming(body, id, start, event_tx)))
|
||||||
parts,
|
|
||||||
measured_incoming(body, id, start, event_tx),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = event_tx.send(ProxyEvent::Error {
|
let _ = event_tx.send(ProxyEvent::Error { id, error: e.to_string() });
|
||||||
id,
|
|
||||||
error: e.to_string(),
|
|
||||||
});
|
|
||||||
Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
|
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>,
|
event_tx: std_mpsc::Sender<ProxyEvent>,
|
||||||
ca: Arc<CertificateAuthority>,
|
ca: Arc<CertificateAuthority>,
|
||||||
) -> Result<Response<BoxBody>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<Response<BoxBody>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let authority = req
|
let authority = req.uri().authority().map(|a| a.to_string()).unwrap_or_default();
|
||||||
.uri()
|
|
||||||
.authority()
|
|
||||||
.map(|a| a.to_string())
|
|
||||||
.unwrap_or_default();
|
|
||||||
let (host, port) = parse_host_port(&authority);
|
let (host, port) = parse_host_port(&authority);
|
||||||
|
|
||||||
let server_config = ca.server_config(&host)?;
|
let server_config = ca.server_config(&host)?;
|
||||||
@@ -189,10 +167,7 @@ async fn handle_connect(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let tls_stream = match acceptor
|
let tls_stream = match acceptor.accept(hyper_util::rt::TokioIo::new(upgraded)).await {
|
||||||
.accept(hyper_util::rt::TokioIo::new(upgraded))
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("TLS accept failed for {host}: {e}");
|
eprintln!("TLS accept failed for {host}: {e}");
|
||||||
@@ -203,10 +178,7 @@ async fn handle_connect(
|
|||||||
let tx = event_tx.clone();
|
let tx = event_tx.clone();
|
||||||
let host_for_requests = host.clone();
|
let host_for_requests = host.clone();
|
||||||
let mut builder = auto::Builder::new(TokioExecutor::new());
|
let mut builder = auto::Builder::new(TokioExecutor::new());
|
||||||
builder
|
builder.http1().preserve_header_case(true).title_case_headers(true);
|
||||||
.http1()
|
|
||||||
.preserve_header_case(true)
|
|
||||||
.title_case_headers(true);
|
|
||||||
if let Err(e) = builder
|
if let Err(e) = builder
|
||||||
.serve_connection_with_upgrades(
|
.serve_connection_with_upgrades(
|
||||||
hyper_util::rt::TokioIo::new(tls_stream),
|
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 id = REQUEST_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
let method = req.method().to_string();
|
let method = req.method().to_string();
|
||||||
let http_version = version_str(req.version());
|
let http_version = version_str(req.version());
|
||||||
let path = req
|
let path = req.uri().path_and_query().map(|pq| pq.to_string()).unwrap_or_else(|| "/".into());
|
||||||
.uri()
|
|
||||||
.path_and_query()
|
|
||||||
.map(|pq| pq.to_string())
|
|
||||||
.unwrap_or_else(|| "/".into());
|
|
||||||
let uri_str = format!("https://{host}{path}");
|
let uri_str = format!("https://{host}{path}");
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
let _ = event_tx.send(ProxyEvent::RequestStart {
|
let _ =
|
||||||
id,
|
event_tx.send(ProxyEvent::RequestStart { id, method, url: uri_str.clone(), http_version });
|
||||||
method,
|
|
||||||
url: uri_str.clone(),
|
|
||||||
http_version,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connect to upstream with TLS
|
// Connect to upstream with TLS
|
||||||
let tcp_stream = TcpStream::connect(target_addr).await?;
|
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 server_name = ServerName::try_from(host.to_string())?;
|
||||||
let tls_stream = connector.connect(server_name, tcp_stream).await?;
|
let tls_stream = connector.connect(server_name, tcp_stream).await?;
|
||||||
|
|
||||||
let negotiated_h2 = tls_stream
|
let negotiated_h2 = tls_stream.get_ref().1.alpn_protocol().map_or(false, |p| p == b"h2");
|
||||||
.get_ref()
|
|
||||||
.1
|
|
||||||
.alpn_protocol()
|
|
||||||
.map_or(false, |p| p == b"h2");
|
|
||||||
|
|
||||||
let io = hyper_util::rt::TokioIo::new(tls_stream);
|
let io = hyper_util::rt::TokioIo::new(tls_stream);
|
||||||
|
|
||||||
let mut sender = if negotiated_h2 {
|
let mut sender = if negotiated_h2 {
|
||||||
let (sender, conn) = hyper::client::conn::http2::Builder::new(TokioExecutor::new())
|
let (sender, conn) =
|
||||||
.handshake(io)
|
hyper::client::conn::http2::Builder::new(TokioExecutor::new()).handshake(io).await?;
|
||||||
.await?;
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Err(e) = conn.await {
|
if let Err(e) = conn.await {
|
||||||
eprintln!("Upstream h2 connection error: {e}");
|
eprintln!("Upstream h2 connection error: {e}");
|
||||||
@@ -340,11 +299,7 @@ async fn forward_https(
|
|||||||
// Capture request metadata
|
// Capture request metadata
|
||||||
let (mut parts, body) = req.into_parts();
|
let (mut parts, body) = req.into_parts();
|
||||||
let body_bytes = body.collect().await?.to_bytes();
|
let body_bytes = body.collect().await?.to_bytes();
|
||||||
let request_body = if body_bytes.is_empty() {
|
let request_body = if body_bytes.is_empty() { None } else { Some(body_bytes.to_vec()) };
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(body_bytes.to_vec())
|
|
||||||
};
|
|
||||||
emit_request_events(&event_tx, id, &parts.headers, &request_body);
|
emit_request_events(&event_tx, id, &parts.headers, &request_body);
|
||||||
|
|
||||||
if negotiated_h2 {
|
if negotiated_h2 {
|
||||||
@@ -365,16 +320,10 @@ async fn forward_https(
|
|||||||
emit_response_events(&event_tx, id, &resp, &start);
|
emit_response_events(&event_tx, id, &resp, &start);
|
||||||
|
|
||||||
let (parts, body) = resp.into_parts();
|
let (parts, body) = resp.into_parts();
|
||||||
Ok(Response::from_parts(
|
Ok(Response::from_parts(parts, measured_incoming(body, id, start, event_tx)))
|
||||||
parts,
|
|
||||||
measured_incoming(body, id, start, event_tx),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = event_tx.send(ProxyEvent::Error {
|
let _ = event_tx.send(ProxyEvent::Error { id, error: e.to_string() });
|
||||||
id,
|
|
||||||
error: e.to_string(),
|
|
||||||
});
|
|
||||||
Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
|
Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod escape;
|
pub mod escape;
|
||||||
pub mod format_json;
|
pub mod format_json;
|
||||||
pub mod strip_json_comments;
|
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
|
pub mod strip_json_comments;
|
||||||
pub mod wasm;
|
pub mod wasm;
|
||||||
|
|
||||||
pub use parser::*;
|
pub use parser::*;
|
||||||
|
|||||||
@@ -113,11 +113,8 @@ pub fn strip_json_comments(text: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove lines that are now empty (were comment-only lines)
|
// Remove lines that are now empty (were comment-only lines)
|
||||||
let result = result
|
let result =
|
||||||
.lines()
|
result.lines().filter(|line| !line.trim().is_empty()).collect::<Vec<&str>>().join("\n");
|
||||||
.filter(|line| !line.trim().is_empty())
|
|
||||||
.collect::<Vec<&str>>()
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
// Remove trailing commas before } or ]
|
// Remove trailing commas before } or ]
|
||||||
strip_trailing_commas(&result)
|
strip_trailing_commas(&result)
|
||||||
@@ -192,10 +189,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_trailing_line_comment() {
|
fn test_trailing_line_comment() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
strip_json_comments(r#"{
|
strip_json_comments(
|
||||||
|
r#"{
|
||||||
"foo": "bar", // this is a comment
|
"foo": "bar", // this is a comment
|
||||||
"baz": 123
|
"baz": 123
|
||||||
}"#),
|
}"#
|
||||||
|
),
|
||||||
r#"{
|
r#"{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
"baz": 123
|
"baz": 123
|
||||||
@@ -206,10 +205,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_whole_line_comment() {
|
fn test_whole_line_comment() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
strip_json_comments(r#"{
|
strip_json_comments(
|
||||||
|
r#"{
|
||||||
// this is a comment
|
// this is a comment
|
||||||
"foo": "bar"
|
"foo": "bar"
|
||||||
}"#),
|
}"#
|
||||||
|
),
|
||||||
r#"{
|
r#"{
|
||||||
"foo": "bar"
|
"foo": "bar"
|
||||||
}"#
|
}"#
|
||||||
@@ -219,9 +220,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_inline_block_comment() {
|
fn test_inline_block_comment() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
strip_json_comments(r#"{
|
strip_json_comments(
|
||||||
|
r#"{
|
||||||
"foo": /* a comment */ "bar"
|
"foo": /* a comment */ "bar"
|
||||||
}"#),
|
}"#
|
||||||
|
),
|
||||||
r#"{
|
r#"{
|
||||||
"foo": "bar"
|
"foo": "bar"
|
||||||
}"#
|
}"#
|
||||||
@@ -231,10 +234,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_whole_line_block_comment() {
|
fn test_whole_line_block_comment() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
strip_json_comments(r#"{
|
strip_json_comments(
|
||||||
|
r#"{
|
||||||
/* a comment */
|
/* a comment */
|
||||||
"foo": "bar"
|
"foo": "bar"
|
||||||
}"#),
|
}"#
|
||||||
|
),
|
||||||
r#"{
|
r#"{
|
||||||
"foo": "bar"
|
"foo": "bar"
|
||||||
}"#
|
}"#
|
||||||
@@ -244,12 +249,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_multiline_block_comment() {
|
fn test_multiline_block_comment() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
strip_json_comments(r#"{
|
strip_json_comments(
|
||||||
|
r#"{
|
||||||
/**
|
/**
|
||||||
* Hello World!
|
* Hello World!
|
||||||
*/
|
*/
|
||||||
"foo": "bar"
|
"foo": "bar"
|
||||||
}"#),
|
}"#
|
||||||
|
),
|
||||||
r#"{
|
r#"{
|
||||||
"foo": "bar"
|
"foo": "bar"
|
||||||
}"#
|
}"#
|
||||||
@@ -276,12 +283,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_multiple_comments() {
|
fn test_multiple_comments() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
strip_json_comments(r#"{
|
strip_json_comments(
|
||||||
|
r#"{
|
||||||
// first comment
|
// first comment
|
||||||
"foo": "bar", // trailing
|
"foo": "bar", // trailing
|
||||||
/* block */
|
/* block */
|
||||||
"baz": 123
|
"baz": 123
|
||||||
}"#),
|
}"#
|
||||||
|
),
|
||||||
r#"{
|
r#"{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
"baz": 123
|
"baz": 123
|
||||||
@@ -292,10 +301,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_trailing_comma_after_comment_removed() {
|
fn test_trailing_comma_after_comment_removed() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
strip_json_comments(r#"{
|
strip_json_comments(
|
||||||
|
r#"{
|
||||||
"a": "aaa",
|
"a": "aaa",
|
||||||
// "b": "bbb"
|
// "b": "bbb"
|
||||||
}"#),
|
}"#
|
||||||
|
),
|
||||||
r#"{
|
r#"{
|
||||||
"a": "aaa"
|
"a": "aaa"
|
||||||
}"#
|
}"#
|
||||||
@@ -304,10 +315,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_trailing_comma_in_array() {
|
fn test_trailing_comma_in_array() {
|
||||||
assert_eq!(
|
assert_eq!(strip_json_comments(r#"[1, 2, /* 3 */]"#), r#"[1, 2]"#);
|
||||||
strip_json_comments(r#"[1, 2, /* 3 */]"#),
|
|
||||||
r#"[1, 2]"#
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ use log::info;
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use yaak_http::path_placeholders::apply_path_placeholders;
|
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_models::render::make_vars_hashmap;
|
||||||
use yaak_templates::{RenderOptions, TemplateCallback, parse_and_render, render_json_value_raw};
|
use yaak_templates::{RenderOptions, TemplateCallback, parse_and_render, render_json_value_raw};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user