mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-24 17:58:27 +02:00
refactor send callers to shared plugin runtime helper
This commit is contained in:
@@ -5,10 +5,15 @@ edition = "2024"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1"
|
||||
log = { workspace = true }
|
||||
md5 = "0.8.0"
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync", "rt"] }
|
||||
yaak-http = { workspace = true }
|
||||
yaak-crypto = { workspace = true }
|
||||
yaak-models = { workspace = true }
|
||||
yaak-plugins = { workspace = true }
|
||||
yaak-templates = { workspace = true }
|
||||
yaak-tls = { workspace = true }
|
||||
|
||||
@@ -1,16 +1,37 @@
|
||||
use crate::render::render_http_request;
|
||||
use async_trait::async_trait;
|
||||
use log::warn;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc;
|
||||
use yaak_http::sender::{HttpResponseEvent as SenderHttpResponseEvent, HttpSender, ReqwestSender};
|
||||
use yaak_http::types::{SendableBody, SendableHttpRequest, SendableHttpRequestOptions};
|
||||
use tokio::sync::watch;
|
||||
use yaak_crypto::manager::EncryptionManager;
|
||||
use yaak_http::client::{
|
||||
HttpConnectionOptions, HttpConnectionProxySetting, HttpConnectionProxySettingAuth,
|
||||
};
|
||||
use yaak_http::cookies::CookieStore;
|
||||
use yaak_http::manager::HttpConnectionManager;
|
||||
use yaak_http::sender::{HttpResponseEvent as SenderHttpResponseEvent, ReqwestSender};
|
||||
use yaak_http::transaction::HttpTransaction;
|
||||
use yaak_http::types::{
|
||||
SendableBody, SendableHttpRequest, SendableHttpRequestOptions, append_query_params,
|
||||
};
|
||||
use yaak_models::blob_manager::BlobManager;
|
||||
use yaak_models::models::{HttpResponse, HttpResponseEvent, HttpResponseHeader, HttpResponseState};
|
||||
use yaak_models::models::{
|
||||
ClientCertificate, CookieJar, DnsOverride, Environment, HttpRequest, HttpResponse,
|
||||
HttpResponseEvent, HttpResponseHeader, HttpResponseState, ProxySetting, ProxySettingAuth,
|
||||
};
|
||||
use yaak_models::query_manager::QueryManager;
|
||||
use yaak_models::util::UpdateSource;
|
||||
use yaak_plugins::events::{
|
||||
CallHttpAuthenticationRequest, HttpHeader, PluginContext, RenderPurpose,
|
||||
};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
use yaak_templates::{RenderOptions, TemplateCallback};
|
||||
use yaak_tls::find_client_certificate;
|
||||
|
||||
const HTTP_EVENT_CHANNEL_CAPACITY: usize = 100;
|
||||
|
||||
@@ -19,15 +40,27 @@ pub enum SendHttpRequestError {
|
||||
#[error("Failed to load request: {0}")]
|
||||
LoadRequest(#[source] yaak_models::error::Error),
|
||||
|
||||
#[error("Failed to load workspace: {0}")]
|
||||
LoadWorkspace(#[source] yaak_models::error::Error),
|
||||
|
||||
#[error("Failed to resolve environments: {0}")]
|
||||
ResolveEnvironments(#[source] yaak_models::error::Error),
|
||||
|
||||
#[error("Failed to resolve inherited request settings: {0}")]
|
||||
ResolveRequestInheritance(#[source] yaak_models::error::Error),
|
||||
|
||||
#[error("Failed to load cookie jar: {0}")]
|
||||
LoadCookieJar(#[source] yaak_models::error::Error),
|
||||
|
||||
#[error("Failed to persist cookie jar: {0}")]
|
||||
PersistCookieJar(#[source] yaak_models::error::Error),
|
||||
|
||||
#[error("Failed to render request templates: {0}")]
|
||||
RenderRequest(#[source] yaak_templates::error::Error),
|
||||
|
||||
#[error("Failed to prepare request before send: {0}")]
|
||||
PrepareSendableRequest(String),
|
||||
|
||||
#[error("Failed to persist response metadata: {0}")]
|
||||
PersistResponse(#[source] yaak_models::error::Error),
|
||||
|
||||
@@ -60,51 +93,370 @@ pub enum SendHttpRequestError {
|
||||
|
||||
pub type Result<T> = std::result::Result<T, SendHttpRequestError>;
|
||||
|
||||
#[async_trait]
|
||||
pub trait PrepareSendableRequest: Send + Sync {
|
||||
async fn prepare_sendable_request(
|
||||
&self,
|
||||
rendered_request: &HttpRequest,
|
||||
auth_context_id: &str,
|
||||
sendable_request: &mut SendableHttpRequest,
|
||||
) -> std::result::Result<(), String>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait SendRequestExecutor: Send + Sync {
|
||||
async fn send(
|
||||
&self,
|
||||
sendable_request: SendableHttpRequest,
|
||||
event_tx: mpsc::Sender<SenderHttpResponseEvent>,
|
||||
cookie_store: Option<CookieStore>,
|
||||
) -> yaak_http::error::Result<yaak_http::sender::HttpResponse>;
|
||||
}
|
||||
|
||||
struct DefaultSendRequestExecutor;
|
||||
|
||||
#[async_trait]
|
||||
impl SendRequestExecutor for DefaultSendRequestExecutor {
|
||||
async fn send(
|
||||
&self,
|
||||
sendable_request: SendableHttpRequest,
|
||||
event_tx: mpsc::Sender<SenderHttpResponseEvent>,
|
||||
cookie_store: Option<CookieStore>,
|
||||
) -> yaak_http::error::Result<yaak_http::sender::HttpResponse> {
|
||||
let sender = ReqwestSender::new()?;
|
||||
let transaction = match cookie_store {
|
||||
Some(store) => HttpTransaction::with_cookie_store(sender, store),
|
||||
None => HttpTransaction::new(sender),
|
||||
};
|
||||
let (_cancel_tx, cancel_rx) = watch::channel(false);
|
||||
transaction.execute_with_cancellation(sendable_request, cancel_rx, event_tx).await
|
||||
}
|
||||
}
|
||||
|
||||
struct PluginPrepareSendableRequest {
|
||||
plugin_manager: Arc<PluginManager>,
|
||||
plugin_context: PluginContext,
|
||||
cancelled_rx: Option<watch::Receiver<bool>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl PrepareSendableRequest for PluginPrepareSendableRequest {
|
||||
async fn prepare_sendable_request(
|
||||
&self,
|
||||
rendered_request: &HttpRequest,
|
||||
auth_context_id: &str,
|
||||
sendable_request: &mut SendableHttpRequest,
|
||||
) -> std::result::Result<(), String> {
|
||||
if let Some(cancelled_rx) = &self.cancelled_rx {
|
||||
let mut cancelled_rx = cancelled_rx.clone();
|
||||
tokio::select! {
|
||||
result = apply_plugin_authentication(
|
||||
sendable_request,
|
||||
rendered_request,
|
||||
auth_context_id,
|
||||
&self.plugin_manager,
|
||||
&self.plugin_context,
|
||||
) => result,
|
||||
_ = cancelled_rx.changed() => Err("Request canceled".to_string()),
|
||||
}
|
||||
} else {
|
||||
apply_plugin_authentication(
|
||||
sendable_request,
|
||||
rendered_request,
|
||||
auth_context_id,
|
||||
&self.plugin_manager,
|
||||
&self.plugin_context,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectionManagerSendRequestExecutor<'a> {
|
||||
connection_manager: &'a HttpConnectionManager,
|
||||
plugin_context_id: String,
|
||||
query_manager: QueryManager,
|
||||
workspace_id: String,
|
||||
cancelled_rx: Option<watch::Receiver<bool>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SendRequestExecutor for ConnectionManagerSendRequestExecutor<'_> {
|
||||
async fn send(
|
||||
&self,
|
||||
sendable_request: SendableHttpRequest,
|
||||
event_tx: mpsc::Sender<SenderHttpResponseEvent>,
|
||||
cookie_store: Option<CookieStore>,
|
||||
) -> yaak_http::error::Result<yaak_http::sender::HttpResponse> {
|
||||
let runtime_config =
|
||||
resolve_http_send_runtime_config(&self.query_manager, &self.workspace_id)
|
||||
.map_err(|e| yaak_http::error::Error::RequestError(e.to_string()))?;
|
||||
let client_certificate =
|
||||
find_client_certificate(&sendable_request.url, &runtime_config.client_certificates);
|
||||
let cached_client = self
|
||||
.connection_manager
|
||||
.get_client(&HttpConnectionOptions {
|
||||
id: self.plugin_context_id.clone(),
|
||||
validate_certificates: runtime_config.validate_certificates,
|
||||
proxy: runtime_config.proxy,
|
||||
client_certificate,
|
||||
dns_overrides: runtime_config.dns_overrides,
|
||||
})
|
||||
.await?;
|
||||
|
||||
cached_client.resolver.set_event_sender(Some(event_tx.clone())).await;
|
||||
|
||||
let sender = ReqwestSender::with_client(cached_client.client);
|
||||
let transaction = match cookie_store {
|
||||
Some(cs) => HttpTransaction::with_cookie_store(sender, cs),
|
||||
None => HttpTransaction::new(sender),
|
||||
};
|
||||
|
||||
let result = if let Some(cancelled_rx) = self.cancelled_rx.clone() {
|
||||
transaction.execute_with_cancellation(sendable_request, cancelled_rx, event_tx).await
|
||||
} else {
|
||||
let (_cancel_tx, cancel_rx) = watch::channel(false);
|
||||
transaction.execute_with_cancellation(sendable_request, cancel_rx, event_tx).await
|
||||
};
|
||||
cached_client.resolver.set_event_sender(None).await;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SendHttpRequestByIdParams<'a, T: TemplateCallback> {
|
||||
pub query_manager: &'a QueryManager,
|
||||
pub blob_manager: &'a BlobManager,
|
||||
pub request_id: &'a str,
|
||||
pub environment_id: Option<&'a str>,
|
||||
pub template_callback: &'a T,
|
||||
pub send_options: SendableHttpRequestOptions,
|
||||
pub update_source: UpdateSource,
|
||||
pub cookie_jar_id: Option<String>,
|
||||
pub cookie_jar_update_source: UpdateSource,
|
||||
pub response_dir: &'a Path,
|
||||
pub persist_events: bool,
|
||||
pub emit_events_to: Option<mpsc::Sender<SenderHttpResponseEvent>>,
|
||||
pub prepare_sendable_request: Option<&'a dyn PrepareSendableRequest>,
|
||||
pub executor: Option<&'a dyn SendRequestExecutor>,
|
||||
}
|
||||
|
||||
pub struct SendHttpRequestParams<'a, T: TemplateCallback> {
|
||||
pub query_manager: &'a QueryManager,
|
||||
pub blob_manager: &'a BlobManager,
|
||||
pub request: HttpRequest,
|
||||
pub environment_id: Option<&'a str>,
|
||||
pub template_callback: &'a T,
|
||||
pub send_options: Option<SendableHttpRequestOptions>,
|
||||
pub update_source: UpdateSource,
|
||||
pub cookie_jar_id: Option<String>,
|
||||
pub cookie_jar_update_source: UpdateSource,
|
||||
pub response_dir: &'a Path,
|
||||
pub persist_events: bool,
|
||||
pub emit_events_to: Option<mpsc::Sender<SenderHttpResponseEvent>>,
|
||||
pub auth_context_id: Option<String>,
|
||||
pub existing_response: Option<HttpResponse>,
|
||||
pub prepare_sendable_request: Option<&'a dyn PrepareSendableRequest>,
|
||||
pub executor: Option<&'a dyn SendRequestExecutor>,
|
||||
}
|
||||
|
||||
pub struct SendHttpRequestWithPluginsParams<'a> {
|
||||
pub query_manager: &'a QueryManager,
|
||||
pub blob_manager: &'a BlobManager,
|
||||
pub request: HttpRequest,
|
||||
pub environment_id: Option<&'a str>,
|
||||
pub update_source: UpdateSource,
|
||||
pub cookie_jar_id: Option<String>,
|
||||
pub cookie_jar_update_source: UpdateSource,
|
||||
pub response_dir: &'a Path,
|
||||
pub persist_events: bool,
|
||||
pub emit_events_to: Option<mpsc::Sender<SenderHttpResponseEvent>>,
|
||||
pub existing_response: Option<HttpResponse>,
|
||||
pub plugin_manager: Arc<PluginManager>,
|
||||
pub encryption_manager: Arc<EncryptionManager>,
|
||||
pub plugin_context: &'a PluginContext,
|
||||
pub cancelled_rx: Option<watch::Receiver<bool>>,
|
||||
pub connection_manager: Option<&'a HttpConnectionManager>,
|
||||
}
|
||||
|
||||
pub struct SendHttpRequestByIdWithPluginsParams<'a> {
|
||||
pub query_manager: &'a QueryManager,
|
||||
pub blob_manager: &'a BlobManager,
|
||||
pub request_id: &'a str,
|
||||
pub environment_id: Option<&'a str>,
|
||||
pub update_source: UpdateSource,
|
||||
pub cookie_jar_id: Option<String>,
|
||||
pub cookie_jar_update_source: UpdateSource,
|
||||
pub response_dir: &'a Path,
|
||||
pub persist_events: bool,
|
||||
pub emit_events_to: Option<mpsc::Sender<SenderHttpResponseEvent>>,
|
||||
pub plugin_manager: Arc<PluginManager>,
|
||||
pub encryption_manager: Arc<EncryptionManager>,
|
||||
pub plugin_context: &'a PluginContext,
|
||||
pub cancelled_rx: Option<watch::Receiver<bool>>,
|
||||
pub connection_manager: Option<&'a HttpConnectionManager>,
|
||||
}
|
||||
|
||||
pub struct SendHttpRequestResult {
|
||||
pub rendered_request: yaak_models::models::HttpRequest,
|
||||
pub rendered_request: HttpRequest,
|
||||
pub response: HttpResponse,
|
||||
pub response_body: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct HttpSendRuntimeConfig {
|
||||
pub send_options: SendableHttpRequestOptions,
|
||||
pub validate_certificates: bool,
|
||||
pub proxy: HttpConnectionProxySetting,
|
||||
pub dns_overrides: Vec<DnsOverride>,
|
||||
pub client_certificates: Vec<ClientCertificate>,
|
||||
}
|
||||
|
||||
pub fn resolve_http_send_runtime_config(
|
||||
query_manager: &QueryManager,
|
||||
workspace_id: &str,
|
||||
) -> Result<HttpSendRuntimeConfig> {
|
||||
let db = query_manager.connect();
|
||||
let workspace = db.get_workspace(workspace_id).map_err(SendHttpRequestError::LoadWorkspace)?;
|
||||
let settings = db.get_settings();
|
||||
|
||||
Ok(HttpSendRuntimeConfig {
|
||||
send_options: SendableHttpRequestOptions {
|
||||
follow_redirects: workspace.setting_follow_redirects,
|
||||
timeout: if workspace.setting_request_timeout > 0 {
|
||||
Some(std::time::Duration::from_millis(
|
||||
workspace.setting_request_timeout.unsigned_abs() as u64,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
},
|
||||
validate_certificates: workspace.setting_validate_certificates,
|
||||
proxy: proxy_setting_from_settings(settings.proxy),
|
||||
dns_overrides: workspace.setting_dns_overrides,
|
||||
client_certificates: settings.client_certificates,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_http_request_by_id_with_plugins(
|
||||
params: SendHttpRequestByIdWithPluginsParams<'_>,
|
||||
) -> Result<SendHttpRequestResult> {
|
||||
let request = params
|
||||
.query_manager
|
||||
.connect()
|
||||
.get_http_request(params.request_id)
|
||||
.map_err(SendHttpRequestError::LoadRequest)?;
|
||||
|
||||
send_http_request_with_plugins(SendHttpRequestWithPluginsParams {
|
||||
query_manager: params.query_manager,
|
||||
blob_manager: params.blob_manager,
|
||||
request,
|
||||
environment_id: params.environment_id,
|
||||
update_source: params.update_source,
|
||||
cookie_jar_id: params.cookie_jar_id,
|
||||
cookie_jar_update_source: params.cookie_jar_update_source,
|
||||
response_dir: params.response_dir,
|
||||
persist_events: params.persist_events,
|
||||
emit_events_to: params.emit_events_to,
|
||||
existing_response: None,
|
||||
plugin_manager: params.plugin_manager,
|
||||
encryption_manager: params.encryption_manager,
|
||||
plugin_context: params.plugin_context,
|
||||
cancelled_rx: params.cancelled_rx,
|
||||
connection_manager: params.connection_manager,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_http_request_with_plugins(
|
||||
params: SendHttpRequestWithPluginsParams<'_>,
|
||||
) -> Result<SendHttpRequestResult> {
|
||||
let template_callback = PluginTemplateCallback::new(
|
||||
params.plugin_manager.clone(),
|
||||
params.encryption_manager.clone(),
|
||||
params.plugin_context,
|
||||
RenderPurpose::Send,
|
||||
);
|
||||
let auth_hook = PluginPrepareSendableRequest {
|
||||
plugin_manager: params.plugin_manager,
|
||||
plugin_context: params.plugin_context.clone(),
|
||||
cancelled_rx: params.cancelled_rx.clone(),
|
||||
};
|
||||
let executor =
|
||||
params.connection_manager.map(|connection_manager| ConnectionManagerSendRequestExecutor {
|
||||
connection_manager,
|
||||
plugin_context_id: params.plugin_context.id.clone(),
|
||||
query_manager: params.query_manager.clone(),
|
||||
workspace_id: params.request.workspace_id.clone(),
|
||||
cancelled_rx: params.cancelled_rx.clone(),
|
||||
});
|
||||
|
||||
send_http_request(SendHttpRequestParams {
|
||||
query_manager: params.query_manager,
|
||||
blob_manager: params.blob_manager,
|
||||
request: params.request,
|
||||
environment_id: params.environment_id,
|
||||
template_callback: &template_callback,
|
||||
send_options: None,
|
||||
update_source: params.update_source,
|
||||
cookie_jar_id: params.cookie_jar_id,
|
||||
cookie_jar_update_source: params.cookie_jar_update_source,
|
||||
response_dir: params.response_dir,
|
||||
persist_events: params.persist_events,
|
||||
emit_events_to: params.emit_events_to,
|
||||
auth_context_id: None,
|
||||
existing_response: params.existing_response,
|
||||
prepare_sendable_request: Some(&auth_hook),
|
||||
executor: executor.as_ref().map(|e| e as &dyn SendRequestExecutor),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_http_request_by_id<T: TemplateCallback>(
|
||||
params: SendHttpRequestByIdParams<'_, T>,
|
||||
) -> Result<SendHttpRequestResult> {
|
||||
let db = params.query_manager.connect();
|
||||
let request =
|
||||
db.get_http_request(params.request_id).map_err(SendHttpRequestError::LoadRequest)?;
|
||||
let environment_chain = db
|
||||
.resolve_environments(
|
||||
&request.workspace_id,
|
||||
request.folder_id.as_deref(),
|
||||
params.environment_id,
|
||||
)
|
||||
.map_err(SendHttpRequestError::ResolveEnvironments)?;
|
||||
let request = params
|
||||
.query_manager
|
||||
.connect()
|
||||
.get_http_request(params.request_id)
|
||||
.map_err(SendHttpRequestError::LoadRequest)?;
|
||||
let (request, auth_context_id) = resolve_inherited_request(params.query_manager, &request)?;
|
||||
|
||||
let (authentication_type, authentication, _auth_context_id) = db
|
||||
.resolve_auth_for_http_request(&request)
|
||||
.map_err(SendHttpRequestError::ResolveRequestInheritance)?;
|
||||
let resolved_headers = db
|
||||
.resolve_headers_for_http_request(&request)
|
||||
.map_err(SendHttpRequestError::ResolveRequestInheritance)?;
|
||||
drop(db);
|
||||
send_http_request(SendHttpRequestParams {
|
||||
query_manager: params.query_manager,
|
||||
blob_manager: params.blob_manager,
|
||||
request,
|
||||
environment_id: params.environment_id,
|
||||
template_callback: params.template_callback,
|
||||
send_options: None,
|
||||
update_source: params.update_source,
|
||||
cookie_jar_id: params.cookie_jar_id,
|
||||
cookie_jar_update_source: params.cookie_jar_update_source,
|
||||
response_dir: params.response_dir,
|
||||
persist_events: params.persist_events,
|
||||
emit_events_to: params.emit_events_to,
|
||||
existing_response: None,
|
||||
prepare_sendable_request: params.prepare_sendable_request,
|
||||
executor: params.executor,
|
||||
auth_context_id: Some(auth_context_id),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
let mut resolved_request = request.clone();
|
||||
resolved_request.authentication_type = authentication_type;
|
||||
resolved_request.authentication = authentication;
|
||||
resolved_request.headers = resolved_headers;
|
||||
pub async fn send_http_request<T: TemplateCallback>(
|
||||
params: SendHttpRequestParams<'_, T>,
|
||||
) -> Result<SendHttpRequestResult> {
|
||||
let environment_chain =
|
||||
resolve_environment_chain(params.query_manager, ¶ms.request, params.environment_id)?;
|
||||
let (resolved_request, auth_context_id) =
|
||||
if let Some(auth_context_id) = params.auth_context_id.clone() {
|
||||
(params.request.clone(), auth_context_id)
|
||||
} else {
|
||||
resolve_inherited_request(params.query_manager, ¶ms.request)?
|
||||
};
|
||||
let runtime_config =
|
||||
resolve_http_send_runtime_config(params.query_manager, ¶ms.request.workspace_id)?;
|
||||
let send_options = params.send_options.unwrap_or(runtime_config.send_options);
|
||||
let mut cookie_jar = load_cookie_jar(params.query_manager, params.cookie_jar_id.as_deref())?;
|
||||
let cookie_store =
|
||||
cookie_jar.as_ref().map(|jar| CookieStore::from_cookies(jar.cookies.clone()));
|
||||
|
||||
let rendered_request = render_http_request(
|
||||
&resolved_request,
|
||||
@@ -115,41 +467,52 @@ pub async fn send_http_request_by_id<T: TemplateCallback>(
|
||||
.await
|
||||
.map_err(SendHttpRequestError::RenderRequest)?;
|
||||
|
||||
let sendable_request =
|
||||
SendableHttpRequest::from_http_request(&rendered_request, params.send_options)
|
||||
let mut sendable_request =
|
||||
SendableHttpRequest::from_http_request(&rendered_request, send_options)
|
||||
.await
|
||||
.map_err(SendHttpRequestError::BuildSendableRequest)?;
|
||||
let request_content_length = sendable_body_length(sendable_request.body.as_ref());
|
||||
|
||||
let mut response = params
|
||||
if let Some(hook) = params.prepare_sendable_request {
|
||||
hook.prepare_sendable_request(&rendered_request, &auth_context_id, &mut sendable_request)
|
||||
.await
|
||||
.map_err(SendHttpRequestError::PrepareSendableRequest)?;
|
||||
}
|
||||
|
||||
let request_content_length = sendable_body_length(sendable_request.body.as_ref());
|
||||
let mut response = params.existing_response.unwrap_or_default();
|
||||
response.request_id = params.request.id.clone();
|
||||
response.workspace_id = params.request.workspace_id.clone();
|
||||
response.request_content_length = request_content_length;
|
||||
response.request_headers = sendable_request
|
||||
.headers
|
||||
.iter()
|
||||
.map(|(name, value)| HttpResponseHeader { name: name.clone(), value: value.clone() })
|
||||
.collect();
|
||||
response.url = sendable_request.url.clone();
|
||||
response.state = HttpResponseState::Initialized;
|
||||
response.error = None;
|
||||
response.content_length = None;
|
||||
response.content_length_compressed = None;
|
||||
response.body_path = None;
|
||||
response.status = 0;
|
||||
response.status_reason = None;
|
||||
response.headers = Vec::new();
|
||||
response.remote_addr = None;
|
||||
response.version = None;
|
||||
response.elapsed = 0;
|
||||
response.elapsed_headers = 0;
|
||||
response.elapsed_dns = 0;
|
||||
response = params
|
||||
.query_manager
|
||||
.connect()
|
||||
.upsert_http_response(
|
||||
&HttpResponse {
|
||||
request_id: request.id.clone(),
|
||||
workspace_id: request.workspace_id.clone(),
|
||||
request_content_length,
|
||||
request_headers: sendable_request
|
||||
.headers
|
||||
.iter()
|
||||
.map(|(name, value)| HttpResponseHeader {
|
||||
name: name.clone(),
|
||||
value: value.clone(),
|
||||
})
|
||||
.collect(),
|
||||
url: sendable_request.url.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
¶ms.update_source,
|
||||
params.blob_manager,
|
||||
)
|
||||
.upsert_http_response(&response, ¶ms.update_source, params.blob_manager)
|
||||
.map_err(SendHttpRequestError::PersistResponse)?;
|
||||
|
||||
let (event_tx, mut event_rx) =
|
||||
mpsc::channel::<SenderHttpResponseEvent>(HTTP_EVENT_CHANNEL_CAPACITY);
|
||||
let event_query_manager = params.query_manager.clone();
|
||||
let event_response_id = response.id.clone();
|
||||
let event_workspace_id = request.workspace_id.clone();
|
||||
let event_workspace_id = params.request.workspace_id.clone();
|
||||
let event_update_source = params.update_source.clone();
|
||||
let emit_events_to = params.emit_events_to.clone();
|
||||
let persist_events = params.persist_events;
|
||||
@@ -175,34 +538,33 @@ pub async fn send_http_request_by_id<T: TemplateCallback>(
|
||||
}
|
||||
});
|
||||
|
||||
let sender = ReqwestSender::new().map_err(SendHttpRequestError::CreateHttpClient)?;
|
||||
let default_executor = DefaultSendRequestExecutor;
|
||||
let executor = params.executor.unwrap_or(&default_executor);
|
||||
let started_at = Instant::now();
|
||||
let request_started_url = sendable_request.url.clone();
|
||||
|
||||
let http_response = match sender.send(sendable_request, event_tx).await {
|
||||
let http_response = match executor.send(sendable_request, event_tx, cookie_store.clone()).await
|
||||
{
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
let _ = params
|
||||
.query_manager
|
||||
.connect()
|
||||
.upsert_http_response(
|
||||
&HttpResponse {
|
||||
state: HttpResponseState::Closed,
|
||||
elapsed: duration_to_i32(started_at.elapsed()),
|
||||
elapsed_headers: duration_to_i32(started_at.elapsed()),
|
||||
error: Some(err.to_string()),
|
||||
url: request_started_url,
|
||||
..response
|
||||
},
|
||||
¶ms.update_source,
|
||||
params.blob_manager,
|
||||
)
|
||||
.map_err(SendHttpRequestError::PersistResponse)?;
|
||||
|
||||
persist_cookie_jar(
|
||||
params.query_manager,
|
||||
cookie_jar.as_mut(),
|
||||
cookie_store.as_ref(),
|
||||
¶ms.cookie_jar_update_source,
|
||||
)?;
|
||||
let _ = persist_response_error(
|
||||
params.query_manager,
|
||||
params.blob_manager,
|
||||
¶ms.update_source,
|
||||
&response,
|
||||
started_at,
|
||||
err.to_string(),
|
||||
request_started_url,
|
||||
);
|
||||
if let Err(join_err) = event_handle.await {
|
||||
warn!("Failed to join response event task: {}", join_err);
|
||||
}
|
||||
|
||||
return Err(SendHttpRequestError::SendRequest(err));
|
||||
}
|
||||
};
|
||||
@@ -279,10 +641,179 @@ pub async fn send_http_request_by_id<T: TemplateCallback>(
|
||||
if let Err(join_err) = event_handle.await {
|
||||
warn!("Failed to join response event task: {}", join_err);
|
||||
}
|
||||
persist_cookie_jar(
|
||||
params.query_manager,
|
||||
cookie_jar.as_mut(),
|
||||
cookie_store.as_ref(),
|
||||
¶ms.cookie_jar_update_source,
|
||||
)?;
|
||||
|
||||
Ok(SendHttpRequestResult { rendered_request, response, response_body })
|
||||
}
|
||||
|
||||
fn resolve_environment_chain(
|
||||
query_manager: &QueryManager,
|
||||
request: &HttpRequest,
|
||||
environment_id: Option<&str>,
|
||||
) -> Result<Vec<Environment>> {
|
||||
let db = query_manager.connect();
|
||||
db.resolve_environments(&request.workspace_id, request.folder_id.as_deref(), environment_id)
|
||||
.map_err(SendHttpRequestError::ResolveEnvironments)
|
||||
}
|
||||
|
||||
fn resolve_inherited_request(
|
||||
query_manager: &QueryManager,
|
||||
request: &HttpRequest,
|
||||
) -> Result<(HttpRequest, String)> {
|
||||
let db = query_manager.connect();
|
||||
let (authentication_type, authentication, auth_context_id) = db
|
||||
.resolve_auth_for_http_request(request)
|
||||
.map_err(SendHttpRequestError::ResolveRequestInheritance)?;
|
||||
let resolved_headers = db
|
||||
.resolve_headers_for_http_request(request)
|
||||
.map_err(SendHttpRequestError::ResolveRequestInheritance)?;
|
||||
|
||||
let mut request = request.clone();
|
||||
request.authentication_type = authentication_type;
|
||||
request.authentication = authentication;
|
||||
request.headers = resolved_headers;
|
||||
|
||||
Ok((request, auth_context_id))
|
||||
}
|
||||
|
||||
fn load_cookie_jar(
|
||||
query_manager: &QueryManager,
|
||||
cookie_jar_id: Option<&str>,
|
||||
) -> Result<Option<CookieJar>> {
|
||||
let Some(cookie_jar_id) = cookie_jar_id else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
query_manager
|
||||
.connect()
|
||||
.get_cookie_jar(cookie_jar_id)
|
||||
.map(Some)
|
||||
.map_err(SendHttpRequestError::LoadCookieJar)
|
||||
}
|
||||
|
||||
fn persist_cookie_jar(
|
||||
query_manager: &QueryManager,
|
||||
cookie_jar: Option<&mut CookieJar>,
|
||||
cookie_store: Option<&CookieStore>,
|
||||
source: &UpdateSource,
|
||||
) -> Result<()> {
|
||||
match (cookie_jar, cookie_store) {
|
||||
(Some(cookie_jar), Some(cookie_store)) => {
|
||||
cookie_jar.cookies = cookie_store.get_all_cookies();
|
||||
query_manager
|
||||
.connect()
|
||||
.upsert_cookie_jar(cookie_jar, source)
|
||||
.map_err(SendHttpRequestError::PersistCookieJar)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn proxy_setting_from_settings(proxy: Option<ProxySetting>) -> HttpConnectionProxySetting {
|
||||
match proxy {
|
||||
None => HttpConnectionProxySetting::System,
|
||||
Some(ProxySetting::Disabled) => HttpConnectionProxySetting::Disabled,
|
||||
Some(ProxySetting::Enabled { http, https, auth, bypass, disabled }) => {
|
||||
if disabled {
|
||||
HttpConnectionProxySetting::System
|
||||
} else {
|
||||
HttpConnectionProxySetting::Enabled {
|
||||
http,
|
||||
https,
|
||||
bypass,
|
||||
auth: auth.map(|ProxySettingAuth { user, password }| {
|
||||
HttpConnectionProxySettingAuth { user, password }
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn apply_plugin_authentication(
|
||||
sendable_request: &mut SendableHttpRequest,
|
||||
request: &HttpRequest,
|
||||
auth_context_id: &str,
|
||||
plugin_manager: &PluginManager,
|
||||
plugin_context: &PluginContext,
|
||||
) -> std::result::Result<(), String> {
|
||||
match &request.authentication_type {
|
||||
None => {}
|
||||
Some(authentication_type) if authentication_type == "none" => {}
|
||||
Some(authentication_type) => {
|
||||
let req = CallHttpAuthenticationRequest {
|
||||
context_id: format!("{:x}", md5::compute(auth_context_id)),
|
||||
values: serde_json::from_value(
|
||||
serde_json::to_value(&request.authentication)
|
||||
.map_err(|e| format!("Failed to serialize auth values: {e}"))?,
|
||||
)
|
||||
.map_err(|e| format!("Failed to parse auth values: {e}"))?,
|
||||
url: sendable_request.url.clone(),
|
||||
method: sendable_request.method.clone(),
|
||||
headers: sendable_request
|
||||
.headers
|
||||
.iter()
|
||||
.map(|(name, value)| HttpHeader {
|
||||
name: name.to_string(),
|
||||
value: value.to_string(),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
let plugin_result = plugin_manager
|
||||
.call_http_authentication(plugin_context, authentication_type, req)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to apply authentication plugin: {e}"))?;
|
||||
|
||||
for header in plugin_result.set_headers.unwrap_or_default() {
|
||||
sendable_request.insert_header((header.name, header.value));
|
||||
}
|
||||
|
||||
if let Some(params) = plugin_result.set_query_parameters {
|
||||
let params = params.into_iter().map(|p| (p.name, p.value)).collect::<Vec<_>>();
|
||||
sendable_request.url = append_query_params(&sendable_request.url, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn persist_response_error(
|
||||
query_manager: &QueryManager,
|
||||
blob_manager: &BlobManager,
|
||||
update_source: &UpdateSource,
|
||||
response: &HttpResponse,
|
||||
started_at: Instant,
|
||||
error: String,
|
||||
fallback_url: String,
|
||||
) -> Result<HttpResponse> {
|
||||
let elapsed = duration_to_i32(started_at.elapsed());
|
||||
query_manager
|
||||
.connect()
|
||||
.upsert_http_response(
|
||||
&HttpResponse {
|
||||
state: HttpResponseState::Closed,
|
||||
elapsed,
|
||||
elapsed_headers: if response.elapsed_headers == 0 {
|
||||
elapsed
|
||||
} else {
|
||||
response.elapsed_headers
|
||||
},
|
||||
error: Some(error),
|
||||
url: if response.url.is_empty() { fallback_url } else { response.url.clone() },
|
||||
..response.clone()
|
||||
},
|
||||
update_source,
|
||||
blob_manager,
|
||||
)
|
||||
.map_err(SendHttpRequestError::PersistResponse)
|
||||
}
|
||||
|
||||
fn sendable_body_length(body: Option<&SendableBody>) -> Option<i32> {
|
||||
match body {
|
||||
Some(SendableBody::Bytes(bytes)) => Some(usize_to_i32(bytes.len())),
|
||||
|
||||
Reference in New Issue
Block a user