feat(client): add a status_raw value
This commit is contained in:
		| @@ -1,15 +1,17 @@ | |||||||
| //! Client Responses | //! Client Responses | ||||||
|  | use std::num::FromPrimitive; | ||||||
| use std::io::{BufferedReader, IoResult}; | use std::io::{BufferedReader, IoResult}; | ||||||
|  |  | ||||||
| use header; | use header; | ||||||
| use header::common::{ContentLength, TransferEncoding}; | use header::common::{ContentLength, TransferEncoding}; | ||||||
| use header::common::transfer_encoding::Encoding::Chunked; | use header::common::transfer_encoding::Encoding::Chunked; | ||||||
| use net::{NetworkStream, HttpStream}; | use net::{NetworkStream, HttpStream}; | ||||||
| use http::{read_status_line, HttpReader}; | use http::{read_status_line, HttpReader, RawStatus}; | ||||||
| use http::HttpReader::{SizedReader, ChunkedReader, EofReader}; | use http::HttpReader::{SizedReader, ChunkedReader, EofReader}; | ||||||
| use status; | use status; | ||||||
| use version; | use version; | ||||||
| use HttpResult; | use HttpResult; | ||||||
|  | use HttpError::HttpStatusError; | ||||||
|  |  | ||||||
| /// A response for a client request to a remote server. | /// A response for a client request to a remote server. | ||||||
| pub struct Response<S = HttpStream> { | pub struct Response<S = HttpStream> { | ||||||
| @@ -19,6 +21,7 @@ pub struct Response<S = HttpStream> { | |||||||
|     pub headers: header::Headers, |     pub headers: header::Headers, | ||||||
|     /// The HTTP version of this response from the server. |     /// The HTTP version of this response from the server. | ||||||
|     pub version: version::HttpVersion, |     pub version: version::HttpVersion, | ||||||
|  |     status_raw: RawStatus, | ||||||
|     body: HttpReader<BufferedReader<Box<NetworkStream + Send>>>, |     body: HttpReader<BufferedReader<Box<NetworkStream + Send>>>, | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -27,10 +30,14 @@ impl Response { | |||||||
|     /// Creates a new response from a server. |     /// Creates a new response from a server. | ||||||
|     pub fn new(stream: Box<NetworkStream + Send>) -> HttpResult<Response> { |     pub fn new(stream: Box<NetworkStream + Send>) -> HttpResult<Response> { | ||||||
|         let mut stream = BufferedReader::new(stream); |         let mut stream = BufferedReader::new(stream); | ||||||
|         let (version, status) = try!(read_status_line(&mut stream)); |         let (version, raw_status) = try!(read_status_line(&mut stream)); | ||||||
|         let headers = try!(header::Headers::from_raw(&mut stream)); |         let status = match FromPrimitive::from_u16(raw_status.0) { | ||||||
|  |             Some(status) => status, | ||||||
|  |             None => return Err(HttpStatusError) | ||||||
|  |         }; | ||||||
|         debug!("{} {}", version, status); |         debug!("{} {}", version, status); | ||||||
|  |  | ||||||
|  |         let headers = try!(header::Headers::from_raw(&mut stream)); | ||||||
|         debug!("{}", headers); |         debug!("{}", headers); | ||||||
|  |  | ||||||
|         let body = if headers.has::<TransferEncoding>() { |         let body = if headers.has::<TransferEncoding>() { | ||||||
| @@ -64,9 +71,15 @@ impl Response { | |||||||
|             version: version, |             version: version, | ||||||
|             headers: headers, |             headers: headers, | ||||||
|             body: body, |             body: body, | ||||||
|  |             status_raw: raw_status, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Get the raw status code and reason. | ||||||
|  |     pub fn status_raw(&self) -> &RawStatus { | ||||||
|  |         &self.status_raw | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /// Unwraps the Request to return the NetworkStream underneath. |     /// Unwraps the Request to return the NetworkStream underneath. | ||||||
|     pub fn unwrap(self) -> Box<NetworkStream + Send> { |     pub fn unwrap(self) -> Box<NetworkStream + Send> { | ||||||
|         self.body.unwrap().unwrap() |         self.body.unwrap().unwrap() | ||||||
| @@ -84,9 +97,11 @@ impl Reader for Response { | |||||||
| mod tests { | mod tests { | ||||||
|     use std::boxed::BoxAny; |     use std::boxed::BoxAny; | ||||||
|     use std::io::BufferedReader; |     use std::io::BufferedReader; | ||||||
|  |     use std::str::Slice; | ||||||
|  |  | ||||||
|     use header::Headers; |     use header::Headers; | ||||||
|     use http::HttpReader::EofReader; |     use http::HttpReader::EofReader; | ||||||
|  |     use http::RawStatus; | ||||||
|     use mock::MockStream; |     use mock::MockStream; | ||||||
|     use net::NetworkStream; |     use net::NetworkStream; | ||||||
|     use status; |     use status; | ||||||
| @@ -101,7 +116,8 @@ mod tests { | |||||||
|             status: status::StatusCode::Ok, |             status: status::StatusCode::Ok, | ||||||
|             headers: Headers::new(), |             headers: Headers::new(), | ||||||
|             version: version::HttpVersion::Http11, |             version: version::HttpVersion::Http11, | ||||||
|             body: EofReader(BufferedReader::new(box MockStream::new() as Box<NetworkStream + Send>)) |             body: EofReader(BufferedReader::new(box MockStream::new() as Box<NetworkStream + Send>)), | ||||||
|  |             status_raw: RawStatus(200, Slice("OK")) | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let b = res.unwrap().downcast::<MockStream>().unwrap(); |         let b = res.unwrap().downcast::<MockStream>().unwrap(); | ||||||
|   | |||||||
							
								
								
									
										77
									
								
								src/http.rs
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								src/http.rs
									
									
									
									
									
								
							| @@ -1,13 +1,14 @@ | |||||||
| //! Pieces pertaining to the HTTP message protocol. | //! Pieces pertaining to the HTTP message protocol. | ||||||
| use std::cmp::min; | use std::cmp::min; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::io::{mod, Reader, IoResult}; | use std::io::{mod, Reader, IoResult, BufWriter}; | ||||||
| use std::str; | use std::num::from_u16; | ||||||
|  | use std::str::{mod, SendStr, Slice, Owned}; | ||||||
|  |  | ||||||
| use url::Url; | use url::Url; | ||||||
|  |  | ||||||
| use method; | use method; | ||||||
| use status; | use status::StatusCode; | ||||||
| use uri; | use uri; | ||||||
| use uri::RequestUri::{AbsolutePath, AbsoluteUri, Authority, Star}; | use uri::RequestUri::{AbsolutePath, AbsoluteUri, Authority, Star}; | ||||||
| use version::HttpVersion; | use version::HttpVersion; | ||||||
| @@ -548,7 +549,11 @@ pub fn read_request_line<R: Reader>(stream: &mut R) -> HttpResult<RequestLine> { | |||||||
| /// `status-line = HTTP-version SP status-code SP reason-phrase CRLF` | /// `status-line = HTTP-version SP status-code SP reason-phrase CRLF` | ||||||
| /// | /// | ||||||
| /// However, reason-phrase is absolutely useless, so its tossed. | /// However, reason-phrase is absolutely useless, so its tossed. | ||||||
| pub type StatusLine = (HttpVersion, status::StatusCode); | pub type StatusLine = (HttpVersion, RawStatus); | ||||||
|  |  | ||||||
|  | /// The raw status code and reason-phrase. | ||||||
|  | #[deriving(PartialEq, Show, Clone)] | ||||||
|  | pub struct RawStatus(pub u16, pub SendStr); | ||||||
|  |  | ||||||
| /// Read the StatusLine, such as `HTTP/1.1 200 OK`. | /// Read the StatusLine, such as `HTTP/1.1 200 OK`. | ||||||
| /// | /// | ||||||
| @@ -573,7 +578,7 @@ pub fn read_status_line<R: Reader>(stream: &mut R) -> HttpResult<StatusLine> { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Read the StatusCode from a stream. | /// Read the StatusCode from a stream. | ||||||
| pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<status::StatusCode> { | pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<RawStatus> { | ||||||
|     let code = [ |     let code = [ | ||||||
|         try!(stream.read_byte()), |         try!(stream.read_byte()), | ||||||
|         try!(stream.read_byte()), |         try!(stream.read_byte()), | ||||||
| @@ -581,25 +586,56 @@ pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<status::StatusCode> | |||||||
|     ]; |     ]; | ||||||
|  |  | ||||||
|     let code = match str::from_utf8(code.as_slice()).and_then(from_str::<u16>) { |     let code = match str::from_utf8(code.as_slice()).and_then(from_str::<u16>) { | ||||||
|         Some(num) => match FromPrimitive::from_u16(num) { |         Some(num) => num, | ||||||
|             Some(code) => code, |  | ||||||
|             None => return Err(HttpStatusError) |  | ||||||
|         }, |  | ||||||
|         None => return Err(HttpStatusError) |         None => return Err(HttpStatusError) | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // reason is purely for humans, so just consume it till we get to CRLF |     match try!(stream.read_byte()) { | ||||||
|  |         b' ' => (), | ||||||
|  |         _ => return Err(HttpStatusError) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let mut buf = [b' ', ..16]; | ||||||
|  |  | ||||||
|  |     { | ||||||
|  |         let mut bufwrt = BufWriter::new(&mut buf); | ||||||
|         loop { |         loop { | ||||||
|             match try!(stream.read_byte()) { |             match try!(stream.read_byte()) { | ||||||
|                 CR => match try!(stream.read_byte()) { |                 CR => match try!(stream.read_byte()) { | ||||||
|                     LF => break, |                     LF => break, | ||||||
|                     _ => return Err(HttpStatusError) |                     _ => return Err(HttpStatusError) | ||||||
|                 }, |                 }, | ||||||
|             _ => () |                 b => match bufwrt.write_u8(b) { | ||||||
|  |                     Ok(_) => (), | ||||||
|  |                     Err(_) => { | ||||||
|  |                         // what sort of reason phrase is this long? | ||||||
|  |                         return Err(HttpStatusError); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Ok(code) |     let reason = match str::from_utf8(buf[]) { | ||||||
|  |         Some(s) => s.trim(), | ||||||
|  |         None => return Err(HttpStatusError) | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let reason = match from_u16::<StatusCode>(code) { | ||||||
|  |         Some(status) => match status.canonical_reason() { | ||||||
|  |             Some(phrase) => { | ||||||
|  |                 if phrase == reason { | ||||||
|  |                     Slice(phrase) | ||||||
|  |                 } else { | ||||||
|  |                     Owned(reason.into_string()) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             _ => Owned(reason.into_string()) | ||||||
|  |         }, | ||||||
|  |         None => return Err(HttpStatusError) | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     Ok(RawStatus(code, reason)) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[inline] | #[inline] | ||||||
| @@ -614,18 +650,19 @@ fn expect(r: IoResult<u8>, expected: u8) -> HttpResult<()> { | |||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use std::io::{mod, MemReader, MemWriter}; |     use std::io::{mod, MemReader, MemWriter}; | ||||||
|  |     use std::str::{Slice, Owned}; | ||||||
|     use test::Bencher; |     use test::Bencher; | ||||||
|     use uri::RequestUri; |     use uri::RequestUri; | ||||||
|     use uri::RequestUri::{Star, AbsoluteUri, AbsolutePath, Authority}; |     use uri::RequestUri::{Star, AbsoluteUri, AbsolutePath, Authority}; | ||||||
|     use method; |     use method; | ||||||
|     use status; |  | ||||||
|     use version::HttpVersion; |     use version::HttpVersion; | ||||||
|     use version::HttpVersion::{Http10, Http11, Http20}; |     use version::HttpVersion::{Http10, Http11, Http20}; | ||||||
|     use HttpError::{HttpVersionError, HttpMethodError}; |     use HttpError::{HttpVersionError, HttpMethodError}; | ||||||
|     use HttpResult; |     use HttpResult; | ||||||
|     use url::Url; |     use url::Url; | ||||||
|  |  | ||||||
|     use super::{read_method, read_uri, read_http_version, read_header, RawHeaderLine, read_status}; |     use super::{read_method, read_uri, read_http_version, read_header, | ||||||
|  |                 RawHeaderLine, read_status, RawStatus}; | ||||||
|  |  | ||||||
|     fn mem(s: &str) -> MemReader { |     fn mem(s: &str) -> MemReader { | ||||||
|         MemReader::new(s.as_bytes().to_vec()) |         MemReader::new(s.as_bytes().to_vec()) | ||||||
| @@ -679,11 +716,13 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_read_status() { |     fn test_read_status() { | ||||||
|         fn read(s: &str, result: HttpResult<status::StatusCode>) { |         fn read(s: &str, result: HttpResult<RawStatus>) { | ||||||
|             assert_eq!(read_status(&mut mem(s)), result); |             assert_eq!(read_status(&mut mem(s)), result); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         read("200 OK\r\n", Ok(status::StatusCode::Ok)); |         read("200 OK\r\n", Ok(RawStatus(200, Slice("OK")))); | ||||||
|  |         read("404 Not Found\r\n", Ok(RawStatus(404, Slice("Not Found")))); | ||||||
|  |         read("200 crazy pants\r\n", Ok(RawStatus(200, Owned("crazy pants".to_string())))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
| @@ -725,4 +764,10 @@ mod tests { | |||||||
|         b.iter(|| assert_eq!(read_method(&mut mem("CONNECT ")), Ok(method::Method::Connect))); |         b.iter(|| assert_eq!(read_method(&mut mem("CONNECT ")), Ok(method::Method::Connect))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[bench] | ||||||
|  |     fn bench_read_status(b: &mut Bencher) { | ||||||
|  |         b.bytes = b"404 Not Found\r\n".len() as u64; | ||||||
|  |         b.iter(|| assert_eq!(read_status(&mut mem("404 Not Found\r\n")), Ok(RawStatus(404, Slice("Not Found"))))); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user