Add http version in response

This commit is contained in:
Fabrice Reix 2020-09-11 21:04:05 +02:00
parent 618634a025
commit abdbc6c13a
3 changed files with 51 additions and 23 deletions

View File

@ -48,7 +48,6 @@ pub struct ClientOptions {
impl Client {
///
/// Init HTTP hurl client
///
@ -107,7 +106,7 @@ impl Client {
}
}
easy::InfoType::HeaderIn => {
eprint!("< {}", str::from_utf8(data).unwrap());
eprint!("< {}", str::from_utf8(data).unwrap());
}
_ => {}
}
@ -147,8 +146,11 @@ impl Client {
}
let status = self.handle.response_code().unwrap();
let first_line = lines.remove(0); // remove the status line
let version = self.parse_response_version(first_line)?;
let headers = self.parse_response_headers(&mut lines);
if let Some(url) = self.get_follow_location(headers.clone()) {
let request = Request {
method: Method::Get,
@ -173,6 +175,7 @@ impl Client {
self.redirect_count = redirect_count;
Ok(Response {
version,
status,
headers,
body,
@ -203,7 +206,7 @@ impl Client {
///
fn set_method(&mut self, method: &Method) {
match method {
Method::Get => self.handle.custom_request("GET").unwrap(),
Method::Get => self.handle.custom_request("GET").unwrap(),
Method::Post => self.handle.custom_request("POST").unwrap(),
Method::Put => self.handle.custom_request("PUT").unwrap(),
Method::Head => self.handle.custom_request("HEAD").unwrap(),
@ -308,12 +311,27 @@ impl Client {
}
///
/// parse response version
///
fn parse_response_version(&mut self, line: String) -> Result<Version, HttpError> {
if line.starts_with("HTTP/1.0") {
Ok(Version::Http10)
} else if line.starts_with("HTTP/1.1") {
Ok(Version::Http11)
} else if line.starts_with("HTTP/2") {
Ok(Version::Http2)
} else {
Err(HttpError::CouldNotParseResponse)
}
}
///
/// parse headers from libcurl responses
///
fn parse_response_headers(&mut self, lines: &mut Vec<String>) -> Vec<Header> {
let mut headers: Vec<Header> = vec![];
lines.remove(0); // remove the status line
lines.pop(); // remove the blank line between headers and body
for line in lines {
if let Some(header) = Header::parse(line.to_string()) {
@ -398,7 +416,6 @@ impl Header {
}
///
/// Split an array of bytes into http lines (\r\n separator)
///
@ -448,6 +465,5 @@ mod tests {
assert_eq!(lines.get(0).unwrap().as_str(), "GET /hello HTTP/1.1");
assert_eq!(lines.get(1).unwrap().as_str(), "Host: localhost:8000");
assert_eq!(lines.get(2).unwrap().as_str(), "");
}
}

View File

@ -32,6 +32,7 @@ pub struct Request {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Response {
pub version: Version,
pub status: u32,
pub headers: Vec<Header>,
pub body: Vec<u8>,
@ -51,6 +52,13 @@ pub enum Method {
Patch,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Version {
Http10,
Http11,
Http2,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Header {
pub name: String,
@ -96,12 +104,9 @@ pub struct Cookie {
}
impl fmt::Display for RequestCookie {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}={}", self.name, self.value)
write!(f, "{}={}", self.name, self.value)
}
}
@ -111,22 +116,18 @@ pub enum HttpError {
CouldNotResolveProxyName,
CouldNotResolveHost,
FailToConnect,
TooManyRedirect
TooManyRedirect,
CouldNotParseResponse,
}
impl Response {
///
/// return a list of headers values for the given header name
///
pub fn get_header_values(&self, expected_name: String) -> Vec<String> {
self.headers
.iter()
.filter_map(|Header{ name, value}| if name.clone() == expected_name { Some(value.to_string())} else { None })
.collect()
get_header_values(self.headers.clone(), expected_name)
}
}
@ -136,27 +137,38 @@ impl Response {
pub fn get_header_values(headers: Vec<Header>, expected_name: String) -> Vec<String> {
headers
.iter()
.filter_map(|Header{ name, value}| if name.clone() == expected_name { Some(value.to_string())} else { None })
.filter_map(|Header { name, value }| if name.clone() == expected_name { Some(value.to_string()) } else { None })
.collect()
}
#[cfg(test)]
mod tests {
pub mod tests {
use super::*;
#[test]
fn get_header_values() {
let response = Response {
version: Version::Http10,
status: 200,
headers: vec![
Header { name: "Content-Length".to_string(), value: "12".to_string() }
],
body: vec![]
body: vec![],
};
assert_eq!(response.get_header_values("Content-Length".to_string()), vec!["12".to_string()]);
assert!(response.get_header_values("Unknown".to_string()).is_empty());
}
pub fn hello_http_request() -> Request {
Request {
method: Method::Get,
url: "http://localhost:8000/hello".to_string(),
querystring: vec![],
headers: vec![],
cookies: vec![],
body: vec![],
multipart: vec![],
form: vec![],
}
}
}

View File

@ -88,6 +88,7 @@ fn test_hello() {
let mut client = default_client();
let request = default_get_request("http://localhost:8000/hello".to_string());
let response = client.execute(&request, 0).unwrap();
assert_eq!(response.version, Version::Http10);
assert_eq!(response.status, 200);
assert_eq!(response.body, b"Hello World!".to_vec());
@ -95,7 +96,6 @@ fn test_hello() {
assert!(response.headers.contains(&Header { name: "Content-Length".to_string(), value: "12".to_string() }));
assert!(response.headers.contains(&Header { name: "Content-Type".to_string(), value: "text/html; charset=utf-8".to_string() }));
assert_eq!(response.get_header_values("Date".to_string()).len(), 1);
}
// endregion