usecrate::connection::Connection; usecrate::http_url::{HttpUrl, Port}; #[cfg(feature = "proxy")] usecrate::proxy::Proxy; usecrate::{Error, Response, ResponseLazy}; use std::collections::HashMap; use std::fmt; use std::fmt::Write;
/// A URL type for requests. pubtype URL = String;
/// An HTTP request method. #[derive(Clone, PartialEq, Eq, Debug)] pubenum Method { /// The GET method
Get, /// The HEAD method
Head, /// The POST method
Post, /// The PUT method
Put, /// The DELETE method
Delete, /// The CONNECT method
Connect, /// The OPTIONS method
Options, /// The TRACE method
Trace, /// The PATCH method
Patch, /// A custom method, use with care: the string will be embedded in /// your request as-is.
Custom(String),
}
impl fmt::Display for Method { /// Formats the Method to the form in the HTTP request, /// ie. Method::Get -> "GET", Method::Post -> "POST", etc. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self {
Method::Get => write!(f, "GET"),
Method::Head => write!(f, "HEAD"),
Method::Post => write!(f, "POST"),
Method::Put => write!(f, "PUT"),
Method::Delete => write!(f, "DELETE"),
Method::Connect => write!(f, "CONNECT"),
Method::Options => write!(f, "OPTIONS"),
Method::Trace => write!(f, "TRACE"),
Method::Patch => write!(f, "PATCH"),
Method::Custom(ref s) => write!(f, "{}", s),
}
}
}
/// An HTTP request. /// /// Generally created by the [`minreq::get`](fn.get.html)-style /// functions, corresponding to the HTTP method we want to use. /// /// # Example /// /// ``` /// let request = minreq::post("http://example.com"); /// ``` /// /// After creating the request, you would generally call /// [`send`](struct.Request.html#method.send) or /// [`send_lazy`](struct.Request.html#method.send_lazy) on it, as it /// doesn't do much on its own. #[derive(Clone, PartialEq, Eq, Debug)] pubstruct Request { pub(crate) method: Method,
url: URL,
params: String,
headers: HashMap<String, String>,
body: Option<Vec<u8>>, pub(crate) timeout: Option<u64>, pub(crate) max_headers_size: Option<usize>, pub(crate) max_status_line_len: Option<usize>,
max_redirects: usize, pub(crate) follow_redirects: bool, #[cfg(feature = "proxy")] pub(crate) proxy: Option<Proxy>,
}
impl Request { /// Creates a new HTTP `Request`. /// /// This is only the request's data, it is not sent yet. For /// sending the request, see [`send`](struct.Request.html#method.send). /// /// If `urlencoding` is not enabled, it is the responsibility of the /// user to ensure there are no illegal characters in the URL. /// /// If `urlencoding` is enabled, the resource part of the URL will be /// encoded. Any URL special characters (e.g. &, #, =) are not encoded /// as they are assumed to be meaningful parameters etc. pubfn new<T: Into<URL>>(method: Method, url: T) -> Request {
Request {
method,
url: url.into(),
params: String::new(),
headers: HashMap::new(),
body: None,
timeout: None,
max_headers_size: None,
max_status_line_len: None,
max_redirects: 100,
follow_redirects: true, #[cfg(feature = "proxy")]
proxy: None,
}
}
/// Add headers to the request this is called on. Use this /// function to add headers to your requests. pubfn with_headers<T, K, V>(mutself, headers: T) -> Request where
T: IntoIterator<Item = (K, V)>,
K: Into<String>,
V: Into<String>,
{ let headers = headers.into_iter().map(|(k, v)| (k.into(), v.into())); self.headers.extend(headers); self
}
/// Adds a header to the request this is called on. Use this /// function to add headers to your requests. pubfn with_header<T: Into<String>, U: Into<String>>(mutself, key: T, value: U) -> Request { self.headers.insert(key.into(), value.into()); self
}
/// Sets the request body. pubfn with_body<T: Into<Vec<u8>>>(mutself, body: T) -> Request { let body = body.into(); let body_length = body.len(); self.body = Some(body); self.with_header("Content-Length", format!("{}", body_length))
}
/// Adds given key and value as query parameter to request url /// (resource). /// /// If `urlencoding` is not enabled, it is the responsibility /// of the user to ensure there are no illegal characters in the /// key or value. /// /// If `urlencoding` is enabled, the key and value are both encoded. pubfn with_param<T: Into<String>, U: Into<String>>(mutself, key: T, value: U) -> Request { let key = key.into(); #[cfg(feature = "urlencoding")] let key = urlencoding::encode(&key); let value = value.into(); #[cfg(feature = "urlencoding")] let value = urlencoding::encode(&value);
if !self.params.is_empty() { self.params.push('&');
} self.params.push_str(&key); self.params.push('='); self.params.push_str(&value); self
}
/// Converts given argument to JSON and sets it as body. /// /// # Errors /// /// Returns /// [`SerdeJsonError`](enum.Error.html#variant.SerdeJsonError) if /// Serde runs into a problem when converting `body` into a /// string. #[cfg(feature = "json-using-serde")] pubfn with_json<T: serde::ser::Serialize>(mutself, body: &T) -> Result<Request, Error> { self.headers.insert( "Content-Type".to_string(), "application/json; charset=UTF-8".to_string(),
); match serde_json::to_string(&body) {
Ok(json) => Ok(self.with_body(json)),
Err(err) => Err(Error::SerdeJsonError(err)),
}
}
/// Sets the request timeout in seconds. pubfn with_timeout(mutself, timeout: u64) -> Request { self.timeout = Some(timeout); self
}
/// Sets the max redirects we follow until giving up. 100 by /// default. /// /// Warning: setting this to a very high number, such as 1000, may /// cause a stack overflow if that many redirects are followed. If /// you have a use for so many redirects that the stack overflow /// becomes a problem, please open an issue. pubfn with_max_redirects(mutself, max_redirects: usize) -> Request { self.max_redirects = max_redirects; self
}
/// Enables or disables redirect handling. Defaults to `true`, i.e. enabled. /// /// If `follow` is `true` and the server returns a 301, 302, 303, or 307 /// status code, minreq will follow the redirect by making another HTTP /// request to the new location, up to the amount of times specified by /// [`Request::with_max_redirects`], returning an error if that amount is /// reached before getting a non-redirection as a response. /// /// Disabling redirection handling with this function by passing in `false` /// can be used to handle the redirects yourself. pubfn with_follow_redirects(mutself, follow_redirects: bool) -> Request { self.follow_redirects = follow_redirects; self
}
/// Sets the maximum size of all the headers this request will /// accept. /// /// If this limit is passed, the request will close the connection /// and return an [Error::HeadersOverflow] error. /// /// The maximum length is counted in bytes, including line-endings /// and other whitespace. Both normal and trailing headers count /// towards this cap. /// /// `None` disables the cap, and may cause the program to use any /// amount of memory if the server responds with a lot of headers /// (or an infinite amount). In minreq versions 2.x.x, the default /// is None, so setting this manually is recommended when talking /// to untrusted servers. pubfn with_max_headers_size<S: Into<Option<usize>>>(mutself, max_headers_size: S) -> Request { self.max_headers_size = max_headers_size.into(); self
}
/// Sets the maximum length of the status line this request will /// accept. /// /// If this limit is passed, the request will close the connection /// and return an [Error::StatusLineOverflow] error. /// /// The maximum length is counted in bytes, including the /// line-ending `\r\n`. /// /// `None` disables the cap, and may cause the program to use any /// amount of memory if the server responds with a long (or /// infinite) status line. In minreq versions 2.x.x, the default /// is None, so setting this manually is recommended when talking /// to untrusted servers. pubfn with_max_status_line_length<S: Into<Option<usize>>>( mutself,
max_status_line_len: S,
) -> Request { self.max_status_line_len = max_status_line_len.into(); self
}
/// Sends this request to the host. /// /// # Errors /// /// Returns `Err` if we run into an error while sending the /// request, or receiving/parsing the response. The specific error /// is described in the `Err`, and it can be any /// [`minreq::Error`](enum.Error.html) except /// [`SerdeJsonError`](enum.Error.html#variant.SerdeJsonError) and /// [`InvalidUtf8InBody`](enum.Error.html#variant.InvalidUtf8InBody). pubfn send(self) -> Result<Response, Error> { let parsed_request = ParsedRequest::new(self)?; if parsed_request.url.https { #[cfg(any(feature = "rustls", feature = "openssl", feature = "native-tls"))]
{ let is_head = parsed_request.config.method == Method::Head; let response = Connection::new(parsed_request).send_https()?;
Response::create(response, is_head)
} #[cfg(not(any(feature = "rustls", feature = "openssl", feature = "native-tls")))]
{
Err(Error::HttpsFeatureNotEnabled)
}
} else { let is_head = parsed_request.config.method == Method::Head; let response = Connection::new(parsed_request).send()?;
Response::create(response, is_head)
}
}
// NOTE: As of 2.10.0, the fragment is intentionally left out of the request, based on: // - [RFC 3986 section 3.5](https://datatracker.ietf.org/doc/html/rfc3986#section-3.5): // "...the fragment identifier is not used in the scheme-specific // processing of a URI; instead, the fragment identifier is separated // from the rest of the URI prior to a dereference..." // - [RFC 7231 section 9.5](https://datatracker.ietf.org/doc/html/rfc7231#section-9.5): // "Although fragment identifiers used within URI references are not // sent in requests..."
// Add the request line and the "Host" header
write!(
http, "{} {} HTTP/1.1\r\nHost: {}", self.config.method, self.url.path_and_query, self.url.host
)
.unwrap(); iflet Port::Explicit(port) = self.url.port {
write!(http, ":{}", port).unwrap();
}
http += "\r\n";
// Add other headers for (k, v) in &self.config.headers {
write!(http, "{}: {}\r\n", k, v).unwrap();
}
ifself.config.method == Method::Post
|| self.config.method == Method::Put
|| self.config.method == Method::Patch
{ let not_length = |key: &String| { let key = key.to_lowercase();
key != "content-length" && key != "transfer-encoding"
}; ifself.config.headers.keys().all(not_length) { // A user agent SHOULD send a Content-Length in a request message when no Transfer-Encoding // is sent and the request method defines a meaning for an enclosed payload body. // refer: https://tools.ietf.org/html/rfc7230#section-3.3.2
/// Returns the HTTP request as bytes, ready to be sent to /// the server. pub(crate) fn as_bytes(&self) -> Vec<u8> { letmut head = self.get_http_head().into_bytes(); iflet Some(body) = &self.config.body {
head.extend(body);
}
head
}
/// Returns the redirected version of this Request, unless an /// infinite redirection loop was detected, or the redirection /// limit was reached. pub(crate) fn redirect_to(&mutself, url: &str) -> Result<(), Error> { if url.contains("://") { letmut url = HttpUrl::parse(url, Some(&self.url)).map_err(|_| { // TODO: Uncomment this for 3.0 // Error::InvalidProtocolInRedirect
Error::IoError(std::io::Error::new(
std::io::ErrorKind::Other, "was redirected to an absolute url with an invalid protocol",
))
})?;
std::mem::swap(&mut url, &mutself.url); self.redirects.push(url);
} else { // The url does not have the protocol part, assuming it's // a relative resource. letmut absolute_url = String::new(); self.url.write_base_url_to(&mut absolute_url).unwrap();
absolute_url.push_str(url); letmut url = HttpUrl::parse(&absolute_url, Some(&self.url))?;
std::mem::swap(&mut url, &mutself.url); self.redirects.push(url);
}
/// Alias for [Request::new](struct.Request.html#method.new) with `method` set to /// [Method::Get](enum.Method.html). pubfn get<T: Into<URL>>(url: T) -> Request {
Request::new(Method::Get, url)
}
/// Alias for [Request::new](struct.Request.html#method.new) with `method` set to /// [Method::Head](enum.Method.html). pubfn head<T: Into<URL>>(url: T) -> Request {
Request::new(Method::Head, url)
}
/// Alias for [Request::new](struct.Request.html#method.new) with `method` set to /// [Method::Post](enum.Method.html). pubfn post<T: Into<URL>>(url: T) -> Request {
Request::new(Method::Post, url)
}
/// Alias for [Request::new](struct.Request.html#method.new) with `method` set to /// [Method::Put](enum.Method.html). pubfn put<T: Into<URL>>(url: T) -> Request {
Request::new(Method::Put, url)
}
/// Alias for [Request::new](struct.Request.html#method.new) with `method` set to /// [Method::Delete](enum.Method.html). pubfn delete<T: Into<URL>>(url: T) -> Request {
Request::new(Method::Delete, url)
}
/// Alias for [Request::new](struct.Request.html#method.new) with `method` set to /// [Method::Connect](enum.Method.html). pubfn connect<T: Into<URL>>(url: T) -> Request {
Request::new(Method::Connect, url)
}
/// Alias for [Request::new](struct.Request.html#method.new) with `method` set to /// [Method::Options](enum.Method.html). pubfn options<T: Into<URL>>(url: T) -> Request {
Request::new(Method::Options, url)
}
/// Alias for [Request::new](struct.Request.html#method.new) with `method` set to /// [Method::Trace](enum.Method.html). pubfn trace<T: Into<URL>>(url: T) -> Request {
Request::new(Method::Trace, url)
}
/// Alias for [Request::new](struct.Request.html#method.new) with `method` set to /// [Method::Patch](enum.Method.html). pubfn patch<T: Into<URL>>(url: T) -> Request {
Request::new(Method::Patch, url)
}
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.