refactor send callers to shared plugin runtime helper

This commit is contained in:
Gregory Schier
2026-02-16 15:09:06 -08:00
parent 7cd47ae811
commit 603ae0650d
5 changed files with 650 additions and 647 deletions

View File

@@ -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 }

View File

@@ -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, &params.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, &params.request)?
};
let runtime_config =
resolve_http_send_runtime_config(params.query_manager, &params.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()
},
&params.update_source,
params.blob_manager,
)
.upsert_http_response(&response, &params.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
},
&params.update_source,
params.blob_manager,
)
.map_err(SendHttpRequestError::PersistResponse)?;
persist_cookie_jar(
params.query_manager,
cookie_jar.as_mut(),
cookie_store.as_ref(),
&params.cookie_jar_update_source,
)?;
let _ = persist_response_error(
params.query_manager,
params.blob_manager,
&params.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(),
&params.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())),