This commit is contained in:
Martin Algesten
2018-06-16 11:01:28 +02:00
parent ca2e193c84
commit 4791db3a63
4 changed files with 51 additions and 20 deletions

View File

@@ -3,28 +3,41 @@ use native_tls::Error as TlsError;
use native_tls::HandshakeError; use native_tls::HandshakeError;
use std::net::TcpStream; use std::net::TcpStream;
/// Errors that are translated to ["synthetic" responses](struct.Response.html#method.synthetic).
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// The url could not be understood. Synthetic error `400`.
BadUrl(String), BadUrl(String),
/// The url scheme could not be understood. Synthetic error `400`.
UnknownScheme(String), UnknownScheme(String),
/// DNS lookup failed. Synthetic error `400`.
DnsFailed(String), DnsFailed(String),
/// Connection to server failed. Synthetic error `500`.
ConnectionFailed(String), ConnectionFailed(String),
/// Too many redirects. Synthetic error `500`.
TooManyRedirects, TooManyRedirects,
/// A status line we don't understand `HTTP/1.1 200 OK`. Synthetic error `500`.
BadStatus, BadStatus,
/// A header line that couldn't be parsed. Synthetic error `500`.
BadHeader, BadHeader,
/// Some unspecified `std::io::Error`. Synthetic error `500`.
Io(IoError), Io(IoError),
/// Some unspecified TLS error. Synthetic error `400`.
Tls(TlsError), Tls(TlsError),
/// Some unspecified TLS handshake error. Synthetic error `500`.
TlsHandshake(HandshakeError<TcpStream>), TlsHandshake(HandshakeError<TcpStream>),
} }
impl Error { impl Error {
/// For synthetic responses, this is the error code.
pub fn status(&self) -> u16 { pub fn status(&self) -> u16 {
match self { match self {
Error::BadUrl(_) => 400, Error::BadUrl(_) => 400,
Error::UnknownScheme(_) => 400, Error::UnknownScheme(_) => 400,
Error::DnsFailed(_) => 400, Error::DnsFailed(_) => 400,
Error::ConnectionFailed(_) => 500, Error::ConnectionFailed(_) => 500,
Error::TooManyRedirects => 400, Error::TooManyRedirects => 500,
Error::BadStatus => 500, Error::BadStatus => 500,
Error::BadHeader => 500, Error::BadHeader => 500,
Error::Io(_) => 500, Error::Io(_) => 500,
@@ -32,6 +45,8 @@ impl Error {
Error::TlsHandshake(_) => 500, Error::TlsHandshake(_) => 500,
} }
} }
/// For synthetic responses, this is the status text.
pub fn status_text(&self) -> &str { pub fn status_text(&self) -> &str {
match self { match self {
Error::BadUrl(e) => { Error::BadUrl(e) => {
@@ -49,6 +64,8 @@ impl Error {
Error::TlsHandshake(_) => "TLS Handshake Error", Error::TlsHandshake(_) => "TLS Handshake Error",
} }
} }
/// For synthetic responses, this is the body text.
pub fn body_text(&self) -> String { pub fn body_text(&self) -> String {
match self { match self {
Error::BadUrl(url) => format!("Bad URL: {}", url), Error::BadUrl(url) => format!("Bad URL: {}", url),

View File

@@ -48,6 +48,7 @@ mod test;
pub use agent::{Agent, Request, Response}; pub use agent::{Agent, Request, Response};
pub use header::Header; pub use header::Header;
pub use error::Error;
// re-export // re-export
pub use serde_json::{to_value as serde_to_value, Map as SerdeMap, Value as SerdeValue}; pub use serde_json::{to_value as serde_to_value, Map as SerdeMap, Value as SerdeValue};

View File

@@ -14,7 +14,20 @@ const DEFAULT_CHARACTER_SET: &'static str = "utf-8";
/// Response instances are created as results of firing off requests. /// Response instances are created as results of firing off requests.
/// ///
/// The `Response` is used to read response headers and decide what to do with the body.
/// Note that the socket connection is open and the body not read until one of
/// [`into_reader()`](#method.into_reader), [`into_json()`](#method.into_json) or
/// [`into_string()`](#method.into_string) consumes the response.
/// ///
/// ```
/// let response = ureq::get("https://www.google.com").call();
///
/// // socket is still open and the response body has not been read.
///
/// let text = response.into_string().unwrap();
///
/// // response is consumed, and body has been read.
/// ```
pub struct Response { pub struct Response {
error: Option<Error>, error: Option<Error>,
status_line: AsciiString, status_line: AsciiString,
@@ -36,6 +49,24 @@ impl ::std::fmt::Debug for Response {
} }
impl Response { impl Response {
/// Construct a response with a status, status text and a string body.
///
/// This is hopefully useful for unit tests.
///
/// Example:
///
/// ```
/// let resp = ureq::Response::new(401, "Authorization Required", "Please log in");
///
/// assert_eq!(*resp.status(), 401);
/// ```
pub fn new(status: u16, status_text: &str, body: &str) -> Self {
let r = format!("HTTP/1.1 {} {}\r\n\r\n{}\n", status, status_text, body);
(r.as_ref() as &str)
.parse::<Response>()
.unwrap_or_else(|e| e.into())
}
/// The entire status line like: `HTTP/1.1 200 OK` /// The entire status line like: `HTTP/1.1 200 OK`
pub fn status_line(&self) -> &str { pub fn status_line(&self) -> &str {
self.status_line.as_str() self.status_line.as_str()
@@ -276,24 +307,6 @@ impl Response {
}) })
} }
/// Construct a response with a status, status text and a string body.
///
/// This is hopefully useful for unit tests.
///
/// Example:
///
/// ```
/// let resp = ureq::Response::new(401, "Authorization Required", "Please log in");
///
/// assert_eq!(*resp.status(), 401);
/// ```
pub fn new(status: u16, status_text: &str, body: &str) -> Self {
let r = format!("HTTP/1.1 {} {}\r\n\r\n{}\n", status, status_text, body);
(r.as_ref() as &str)
.parse::<Response>()
.unwrap_or_else(|e| e.into())
}
/// Create a response from a Read trait impl. /// Create a response from a Read trait impl.
/// ///
/// This is hopefully useful for unit tests. /// This is hopefully useful for unit tests.

View File

@@ -42,7 +42,7 @@ fn content_length_limited() {
} }
#[test] #[test]
// content-length should be ignnored when chunked // content-length should be ignored when chunked
fn ignore_content_length_when_chunked() { fn ignore_content_length_when_chunked() {
test::set_handler("/ignore_content_length_when_chunked", |_req, _url| { test::set_handler("/ignore_content_length_when_chunked", |_req, _url| {
test::make_response( test::make_response(