sentry/transports/
reqwest.rs1use 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
57 .build()
58 .expect("Failed to build `reqwest` client as a TLS backend is not available. Enable either the `native-tls` or the `rustls` feature of the `sentry` crate.")
59 });
60 let dsn = options.dsn.as_ref().unwrap();
61 let user_agent = options.user_agent.clone();
62 let auth = dsn.to_auth(Some(&user_agent)).to_string();
63 let url = dsn.envelope_api_url().to_string();
64
65 let thread = TransportThread::new(move |envelope, mut rl| {
66 let mut body = Vec::new();
67 envelope.to_writer(&mut body).unwrap();
68 let request = client.post(&url).header("X-Sentry-Auth", &auth).body(body);
69
70 async move {
73 match request.send().await {
74 Ok(response) => {
75 let headers = response.headers();
76
77 if let Some(sentry_header) = headers
78 .get("x-sentry-rate-limits")
79 .and_then(|x| x.to_str().ok())
80 {
81 rl.update_from_sentry_header(sentry_header);
82 } else if let Some(retry_after) = headers
83 .get(ReqwestHeaders::RETRY_AFTER)
84 .and_then(|x| x.to_str().ok())
85 {
86 rl.update_from_retry_after(retry_after);
87 } else if response.status() == StatusCode::TOO_MANY_REQUESTS {
88 rl.update_from_429();
89 }
90
91 match response.text().await {
92 Err(err) => {
93 sentry_debug!("Failed to read sentry response: {}", err);
94 }
95 Ok(text) => {
96 sentry_debug!("Get response: `{}`", text);
97 }
98 }
99 }
100 Err(err) => {
101 sentry_debug!("Failed to send envelope: {}", err);
102 }
103 }
104 rl
105 }
106 });
107 Self { thread }
108 }
109}
110
111impl Transport for ReqwestHttpTransport {
112 fn send_envelope(&self, envelope: Envelope) {
113 self.thread.send(envelope)
114 }
115 fn flush(&self, timeout: Duration) -> bool {
116 self.thread.flush(timeout)
117 }
118
119 fn shutdown(&self, timeout: Duration) -> bool {
120 self.flush(timeout)
121 }
122}