Add error_for_status.
This makes it it easy to turn error responses into error results.
This commit is contained in:
		
				
					committed by
					
						 Sean McArthur
						Sean McArthur
					
				
			
			
				
	
			
			
			
						parent
						
							a25b9a6002
						
					
				
				
					commit
					855e6615eb
				
			| @@ -59,6 +59,40 @@ impl Response { | |||||||
|             _marker: PhantomData, |             _marker: PhantomData, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Turn a response into an error if the server returned an error. | ||||||
|  |     // XXX: example disabled since rustdoc still tries to run it | ||||||
|  |     // when the 'unstable' feature isn't active, making the import | ||||||
|  |     // fail. | ||||||
|  |     // | ||||||
|  |     // # Example | ||||||
|  |     // | ||||||
|  |     // ``` | ||||||
|  |     // # use reqwest::unstable::async::Response; | ||||||
|  |     // fn on_response(res: Response) { | ||||||
|  |     //     match res.error_for_status() { | ||||||
|  |     //         Ok(_res) => (), | ||||||
|  |     //         Err(err) => { | ||||||
|  |     //             // asserting a 400 as an example | ||||||
|  |     //             // it could be any status between 400...599 | ||||||
|  |     //             assert_eq!( | ||||||
|  |     //                 err.status(), | ||||||
|  |     //                 Some(reqwest::StatusCode::BadRequest) | ||||||
|  |     //             ); | ||||||
|  |     //         } | ||||||
|  |     //     } | ||||||
|  |     // } | ||||||
|  |     // ``` | ||||||
|  |     #[inline] | ||||||
|  |     pub fn error_for_status(self) -> ::Result<Self> { | ||||||
|  |         if self.status.is_client_error() { | ||||||
|  |             Err(::error::client_error(self.url, self.status)) | ||||||
|  |         } else if self.status.is_server_error() { | ||||||
|  |             Err(::error::server_error(self.url, self.status)) | ||||||
|  |         } else { | ||||||
|  |             Ok(self) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										67
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								src/error.rs
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ use std::error::Error as StdError; | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::io; | use std::io; | ||||||
|  |  | ||||||
| use Url; | use {StatusCode, Url}; | ||||||
|  |  | ||||||
| /// The Errors that may occur when processing a `Request`. | /// The Errors that may occur when processing a `Request`. | ||||||
| /// | /// | ||||||
| @@ -118,7 +118,9 @@ impl Error { | |||||||
|             Kind::UrlEncoded(ref e) => Some(e), |             Kind::UrlEncoded(ref e) => Some(e), | ||||||
|             Kind::Json(ref e) => Some(e), |             Kind::Json(ref e) => Some(e), | ||||||
|             Kind::TooManyRedirects | |             Kind::TooManyRedirects | | ||||||
|             Kind::RedirectLoop => None, |             Kind::RedirectLoop | | ||||||
|  |             Kind::ClientError(_) | | ||||||
|  |             Kind::ServerError(_) => None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -150,6 +152,34 @@ impl Error { | |||||||
|             _ => false, |             _ => false, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Returns true if the error is from a request returning a 4xx error. | ||||||
|  |     #[inline] | ||||||
|  |     pub fn is_client_error(&self) -> bool { | ||||||
|  |         match self.kind { | ||||||
|  |             Kind::ClientError(_) => true, | ||||||
|  |             _ => false, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns true if the error is from a request returning a 5xx error. | ||||||
|  |     #[inline] | ||||||
|  |     pub fn is_server_error(&self) -> bool { | ||||||
|  |         match self.kind { | ||||||
|  |             Kind::ServerError(_) => true, | ||||||
|  |             _ => false, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns the status code, if the error was generated from a response. | ||||||
|  |     #[inline] | ||||||
|  |     pub fn status(&self) -> Option<StatusCode> { | ||||||
|  |         match self.kind { | ||||||
|  |             Kind::ClientError(code) | | ||||||
|  |             Kind::ServerError(code) => Some(code), | ||||||
|  |             _ => None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Display for Error { | impl fmt::Display for Error { | ||||||
| @@ -167,6 +197,14 @@ impl fmt::Display for Error { | |||||||
|             Kind::Json(ref e) => fmt::Display::fmt(e, f), |             Kind::Json(ref e) => fmt::Display::fmt(e, f), | ||||||
|             Kind::TooManyRedirects => f.write_str("Too many redirects"), |             Kind::TooManyRedirects => f.write_str("Too many redirects"), | ||||||
|             Kind::RedirectLoop => f.write_str("Infinite redirect loop"), |             Kind::RedirectLoop => f.write_str("Infinite redirect loop"), | ||||||
|  |             Kind::ClientError(ref code) => { | ||||||
|  |                 f.write_str("Client Error: ")?; | ||||||
|  |                 fmt::Display::fmt(code, f) | ||||||
|  |             } | ||||||
|  |             Kind::ServerError(ref code) => { | ||||||
|  |                 f.write_str("Server Error: ")?; | ||||||
|  |                 fmt::Display::fmt(code, f) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -182,6 +220,8 @@ impl StdError for Error { | |||||||
|             Kind::Json(ref e) => e.description(), |             Kind::Json(ref e) => e.description(), | ||||||
|             Kind::TooManyRedirects => "Too many redirects", |             Kind::TooManyRedirects => "Too many redirects", | ||||||
|             Kind::RedirectLoop => "Infinite redirect loop", |             Kind::RedirectLoop => "Infinite redirect loop", | ||||||
|  |             Kind::ClientError(_) => "Client Error", | ||||||
|  |             Kind::ServerError(_) => "Server Error", | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -194,7 +234,9 @@ impl StdError for Error { | |||||||
|             Kind::UrlEncoded(ref e) => e.cause(), |             Kind::UrlEncoded(ref e) => e.cause(), | ||||||
|             Kind::Json(ref e) => e.cause(), |             Kind::Json(ref e) => e.cause(), | ||||||
|             Kind::TooManyRedirects | |             Kind::TooManyRedirects | | ||||||
|             Kind::RedirectLoop => None, |             Kind::RedirectLoop | | ||||||
|  |             Kind::ClientError(_) | | ||||||
|  |             Kind::ServerError(_) => None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -211,6 +253,8 @@ pub enum Kind { | |||||||
|     Json(::serde_json::Error), |     Json(::serde_json::Error), | ||||||
|     TooManyRedirects, |     TooManyRedirects, | ||||||
|     RedirectLoop, |     RedirectLoop, | ||||||
|  |     ClientError(StatusCode), | ||||||
|  |     ServerError(StatusCode), | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -374,6 +418,22 @@ pub fn timedout(url: Option<Url>) -> Error { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[inline] | ||||||
|  | pub fn client_error(url: Url, status: StatusCode) -> Error { | ||||||
|  |     Error { | ||||||
|  |         kind: Kind::ClientError(status), | ||||||
|  |         url: Some(url), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[inline] | ||||||
|  | pub fn server_error(url: Url, status: StatusCode) -> Error { | ||||||
|  |     Error { | ||||||
|  |         kind: Kind::ServerError(status), | ||||||
|  |         url: Some(url), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use super::*; |     use super::*; | ||||||
| @@ -438,7 +498,6 @@ mod tests { | |||||||
|         let link = Chain(Some(root)); |         let link = Chain(Some(root)); | ||||||
|         let io = ::std::io::Error::new(::std::io::ErrorKind::Other, link); |         let io = ::std::io::Error::new(::std::io::ErrorKind::Other, link); | ||||||
|         let err = Error { kind: Kind::Io(io), url: None }; |         let err = Error { kind: Kind::Io(io), url: None }; | ||||||
|  |  | ||||||
|         assert!(err.cause().is_some()); |         assert!(err.cause().is_some()); | ||||||
|         assert_eq!(err.to_string(), "chain: root"); |         assert_eq!(err.to_string(), "chain: root"); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -156,6 +156,34 @@ impl Response { | |||||||
|         // Went for easier for now, just to get it working. |         // Went for easier for now, just to get it working. | ||||||
|         serde_json::from_reader(self).map_err(::error::from) |         serde_json::from_reader(self).map_err(::error::from) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Turn a response into an error if the server returned an error. | ||||||
|  |     /// | ||||||
|  |     /// # Example | ||||||
|  |     /// | ||||||
|  |     /// ```rust,no_run | ||||||
|  |     /// # extern crate reqwest; | ||||||
|  |     /// # fn run() -> Result<(), Box<::std::error::Error>> { | ||||||
|  |     /// let res = reqwest::get("http://httpbin.org/status/400")? | ||||||
|  |     ///     .error_for_status(); | ||||||
|  |     /// if let Err(err) = res { | ||||||
|  |     ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BadRequest)); | ||||||
|  |     /// } | ||||||
|  |     /// # Ok(()) | ||||||
|  |     /// # } | ||||||
|  |     /// # fn main() {} | ||||||
|  |     /// ``` | ||||||
|  |     #[inline] | ||||||
|  |     pub fn error_for_status(self) -> ::Result<Self> { | ||||||
|  |         let Response { body, inner, _thread_handle } = self; | ||||||
|  |         inner.error_for_status().map(move |inner| { | ||||||
|  |             Response { | ||||||
|  |                 body: body, | ||||||
|  |                 inner: inner, | ||||||
|  |                 _thread_handle: _thread_handle, | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Read for Response { | impl Read for Response { | ||||||
|   | |||||||
| @@ -80,3 +80,61 @@ fn test_post() { | |||||||
|     let n = res.read(&mut buf).unwrap(); |     let n = res.read(&mut buf).unwrap(); | ||||||
|     assert_eq!(n, 0) |     assert_eq!(n, 0) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Calling `Response::error_for_status`` on a response with status in 4xx | ||||||
|  | /// returns a error. | ||||||
|  | #[test] | ||||||
|  | fn test_error_for_status_4xx() { | ||||||
|  |     let server = server! { | ||||||
|  |         request: b"\ | ||||||
|  |             GET /1 HTTP/1.1\r\n\ | ||||||
|  |             Host: $HOST\r\n\ | ||||||
|  |             User-Agent: $USERAGENT\r\n\ | ||||||
|  |             Accept: */*\r\n\ | ||||||
|  |             Accept-Encoding: gzip\r\n\ | ||||||
|  |             \r\n\ | ||||||
|  |             ", | ||||||
|  |         response: b"\ | ||||||
|  |             HTTP/1.1 400 OK\r\n\ | ||||||
|  |             Server: test\r\n\ | ||||||
|  |             Content-Length: 0\r\n\ | ||||||
|  |             \r\n\ | ||||||
|  |             " | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let url = format!("http://{}/1", server.addr()); | ||||||
|  |     let res = reqwest::get(&url).unwrap(); | ||||||
|  |  | ||||||
|  |     let err = res.error_for_status().err().unwrap(); | ||||||
|  |     assert!(err.is_client_error()); | ||||||
|  |     assert_eq!(err.status(), Some(reqwest::StatusCode::BadRequest)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Calling `Response::error_for_status`` on a response with status in 5xx | ||||||
|  | /// returns a error. | ||||||
|  | #[test] | ||||||
|  | fn test_error_for_status_5xx() { | ||||||
|  |     let server = server! { | ||||||
|  |         request: b"\ | ||||||
|  |             GET /1 HTTP/1.1\r\n\ | ||||||
|  |             Host: $HOST\r\n\ | ||||||
|  |             User-Agent: $USERAGENT\r\n\ | ||||||
|  |             Accept: */*\r\n\ | ||||||
|  |             Accept-Encoding: gzip\r\n\ | ||||||
|  |             \r\n\ | ||||||
|  |             ", | ||||||
|  |         response: b"\ | ||||||
|  |             HTTP/1.1 500 OK\r\n\ | ||||||
|  |             Server: test\r\n\ | ||||||
|  |             Content-Length: 0\r\n\ | ||||||
|  |             \r\n\ | ||||||
|  |             " | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let url = format!("http://{}/1", server.addr()); | ||||||
|  |     let res = reqwest::get(&url).unwrap(); | ||||||
|  |  | ||||||
|  |     let err = res.error_for_status().err().unwrap(); | ||||||
|  |     assert!(err.is_server_error()); | ||||||
|  |     assert_eq!(err.status(), Some(reqwest::StatusCode::InternalServerError)); | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user