mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-12-27 21:15:41 +03:00
Add HTTP API for libcurl
This commit is contained in:
parent
7152d4a0bd
commit
190d0871d5
@ -16,40 +16,438 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::core::{HttpError, Request, Response, Method};
|
use std::str;
|
||||||
use curl::easy::Easy;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
use curl::easy;
|
||||||
pub struct Client {}
|
|
||||||
|
use super::core::*;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Client {
|
||||||
|
pub handle: Box<easy::Easy>,
|
||||||
|
|
||||||
|
/// unfortunately, follow-location feature from libcurl can not be used
|
||||||
|
/// libcurl returns a single list of headers for the 2 responses
|
||||||
|
/// hurl needs the return the headers only for the second (last) response)
|
||||||
|
pub follow_location: bool,
|
||||||
|
pub redirect_count: usize,
|
||||||
|
pub max_redirect: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ClientOptions {
|
||||||
|
pub follow_location: bool,
|
||||||
|
pub max_redirect: Option<usize>,
|
||||||
|
pub cookie_file: Option<String>,
|
||||||
|
pub cookie_jar: Option<String>,
|
||||||
|
pub proxy: Option<String>,
|
||||||
|
pub verbose: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn execute(&self, request: &Request) -> Result<Response, HttpError> {
|
|
||||||
let mut handle = Easy::new();
|
|
||||||
let mut body = Vec::<u8>::new();
|
|
||||||
|
|
||||||
match request.method {
|
///
|
||||||
Method::Get => handle.get(true).unwrap(),
|
/// Init HTTP hurl client
|
||||||
Method::Post => handle.post(true).unwrap(),
|
///
|
||||||
Method::Put => handle.put(true).unwrap(),
|
pub fn init(options: ClientOptions) -> Client {
|
||||||
_ => { todo!()}
|
let mut h = easy::Easy::new();
|
||||||
|
|
||||||
|
// Activate cookie storage
|
||||||
|
// with or without persistence (empty string)
|
||||||
|
h.cookie_file(options.cookie_file.unwrap_or_else(|| "".to_string()).as_str()).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
if let Some(cookie_jar) = options.cookie_jar {
|
||||||
|
h.cookie_jar(cookie_jar.as_str()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.url(request.url.as_str()).unwrap();
|
if let Some(proxy) = options.proxy {
|
||||||
|
h.proxy(proxy.as_str()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
h.verbose(options.verbose).unwrap();
|
||||||
|
|
||||||
|
Client {
|
||||||
|
handle: Box::new(h),
|
||||||
|
follow_location: options.follow_location,
|
||||||
|
max_redirect: options.max_redirect,
|
||||||
|
redirect_count: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Execute an http request
|
||||||
|
///
|
||||||
|
pub fn execute(&mut self, request: &Request, redirect_count: usize) -> Result<Response, HttpError> {
|
||||||
|
self.set_url(&request.url, &request.querystring);
|
||||||
|
self.set_method(&request.method);
|
||||||
|
|
||||||
|
self.set_cookies(&request.cookies);
|
||||||
|
self.set_form(&request.form);
|
||||||
|
self.set_multipart(&request.multipart);
|
||||||
|
|
||||||
|
|
||||||
|
let b = request.body.clone();
|
||||||
|
let mut data: &[u8] = b.as_ref();
|
||||||
|
self.set_body(data);
|
||||||
|
self.set_headers(&request.headers, data.is_empty());
|
||||||
|
|
||||||
|
self.handle.debug_function(|info_type, data|
|
||||||
|
match info_type {
|
||||||
|
|
||||||
|
// return all request headers (not one by one)
|
||||||
|
easy::InfoType::HeaderOut => {
|
||||||
|
let lines = split_lines(data);
|
||||||
|
for line in lines {
|
||||||
|
eprintln!("> {}", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
easy::InfoType::HeaderIn => {
|
||||||
|
eprint!("< {}", str::from_utf8(data).unwrap());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let mut lines = vec![];
|
||||||
|
let mut body = Vec::<u8>::new();
|
||||||
{
|
{
|
||||||
let mut transfer = handle.transfer();
|
let mut transfer = self.handle.transfer();
|
||||||
|
if !data.is_empty() {
|
||||||
|
transfer.read_function(|buf| {
|
||||||
|
Ok(data.read(buf).unwrap_or(0))
|
||||||
|
}).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
transfer.header_function(|h| {
|
||||||
|
match str::from_utf8(h) {
|
||||||
|
Ok(s) => lines.push(s.to_string()),
|
||||||
|
Err(e) => println!("Error decoding header {:?}", e),
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
transfer.write_function(|data| {
|
transfer.write_function(|data| {
|
||||||
body.extend(data);
|
body.extend(data);
|
||||||
Ok(data.len())
|
Ok(data.len())
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
transfer.perform().unwrap();
|
|
||||||
|
if let Err(e) = transfer.perform() {
|
||||||
|
match e.code() {
|
||||||
|
5 => return Err(HttpError::CouldNotResolveProxyName),
|
||||||
|
6 => return Err(HttpError::CouldNotResolveHost),
|
||||||
|
7 => return Err(HttpError::FailToConnect),
|
||||||
|
_ => panic!("{:#?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let status= handle.response_code().unwrap();
|
let status = self.handle.response_code().unwrap();
|
||||||
|
let headers = self.parse_response_headers(&mut lines);
|
||||||
|
|
||||||
|
if let Some(url) = self.get_follow_location(headers.clone()) {
|
||||||
|
let request = Request {
|
||||||
|
method: Method::Get,
|
||||||
|
url,
|
||||||
|
headers: vec![],
|
||||||
|
querystring: vec![],
|
||||||
|
form: vec![],
|
||||||
|
multipart: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let redirect_count = redirect_count + 1;
|
||||||
|
if let Some(max_redirect) = self.max_redirect {
|
||||||
|
if redirect_count > max_redirect {
|
||||||
|
return Err(HttpError::TooManyRedirect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.execute(&request, redirect_count);
|
||||||
|
}
|
||||||
|
self.redirect_count = redirect_count;
|
||||||
|
|
||||||
Ok(Response {
|
Ok(Response {
|
||||||
status,
|
status,
|
||||||
body
|
headers,
|
||||||
|
body,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// set url
|
||||||
|
///
|
||||||
|
fn set_url(&mut self, url: &str, params: &[Param]) {
|
||||||
|
let url = if params.is_empty() {
|
||||||
|
url.to_string()
|
||||||
|
} else {
|
||||||
|
let url = if url.ends_with('?') {
|
||||||
|
url.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}?", url)
|
||||||
|
};
|
||||||
|
let s = self.encode_params(params);
|
||||||
|
format!("{}{}", url, s)
|
||||||
|
};
|
||||||
|
self.handle.url(url.as_str()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// set method
|
||||||
|
///
|
||||||
|
fn set_method(&mut self, method: &Method) {
|
||||||
|
match method {
|
||||||
|
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(),
|
||||||
|
Method::Delete => self.handle.custom_request("DELETE").unwrap(),
|
||||||
|
Method::Connect => self.handle.custom_request("CONNECT").unwrap(),
|
||||||
|
Method::Options => self.handle.custom_request("OPTIONS").unwrap(),
|
||||||
|
Method::Trace => self.handle.custom_request("TRACE").unwrap(),
|
||||||
|
Method::Patch => self.handle.custom_request("PATCH").unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// set request headers
|
||||||
|
///
|
||||||
|
fn set_headers(&mut self, headers: &[Header], default_content_type: bool) {
|
||||||
|
let mut list = easy::List::new();
|
||||||
|
|
||||||
|
for header in headers.to_owned() {
|
||||||
|
list.append(format!("{}: {}", header.name, header.value).as_str()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if get_header_values(headers.to_vec(), "Content-Type".to_string()).is_empty() && !default_content_type {
|
||||||
|
list.append("Content-Type:").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
list.append(format!("User-Agent: hurl/{}", clap::crate_version!()).as_str()).unwrap();
|
||||||
|
|
||||||
|
self.handle.http_headers(list).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// set request cookies
|
||||||
|
///
|
||||||
|
fn set_cookies(&mut self, cookies: &[RequestCookie]) {
|
||||||
|
for cookie in cookies {
|
||||||
|
self.handle.cookie(cookie.to_string().as_str()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// set form
|
||||||
|
///
|
||||||
|
fn set_form(&mut self, params: &[Param]) {
|
||||||
|
if !params.is_empty() {
|
||||||
|
let s = self.encode_params(params);
|
||||||
|
self.handle.post_fields_copy(s.as_str().as_bytes()).unwrap();
|
||||||
|
//self.handle.write_function(sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// set form
|
||||||
|
///
|
||||||
|
fn set_multipart(&mut self, params: &[MultipartParam]) {
|
||||||
|
if !params.is_empty() {
|
||||||
|
let mut form = easy::Form::new();
|
||||||
|
for param in params {
|
||||||
|
match param {
|
||||||
|
MultipartParam::Param(Param { name, value }) => {
|
||||||
|
form.part(name)
|
||||||
|
.contents(value.as_bytes())
|
||||||
|
.add()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
MultipartParam::FileParam(FileParam { name, filename, data, content_type }) => {
|
||||||
|
form.part(name)
|
||||||
|
.buffer(filename, data.clone())
|
||||||
|
.content_type(content_type)
|
||||||
|
.add()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.handle.httppost(form).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// set body
|
||||||
|
///
|
||||||
|
fn set_body(&mut self, data: &[u8]) {
|
||||||
|
if !data.is_empty() {
|
||||||
|
self.handle.post(true).unwrap();
|
||||||
|
self.handle.post_field_size(data.len() as u64).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// encode parameters
|
||||||
|
///
|
||||||
|
fn encode_params(&mut self, params: &[Param]) -> String {
|
||||||
|
params
|
||||||
|
.iter()
|
||||||
|
.map(|p| {
|
||||||
|
let value = self.handle.url_encode(p.value.as_bytes());
|
||||||
|
format!("{}={}", p.name, value)
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("&")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// 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()) {
|
||||||
|
headers.push(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headers
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// retrieve an optional location to follow
|
||||||
|
/// You need:
|
||||||
|
/// 1. the option follow_location set to true
|
||||||
|
/// 2. a 3xx response code
|
||||||
|
/// 3. a header Location
|
||||||
|
///
|
||||||
|
fn get_follow_location(&mut self, headers: Vec<Header>) -> Option<String> {
|
||||||
|
if !self.follow_location {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let response_code = self.handle.response_code().unwrap();
|
||||||
|
if response_code < 300 || response_code >= 400 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let location = match get_header_values(headers, "Location".to_string()).get(0) {
|
||||||
|
None => return None,
|
||||||
|
Some(value) => value.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if location.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// get cookie storage
|
||||||
|
///
|
||||||
|
pub fn get_cookie_storage(&mut self) -> Vec<Cookie> {
|
||||||
|
let list = self.handle.cookies().unwrap();
|
||||||
|
let mut cookies = vec![];
|
||||||
|
for cookie in list.iter() {
|
||||||
|
let line = str::from_utf8(cookie).unwrap().to_string();
|
||||||
|
let fields: Vec<&str> = line.split('\t').collect();
|
||||||
|
|
||||||
|
let domain = fields.get(0).unwrap().to_string();
|
||||||
|
let include_subdomain = fields.get(1).unwrap().to_string();
|
||||||
|
let path = fields.get(2).unwrap().to_string();
|
||||||
|
let https = fields.get(3).unwrap().to_string();
|
||||||
|
let expires = fields.get(4).unwrap().to_string();
|
||||||
|
let name = fields.get(5).unwrap().to_string();
|
||||||
|
let value = fields.get(6).unwrap().to_string();
|
||||||
|
cookies.push(Cookie { domain, include_subdomain, path, https, expires, name, value });
|
||||||
|
}
|
||||||
|
cookies
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
///
|
||||||
|
/// Parse an http header line received from the server
|
||||||
|
/// It does not panic. Just return none if it can not be parsed
|
||||||
|
///
|
||||||
|
pub fn parse(line: String) -> Option<Header> {
|
||||||
|
match line.find(':') {
|
||||||
|
Some(index) => {
|
||||||
|
let (name, value) = line.split_at(index);
|
||||||
|
Some(Header {
|
||||||
|
name: name.to_string().trim().to_string(),
|
||||||
|
value: value[1..].to_string().trim().to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Split an array of bytes into http lines (\r\n separator)
|
||||||
|
///
|
||||||
|
fn split_lines(data: &[u8]) -> Vec<String> {
|
||||||
|
let mut lines = vec![];
|
||||||
|
let mut start = 0;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < (data.len() - 1) {
|
||||||
|
if data[i] == 13 && data[i + 1] == 10 {
|
||||||
|
if let Ok(s) = str::from_utf8(&data[start..i]) {
|
||||||
|
lines.push(s.to_string());
|
||||||
|
}
|
||||||
|
start = i + 2;
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_header() {
|
||||||
|
assert_eq!(Header::parse("Foo: Bar\r\n".to_string()).unwrap(),
|
||||||
|
Header {
|
||||||
|
name: "Foo".to_string(),
|
||||||
|
value: "Bar".to_string(),
|
||||||
|
});
|
||||||
|
assert_eq!(Header::parse("Location: http://localhost:8000/redirected\r\n".to_string()).unwrap(),
|
||||||
|
Header {
|
||||||
|
name: "Location".to_string(),
|
||||||
|
value: "http://localhost:8000/redirected".to_string(),
|
||||||
|
});
|
||||||
|
assert!(Header::parse("Foo".to_string()).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_split_lines_header() {
|
||||||
|
let data = b"GET /hello HTTP/1.1\r\nHost: localhost:8000\r\n\r\n";
|
||||||
|
let lines = split_lines(data);
|
||||||
|
assert_eq!(lines.len(), 3);
|
||||||
|
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(), "");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,28 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Request {
|
||||||
|
pub method: Method,
|
||||||
|
pub url: String,
|
||||||
|
pub headers: Vec<Header>,
|
||||||
|
pub querystring: Vec<Param>,
|
||||||
|
pub form: Vec<Param>,
|
||||||
|
pub multipart: Vec<MultipartParam>,
|
||||||
|
pub cookies: Vec<RequestCookie>,
|
||||||
|
pub body: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Response {
|
||||||
|
pub status: u32,
|
||||||
|
pub headers: Vec<Header>,
|
||||||
|
pub body: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Method {
|
pub enum Method {
|
||||||
Get,
|
Get,
|
||||||
@ -29,22 +51,112 @@ pub enum Method {
|
|||||||
Patch,
|
Patch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Header {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Request {
|
pub struct Param {
|
||||||
pub method: Method,
|
pub name: String,
|
||||||
pub url: String,
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum MultipartParam {
|
||||||
|
Param(Param),
|
||||||
|
FileParam(FileParam),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct FileParam {
|
||||||
|
pub name: String,
|
||||||
|
pub filename: String,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
pub content_type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Response {
|
pub struct RequestCookie {
|
||||||
pub status: u32,
|
pub name: String,
|
||||||
pub body: Vec<u8>,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Cookie {
|
||||||
|
pub domain: String,
|
||||||
|
pub include_subdomain: String,
|
||||||
|
pub path: String,
|
||||||
|
pub https: String,
|
||||||
|
pub expires: String,
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl fmt::Display for RequestCookie {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}={}", self.name, self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum HttpError {
|
pub enum HttpError {
|
||||||
UNDEFINED
|
CouldNotResolveProxyName,
|
||||||
|
CouldNotResolveHost,
|
||||||
|
FailToConnect,
|
||||||
|
TooManyRedirect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// return a list of headers values for the given header name
|
||||||
|
///
|
||||||
|
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 })
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_header_values() {
|
||||||
|
let response = Response {
|
||||||
|
status: 200,
|
||||||
|
headers: vec![
|
||||||
|
Header { name: "Content-Length".to_string(), value: "12".to_string() }
|
||||||
|
],
|
||||||
|
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());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
523
tests/libcurl.rs
523
tests/libcurl.rs
@ -1,13 +1,48 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
use curl::easy::Easy;
|
||||||
|
|
||||||
|
use hurl::http::libcurl;
|
||||||
|
use hurl::http::libcurl::client::ClientOptions;
|
||||||
|
use hurl::http::libcurl::core::*;
|
||||||
|
use server::Server;
|
||||||
|
|
||||||
|
macro_rules! t {
|
||||||
|
($e:expr) => {
|
||||||
|
match $e {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod server;
|
||||||
|
|
||||||
|
pub fn new_header(name: &str, value: &str) -> Header {
|
||||||
|
Header {
|
||||||
|
name: name.to_string(),
|
||||||
|
value: value.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_easy() {
|
fn get_easy() {
|
||||||
use curl::easy::Easy;
|
let s = Server::new();
|
||||||
let url ="http://localhost:8000/hello";
|
s.receive(
|
||||||
|
"\
|
||||||
|
GET /hello HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:$PORT\r\n\
|
||||||
|
Accept: */*\r\n\
|
||||||
|
\r\n",
|
||||||
|
);
|
||||||
|
s.send("HTTP/1.1 200 OK\r\n\r\nHello World!");
|
||||||
|
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
let mut handle = Easy::new();
|
let mut handle = Easy::new();
|
||||||
handle.url(url).unwrap();
|
|
||||||
|
handle.url(&s.url("/hello")).unwrap();
|
||||||
{
|
{
|
||||||
let mut transfer = handle.transfer();
|
let mut transfer = handle.transfer();
|
||||||
transfer.write_function(|new_data| {
|
transfer.write_function(|new_data| {
|
||||||
@ -17,25 +52,479 @@ fn test_easy() {
|
|||||||
transfer.perform().unwrap();
|
transfer.perform().unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(data, b"Hello World!");
|
assert_eq!(data, b"Hello World!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn default_client() -> libcurl::client::Client {
|
||||||
|
let options = ClientOptions {
|
||||||
|
follow_location: false,
|
||||||
|
max_redirect: None,
|
||||||
|
cookie_file: None,
|
||||||
|
cookie_jar: None,
|
||||||
|
proxy: None,
|
||||||
|
verbose: false,
|
||||||
|
};
|
||||||
|
libcurl::client::Client::init(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_get_request(url: String) -> Request {
|
||||||
|
Request {
|
||||||
|
method: Method::Get,
|
||||||
|
url,
|
||||||
|
headers: vec![],
|
||||||
|
querystring: vec![],
|
||||||
|
form: vec![],
|
||||||
|
multipart: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// region basic
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
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.status, 200);
|
||||||
|
assert_eq!(response.body, b"Hello World!".to_vec());
|
||||||
|
|
||||||
|
assert_eq!(response.headers.len(), 4);
|
||||||
|
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
|
||||||
|
|
||||||
|
// region http method
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_put() {
|
||||||
|
let mut client = default_client();
|
||||||
|
let request = Request {
|
||||||
|
method: Method::Put,
|
||||||
|
url: "http://localhost:8000/put".to_string(),
|
||||||
|
headers: vec![],
|
||||||
|
querystring: vec![],
|
||||||
|
form: vec![],
|
||||||
|
multipart: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: vec![]
|
||||||
|
};
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hello() {
|
fn test_patch() {
|
||||||
use hurl::http::libcurl;
|
|
||||||
use hurl::http::libcurl::core::*;
|
|
||||||
|
|
||||||
let client = libcurl::client::Client {};
|
let mut client = default_client();
|
||||||
let request = Request {
|
let request = Request {
|
||||||
method: Method::Get,
|
method: Method::Patch,
|
||||||
url: "http://localhost:8000/hello".to_string()
|
url: "http://localhost:8000/patch/file.txt".to_string(),
|
||||||
|
headers: vec![
|
||||||
|
Header { name: "Host".to_string(), value: "www.example.com".to_string() },
|
||||||
|
Header { name: "Content-Type".to_string(), value: "application/example".to_string() },
|
||||||
|
Header { name: "If-Match".to_string(), value: "\"e0023aa4e\"".to_string() },
|
||||||
|
],
|
||||||
|
querystring: vec![],
|
||||||
|
form: vec![],
|
||||||
|
multipart: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: vec![]
|
||||||
};
|
};
|
||||||
assert_eq!(
|
let response = client.execute(&request, 0).unwrap();
|
||||||
client.execute(&request),
|
assert_eq!(response.status, 204);
|
||||||
Ok(Response {
|
assert!(response.body.is_empty());
|
||||||
status: 200,
|
|
||||||
body: b"Hello World!".to_vec(),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region headers
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_custom_headers() {
|
||||||
|
let mut client = default_client();
|
||||||
|
let request = Request {
|
||||||
|
method: Method::Get,
|
||||||
|
url: "http://localhost:8000/custom-headers".to_string(),
|
||||||
|
headers: vec![
|
||||||
|
new_header("Fruit", "Raspberry"),
|
||||||
|
new_header("Fruit", "Apple"),
|
||||||
|
new_header("Fruit", "Banana"),
|
||||||
|
new_header("Fruit", "Grape"),
|
||||||
|
new_header("Color", "Green"),
|
||||||
|
],
|
||||||
|
querystring: vec![],
|
||||||
|
form: vec![],
|
||||||
|
multipart: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: vec![]
|
||||||
|
};
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region querystrings
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_querystring_params() {
|
||||||
|
let mut client = default_client();
|
||||||
|
let request = Request {
|
||||||
|
method: Method::Get,
|
||||||
|
url: "http://localhost:8000/querystring-params".to_string(),
|
||||||
|
headers: vec![],
|
||||||
|
querystring: vec![
|
||||||
|
Param { name: "param1".to_string(), value: "value1".to_string() },
|
||||||
|
Param { name: "param2".to_string(), value: "".to_string() },
|
||||||
|
Param { name: "param3".to_string(), value: "a=b".to_string() },
|
||||||
|
Param { name: "param4".to_string(), value: "1,2,3".to_string() }
|
||||||
|
],
|
||||||
|
form: vec![],
|
||||||
|
multipart: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: vec![]
|
||||||
|
};
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region form params
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_form_params() {
|
||||||
|
let mut client = default_client();
|
||||||
|
let request = Request {
|
||||||
|
method: Method::Post,
|
||||||
|
url: "http://localhost:8000/form-params".to_string(),
|
||||||
|
headers: vec![],
|
||||||
|
querystring: vec![],
|
||||||
|
form: vec![
|
||||||
|
Param { name: "param1".to_string(), value: "value1".to_string() },
|
||||||
|
Param { name: "param2".to_string(), value: "".to_string() },
|
||||||
|
Param { name: "param3".to_string(), value: "a=b".to_string() },
|
||||||
|
Param { name: "param4".to_string(), value: "a%3db".to_string() }
|
||||||
|
],
|
||||||
|
multipart: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: vec![]
|
||||||
|
};
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
|
|
||||||
|
// make sure you can reuse client for other request
|
||||||
|
let request = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert_eq!(response.body, b"Hello World!".to_vec());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region redirect
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_follow_location() {
|
||||||
|
let request = default_get_request("http://localhost:8000/redirect".to_string());
|
||||||
|
|
||||||
|
let mut client = default_client();
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 302);
|
||||||
|
assert_eq!(response.get_header_values("Location".to_string()).get(0).unwrap(),
|
||||||
|
"http://localhost:8000/redirected");
|
||||||
|
assert_eq!(client.redirect_count, 0);
|
||||||
|
|
||||||
|
let options = ClientOptions {
|
||||||
|
follow_location: true,
|
||||||
|
max_redirect: None,
|
||||||
|
cookie_file: None,
|
||||||
|
cookie_jar: None,
|
||||||
|
proxy: None,
|
||||||
|
verbose: false,
|
||||||
|
};
|
||||||
|
let mut client = libcurl::client::Client::init(options);
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert_eq!(response.get_header_values("Content-Length".to_string()).get(0).unwrap(), "0");
|
||||||
|
assert_eq!(client.redirect_count, 1);
|
||||||
|
|
||||||
|
|
||||||
|
// make sure that the redirect count is reset to 0
|
||||||
|
let request = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert_eq!(response.body, b"Hello World!".to_vec());
|
||||||
|
assert_eq!(client.redirect_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_max_redirect() {
|
||||||
|
let options = ClientOptions {
|
||||||
|
follow_location: true,
|
||||||
|
max_redirect: Some(10),
|
||||||
|
cookie_file: None,
|
||||||
|
cookie_jar: None,
|
||||||
|
proxy: None,
|
||||||
|
verbose: false,
|
||||||
|
};
|
||||||
|
let mut client = libcurl::client::Client::init(options);
|
||||||
|
let request = default_get_request("http://localhost:8000/redirect".to_string());
|
||||||
|
let response = client.execute(&request, 5).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert_eq!(client.redirect_count, 6);
|
||||||
|
|
||||||
|
let error = client.execute(&request, 11).err().unwrap();
|
||||||
|
assert_eq!(error, HttpError::TooManyRedirect);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region multipart
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multipart_form_data() {
|
||||||
|
|
||||||
|
let mut client = default_client();
|
||||||
|
let request = Request {
|
||||||
|
method: Method::Post,
|
||||||
|
url: "http://localhost:8000/multipart-form-data".to_string(),
|
||||||
|
headers: vec![],
|
||||||
|
querystring: vec![],
|
||||||
|
form: vec![],
|
||||||
|
multipart: vec![
|
||||||
|
MultipartParam::Param(Param{
|
||||||
|
name: "key1".to_string(),
|
||||||
|
value: "value1".to_string()
|
||||||
|
}),
|
||||||
|
MultipartParam::FileParam(FileParam{
|
||||||
|
name: "upload1".to_string(),
|
||||||
|
filename: "hello.txt".to_string(),
|
||||||
|
data: b"Hello World!".to_vec(),
|
||||||
|
content_type: "text/plain".to_string()
|
||||||
|
}),
|
||||||
|
MultipartParam::FileParam(FileParam{
|
||||||
|
name: "upload2".to_string(),
|
||||||
|
filename: "hello.html".to_string(),
|
||||||
|
data: b"Hello <b>World</b>!".to_vec(),
|
||||||
|
content_type: "text/html".to_string()
|
||||||
|
}),
|
||||||
|
MultipartParam::FileParam(FileParam{
|
||||||
|
name: "upload3".to_string(),
|
||||||
|
filename: "hello.txt".to_string(),
|
||||||
|
data: b"Hello World!".to_vec(),
|
||||||
|
content_type: "text/html".to_string()
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
cookies: vec![],
|
||||||
|
body: vec![]
|
||||||
|
|
||||||
|
};
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region http body
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_post_bytes() {
|
||||||
|
|
||||||
|
let mut client = default_client();
|
||||||
|
let request = Request {
|
||||||
|
method: Method::Post,
|
||||||
|
url: "http://localhost:8000/post-base64".to_string(),
|
||||||
|
headers: vec![],
|
||||||
|
querystring: vec![],
|
||||||
|
form: vec![],
|
||||||
|
multipart: vec![],
|
||||||
|
cookies: vec![],
|
||||||
|
body: b"Hello World!".to_vec(),
|
||||||
|
};
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region error
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_could_not_resolve_host() {
|
||||||
|
let mut client = default_client();
|
||||||
|
let request = default_get_request("http://unknown".to_string());
|
||||||
|
let error = client.execute(&request, 0).err().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(error, HttpError::CouldNotResolveHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_fail_to_connect() {
|
||||||
|
let mut client = default_client();
|
||||||
|
let request = default_get_request("http://localhost:9999".to_string());
|
||||||
|
let error = client.execute(&request, 0).err().unwrap();
|
||||||
|
assert_eq!(error, HttpError::FailToConnect);
|
||||||
|
|
||||||
|
|
||||||
|
let options = ClientOptions {
|
||||||
|
follow_location: false,
|
||||||
|
max_redirect: None,
|
||||||
|
cookie_file: None,
|
||||||
|
cookie_jar: None,
|
||||||
|
proxy: Some("localhost:9999".to_string()),
|
||||||
|
verbose: true,
|
||||||
|
};
|
||||||
|
let mut client = libcurl::client::Client::init(options);
|
||||||
|
let request = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
|
let error = client.execute(&request, 0).err().unwrap();
|
||||||
|
assert_eq!(error, HttpError::FailToConnect);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_could_not_resolve_proxy_name() {
|
||||||
|
let options = ClientOptions {
|
||||||
|
follow_location: false,
|
||||||
|
max_redirect: None,
|
||||||
|
cookie_file: None,
|
||||||
|
cookie_jar: None,
|
||||||
|
proxy: Some("unknown".to_string()),
|
||||||
|
verbose: false,
|
||||||
|
};
|
||||||
|
let mut client = libcurl::client::Client::init(options);
|
||||||
|
let request = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
|
let error = client.execute(&request, 0).err().unwrap();
|
||||||
|
assert_eq!(error, HttpError::CouldNotResolveProxyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region cookie
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cookie() {
|
||||||
|
let mut client = default_client();
|
||||||
|
let request = Request {
|
||||||
|
method: Method::Get,
|
||||||
|
url: "http://localhost:8000/cookies/set-request-cookie1-valueA".to_string(),
|
||||||
|
headers: vec![],
|
||||||
|
querystring: vec![],
|
||||||
|
form: vec![],
|
||||||
|
multipart: vec![],
|
||||||
|
cookies: vec![
|
||||||
|
RequestCookie { name: "cookie1".to_string(), value: "valueA".to_string() }
|
||||||
|
],
|
||||||
|
body: vec![]
|
||||||
|
};
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
|
|
||||||
|
// For the time-being setting a cookie on a request
|
||||||
|
// update the cookie store as well
|
||||||
|
// The same cookie does not need to be set explicitly on further requests
|
||||||
|
let request = default_get_request("http://localhost:8000/cookies/set-request-cookie1-valueA".to_string());
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cookie_storage() {
|
||||||
|
let mut client = default_client();
|
||||||
|
let request = default_get_request("http://localhost:8000/cookies/set-session-cookie2-valueA".to_string());
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
|
let cookie_store = client.get_cookie_storage();
|
||||||
|
assert_eq!(cookie_store.get(0).unwrap().clone(), Cookie {
|
||||||
|
domain: "localhost".to_string(),
|
||||||
|
include_subdomain: "FALSE".to_string(),
|
||||||
|
path: "/".to_string(),
|
||||||
|
https: "FALSE".to_string(),
|
||||||
|
expires: "0".to_string(),
|
||||||
|
name: "cookie2".to_string(),
|
||||||
|
value: "valueA".to_string(),
|
||||||
|
});
|
||||||
|
let request = default_get_request("http://localhost:8000/cookies/assert-that-cookie2-is-valueA".to_string());
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cookie_file() {
|
||||||
|
let temp_file = "/tmp/cookies";
|
||||||
|
let mut file = File::create(temp_file).expect("can not create temp file!");
|
||||||
|
file.write_all(b"localhost\tFALSE\t/\tFALSE\t0\tcookie2\tvalueA\n").unwrap();
|
||||||
|
|
||||||
|
let options = ClientOptions {
|
||||||
|
follow_location: false,
|
||||||
|
max_redirect: None,
|
||||||
|
cookie_file: Some(temp_file.to_string()),
|
||||||
|
cookie_jar: None,
|
||||||
|
proxy: None,
|
||||||
|
verbose: false,
|
||||||
|
};
|
||||||
|
let mut client = libcurl::client::Client::init(options);
|
||||||
|
let request = default_get_request("http://localhost:8000/cookies/assert-that-cookie2-is-valueA".to_string());
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert!(response.body.is_empty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region proxy
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_proxy() {
|
||||||
|
// mitmproxy listening on port 8080
|
||||||
|
let options = ClientOptions {
|
||||||
|
follow_location: false,
|
||||||
|
max_redirect: None,
|
||||||
|
cookie_file: None,
|
||||||
|
cookie_jar: None,
|
||||||
|
proxy: Some("localhost:8080".to_string()),
|
||||||
|
verbose: false,
|
||||||
|
};
|
||||||
|
let mut client = libcurl::client::Client::init(options);
|
||||||
|
let request = default_get_request("http://localhost:8000/hello".to_string());
|
||||||
|
let response = client.execute(&request, 0).unwrap();
|
||||||
|
assert_eq!(response.status, 200);
|
||||||
|
assert_eq!(response.body, b"Hello World!".to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user