From 5aa0f25fd5d7e15080064919429ad2968f5dcbb5 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 16 Feb 2019 21:40:47 +0100 Subject: [PATCH 1/3] feat: Initial work on optional transports --- Cargo.toml | 8 +++++--- src/lib.rs | 8 +++++++- src/transport.rs | 49 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 413c38171..b92ed1ee0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,10 @@ autoexamples = true all-features = true [features] -default = ["with_client_implementation", "with_panic", "with_failure", "with_log", "with_env_logger", "with_device_info", "with_rust_info"] -with_client_implementation = ["reqwest", "im", "url", "with_backtrace"] +default = ["with_client_implementation", "with_default_transport", "with_panic", "with_failure", "with_log", "with_env_logger", "with_device_info", "with_rust_info"] +with_reqwest_transport = ["reqwest", "httpdate"] +with_default_transport = ["with_reqwest_transport"] +with_client_implementation = ["im", "url", "with_backtrace"] with_backtrace = ["backtrace", "regex"] with_panic = ["with_backtrace"] with_failure = ["failure", "with_backtrace"] @@ -48,7 +50,7 @@ libc = { version = "0.2.48", optional = true } hostname = { version = "0.1.5", optional = true } findshlibs = { version = "0.4.1", optional = true } rand = "0.6.5" -httpdate = "0.3.2" +httpdate = { version = "0.3.2", optional = true } [target."cfg(not(windows))".dependencies] uname = { version = "0.1.1", optional = true } diff --git a/src/lib.rs b/src/lib.rs index d80096f41..a8e788ca4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,7 @@ //! default flags: //! //! * `with_client_implementation`: turns on the real client implementation. +//! * `with_default_transport`: compiles in the default HTTP transport. //! * `with_backtrace`: enables backtrace support (automatically turned on in a few cases) //! * `with_panic`: enables the panic integration //! * `with_failure`: enables the `failure` integration @@ -104,6 +105,8 @@ //! //! * `with_error_chain`: enables the error-chain integration //! * `with_test_support`: enables the test support module +//! * `with_reqwest_transport`: enables the reqwest transport explicitly. This +//! is currently the default transport. #![warn(missing_docs)] #[macro_use] @@ -142,9 +145,12 @@ pub mod internals { #[cfg(feature = "with_client_implementation")] pub use crate::{ client::{ClientInitGuard, IntoDsn}, - transport::{DefaultTransportFactory, HttpTransport, Transport, TransportFactory}, + transport::{DefaultTransportFactory, Transport, TransportFactory}, }; + #[cfg(feature = "with_reqwest_transport")] + pub use crate::transport::{HttpTransport, ReqwestHttpTransport}; + pub use sentry_types::{ Auth, ChronoParseError, DateTime, DebugId, Dsn, DsnParseError, ParseDebugIdError, ProjectId, ProjectIdParseError, Scheme, TimeZone, Utc, Uuid, UuidVariant, UuidVersion, diff --git a/src/transport.rs b/src/transport.rs index d56f0462a..1654501b1 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -4,8 +4,11 @@ use std::sync::{Arc, Condvar, Mutex}; use std::thread::{self, JoinHandle}; use std::time::{Duration, SystemTime}; -use httpdate::parse_http_date; -use reqwest::{header::RETRY_AFTER, Client, Proxy}; +#[cfg(feature = "with_reqwest_transport")] +use { + httpdate::parse_http_date, + reqwest::{header::RETRY_AFTER, Client, Proxy}, +}; use crate::client::ClientOptions; use crate::internals::Dsn; @@ -88,19 +91,34 @@ impl TransportFactory for Arc { /// Creates the default HTTP transport. /// /// This is the default value for `transport` on the client options. It -/// creates a `HttpTransport`. +/// creates a `HttpTransport`. If no http transport was compiled into the +/// library it will panic on transport creation. #[derive(Clone)] pub struct DefaultTransportFactory; impl TransportFactory for DefaultTransportFactory { fn create_transport(&self, options: &ClientOptions) -> Box { - Box::new(HttpTransport::new(options)) + #[cfg(feature = "with_reqwest_transport")] + { + Box::new(HttpTransport::new(options)) + } + #[cfg(not(feature = "with_reqwest_transport"))] + { + panic!( + "sentry crate was compiled without transport, enable at \ + least with_default_transport for default implementation." + ) + } } } -/// A transport can send events via HTTP to sentry. +/// A transport can send events via HTTP to sentry via `reqwest`. +/// +/// When the `with_default_transport` feature is enabled this will currently +/// be the default transport. +#[cfg(feature = "with_reqwest_transport")] #[derive(Debug)] -pub struct HttpTransport { +pub struct ReqwestHttpTransport { dsn: Dsn, sender: Mutex>>>, shutdown_signal: Arc, @@ -109,6 +127,7 @@ pub struct HttpTransport { _handle: Option>, } +#[cfg(feature = "with_reqwest_transport")] fn parse_retry_after(s: &str) -> Option { if let Ok(value) = s.parse::() { Some(SystemTime::now() + Duration::from_secs(value.ceil() as u64)) @@ -119,6 +138,7 @@ fn parse_retry_after(s: &str) -> Option { } } +#[cfg(feature = "with_reqwest_transport")] fn spawn_http_sender( client: Client, receiver: Receiver>>, @@ -184,9 +204,10 @@ fn spawn_http_sender( }) } -impl HttpTransport { +#[cfg(feature = "with_reqwest_transport")] +impl ReqwestHttpTransport { /// Creates a new transport. - pub fn new(options: &ClientOptions) -> HttpTransport { + pub fn new(options: &ClientOptions) -> ReqwestHttpTransport { let dsn = options.dsn.clone().unwrap(); let user_agent = options.user_agent.to_string(); let http_proxy = options.http_proxy.as_ref().map(|x| x.to_string()); @@ -213,7 +234,7 @@ impl HttpTransport { queue_size.clone(), user_agent, )); - HttpTransport { + ReqwestHttpTransport { dsn, sender: Mutex::new(sender), shutdown_signal, @@ -224,7 +245,8 @@ impl HttpTransport { } } -impl Transport for HttpTransport { +#[cfg(feature = "with_reqwest_transport")] +impl Transport for ReqwestHttpTransport { fn send_event(&self, event: Event<'static>) { // we count up before we put the item on the queue and in case the // queue is filled with too many items or we shut down, we decrement @@ -249,7 +271,8 @@ impl Transport for HttpTransport { } } -impl Drop for HttpTransport { +#[cfg(feature = "with_reqwest_transport")] +impl Drop for ReqwestHttpTransport { fn drop(&mut self) { sentry_debug!("dropping http transport"); self.shutdown_immediately.store(true, Ordering::SeqCst); @@ -258,3 +281,7 @@ impl Drop for HttpTransport { } } } + +/// The default http transport. +#[cfg(feature = "with_reqwest_transport")] +pub type HttpTransport = ReqwestHttpTransport; From 6d13f821fedc24d615fd16f37536f47e4129fdd0 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 16 Feb 2019 22:58:53 +0100 Subject: [PATCH 2/3] feat: Added curl transport --- Cargo.toml | 5 +- Makefile | 9 +- src/lib.rs | 22 ++- src/transport.rs | 415 +++++++++++++++++++++++++++++++---------------- src/utils.rs | 14 +- 5 files changed, 312 insertions(+), 153 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b92ed1ee0..8564f60e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,8 @@ all-features = true [features] default = ["with_client_implementation", "with_default_transport", "with_panic", "with_failure", "with_log", "with_env_logger", "with_device_info", "with_rust_info"] -with_reqwest_transport = ["reqwest", "httpdate"] +with_reqwest_transport = ["reqwest", "httpdate", "with_client_implementation"] +with_curl_transport = ["curl", "httpdate", "serde_json", "with_client_implementation"] with_default_transport = ["with_reqwest_transport"] with_client_implementation = ["im", "url", "with_backtrace"] with_backtrace = ["backtrace", "regex"] @@ -51,6 +52,8 @@ hostname = { version = "0.1.5", optional = true } findshlibs = { version = "0.4.1", optional = true } rand = "0.6.5" httpdate = { version = "0.3.2", optional = true } +curl = { version = "0.4.19", optional = true } +serde_json = { version = "1.0.38", optional = true } [target."cfg(not(windows))".dependencies] uname = { version = "0.1.1", optional = true } diff --git a/Makefile b/Makefile index 965da1c20..e8bd15737 100644 --- a/Makefile +++ b/Makefile @@ -53,6 +53,13 @@ check-all-impls: @RUSTFLAGS=-Dwarnings cargo check --no-default-features --features 'with_failure,with_log,with_panic,with_error_chain' .PHONY: check-all-impls +check-curl-transport: + @echo 'CURL TRANSPORT' + @RUSTFLAGS=-Dwarnings cargo check --features with_curl_transport + @echo 'CURL TRANSPORT ONLY' + @RUSTFLAGS=-Dwarnings cargo check --no-default-features --features 'with_curl_transport,with_client_implementation,with_panic' +.PHONY: check-curl-transport + check-actix: @echo 'ACTIX INTEGRATION' @RUSTFLAGS=-Dwarnings cargo check --manifest-path integrations/sentry-actix/Cargo.toml @@ -61,7 +68,7 @@ check-actix: check: check-no-default-features check-default-features .PHONY: check-all-features -checkall: check-all-features check-no-default-features check-default-features check-failure check-log check-panic check-error-chain check-all-impls check-actix +checkall: check-all-features check-no-default-features check-default-features check-failure check-log check-panic check-error-chain check-all-impls check-curl-transport check-actix .PHONY: checkall cargotest: diff --git a/src/lib.rs b/src/lib.rs index a8e788ca4..7bef80100 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,6 +107,7 @@ //! * `with_test_support`: enables the test support module //! * `with_reqwest_transport`: enables the reqwest transport explicitly. This //! is currently the default transport. +//! * `with_curl_transport`: enables the curl transport. #![warn(missing_docs)] #[macro_use] @@ -145,18 +146,31 @@ pub mod internals { #[cfg(feature = "with_client_implementation")] pub use crate::{ client::{ClientInitGuard, IntoDsn}, - transport::{DefaultTransportFactory, Transport, TransportFactory}, + transport::{Transport, TransportFactory}, }; - #[cfg(feature = "with_reqwest_transport")] - pub use crate::transport::{HttpTransport, ReqwestHttpTransport}; - pub use sentry_types::{ Auth, ChronoParseError, DateTime, DebugId, Dsn, DsnParseError, ParseDebugIdError, ProjectId, ProjectIdParseError, Scheme, TimeZone, Utc, Uuid, UuidVariant, UuidVersion, }; } +/// The provided transports. +/// +/// This module exposes all transports that are compiled into the sentry +/// library. The `with_reqwest_transport` and `with_curl_transport` flags +/// turn on these transports. +pub mod transports { + #[cfg(any(feature = "with_reqwest_transport", feature = "with_curl_transport"))] + pub use crate::transport::{DefaultTransportFactory, HttpTransport}; + + #[cfg(feature = "with_reqwest_transport")] + pub use crate::transport::ReqwestHttpTransport; + + #[cfg(feature = "with_curl_transport")] + pub use crate::transport::CurlHttpTransport; +} + // public api or exports from this crate pub use crate::api::*; pub use crate::hub::Hub; diff --git a/src/transport.rs b/src/transport.rs index 1654501b1..c1e511248 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -4,14 +4,16 @@ use std::sync::{Arc, Condvar, Mutex}; use std::thread::{self, JoinHandle}; use std::time::{Duration, SystemTime}; +#[cfg(any(feature = "with_reqwest_transport", feature = "with_curl_transport"))] +use httpdate::parse_http_date; + #[cfg(feature = "with_reqwest_transport")] -use { - httpdate::parse_http_date, - reqwest::{header::RETRY_AFTER, Client, Proxy}, -}; +use reqwest::{header::RETRY_AFTER, Client, Proxy}; + +#[cfg(feature = "with_curl_transport")] +use {crate::internals::Scheme, curl, std::io::Read}; use crate::client::ClientOptions; -use crate::internals::Dsn; use crate::protocol::Event; /// The trait for transports. @@ -98,36 +100,18 @@ pub struct DefaultTransportFactory; impl TransportFactory for DefaultTransportFactory { fn create_transport(&self, options: &ClientOptions) -> Box { - #[cfg(feature = "with_reqwest_transport")] + #[cfg(any(feature = "with_reqwest_transport", feature = "with_curl_transport"))] { Box::new(HttpTransport::new(options)) } - #[cfg(not(feature = "with_reqwest_transport"))] + #[cfg(not(any(feature = "with_reqwest_transport", feature = "with_curl_transport")))] { - panic!( - "sentry crate was compiled without transport, enable at \ - least with_default_transport for default implementation." - ) + panic!("sentry crate was compiled without transport") } } } -/// A transport can send events via HTTP to sentry via `reqwest`. -/// -/// When the `with_default_transport` feature is enabled this will currently -/// be the default transport. -#[cfg(feature = "with_reqwest_transport")] -#[derive(Debug)] -pub struct ReqwestHttpTransport { - dsn: Dsn, - sender: Mutex>>>, - shutdown_signal: Arc, - shutdown_immediately: Arc, - queue_size: Arc>, - _handle: Option>, -} - -#[cfg(feature = "with_reqwest_transport")] +#[cfg(any(feature = "with_reqwest_transport", feature = "with_curl_transport"))] fn parse_retry_after(s: &str) -> Option { if let Ok(value) = s.parse::() { Some(SystemTime::now() + Duration::from_secs(value.ceil() as u64)) @@ -138,86 +122,105 @@ fn parse_retry_after(s: &str) -> Option { } } -#[cfg(feature = "with_reqwest_transport")] -fn spawn_http_sender( - client: Client, - receiver: Receiver>>, - dsn: Dsn, - signal: Arc, - shutdown_immediately: Arc, - queue_size: Arc>, - user_agent: String, -) -> JoinHandle<()> { - let mut disabled = SystemTime::now(); - - thread::spawn(move || { - let url = dsn.store_api_url().to_string(); - - while let Some(event) = receiver.recv().unwrap_or(None) { - // on drop we want to not continue processing the queue. - if shutdown_immediately.load(Ordering::SeqCst) { - let mut size = queue_size.lock().unwrap(); - *size = 0; - signal.notify_all(); - break; +macro_rules! implement_http_transport { + ( + $(#[$attr:meta])* + pub struct $typename:ident; + fn spawn($($argname:ident: $argty:ty,)*) $body:block + ) => { + $(#[$attr])* + pub struct $typename { + sender: Mutex>>>, + shutdown_signal: Arc, + shutdown_immediately: Arc, + queue_size: Arc>, + _handle: Option>, + } + + impl $typename { + /// Creates a new transport. + pub fn new(options: &ClientOptions) -> $typename { + fn spawn($($argname: $argty,)*) -> JoinHandle<()> { $body } + + let (sender, receiver) = sync_channel(30); + let shutdown_signal = Arc::new(Condvar::new()); + let shutdown_immediately = Arc::new(AtomicBool::new(false)); + #[allow(clippy::mutex_atomic)] + let queue_size = Arc::new(Mutex::new(0)); + let _handle = Some(spawn( + options, + receiver, + shutdown_signal.clone(), + shutdown_immediately.clone(), + queue_size.clone(), + )); + $typename { + sender: Mutex::new(sender), + shutdown_signal, + shutdown_immediately, + queue_size, + _handle, + } } + } - // while we are disabled due to rate limits, skip - let now = SystemTime::now(); - if let Ok(time_left) = disabled.duration_since(now) { - sentry_debug!( - "Skipping event send because we're disabled due to rate limits for {}s", - time_left.as_secs() - ); - continue; + impl Transport for $typename { + fn send_event(&self, event: Event<'static>) { + // we count up before we put the item on the queue and in case the + // queue is filled with too many items or we shut down, we decrement + // the count again as there is nobody that can pick it up. + *self.queue_size.lock().unwrap() += 1; + if self.sender.lock().unwrap().try_send(Some(event)).is_err() { + *self.queue_size.lock().unwrap() -= 1; + } } - match client - .post(url.as_str()) - .json(&event) - .header("X-Sentry-Auth", dsn.to_auth(Some(&user_agent)).to_string()) - .send() - { - Ok(resp) => { - if resp.status() == 429 { - if let Some(retry_after) = resp - .headers() - .get(RETRY_AFTER) - .and_then(|x| x.to_str().ok()) - .and_then(parse_retry_after) - { - disabled = retry_after; - } + fn shutdown(&self, timeout: Duration) -> bool { + sentry_debug!("shutting down http transport"); + let guard = self.queue_size.lock().unwrap(); + if *guard == 0 { + true + } else { + if let Ok(sender) = self.sender.lock() { + sender.send(None).ok(); } - } - Err(err) => { - sentry_debug!("Failed to send event: {}", err); + self.shutdown_signal.wait_timeout(guard, timeout).is_ok() } } + } - let mut size = queue_size.lock().unwrap(); - *size -= 1; - if *size == 0 { - signal.notify_all(); + impl Drop for $typename { + fn drop(&mut self) { + sentry_debug!("dropping http transport"); + self.shutdown_immediately.store(true, Ordering::SeqCst); + if let Ok(sender) = self.sender.lock() { + sender.send(None).ok(); + } } } - }) + } } #[cfg(feature = "with_reqwest_transport")] -impl ReqwestHttpTransport { - /// Creates a new transport. - pub fn new(options: &ClientOptions) -> ReqwestHttpTransport { +implement_http_transport! { + /// A transport can send events via HTTP to sentry via `reqwest`. + /// + /// When the `with_default_transport` feature is enabled this will currently + /// be the default transport. This is separately enabled by the + /// `with_reqwest_transport` flag. + pub struct ReqwestHttpTransport; + + fn spawn( + options: &ClientOptions, + receiver: Receiver>>, + signal: Arc, + shutdown_immediately: Arc, + queue_size: Arc>, + ) { let dsn = options.dsn.clone().unwrap(); let user_agent = options.user_agent.to_string(); let http_proxy = options.http_proxy.as_ref().map(|x| x.to_string()); let https_proxy = options.https_proxy.as_ref().map(|x| x.to_string()); - - let (sender, receiver) = sync_channel(30); - let shutdown_signal = Arc::new(Condvar::new()); - let shutdown_immediately = Arc::new(AtomicBool::new(false)); - #[allow(clippy::mutex_atomic)] - let queue_size = Arc::new(Mutex::new(0)); let mut client = Client::builder(); if let Some(url) = http_proxy { client = client.proxy(Proxy::http(&url).unwrap()); @@ -225,63 +228,197 @@ impl ReqwestHttpTransport { if let Some(url) = https_proxy { client = client.proxy(Proxy::https(&url).unwrap()); }; - let _handle = Some(spawn_http_sender( - client.build().unwrap(), - receiver, - dsn.clone(), - shutdown_signal.clone(), - shutdown_immediately.clone(), - queue_size.clone(), - user_agent, - )); - ReqwestHttpTransport { - dsn, - sender: Mutex::new(sender), - shutdown_signal, - shutdown_immediately, - queue_size, - _handle, - } + let client = client.build().unwrap(); + + let mut disabled = SystemTime::now(); + + thread::spawn(move || { + sentry_debug!("spawning reqwest transport"); + let url = dsn.store_api_url().to_string(); + + while let Some(event) = receiver.recv().unwrap_or(None) { + // on drop we want to not continue processing the queue. + if shutdown_immediately.load(Ordering::SeqCst) { + let mut size = queue_size.lock().unwrap(); + *size = 0; + signal.notify_all(); + break; + } + + // while we are disabled due to rate limits, skip + let now = SystemTime::now(); + if let Ok(time_left) = disabled.duration_since(now) { + sentry_debug!( + "Skipping event send because we're disabled due to rate limits for {}s", + time_left.as_secs() + ); + continue; + } + + match client + .post(url.as_str()) + .json(&event) + .header("X-Sentry-Auth", dsn.to_auth(Some(&user_agent)).to_string()) + .send() + { + Ok(resp) => { + if resp.status() == 429 { + if let Some(retry_after) = resp + .headers() + .get(RETRY_AFTER) + .and_then(|x| x.to_str().ok()) + .and_then(parse_retry_after) + { + disabled = retry_after; + } + } + } + Err(err) => { + sentry_debug!("Failed to send event: {}", err); + } + } + + let mut size = queue_size.lock().unwrap(); + *size -= 1; + if *size == 0 { + signal.notify_all(); + } + } + }) } } -#[cfg(feature = "with_reqwest_transport")] -impl Transport for ReqwestHttpTransport { - fn send_event(&self, event: Event<'static>) { - // we count up before we put the item on the queue and in case the - // queue is filled with too many items or we shut down, we decrement - // the count again as there is nobody that can pick it up. - *self.queue_size.lock().unwrap() += 1; - if self.sender.lock().unwrap().try_send(Some(event)).is_err() { - *self.queue_size.lock().unwrap() -= 1; - } - } +#[cfg(feature = "with_curl_transport")] +implement_http_transport! { + /// A transport can send events via HTTP to sentry via `curl`. + /// + /// This is enabled by the `with_curl_transport` flag. + pub struct CurlHttpTransport; - fn shutdown(&self, timeout: Duration) -> bool { - sentry_debug!("shutting down http transport"); - let guard = self.queue_size.lock().unwrap(); - if *guard == 0 { - true - } else { - if let Ok(sender) = self.sender.lock() { - sender.send(None).ok(); + fn spawn( + options: &ClientOptions, + receiver: Receiver>>, + signal: Arc, + shutdown_immediately: Arc, + queue_size: Arc>, + ) { + let dsn = options.dsn.clone().unwrap(); + let user_agent = options.user_agent.to_string(); + let http_proxy = options.http_proxy.as_ref().map(|x| x.to_string()); + let https_proxy = options.https_proxy.as_ref().map(|x| x.to_string()); + + let mut disabled = SystemTime::now(); + let mut handle = curl::easy::Easy::new(); + + thread::spawn(move || { + sentry_debug!("spawning curl transport"); + let url = dsn.store_api_url().to_string(); + + while let Some(event) = receiver.recv().unwrap_or(None) { + // on drop we want to not continue processing the queue. + if shutdown_immediately.load(Ordering::SeqCst) { + let mut size = queue_size.lock().unwrap(); + *size = 0; + signal.notify_all(); + break; + } + + // while we are disabled due to rate limits, skip + let now = SystemTime::now(); + if let Ok(time_left) = disabled.duration_since(now) { + sentry_debug!( + "Skipping event send because we're disabled due to rate limits for {}s", + time_left.as_secs() + ); + continue; + } + + handle.reset(); + handle.url(&url).unwrap(); + handle.custom_request("POST").unwrap(); + + if dsn.scheme() == Scheme::Https { + if let Some(ref proxy) = https_proxy { + handle.proxy(&proxy).unwrap(); + } + } else { + if let Some(ref proxy) = http_proxy { + handle.proxy(&proxy).unwrap(); + } + } + + let body = serde_json::to_vec(&event).unwrap(); + let mut retry_after = None; + let mut headers = curl::easy::List::new(); + headers.append(&format!("X-Sentry-Auth: {}", dsn.to_auth(Some(&user_agent)))).unwrap(); + headers.append("Expect:").unwrap(); + headers.append("Content-Type: application/json").unwrap(); + handle.http_headers(headers).unwrap(); + handle.upload(true).unwrap(); + handle.in_filesize(body.len() as u64).unwrap(); + handle.read_function(move |buf| Ok((&body[..]).read(buf).unwrap_or(0))).unwrap(); + handle.verbose(true).unwrap(); + handle.debug_function(move |info, data| { + let prefix = match info { + curl::easy::InfoType::HeaderIn => "< ", + curl::easy::InfoType::HeaderOut => "> ", + curl::easy::InfoType::DataOut => "", + _ => return + }; + sentry_debug!("curl: {}{}", prefix, String::from_utf8_lossy(data).trim()); + }).unwrap(); + + { + let mut handle = handle.transfer(); + let retry_after_setter = &mut retry_after; + handle.header_function(move |data| { + if let Ok(data) = std::str::from_utf8(data) { + let mut iter = data.split(':'); + if let Some(key) = iter.next().map(|x| x.to_lowercase()) { + if key == "retry-after" { + *retry_after_setter = iter.next().map(|x| x.trim().to_string()); + } + } + } + true + }).unwrap(); + handle.perform().ok(); + } + + match handle.response_code() { + Ok(429) => { + if let Some(retry_after) = retry_after + .as_ref() + .map(|x| x.as_str()) + .and_then(parse_retry_after) + { + disabled = retry_after; + } + } + Ok(200) | Ok(201) => {} + _ => { + sentry_debug!("Failed to send event"); + } + } + + let mut size = queue_size.lock().unwrap(); + *size -= 1; + if *size == 0 { + signal.notify_all(); + } } - self.shutdown_signal.wait_timeout(guard, timeout).is_ok() - } + }) } } #[cfg(feature = "with_reqwest_transport")] -impl Drop for ReqwestHttpTransport { - fn drop(&mut self) { - sentry_debug!("dropping http transport"); - self.shutdown_immediately.store(true, Ordering::SeqCst); - if let Ok(sender) = self.sender.lock() { - sender.send(None).ok(); - } - } -} +type DefaultTransport = ReqwestHttpTransport; + +#[cfg(all( + feature = "with_curl_transport", + not(feature = "with_reqwest_transport") +))] +type DefaultTransport = CurlHttpTransport; /// The default http transport. -#[cfg(feature = "with_reqwest_transport")] -pub type HttpTransport = ReqwestHttpTransport; +pub type HttpTransport = DefaultTransport; diff --git a/src/utils.rs b/src/utils.rs index 8c148a724..08a1ffd9f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,9 +1,7 @@ //! Useful utilities for working with events. use std::thread; -use crate::protocol::{ - Context, DebugImage, DeviceContext, Map, OsContext, RuntimeContext, Stacktrace, Thread, -}; +use crate::protocol::{Context, DebugImage, Stacktrace, Thread}; #[cfg(all(feature = "with_device_info", target_os = "macos"))] mod model_support { @@ -230,7 +228,7 @@ pub fn os_context() -> Option { use uname::uname; if let Ok(info) = uname() { Some( - OsContext { + crate::protocol::OsContext { name: Some(info.sysname), kernel_version: Some(info.version), version: Some(info.release), @@ -246,7 +244,7 @@ pub fn os_context() -> Option { { use crate::constants::PLATFORM; Some( - OsContext { + crate::protocol::OsContext { name: Some(PLATFORM.into()), ..Default::default() } @@ -264,11 +262,11 @@ pub fn rust_context() -> Option { #[cfg(feature = "with_device_info")] { use crate::constants::{RUSTC_CHANNEL, RUSTC_VERSION}; - let ctx = RuntimeContext { + let ctx = crate::protocol::RuntimeContext { name: Some("rustc".into()), version: RUSTC_VERSION.map(|x| x.into()), other: { - let mut map = Map::default(); + let mut map = crate::protocol::Map::default(); if let Some(channel) = RUSTC_CHANNEL { map.insert("channel".to_string(), channel.into()); } @@ -292,7 +290,7 @@ pub fn device_context() -> Option { let family = device_family(); let arch = cpu_arch(); Some( - DeviceContext { + crate::protocol::DeviceContext { model, family, arch, From c6db93f1f7ffda99a8841b6b02dc6bcaf171bb23 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 17 Feb 2019 00:04:52 +0100 Subject: [PATCH 3/3] fix: Fixed proxy support --- src/transport.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/transport.rs b/src/transport.rs index c1e511248..feb288f84 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -337,14 +337,14 @@ implement_http_transport! { handle.url(&url).unwrap(); handle.custom_request("POST").unwrap(); - if dsn.scheme() == Scheme::Https { - if let Some(ref proxy) = https_proxy { + match (dsn.scheme(), &http_proxy, &https_proxy) { + (Scheme::Https, _, &Some(ref proxy)) => { handle.proxy(&proxy).unwrap(); } - } else { - if let Some(ref proxy) = http_proxy { + (_, &Some(ref proxy), _) => { handle.proxy(&proxy).unwrap(); } + _ => {} } let body = serde_json::to_vec(&event).unwrap();