sentry/transports/
reqwest.rs
1use std::time::Duration;
2
3use reqwest::{header as ReqwestHeaders, Client as ReqwestClient, Proxy, StatusCode};
4
5use super::tokio_thread::TransportThread;
6
7use crate::{sentry_debug, ClientOptions, Envelope, Transport};
8
9#[cfg_attr(doc_cfg, doc(cfg(feature = "reqwest")))]
15pub struct ReqwestHttpTransport {
16 thread: TransportThread,
17}
18
19impl ReqwestHttpTransport {
20 pub fn new(options: &ClientOptions) -> Self {
22 Self::new_internal(options, None)
23 }
24
25 pub fn with_client(options: &ClientOptions, client: ReqwestClient) -> Self {
27 Self::new_internal(options, Some(client))
28 }
29
30 fn new_internal(options: &ClientOptions, client: Option<ReqwestClient>) -> Self {
31 let client = client.unwrap_or_else(|| {
32 let mut builder = reqwest::Client::builder();
33 if options.accept_invalid_certs {
34 builder = builder.danger_accept_invalid_certs(true);
35 }
36 if let Some(url) = options.http_proxy.as_ref() {
37 match Proxy::http(url.as_ref()) {
38 Ok(proxy) => {
39 builder = builder.proxy(proxy);
40 }
41 Err(err) => {
42 sentry_debug!("invalid proxy: {:?}", err);
43 }
44 }
45 };
46 if let Some(url) = options.https_proxy.as_ref() {
47 match Proxy::https(url.as_ref()) {
48 Ok(proxy) => {
49 builder = builder.proxy(proxy);
50 }
51 Err(err) => {
52 sentry_debug!("invalid proxy: {:?}", err);
53 }
54 }
55 };
56 builder.build().unwrap()
57 });
58 let dsn = options.dsn.as_ref().unwrap();
59 let user_agent = options.user_agent.clone();
60 let auth = dsn.to_auth(Some(&user_agent)).to_string();
61 let url = dsn.envelope_api_url().to_string();
62
63 let thread = TransportThread::new(move |envelope, mut rl| {
64 let mut body = Vec::new();
65 envelope.to_writer(&mut body).unwrap();
66 let request = client.post(&url).header("X-Sentry-Auth", &auth).body(body);
67
68 async move {
71 match request.send().await {
72 Ok(response) => {
73 let headers = response.headers();
74
75 if let Some(sentry_header) = headers
76 .get("x-sentry-rate-limits")
77 .and_then(|x| x.to_str().ok())
78 {
79 rl.update_from_sentry_header(sentry_header);
80 } else if let Some(retry_after) = headers
81 .get(ReqwestHeaders::RETRY_AFTER)
82 .and_then(|x| x.to_str().ok())
83 {
84 rl.update_from_retry_after(retry_after);
85 } else if response.status() == StatusCode::TOO_MANY_REQUESTS {
86 rl.update_from_429();
87 }
88
89 match response.text().await {
90 Err(err) => {
91 sentry_debug!("Failed to read sentry response: {}", err);
92 }
93 Ok(text) => {
94 sentry_debug!("Get response: `{}`", text);
95 }
96 }
97 }
98 Err(err) => {
99 sentry_debug!("Failed to send envelope: {}", err);
100 }
101 }
102 rl
103 }
104 });
105 Self { thread }
106 }
107}
108
109impl Transport for ReqwestHttpTransport {
110 fn send_envelope(&self, envelope: Envelope) {
111 self.thread.send(envelope)
112 }
113 fn flush(&self, timeout: Duration) -> bool {
114 self.thread.flush(timeout)
115 }
116
117 fn shutdown(&self, timeout: Duration) -> bool {
118 self.flush(timeout)
119 }
120}