usecrate::request::ParsedRequest; usecrate::{Error, Method, ResponseLazy}; use std::env; use std::io::{self, Read, Write}; use std::net::{TcpStream, ToSocketAddrs}; use std::time::{Duration, Instant};
type UnsecuredStream = TcpStream;
#[cfg(feature = "rustls")] mod rustls_stream; #[cfg(feature = "rustls")] type SecuredStream = rustls_stream::SecuredStream;
#[cfg(all(not(feature = "rustls"), feature = "native-tls"))] mod native_tls_stream; #[cfg(all(not(feature = "rustls"), feature = "native-tls"))] type SecuredStream = native_tls_stream::SecuredStream;
impl Read for HttpStream { fn read(&mutself, buf: &mut [u8]) -> io::Result<usize> { let timeout = |tcp: &TcpStream, timeout_at: Option<Instant>| -> io::Result<()> { let _ = tcp.set_read_timeout(timeout_at_to_duration(timeout_at)?);
Ok(())
};
let result = matchself {
HttpStream::Unsecured(inner, timeout_at) => {
timeout(inner, *timeout_at)?;
inner.read(buf)
} #[cfg(any(feature = "rustls", feature = "openssl", feature = "native-tls"))]
HttpStream::Secured(inner, timeout_at) => {
timeout(inner.get_ref(), *timeout_at)?;
inner.read(buf)
}
}; match result {
Err(e) if e.kind() == io::ErrorKind::WouldBlock => { // We're a blocking socket, so EWOULDBLOCK indicates a timeout
Err(timeout_err())
}
r => r,
}
}
}
/// A connection to the server for sending /// [`Request`](struct.Request.html)s. pubstruct Connection {
request: ParsedRequest,
timeout_at: Option<Instant>,
}
impl Connection { /// Creates a new `Connection`. See [Request] and [ParsedRequest] /// for specifics about *what* is being sent. pub(crate) fn new(request: ParsedRequest) -> Connection { let timeout = request
.config
.timeout
.or_else(|| match env::var("MINREQ_TIMEOUT") {
Ok(t) => t.parse::<u64>().ok(),
Err(_) => None,
}); let timeout_at = timeout.map(|t| Instant::now() + Duration::from_secs(t));
Connection {
request,
timeout_at,
}
}
/// Returns the timeout duration for operations that should end at /// timeout and are starting "now". /// /// The Result will be Err if the timeout has already passed. fn timeout(&self) -> Result<Option<Duration>, io::Error> { let timeout = timeout_at_to_duration(self.timeout_at);
#[cfg(feature = "log")]
log::trace!("Timeout requested, it is currently: {:?}", timeout);
timeout
}
/// Sends the [`Request`](struct.Request.html), consumes this /// connection, and returns a [`Response`](struct.Response.html). #[cfg(any(feature = "rustls", feature = "native-tls", feature = "openssl",))] pub(crate) fn send_https(mutself) -> Result<ResponseLazy, Error> {
enforce_timeout(self.timeout_at, move || { self.request.url.host = ensure_ascii_host(self.request.url.host)?;
fn connect(&self) -> Result<TcpStream, Error> { let tcp_connect = |host: &str, port: u32| -> Result<TcpStream, Error> { let addrs = (host, port as u16)
.to_socket_addrs()
.map_err(Error::IoError)?; let addrs_count = addrs.len();
// Try all resolved addresses. Return the first one to which we could connect. If all // failed return the last error encountered. for (i, addr) in addrs.enumerate() { let stream = iflet Some(timeout) = self.timeout()? {
TcpStream::connect_timeout(&addr, timeout)
} else {
TcpStream::connect(addr)
}; if stream.is_ok() || i == addrs_count - 1 { return stream.map_err(Error::from);
}
}
Err(Error::AddressNotFound)
};
#[cfg(feature = "proxy")] matchself.request.config.proxy {
Some(ref proxy) => { // do proxy things letmut tcp = tcp_connect(&proxy.server, proxy.port)?;
#[cfg(feature = "punycode")]
{ letmut result = String::with_capacity(host.len() * 2); for s in host.split('.') { if s.is_ascii() {
result += s;
} else { match punycode::encode(s) {
Ok(s) => result = result + "xn--" + &s,
Err(_) => return Err(Error::PunycodeConversionFailed),
}
}
result += ".";
}
result.truncate(result.len() - 1); // Remove the trailing dot
Ok(result)
}
}
}
/// Enforce the timeout by running the function in a new thread and /// parking the current one with a timeout. /// /// While minreq does use timeouts (somewhat) properly, some /// interfaces such as [ToSocketAddrs] don't allow for specifying the /// timeout. Hence this. fn enforce_timeout<F, R>(timeout_at: Option<Instant>, f: F) -> Result<R, Error> where
F: 'static + Send + FnOnce() -> Result<R, Error>,
R: 'static + Send,
{ use std::sync::mpsc::{channel, RecvTimeoutError};
match timeout_at {
Some(deadline) => { let (sender, receiver) = channel(); let thread = std::thread::spawn(move || { let result = f(); let _ = sender.send(());
result
}); iflet Some(timeout_duration) = deadline.checked_duration_since(Instant::now()) { match receiver.recv_timeout(timeout_duration) {
Ok(()) => thread.join().unwrap(),
Err(err) => match err {
RecvTimeoutError::Timeout => Err(Error::IoError(timeout_err())),
RecvTimeoutError::Disconnected => {
Err(Error::Other("request connection paniced"))
}
},
}
} else {
Err(Error::IoError(timeout_err()))
}
}
None => f(),
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.