feat(client): add a status_raw value
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
//! Client Responses
|
||||
use std::num::FromPrimitive;
|
||||
use std::io::{BufferedReader, IoResult};
|
||||
|
||||
use header;
|
||||
use header::common::{ContentLength, TransferEncoding};
|
||||
use header::common::transfer_encoding::Encoding::Chunked;
|
||||
use net::{NetworkStream, HttpStream};
|
||||
use http::{read_status_line, HttpReader};
|
||||
use http::{read_status_line, HttpReader, RawStatus};
|
||||
use http::HttpReader::{SizedReader, ChunkedReader, EofReader};
|
||||
use status;
|
||||
use version;
|
||||
use HttpResult;
|
||||
use HttpError::HttpStatusError;
|
||||
|
||||
/// A response for a client request to a remote server.
|
||||
pub struct Response<S = HttpStream> {
|
||||
@@ -19,6 +21,7 @@ pub struct Response<S = HttpStream> {
|
||||
pub headers: header::Headers,
|
||||
/// The HTTP version of this response from the server.
|
||||
pub version: version::HttpVersion,
|
||||
status_raw: RawStatus,
|
||||
body: HttpReader<BufferedReader<Box<NetworkStream + Send>>>,
|
||||
}
|
||||
|
||||
@@ -27,10 +30,14 @@ impl Response {
|
||||
/// Creates a new response from a server.
|
||||
pub fn new(stream: Box<NetworkStream + Send>) -> HttpResult<Response> {
|
||||
let mut stream = BufferedReader::new(stream);
|
||||
let (version, status) = try!(read_status_line(&mut stream));
|
||||
let headers = try!(header::Headers::from_raw(&mut stream));
|
||||
|
||||
let (version, raw_status) = try!(read_status_line(&mut stream));
|
||||
let status = match FromPrimitive::from_u16(raw_status.0) {
|
||||
Some(status) => status,
|
||||
None => return Err(HttpStatusError)
|
||||
};
|
||||
debug!("{} {}", version, status);
|
||||
|
||||
let headers = try!(header::Headers::from_raw(&mut stream));
|
||||
debug!("{}", headers);
|
||||
|
||||
let body = if headers.has::<TransferEncoding>() {
|
||||
@@ -64,9 +71,15 @@ impl Response {
|
||||
version: version,
|
||||
headers: headers,
|
||||
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.
|
||||
pub fn unwrap(self) -> Box<NetworkStream + Send> {
|
||||
self.body.unwrap().unwrap()
|
||||
@@ -84,9 +97,11 @@ impl Reader for Response {
|
||||
mod tests {
|
||||
use std::boxed::BoxAny;
|
||||
use std::io::BufferedReader;
|
||||
use std::str::Slice;
|
||||
|
||||
use header::Headers;
|
||||
use http::HttpReader::EofReader;
|
||||
use http::RawStatus;
|
||||
use mock::MockStream;
|
||||
use net::NetworkStream;
|
||||
use status;
|
||||
@@ -101,7 +116,8 @@ mod tests {
|
||||
status: status::StatusCode::Ok,
|
||||
headers: Headers::new(),
|
||||
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();
|
||||
|
||||
77
src/http.rs
77
src/http.rs
@@ -1,13 +1,14 @@
|
||||
//! Pieces pertaining to the HTTP message protocol.
|
||||
use std::cmp::min;
|
||||
use std::fmt;
|
||||
use std::io::{mod, Reader, IoResult};
|
||||
use std::str;
|
||||
use std::io::{mod, Reader, IoResult, BufWriter};
|
||||
use std::num::from_u16;
|
||||
use std::str::{mod, SendStr, Slice, Owned};
|
||||
|
||||
use url::Url;
|
||||
|
||||
use method;
|
||||
use status;
|
||||
use status::StatusCode;
|
||||
use uri;
|
||||
use uri::RequestUri::{AbsolutePath, AbsoluteUri, Authority, Star};
|
||||
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`
|
||||
///
|
||||
/// 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`.
|
||||
///
|
||||
@@ -573,7 +578,7 @@ pub fn read_status_line<R: Reader>(stream: &mut R) -> HttpResult<StatusLine> {
|
||||
}
|
||||
|
||||
/// 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 = [
|
||||
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>) {
|
||||
Some(num) => match FromPrimitive::from_u16(num) {
|
||||
Some(code) => code,
|
||||
None => return Err(HttpStatusError)
|
||||
},
|
||||
Some(num) => num,
|
||||
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 {
|
||||
match try!(stream.read_byte()) {
|
||||
CR => match try!(stream.read_byte()) {
|
||||
LF => break,
|
||||
_ => 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]
|
||||
@@ -614,18 +650,19 @@ fn expect(r: IoResult<u8>, expected: u8) -> HttpResult<()> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::{mod, MemReader, MemWriter};
|
||||
use std::str::{Slice, Owned};
|
||||
use test::Bencher;
|
||||
use uri::RequestUri;
|
||||
use uri::RequestUri::{Star, AbsoluteUri, AbsolutePath, Authority};
|
||||
use method;
|
||||
use status;
|
||||
use version::HttpVersion;
|
||||
use version::HttpVersion::{Http10, Http11, Http20};
|
||||
use HttpError::{HttpVersionError, HttpMethodError};
|
||||
use HttpResult;
|
||||
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 {
|
||||
MemReader::new(s.as_bytes().to_vec())
|
||||
@@ -679,11 +716,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
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);
|
||||
}
|
||||
|
||||
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]
|
||||
@@ -725,4 +764,10 @@ mod tests {
|
||||
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