mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-12-23 11:02:43 +03:00
Merge pull request #22 from Orange-OpenSource/feature/refacto-http
Clean/Refacto http module
This commit is contained in:
commit
7fbedb5a7a
@ -30,7 +30,7 @@ use clap::{AppSettings, ArgMatches};
|
||||
use hurl::cli;
|
||||
use hurl::core::common::FormatError;
|
||||
use hurl::html;
|
||||
use hurl::http::libcurl;
|
||||
use hurl::http;
|
||||
use hurl::parser;
|
||||
use hurl::runner;
|
||||
use hurl::runner::core::*;
|
||||
@ -109,7 +109,7 @@ fn execute(filename: &str,
|
||||
let proxy = cli_options.proxy;
|
||||
let no_proxy = cli_options.no_proxy;
|
||||
let cookie_input_file = cli_options.cookie_input_file;
|
||||
let options = libcurl::client::ClientOptions {
|
||||
let options = http::ClientOptions {
|
||||
follow_location,
|
||||
max_redirect,
|
||||
cookie_input_file,
|
||||
@ -118,7 +118,7 @@ fn execute(filename: &str,
|
||||
verbose,
|
||||
insecure,
|
||||
};
|
||||
let mut client = libcurl::client::Client::init(options);
|
||||
let mut client = http::Client::init(options);
|
||||
|
||||
|
||||
let context_dir = match file_root {
|
||||
|
@ -16,15 +16,26 @@
|
||||
*
|
||||
*/
|
||||
|
||||
use std::io::Read;
|
||||
use std::str;
|
||||
|
||||
use curl::easy;
|
||||
use encoding::all::ISO_8859_1;
|
||||
use encoding::{DecoderTrap, Encoding};
|
||||
|
||||
use super::core::*;
|
||||
use std::io::Read;
|
||||
use encoding::{Encoding, DecoderTrap};
|
||||
use encoding::all::ISO_8859_1;
|
||||
use super::request::*;
|
||||
use super::response::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum HttpError {
|
||||
CouldNotResolveProxyName,
|
||||
CouldNotResolveHost,
|
||||
FailToConnect,
|
||||
TooManyRedirect,
|
||||
CouldNotParseResponse,
|
||||
SSLCertificate,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Client {
|
||||
@ -50,9 +61,7 @@ pub struct ClientOptions {
|
||||
pub insecure: bool,
|
||||
}
|
||||
|
||||
|
||||
impl Client {
|
||||
|
||||
///
|
||||
/// Init HTTP hurl client
|
||||
///
|
||||
@ -61,7 +70,13 @@ impl Client {
|
||||
|
||||
// Activate cookie storage
|
||||
// with or without persistence (empty string)
|
||||
h.cookie_file(options.cookie_input_file.unwrap_or_else(|| "".to_string()).as_str()).unwrap();
|
||||
h.cookie_file(
|
||||
options
|
||||
.cookie_input_file
|
||||
.unwrap_or_else(|| "".to_string())
|
||||
.as_str(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let Some(proxy) = options.proxy {
|
||||
h.proxy(proxy.as_str()).unwrap();
|
||||
@ -93,7 +108,11 @@ impl Client {
|
||||
///
|
||||
/// Execute an http request
|
||||
///
|
||||
pub fn execute(&mut self, request: &Request, redirect_count: usize) -> Result<Response, HttpError> {
|
||||
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);
|
||||
|
||||
@ -101,16 +120,13 @@ impl Client {
|
||||
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);
|
||||
|
||||
|
||||
self.handle.debug_function(|info_type, data|
|
||||
match info_type {
|
||||
|
||||
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);
|
||||
@ -124,30 +140,34 @@ impl Client {
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut lines = vec![];
|
||||
let mut body = Vec::<u8>::new();
|
||||
{
|
||||
let mut transfer = self.handle.transfer();
|
||||
if !data.is_empty() {
|
||||
transfer.read_function(|buf| {
|
||||
Ok(data.read(buf).unwrap_or(0))
|
||||
}).unwrap();
|
||||
transfer
|
||||
.read_function(|buf| Ok(data.read(buf).unwrap_or(0)))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
transfer.header_function(|h| {
|
||||
if let Some(s) = decode_header(h) {
|
||||
lines.push(s)
|
||||
}
|
||||
true
|
||||
}).unwrap();
|
||||
transfer
|
||||
.header_function(|h| {
|
||||
if let Some(s) = decode_header(h) {
|
||||
lines.push(s)
|
||||
}
|
||||
true
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
transfer.write_function(|data| {
|
||||
body.extend(data);
|
||||
Ok(data.len())
|
||||
}).unwrap();
|
||||
transfer
|
||||
.write_function(|data| {
|
||||
body.extend(data);
|
||||
Ok(data.len())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if let Err(e) = transfer.perform() {
|
||||
match e.code() {
|
||||
@ -161,11 +181,10 @@ impl Client {
|
||||
}
|
||||
|
||||
let status = self.handle.response_code().unwrap();
|
||||
let first_line = lines.remove(0); // remove the status line
|
||||
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,
|
||||
@ -219,7 +238,6 @@ impl Client {
|
||||
self.handle.url(url.as_str()).unwrap();
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// set method
|
||||
///
|
||||
@ -237,7 +255,6 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// set request headers
|
||||
///
|
||||
@ -245,29 +262,31 @@ impl Client {
|
||||
let mut list = easy::List::new();
|
||||
|
||||
for header in request.headers.clone() {
|
||||
list.append(format!("{}: {}", header.name, header.value).as_str()).unwrap();
|
||||
list.append(format!("{}: {}", header.name, header.value).as_str())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if get_header_values(request.headers.clone(), "Content-Type".to_string()).is_empty() {
|
||||
if let Some(s) = request.content_type.clone() {
|
||||
list.append(format!("Content-Type: {}", s).as_str()).unwrap();
|
||||
list.append(format!("Content-Type: {}", s).as_str())
|
||||
.unwrap();
|
||||
} else {
|
||||
list.append("Content-Type:").unwrap(); // remove header Content-Type
|
||||
list.append("Content-Type:").unwrap(); // remove header Content-Type
|
||||
}
|
||||
}
|
||||
|
||||
// if request.form.is_empty() && request.multipart.is_empty() && request.body.is_empty() {
|
||||
// list.append("Content-Length:").unwrap();
|
||||
// }
|
||||
// if request.form.is_empty() && request.multipart.is_empty() && request.body.is_empty() {
|
||||
// list.append("Content-Length:").unwrap();
|
||||
// }
|
||||
|
||||
if get_header_values(request.headers.clone(), "User-Agent".to_string()).is_empty() {
|
||||
list.append(format!("User-Agent: hurl/{}", clap::crate_version!()).as_str()).unwrap();
|
||||
list.append(format!("User-Agent: hurl/{}", clap::crate_version!()).as_str())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
self.handle.http_headers(list).unwrap();
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// set request cookies
|
||||
///
|
||||
@ -277,7 +296,6 @@ impl Client {
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// set form
|
||||
///
|
||||
@ -289,7 +307,6 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// set form
|
||||
///
|
||||
@ -299,18 +316,19 @@ impl Client {
|
||||
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()
|
||||
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();
|
||||
@ -341,7 +359,6 @@ impl Client {
|
||||
.join("&")
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// parse response version
|
||||
///
|
||||
@ -357,13 +374,12 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// parse headers from libcurl responses
|
||||
///
|
||||
fn parse_response_headers(&mut self, lines: &mut Vec<String>) -> Vec<Header> {
|
||||
let mut headers: Vec<Header> = vec![];
|
||||
lines.pop(); // remove the blank line between headers and body
|
||||
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);
|
||||
@ -372,7 +388,6 @@ impl Client {
|
||||
headers
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// retrieve an optional location to follow
|
||||
/// You need:
|
||||
@ -402,7 +417,6 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// get cookie storage
|
||||
///
|
||||
@ -420,7 +434,15 @@ impl Client {
|
||||
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.push(Cookie {
|
||||
domain,
|
||||
include_subdomain,
|
||||
path,
|
||||
https,
|
||||
expires,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
}
|
||||
cookies
|
||||
}
|
||||
@ -433,11 +455,12 @@ impl Client {
|
||||
eprintln!("* add to cookie store: {}", cookie);
|
||||
//self.handle.cookie_list(format!("Set-Cookie: {}={}", cookie.name, cookie.value).as_str()).unwrap();
|
||||
}
|
||||
self.handle.cookie_list(cookie.to_string().as_str()).unwrap();
|
||||
self.handle
|
||||
.cookie_list(cookie.to_string().as_str())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Header {
|
||||
///
|
||||
/// Parse an http header line received from the server
|
||||
@ -457,7 +480,6 @@ impl Header {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Split an array of bytes into http lines (\r\n separator)
|
||||
///
|
||||
@ -479,42 +501,42 @@ fn split_lines(data: &[u8]) -> Vec<String> {
|
||||
lines
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Decode optionally header value as text with utf8 or iso-8859-1 encoding
|
||||
///
|
||||
pub fn decode_header(data: &[u8]) -> Option<String> {
|
||||
match str::from_utf8(data) {
|
||||
Ok(s) => Some(s.to_string()),
|
||||
Err(_) => {
|
||||
match ISO_8859_1.decode(data, DecoderTrap::Strict) {
|
||||
Ok(s) => Some(s),
|
||||
Err(_) => {
|
||||
println!("Error decoding header both utf8 and iso-8859-1 {:?}", data);
|
||||
None
|
||||
}
|
||||
Err(_) => match ISO_8859_1.decode(data, DecoderTrap::Strict) {
|
||||
Ok(s) => Some(s),
|
||||
Err(_) => {
|
||||
println!("Error decoding header both utf8 and iso-8859-1 {:?}", data);
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[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_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());
|
||||
}
|
||||
|
74
src/http/core.rs
Normal file
74
src/http/core.rs
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 Orange
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Header {
|
||||
pub name: String,
|
||||
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 Header {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}: {}", self.name, self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Cookie {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}\t{}\t{}\t{}\t{}\t{}\t{}",
|
||||
self.domain,
|
||||
self.include_subdomain,
|
||||
self.path,
|
||||
self.https,
|
||||
self.expires,
|
||||
self.name,
|
||||
self.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// 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()
|
||||
}
|
@ -1,423 +0,0 @@
|
||||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 Orange
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
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>,
|
||||
pub content_type: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
//impl Request {
|
||||
//
|
||||
// ///
|
||||
// /// Get implicit content-type from request
|
||||
// /// Note that for multipart, the content-type is not returned because it is generated at runtime by the client
|
||||
// ///
|
||||
// pub fn content_type(&self) -> Option<String> {
|
||||
// if self.form.is_empty() {
|
||||
// Some("application/x-www-form-urlencoded".to_string())
|
||||
// // } else if self..mform.is_empty() {
|
||||
// // Some("application/x-www-form-urlencoded".to_string())
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Response {
|
||||
pub version: Version,
|
||||
pub status: u32,
|
||||
pub headers: Vec<Header>,
|
||||
pub body: Vec<u8>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Method {
|
||||
Get,
|
||||
Head,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Connect,
|
||||
Options,
|
||||
Trace,
|
||||
Patch,
|
||||
}
|
||||
|
||||
impl fmt::Display for Method {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let value = match self {
|
||||
Method::Get => "GET",
|
||||
Method::Head => "HEAD",
|
||||
Method::Post => "POST",
|
||||
Method::Put => "PUT",
|
||||
Method::Delete => "DELETE",
|
||||
Method::Connect => "CONNECT",
|
||||
Method::Options => "OPTIONS",
|
||||
Method::Trace => "TRACE",
|
||||
Method::Patch => "PATCH"
|
||||
};
|
||||
write!(f, "{}", value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Version {
|
||||
Http10,
|
||||
Http11,
|
||||
Http2,
|
||||
}
|
||||
|
||||
impl fmt::Display for Version {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let value = match self {
|
||||
Version::Http10 => "1.0",
|
||||
Version::Http11 => "1.1",
|
||||
Version::Http2 => "2",
|
||||
};
|
||||
write!(f, "{}", value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Header {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for Header {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}: {}", self.name, self.value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Param {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for Param {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}: {}", self.name, self.value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum MultipartParam {
|
||||
Param(Param),
|
||||
FileParam(FileParam),
|
||||
}
|
||||
|
||||
impl fmt::Display for MultipartParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
MultipartParam::Param(param) => write!(f, "{}", param.to_string()),
|
||||
MultipartParam::FileParam(param) => write!(f, "{}", param.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct FileParam {
|
||||
pub name: String,
|
||||
pub filename: String,
|
||||
pub data: Vec<u8>,
|
||||
pub content_type: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for FileParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}: file,{}; {}", self.name, self.filename, self.content_type)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct RequestCookie {
|
||||
pub name: String,
|
||||
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 Cookie {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}\t{}\t{}\t{}\t{}\t{}\t{}", self.domain, self.include_subdomain, self.path, self.https, self.expires, self.name, self.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)]
|
||||
pub enum HttpError {
|
||||
CouldNotResolveProxyName,
|
||||
CouldNotResolveHost,
|
||||
FailToConnect,
|
||||
TooManyRedirect,
|
||||
CouldNotParseResponse,
|
||||
SSLCertificate,
|
||||
}
|
||||
|
||||
|
||||
impl Response {
|
||||
///
|
||||
/// return a list of headers values for the given header name
|
||||
///
|
||||
pub fn get_header_values(&self, expected_name: String) -> Vec<String> {
|
||||
get_header_values(self.headers.clone(), expected_name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// 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)]
|
||||
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![],
|
||||
};
|
||||
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![],
|
||||
content_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn custom_http_request() -> Request {
|
||||
Request {
|
||||
method: Method::Get,
|
||||
url: "http://localhost/custom".to_string(),
|
||||
querystring: vec![],
|
||||
headers: vec![
|
||||
Header { name: String::from("User-Agent"), value: String::from("iPhone") },
|
||||
Header { name: String::from("Foo"), value: String::from("Bar") },
|
||||
],
|
||||
cookies: vec![
|
||||
RequestCookie {
|
||||
name: String::from("theme"),
|
||||
value: String::from("light"),
|
||||
},
|
||||
RequestCookie {
|
||||
name: String::from("sessionToken"),
|
||||
value: String::from("abc123"),
|
||||
}
|
||||
],
|
||||
body: vec![],
|
||||
multipart: vec![],
|
||||
form: vec![],
|
||||
content_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
// GET http://localhost:8000/querystring-params?param1=value1¶m2
|
||||
pub fn query_http_request() -> Request {
|
||||
Request {
|
||||
method: Method::Get,
|
||||
url: "http://localhost:8000/querystring-params".to_string(),
|
||||
querystring: vec![
|
||||
Param { name: String::from("param1"), value: String::from("value1") },
|
||||
Param { name: String::from("param2"), value: String::from("a b") },
|
||||
],
|
||||
headers: vec![],
|
||||
cookies: vec![],
|
||||
body: vec![],
|
||||
multipart: vec![],
|
||||
form: vec![],
|
||||
content_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn form_http_request() -> Request {
|
||||
Request {
|
||||
method: Method::Post,
|
||||
url: "http://localhost/form-params".to_string(),
|
||||
querystring: vec![],
|
||||
headers: vec![
|
||||
Header { name: String::from("Content-Type"), value: String::from("application/x-www-form-urlencoded") },
|
||||
],
|
||||
cookies: vec![],
|
||||
body: "param1=value1¶m2=¶m3=a%3db¶m4=a%253db".to_string().into_bytes(),
|
||||
multipart: vec![],
|
||||
form: vec![],
|
||||
content_type: Some("multipart/form-data".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hello_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") },
|
||||
Header { name: String::from("Content-Length"), value: String::from("12") },
|
||||
],
|
||||
body: String::into_bytes(String::from("Hello World!")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn html_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") },
|
||||
],
|
||||
body: String::into_bytes(String::from("<html><head><meta charset=\"UTF-8\"></head><body><br></body></html>")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xml_invalid_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") },
|
||||
Header { name: String::from("Content-Length"), value: String::from("12") },
|
||||
],
|
||||
body: String::into_bytes(r#"
|
||||
xxx
|
||||
"#.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xml_two_users_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") },
|
||||
Header { name: String::from("Content-Length"), value: String::from("12") },
|
||||
],
|
||||
body: String::into_bytes(r#"
|
||||
<?xml version="1.0"?>
|
||||
<users>
|
||||
<user id="1">Bob</user>
|
||||
<user id="2">Bill</user>
|
||||
</users>
|
||||
"#.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xml_three_users_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") },
|
||||
Header { name: String::from("Content-Length"), value: String::from("12") },
|
||||
],
|
||||
body: String::into_bytes(r#"
|
||||
<?xml version="1.0"?>
|
||||
<users>
|
||||
<user id="1">Bob</user>
|
||||
<user id="2">Bill</user>
|
||||
<user id="3">Bruce</user>
|
||||
</users>
|
||||
"#.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn json_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 0,
|
||||
headers: vec![],
|
||||
body: String::into_bytes(r#"
|
||||
{
|
||||
"success":false,
|
||||
"errors": [
|
||||
{ "id": "error1"},
|
||||
{"id": "error2"}
|
||||
],
|
||||
"duration": 1.5
|
||||
}
|
||||
"#.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header { name: String::from("Content-Type"), value: String::from("application/octet-stream") },
|
||||
Header { name: String::from("Content-Length"), value: String::from("1") },
|
||||
],
|
||||
body: vec![255],
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 Orange
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
pub mod client;
|
||||
pub mod core;
|
@ -16,4 +16,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
pub mod libcurl;
|
||||
pub use self::client::{Client, ClientOptions, HttpError};
|
||||
pub use self::core::{Cookie, Header};
|
||||
#[cfg(test)]
|
||||
pub use self::request::tests::*;
|
||||
pub use self::request::{FileParam, Method, MultipartParam, Param, Request, RequestCookie};
|
||||
#[cfg(test)]
|
||||
pub use self::response::tests::*;
|
||||
pub use self::response::{Response, Version};
|
||||
|
||||
mod client;
|
||||
mod core;
|
||||
mod request;
|
||||
mod response;
|
||||
|
214
src/http/request.rs
Normal file
214
src/http/request.rs
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 Orange
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use super::core::*;
|
||||
|
||||
#[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>,
|
||||
pub content_type: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Method {
|
||||
Get,
|
||||
Head,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Connect,
|
||||
Options,
|
||||
Trace,
|
||||
Patch,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Param {
|
||||
pub name: 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)]
|
||||
pub struct RequestCookie {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for Method {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let value = match self {
|
||||
Method::Get => "GET",
|
||||
Method::Head => "HEAD",
|
||||
Method::Post => "POST",
|
||||
Method::Put => "PUT",
|
||||
Method::Delete => "DELETE",
|
||||
Method::Connect => "CONNECT",
|
||||
Method::Options => "OPTIONS",
|
||||
Method::Trace => "TRACE",
|
||||
Method::Patch => "PATCH",
|
||||
};
|
||||
write!(f, "{}", value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Param {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}: {}", self.name, self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MultipartParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
MultipartParam::Param(param) => write!(f, "{}", param.to_string()),
|
||||
MultipartParam::FileParam(param) => write!(f, "{}", param.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FileParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}: file,{}; {}",
|
||||
self.name, self.filename, self.content_type
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RequestCookie {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}={}", self.name, self.value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
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![],
|
||||
content_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn custom_http_request() -> Request {
|
||||
Request {
|
||||
method: Method::Get,
|
||||
url: "http://localhost/custom".to_string(),
|
||||
querystring: vec![],
|
||||
headers: vec![
|
||||
Header {
|
||||
name: String::from("User-Agent"),
|
||||
value: String::from("iPhone"),
|
||||
},
|
||||
Header {
|
||||
name: String::from("Foo"),
|
||||
value: String::from("Bar"),
|
||||
},
|
||||
],
|
||||
cookies: vec![
|
||||
RequestCookie {
|
||||
name: String::from("theme"),
|
||||
value: String::from("light"),
|
||||
},
|
||||
RequestCookie {
|
||||
name: String::from("sessionToken"),
|
||||
value: String::from("abc123"),
|
||||
},
|
||||
],
|
||||
body: vec![],
|
||||
multipart: vec![],
|
||||
form: vec![],
|
||||
content_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_http_request() -> Request {
|
||||
Request {
|
||||
method: Method::Get,
|
||||
url: "http://localhost:8000/querystring-params".to_string(),
|
||||
querystring: vec![
|
||||
Param {
|
||||
name: String::from("param1"),
|
||||
value: String::from("value1"),
|
||||
},
|
||||
Param {
|
||||
name: String::from("param2"),
|
||||
value: String::from("a b"),
|
||||
},
|
||||
],
|
||||
headers: vec![],
|
||||
cookies: vec![],
|
||||
body: vec![],
|
||||
multipart: vec![],
|
||||
form: vec![],
|
||||
content_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn form_http_request() -> Request {
|
||||
Request {
|
||||
method: Method::Post,
|
||||
url: "http://localhost/form-params".to_string(),
|
||||
querystring: vec![],
|
||||
headers: vec![Header {
|
||||
name: String::from("Content-Type"),
|
||||
value: String::from("application/x-www-form-urlencoded"),
|
||||
}],
|
||||
cookies: vec![],
|
||||
body: "param1=value1¶m2=¶m3=a%3db¶m4=a%253db"
|
||||
.to_string()
|
||||
.into_bytes(),
|
||||
multipart: vec![],
|
||||
form: vec![],
|
||||
content_type: Some("multipart/form-data".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
251
src/http/response.rs
Normal file
251
src/http/response.rs
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 Orange
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use super::core::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Response {
|
||||
pub version: Version,
|
||||
pub status: u32,
|
||||
pub headers: Vec<Header>,
|
||||
pub body: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Version {
|
||||
Http10,
|
||||
Http11,
|
||||
Http2,
|
||||
}
|
||||
|
||||
impl fmt::Display for Version {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let value = match self {
|
||||
Version::Http10 => "1.0",
|
||||
Version::Http11 => "1.1",
|
||||
Version::Http2 => "2",
|
||||
};
|
||||
write!(f, "{}", value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
///
|
||||
/// return a list of headers values for the given header name
|
||||
///
|
||||
pub fn get_header_values(&self, expected_name: String) -> Vec<String> {
|
||||
get_header_values(self.headers.clone(), expected_name)
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn get_header(&self, name: String) -> Vec<String> {
|
||||
self.headers
|
||||
.iter()
|
||||
.filter(|&h| h.name.to_lowercase() == name.to_lowercase())
|
||||
.map(|h| h.value.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
///
|
||||
/// Return optional Content-type header value
|
||||
///
|
||||
pub fn content_type(&self) -> Option<String> {
|
||||
for header in self.headers.clone() {
|
||||
if header.name.to_lowercase().as_str() == "content-type" {
|
||||
return Some(header.value);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
pub fn hello_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header {
|
||||
name: String::from("Content-Type"),
|
||||
value: String::from("text/html; charset=utf-8"),
|
||||
},
|
||||
Header {
|
||||
name: String::from("Content-Length"),
|
||||
value: String::from("12"),
|
||||
},
|
||||
],
|
||||
body: String::into_bytes(String::from("Hello World!")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn html_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![Header {
|
||||
name: String::from("Content-Type"),
|
||||
value: String::from("text/html; charset=utf-8"),
|
||||
}],
|
||||
body: String::into_bytes(String::from(
|
||||
"<html><head><meta charset=\"UTF-8\"></head><body><br></body></html>",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xml_invalid_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header {
|
||||
name: String::from("Content-Type"),
|
||||
value: String::from("text/html; charset=utf-8"),
|
||||
},
|
||||
Header {
|
||||
name: String::from("Content-Length"),
|
||||
value: String::from("12"),
|
||||
},
|
||||
],
|
||||
body: String::into_bytes(
|
||||
r#"
|
||||
xxx
|
||||
"#
|
||||
.to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xml_two_users_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header {
|
||||
name: String::from("Content-Type"),
|
||||
value: String::from("text/html; charset=utf-8"),
|
||||
},
|
||||
Header {
|
||||
name: String::from("Content-Length"),
|
||||
value: String::from("12"),
|
||||
},
|
||||
],
|
||||
body: String::into_bytes(
|
||||
r#"
|
||||
<?xml version="1.0"?>
|
||||
<users>
|
||||
<user id="1">Bob</user>
|
||||
<user id="2">Bill</user>
|
||||
</users>
|
||||
"#
|
||||
.to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xml_three_users_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header {
|
||||
name: String::from("Content-Type"),
|
||||
value: String::from("text/html; charset=utf-8"),
|
||||
},
|
||||
Header {
|
||||
name: String::from("Content-Length"),
|
||||
value: String::from("12"),
|
||||
},
|
||||
],
|
||||
body: String::into_bytes(
|
||||
r#"
|
||||
<?xml version="1.0"?>
|
||||
<users>
|
||||
<user id="1">Bob</user>
|
||||
<user id="2">Bill</user>
|
||||
<user id="3">Bruce</user>
|
||||
</users>
|
||||
"#
|
||||
.to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn json_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 0,
|
||||
headers: vec![],
|
||||
body: String::into_bytes(
|
||||
r#"
|
||||
{
|
||||
"success":false,
|
||||
"errors": [
|
||||
{ "id": "error1"},
|
||||
{"id": "error2"}
|
||||
],
|
||||
"duration": 1.5
|
||||
}
|
||||
"#
|
||||
.to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_http_response() -> Response {
|
||||
Response {
|
||||
version: Version::Http10,
|
||||
status: 200,
|
||||
headers: vec![
|
||||
Header {
|
||||
name: String::from("Content-Type"),
|
||||
value: String::from("application/octet-stream"),
|
||||
},
|
||||
Header {
|
||||
name: String::from("Content-Length"),
|
||||
value: String::from("1"),
|
||||
},
|
||||
],
|
||||
body: vec![255],
|
||||
}
|
||||
}
|
||||
|
||||
#[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![],
|
||||
};
|
||||
assert_eq!(
|
||||
response.get_header_values("Content-Length".to_string()),
|
||||
vec!["12".to_string()]
|
||||
);
|
||||
assert!(response.get_header_values("Unknown".to_string()).is_empty());
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::core::common::Value;
|
||||
use crate::http::libcurl;
|
||||
use crate::http;
|
||||
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::core::*;
|
||||
@ -107,7 +107,7 @@ impl AssertResult {
|
||||
}
|
||||
|
||||
impl Assert {
|
||||
pub fn eval(self, http_response: libcurl::core::Response, variables: &HashMap<String, Value>) -> AssertResult {
|
||||
pub fn eval(self, http_response: http::Response, variables: &HashMap<String, Value>) -> AssertResult {
|
||||
let actual = self.query.eval(variables, http_response);
|
||||
let source_info = self.predicate.clone().predicate_func.source_info;
|
||||
let predicate_result = match actual.clone() {
|
||||
@ -162,7 +162,7 @@ pub mod tests {
|
||||
fn test_eval() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
assert_count_user().eval(libcurl::core::tests::xml_three_users_http_response(), &variables),
|
||||
assert_count_user().eval(http::xml_three_users_http_response(), &variables),
|
||||
AssertResult::Explicit {
|
||||
actual: Ok(Some(Value::Nodeset(3))),
|
||||
source_info: SourceInfo::init(1, 14, 1, 27),
|
||||
|
@ -20,7 +20,7 @@ use std::collections::HashMap;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::core::common::Value;
|
||||
use crate::http::libcurl;
|
||||
use crate::http;
|
||||
|
||||
use super::core::{CaptureResult, Error};
|
||||
use super::core::RunnerError;
|
||||
@ -28,7 +28,7 @@ use super::super::core::ast::*;
|
||||
|
||||
impl Capture {
|
||||
|
||||
pub fn eval(self, variables: &HashMap<String, Value>, http_response: libcurl::core::Response) -> Result<CaptureResult, Error> {
|
||||
pub fn eval(self, variables: &HashMap<String, Value>, http_response: http::Response) -> Result<CaptureResult, Error> {
|
||||
let name = self.name.value;
|
||||
let value = self.query.clone().eval(variables, http_response)?;
|
||||
let value = match value {
|
||||
@ -184,7 +184,7 @@ pub mod tests {
|
||||
},
|
||||
};
|
||||
|
||||
let error = capture.eval(&variables, libcurl::core::tests::xml_three_users_http_response()).err().unwrap();
|
||||
let error = capture.eval(&variables, http::xml_three_users_http_response()).err().unwrap();
|
||||
assert_eq!(error.source_info.start, Pos { line: 1, column: 7 });
|
||||
assert_eq!(error.inner, RunnerError::QueryInvalidXpathEval)
|
||||
}
|
||||
@ -239,13 +239,13 @@ pub mod tests {
|
||||
#[test]
|
||||
fn test_capture() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(user_count_capture().eval(&variables, libcurl::core::tests::xml_three_users_http_response()).unwrap(),
|
||||
assert_eq!(user_count_capture().eval(&variables, http::xml_three_users_http_response()).unwrap(),
|
||||
CaptureResult {
|
||||
name: "UserCount".to_string(),
|
||||
value: Value::from_f64(3.0),
|
||||
});
|
||||
|
||||
assert_eq!(duration_capture().eval(&variables, libcurl::core::tests::json_http_response()).unwrap(),
|
||||
assert_eq!(duration_capture().eval(&variables, http::json_http_response()).unwrap(),
|
||||
CaptureResult {
|
||||
name: "duration".to_string(),
|
||||
value: Value::from_f64(1.5),
|
||||
|
@ -25,17 +25,6 @@
|
||||
/// and not by the http client.
|
||||
///
|
||||
|
||||
use crate::http::libcurl::core::Response;
|
||||
|
||||
impl Response {
|
||||
pub fn cookies(&self) -> Vec<ResponseCookie> {
|
||||
self.headers
|
||||
.iter()
|
||||
.filter(|&h| h.name.to_lowercase() == "set-cookie")
|
||||
.filter_map(|h| ResponseCookie::parse(h.value.clone()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Cookie return from HTTP Response
|
||||
|
@ -19,7 +19,7 @@ use std::collections::HashMap;
|
||||
|
||||
|
||||
use crate::core::common::{FormatError, SourceInfo, Value};
|
||||
use crate::http::libcurl;
|
||||
use crate::http;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct RunnerOptions {
|
||||
@ -35,7 +35,7 @@ pub struct HurlResult {
|
||||
pub entries: Vec<EntryResult>,
|
||||
pub time_in_ms: u128,
|
||||
pub success: bool,
|
||||
pub cookies: Vec<libcurl::core::Cookie>,
|
||||
pub cookies: Vec<http::Cookie>,
|
||||
}
|
||||
|
||||
impl HurlResult {
|
||||
@ -50,8 +50,8 @@ impl HurlResult {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct EntryResult {
|
||||
pub request: Option<libcurl::core::Request>,
|
||||
pub response: Option<libcurl::core::Response>,
|
||||
pub request: Option<http::Request>,
|
||||
pub response: Option<http::Response>,
|
||||
//pub captures: Vec<(String, Value)>,
|
||||
pub captures: Vec<CaptureResult>,
|
||||
pub asserts: Vec<AssertResult>,
|
||||
|
@ -22,7 +22,7 @@ use std::time::Instant;
|
||||
use crate::core::ast::*;
|
||||
use crate::core::common::SourceInfo;
|
||||
use crate::core::common::Value;
|
||||
use crate::http::libcurl;
|
||||
use crate::http;
|
||||
|
||||
|
||||
use super::core::*;
|
||||
@ -49,7 +49,7 @@ use crate::format::logger::Logger;
|
||||
//// });
|
||||
/// ```
|
||||
pub fn run(entry: Entry,
|
||||
http_client: &mut libcurl::client::Client,
|
||||
http_client: &mut http::Client,
|
||||
entry_index: usize,
|
||||
variables: &mut HashMap<String, Value>,
|
||||
context_dir: String,
|
||||
@ -80,7 +80,7 @@ pub fn run(entry: Entry,
|
||||
use url::Url;
|
||||
if let Ok(url) = Url::parse(http_request.url.as_str()) {
|
||||
for c in http_request.cookies.clone() {
|
||||
let cookie = libcurl::core::Cookie {
|
||||
let cookie = http::Cookie {
|
||||
domain: url.host_str().unwrap().to_string(),
|
||||
include_subdomain: "FALSE".to_string(),
|
||||
path: "/".to_string(),
|
||||
@ -182,7 +182,7 @@ pub fn run(entry: Entry,
|
||||
}
|
||||
|
||||
|
||||
pub fn log_request(logger: &Logger, request: &libcurl::core::Request) {
|
||||
pub fn log_request(logger: &Logger, request: &http::Request) {
|
||||
logger.verbose("Request");
|
||||
logger.verbose(format!("{} {}", request.method, request.url).as_str());
|
||||
for header in request.headers.clone() {
|
||||
|
@ -20,7 +20,7 @@ use std::time::Instant;
|
||||
|
||||
use crate::core::ast::*;
|
||||
use crate::core::common::Value;
|
||||
use crate::http::libcurl;
|
||||
use crate::http;
|
||||
|
||||
|
||||
use super::core::*;
|
||||
@ -35,7 +35,7 @@ use crate::core::common::FormatError;
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use hurl::http::libcurl;
|
||||
/// use hurl::http;
|
||||
/// use hurl::runner;
|
||||
/// use hurl::format;
|
||||
///
|
||||
@ -48,7 +48,7 @@ use crate::core::common::FormatError;
|
||||
/// let hurl_file = hurl::parser::parse_hurl_file(s).unwrap();
|
||||
///
|
||||
/// // Create an http client
|
||||
/// let options = libcurl::client::ClientOptions {
|
||||
/// let options = http::ClientOptions {
|
||||
/// follow_location: false,
|
||||
/// max_redirect: None,
|
||||
/// cookie_input_file: None,
|
||||
@ -57,7 +57,7 @@ use crate::core::common::FormatError;
|
||||
/// verbose: false,
|
||||
/// insecure: false,
|
||||
/// };
|
||||
/// let mut client = libcurl::client::Client::init(options);
|
||||
/// let mut client = http::Client::init(options);
|
||||
///
|
||||
/// // Define runner options
|
||||
/// let variables = std::collections::HashMap::new();
|
||||
@ -92,7 +92,7 @@ use crate::core::common::FormatError;
|
||||
/// ```
|
||||
pub fn run(
|
||||
hurl_file: HurlFile,
|
||||
http_client: &mut libcurl::client::Client,
|
||||
http_client: &mut http::Client,
|
||||
filename: String,
|
||||
context_dir: String,
|
||||
options: RunnerOptions,
|
||||
|
@ -16,33 +16,25 @@
|
||||
*
|
||||
*/
|
||||
|
||||
use crate::http::libcurl::core::Response;
|
||||
use crate::http::Response;
|
||||
use super::core::RunnerError;
|
||||
use super::cookie::ResponseCookie;
|
||||
use encoding::{EncodingRef, DecoderTrap};
|
||||
|
||||
|
||||
///
|
||||
/// get body content as text from http response
|
||||
/// used by query
|
||||
///
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
||||
impl Response {
|
||||
|
||||
|
||||
///
|
||||
/// Return optional Content-type header value
|
||||
///
|
||||
fn content_type(&self) -> Option<String> {
|
||||
for header in self.headers.clone() {
|
||||
if header.name.to_lowercase() == "content-type" {
|
||||
return Some(header.value);
|
||||
}
|
||||
}
|
||||
None
|
||||
pub fn cookies(&self) -> Vec<ResponseCookie> {
|
||||
self.headers
|
||||
.iter()
|
||||
.filter(|&h| h.name.to_lowercase().as_str() == "set-cookie")
|
||||
.filter_map(|h| ResponseCookie::parse(h.value.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Return encoding of the response
|
||||
///
|
||||
@ -61,6 +53,9 @@ impl Response {
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// return response body as text
|
||||
///
|
||||
pub fn text(&self) -> Result<String, RunnerError> {
|
||||
let encoding = self.encoding()?;
|
||||
match encoding.decode(&self.body, DecoderTrap::Strict) {
|
||||
@ -68,6 +63,30 @@ impl Response {
|
||||
Err(_) => Err(RunnerError::InvalidDecoding { charset: encoding.name().to_string() })
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// return true if response is an html response
|
||||
///
|
||||
pub fn is_html(&self) -> bool {
|
||||
match self.content_type() {
|
||||
None => false,
|
||||
Some(s) => s.starts_with("text/html")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Return option cookie from response
|
||||
///
|
||||
pub fn get_cookie(&self, name: String) -> Option<ResponseCookie> {
|
||||
for cookie in self.cookies() {
|
||||
if cookie.name == name {
|
||||
return Some(cookie);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
@ -86,7 +105,7 @@ fn mime_charset(mime_type: String) -> Option<String> {
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::http::libcurl::core::{Version, Header};
|
||||
use crate::http::*;
|
||||
|
||||
#[test]
|
||||
pub fn test_charset() {
|
||||
|
@ -17,8 +17,7 @@
|
||||
*/
|
||||
|
||||
|
||||
use crate::http::libcurl::core::*;
|
||||
|
||||
use crate::http::*;
|
||||
use super::cookie::*;
|
||||
use super::core::*;
|
||||
|
||||
@ -350,8 +349,6 @@ fn parse_version(s: String) -> Result<Version, ParseError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::http::libcurl::core::tests::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
@ -19,7 +19,8 @@
|
||||
use serde::ser::{Serializer, SerializeStruct};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::http::libcurl::core::*;
|
||||
use crate::http::*;
|
||||
|
||||
|
||||
use super::cookie::*;
|
||||
use super::core::*;
|
||||
|
@ -28,7 +28,7 @@ use std::path::Path;
|
||||
|
||||
use crate::core::ast::*;
|
||||
use crate::core::common::Value;
|
||||
use crate::http::libcurl;
|
||||
use crate::http;
|
||||
|
||||
use super::core::{Error, RunnerError};
|
||||
|
||||
@ -38,23 +38,23 @@ impl MultipartParam {
|
||||
pub fn eval(self,
|
||||
variables: &HashMap<String, Value>,
|
||||
context_dir: String,
|
||||
) -> Result<libcurl::core::MultipartParam, Error> {
|
||||
) -> Result<http::MultipartParam, Error> {
|
||||
match self {
|
||||
MultipartParam::Param(KeyValue { key, value, .. }) => {
|
||||
let name = key.value;
|
||||
let value = value.eval(variables)?;
|
||||
Ok(libcurl::core::MultipartParam::Param(libcurl::core::Param { name, value }))
|
||||
Ok(http::MultipartParam::Param(http::Param { name, value }))
|
||||
}
|
||||
MultipartParam::FileParam(param) => {
|
||||
let file_param = param.eval(context_dir)?;
|
||||
Ok(libcurl::core::MultipartParam::FileParam(file_param))
|
||||
Ok(http::MultipartParam::FileParam(file_param))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileParam {
|
||||
pub fn eval(self, context_dir: String) -> Result<libcurl::core::FileParam, Error> {
|
||||
pub fn eval(self, context_dir: String) -> Result<http::FileParam, Error> {
|
||||
let name = self.key.value;
|
||||
|
||||
let filename = self.value.filename.clone();
|
||||
@ -93,7 +93,7 @@ impl FileParam {
|
||||
}
|
||||
|
||||
let content_type = self.value.content_type();
|
||||
Ok(libcurl::core::FileParam {
|
||||
Ok(http::FileParam {
|
||||
name,
|
||||
filename: filename.value,
|
||||
data,
|
||||
@ -166,7 +166,7 @@ mod tests {
|
||||
},
|
||||
line_terminator0: line_terminator,
|
||||
}.eval("integration/tests".to_string()).unwrap(),
|
||||
libcurl::core::FileParam {
|
||||
http::FileParam {
|
||||
name: "upload1".to_string(),
|
||||
filename: "hello.txt".to_string(),
|
||||
data: b"Hello World!".to_vec(),
|
||||
|
@ -21,7 +21,7 @@ use std::collections::HashMap;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::core::common::Value;
|
||||
use crate::http::libcurl;
|
||||
use crate::http;
|
||||
use crate::jsonpath;
|
||||
|
||||
use super::cookie;
|
||||
@ -32,29 +32,8 @@ use super::xpath;
|
||||
pub type QueryResult = Result<Option<Value>, Error>;
|
||||
|
||||
|
||||
impl libcurl::core::Response {
|
||||
pub fn is_html(&self) -> bool {
|
||||
for header in self.headers.clone() {
|
||||
if header.name.to_lowercase() == "content-type" {
|
||||
return header.value.contains("html");
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_cookie(&self, name: String) -> Option<cookie::ResponseCookie> {
|
||||
for cookie in self.cookies() {
|
||||
if cookie.name == name {
|
||||
return Some(cookie);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Query {
|
||||
pub fn eval(self, variables: &HashMap<String, Value>, http_response: libcurl::core::Response) -> QueryResult {
|
||||
pub fn eval(self, variables: &HashMap<String, Value>, http_response: http::Response) -> QueryResult {
|
||||
match self.value {
|
||||
QueryValue::Status {} => Ok(Some(Value::Integer(i64::from(http_response.status)))),
|
||||
QueryValue::Header { name, .. } => {
|
||||
@ -224,7 +203,6 @@ impl CookieAttributeName {
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::core::common::{Pos, SourceInfo};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub fn xpath_invalid_query() -> Query {
|
||||
@ -297,9 +275,9 @@ pub mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn json_http_response() -> libcurl::core::Response {
|
||||
libcurl::core::Response {
|
||||
version: libcurl::core::Version::Http10,
|
||||
pub fn json_http_response() -> http::Response {
|
||||
http::Response {
|
||||
version: http::Version::Http10,
|
||||
status: 0,
|
||||
headers: vec![],
|
||||
body: String::into_bytes(r#"
|
||||
@ -436,7 +414,7 @@ pub mod tests {
|
||||
fn test_query_status() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
Query { source_info: SourceInfo::init(0, 0, 0, 0), value: QueryValue::Status {} }.eval(&variables, libcurl::core::tests::hello_http_response()).unwrap().unwrap(),
|
||||
Query { source_info: SourceInfo::init(0, 0, 0, 0), value: QueryValue::Status {} }.eval(&variables, http::hello_http_response()).unwrap().unwrap(),
|
||||
Value::Integer(200)
|
||||
);
|
||||
}
|
||||
@ -464,7 +442,7 @@ pub mod tests {
|
||||
// let error = query_header.eval(http::hello_http_response()).err().unwrap();
|
||||
// assert_eq!(error.source_info.start, Pos { line: 1, column: 8 });
|
||||
// assert_eq!(error.inner, RunnerError::QueryHeaderNotFound);
|
||||
assert_eq!(query_header.eval(&variables, libcurl::core::tests::hello_http_response()).unwrap(), None);
|
||||
assert_eq!(query_header.eval(&variables, http::hello_http_response()).unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -488,7 +466,7 @@ pub mod tests {
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
query_header.eval(&variables, libcurl::core::tests::hello_http_response()).unwrap().unwrap(),
|
||||
query_header.eval(&variables, http::hello_http_response()).unwrap().unwrap(),
|
||||
Value::String(String::from("text/html; charset=utf-8"))
|
||||
);
|
||||
}
|
||||
@ -500,11 +478,11 @@ pub mod tests {
|
||||
value: String::from(""),
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
};
|
||||
let response = libcurl::core::Response {
|
||||
version: libcurl::core::Version::Http10,
|
||||
let response = http::Response {
|
||||
version: http::Version::Http10,
|
||||
status: 0,
|
||||
headers: vec![
|
||||
libcurl::core::Header {
|
||||
http::Header {
|
||||
name: "Set-Cookie".to_string(),
|
||||
value: "LSID=DQAAAKEaem_vYg; Path=/accounts; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly".to_string(),
|
||||
}
|
||||
@ -649,22 +627,22 @@ pub mod tests {
|
||||
Query {
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
value: QueryValue::Body {},
|
||||
}.eval(&variables, libcurl::core::tests::hello_http_response()).unwrap().unwrap(),
|
||||
}.eval(&variables, http::hello_http_response()).unwrap().unwrap(),
|
||||
Value::String(String::from("Hello World!"))
|
||||
);
|
||||
let error = Query {
|
||||
source_info: SourceInfo::init(1, 1, 1, 2),
|
||||
value: QueryValue::Body {},
|
||||
}.eval(&variables, libcurl::core::tests::bytes_http_response()).err().unwrap();
|
||||
}.eval(&variables, http::bytes_http_response()).err().unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 1, 1, 2));
|
||||
assert_eq!(error.inner, RunnerError::InvalidDecoding { charset: "utf-8".to_string()});
|
||||
assert_eq!(error.inner, RunnerError::InvalidDecoding { charset: "utf-8".to_string() });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_invalid_utf8() {
|
||||
let variables = HashMap::new();
|
||||
let http_response = libcurl::core::Response {
|
||||
version: libcurl::core::Version::Http10,
|
||||
let http_response = http::Response {
|
||||
version: http::Version::Http10,
|
||||
status: 0,
|
||||
headers: vec![],
|
||||
body: vec![200],
|
||||
@ -697,7 +675,7 @@ pub mod tests {
|
||||
},
|
||||
},
|
||||
};
|
||||
let error = query.eval(&variables, libcurl::core::tests::xml_two_users_http_response()).err().unwrap();
|
||||
let error = query.eval(&variables, http::xml_two_users_http_response()).err().unwrap();
|
||||
assert_eq!(error.inner, RunnerError::QueryInvalidXpathEval);
|
||||
assert_eq!(error.source_info.start, Pos { line: 1, column: 7 });
|
||||
}
|
||||
@ -706,8 +684,8 @@ pub mod tests {
|
||||
fn test_query_xpath() {
|
||||
let variables = HashMap::new();
|
||||
|
||||
assert_eq!(xpath_users().eval(&variables, libcurl::core::tests::xml_two_users_http_response()).unwrap().unwrap(), Value::Nodeset(2));
|
||||
assert_eq!(xpath_count_user_query().eval(&variables, libcurl::core::tests::xml_two_users_http_response()).unwrap().unwrap(), Value::Float(2, 0));
|
||||
assert_eq!(xpath_users().eval(&variables, http::xml_two_users_http_response()).unwrap().unwrap(), Value::Nodeset(2));
|
||||
assert_eq!(xpath_count_user_query().eval(&variables, http::xml_two_users_http_response()).unwrap().unwrap(), Value::Float(2, 0));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -738,7 +716,7 @@ pub mod tests {
|
||||
#[test]
|
||||
fn test_query_xpath_with_html() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(xpath_html_charset().eval(&variables, libcurl::core::tests::html_http_response()).unwrap().unwrap(), Value::String(String::from("UTF-8")));
|
||||
assert_eq!(xpath_html_charset().eval(&variables, http::html_http_response()).unwrap().unwrap(), Value::String(String::from("UTF-8")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -775,8 +753,8 @@ pub mod tests {
|
||||
#[test]
|
||||
fn test_query_invalid_json() {
|
||||
let variables = HashMap::new();
|
||||
let http_response = libcurl::core::Response {
|
||||
version: libcurl::core::Version::Http10,
|
||||
let http_response = http::Response {
|
||||
version: http::Version::Http10,
|
||||
status: 0,
|
||||
headers: vec![],
|
||||
body: String::into_bytes(String::from("xxx")),
|
||||
@ -789,8 +767,8 @@ pub mod tests {
|
||||
#[test]
|
||||
fn test_query_json_not_found() {
|
||||
let variables = HashMap::new();
|
||||
let http_response = libcurl::core::Response {
|
||||
version: libcurl::core::Version::Http10,
|
||||
let http_response = http::Response {
|
||||
version: http::Version::Http10,
|
||||
status: 0,
|
||||
headers: vec![],
|
||||
body: String::into_bytes(String::from("{}")),
|
||||
@ -819,11 +797,11 @@ pub mod tests {
|
||||
fn test_query_regex() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
regex_name().eval(&variables, libcurl::core::tests::hello_http_response()).unwrap().unwrap(),
|
||||
regex_name().eval(&variables, http::hello_http_response()).unwrap().unwrap(),
|
||||
Value::String("World".to_string())
|
||||
);
|
||||
|
||||
let error = regex_invalid().eval(&variables, libcurl::core::tests::hello_http_response()).err().unwrap();
|
||||
let error = regex_invalid().eval(&variables, http::hello_http_response()).err().unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 7, 1, 10));
|
||||
assert_eq!(error.inner, RunnerError::InvalidRegex());
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ use std::io::prelude::*;
|
||||
|
||||
use crate::core::ast::*;
|
||||
use crate::core::common::Value;
|
||||
use crate::http::libcurl;
|
||||
use crate::http;
|
||||
|
||||
use super::core::Error;
|
||||
|
||||
@ -33,38 +33,38 @@ impl Request {
|
||||
pub fn eval(self,
|
||||
variables: &HashMap<String, Value>,
|
||||
context_dir: String,
|
||||
) -> Result<libcurl::core::Request, Error> {
|
||||
) -> Result<http::Request, Error> {
|
||||
let method = self.method.clone().eval();
|
||||
|
||||
let url = self.clone().url.eval(&variables)?;
|
||||
|
||||
|
||||
// headers
|
||||
let mut headers: Vec<libcurl::core::Header> = vec![];
|
||||
let mut headers: Vec<http::Header> = vec![];
|
||||
for header in self.clone().headers {
|
||||
let name = header.key.value;
|
||||
let value = header.value.eval(variables)?;
|
||||
headers.push(libcurl::core::Header {
|
||||
headers.push(http::Header {
|
||||
name,
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
let mut querystring: Vec<libcurl::core::Param> = vec![];
|
||||
let mut querystring: Vec<http::Param> = vec![];
|
||||
for param in self.clone().querystring_params() {
|
||||
let name = param.key.value;
|
||||
let value = param.value.eval(variables)?;
|
||||
querystring.push(libcurl::core::Param { name, value });
|
||||
querystring.push(http::Param { name, value });
|
||||
}
|
||||
|
||||
let mut form: Vec<libcurl::core::Param> = vec![];
|
||||
let mut form: Vec<http::Param> = vec![];
|
||||
for param in self.clone().form_params() {
|
||||
let name = param.key.value;
|
||||
let value = param.value.eval(variables)?;
|
||||
form.push(libcurl::core::Param { name, value });
|
||||
form.push(http::Param { name, value });
|
||||
}
|
||||
// if !self.clone().form_params().is_empty() {
|
||||
// headers.push(libcurl::core::Header {
|
||||
// headers.push(http::core::Header {
|
||||
// name: String::from("Content-Type"),
|
||||
// value: String::from("application/x-www-form-urlencoded"),
|
||||
// });
|
||||
@ -72,7 +72,7 @@ impl Request {
|
||||
|
||||
let mut cookies = vec![];
|
||||
for cookie in self.clone().cookies() {
|
||||
let cookie = libcurl::core::RequestCookie {
|
||||
let cookie = http::RequestCookie {
|
||||
name: cookie.clone().name.value,
|
||||
value: cookie.clone().value.value,
|
||||
};
|
||||
@ -104,7 +104,7 @@ impl Request {
|
||||
// if self.content_type().is_none() {
|
||||
// if let Some(body) = self.body {
|
||||
// if let Bytes::Json { .. } = body.value {
|
||||
// headers.push(libcurl::core::Header {
|
||||
// headers.push(http::core::Header {
|
||||
// name: String::from("Content-Type"),
|
||||
// value: String::from("application/json"),
|
||||
// });
|
||||
@ -112,7 +112,7 @@ impl Request {
|
||||
// }
|
||||
// }
|
||||
|
||||
Ok(libcurl::core::Request {
|
||||
Ok(http::Request {
|
||||
method,
|
||||
url,
|
||||
querystring,
|
||||
@ -136,42 +136,42 @@ impl Request {
|
||||
}
|
||||
|
||||
impl Method {
|
||||
fn eval(self) -> libcurl::core::Method {
|
||||
fn eval(self) -> http::Method {
|
||||
match self {
|
||||
Method::Get => libcurl::core::Method::Get,
|
||||
Method::Head => libcurl::core::Method::Head,
|
||||
Method::Post => libcurl::core::Method::Post,
|
||||
Method::Put => libcurl::core::Method::Put,
|
||||
Method::Delete => libcurl::core::Method::Delete,
|
||||
Method::Connect => libcurl::core::Method::Connect,
|
||||
Method::Options => libcurl::core::Method::Options,
|
||||
Method::Trace => libcurl::core::Method::Trace,
|
||||
Method::Patch => libcurl::core::Method::Patch,
|
||||
Method::Get => http::Method::Get,
|
||||
Method::Head => http::Method::Head,
|
||||
Method::Post => http::Method::Post,
|
||||
Method::Put => http::Method::Put,
|
||||
Method::Delete => http::Method::Delete,
|
||||
Method::Connect => http::Method::Connect,
|
||||
Method::Options => http::Method::Options,
|
||||
Method::Trace => http::Method::Trace,
|
||||
Method::Patch => http::Method::Patch,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split_url(url: String) -> (String, Vec<libcurl::core::Param>) {
|
||||
match url.find('?') {
|
||||
None => (url, vec![]),
|
||||
Some(index) => {
|
||||
let (url, params) = url.split_at(index);
|
||||
let params: Vec<libcurl::core::Param> = params[1..].split('&')
|
||||
.map(|s| {
|
||||
match s.find('=') {
|
||||
None => libcurl::core::Param { name: s.to_string(), value: String::from("") },
|
||||
Some(index) => {
|
||||
let (name, value) = s.split_at(index);
|
||||
libcurl::core::Param { name: name.to_string(), value: value[1..].to_string() }
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
(url.to_string(), params)
|
||||
}
|
||||
}
|
||||
}
|
||||
//pub fn split_url(url: String) -> (String, Vec<http::Param>) {
|
||||
// match url.find('?') {
|
||||
// None => (url, vec![]),
|
||||
// Some(index) => {
|
||||
// let (url, params) = url.split_at(index);
|
||||
// let params: Vec<http::Param> = params[1..].split('&')
|
||||
// .map(|s| {
|
||||
// match s.find('=') {
|
||||
// None => http::Param { name: s.to_string(), value: String::from("") },
|
||||
// Some(index) => {
|
||||
// let (name, value) = s.split_at(index);
|
||||
// http::Param { name: name.to_string(), value: value[1..].to_string() }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .collect();
|
||||
//
|
||||
// (url.to_string(), params)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
@ -333,7 +333,7 @@ mod tests {
|
||||
let mut variables = HashMap::new();
|
||||
variables.insert(String::from("base_url"), Value::String(String::from("http://localhost:8000")));
|
||||
let http_request = hello_request().eval(&variables, "current_dir".to_string()).unwrap();
|
||||
assert_eq!(http_request, libcurl::core::tests::hello_http_request());
|
||||
assert_eq!(http_request, http::hello_http_request());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -341,6 +341,6 @@ mod tests {
|
||||
let mut variables = HashMap::new();
|
||||
variables.insert(String::from("param1"), Value::String(String::from("value1")));
|
||||
let http_request = query_request().eval(&variables, "current_dir".to_string()).unwrap();
|
||||
assert_eq!(http_request, libcurl::core::tests::query_http_request());
|
||||
assert_eq!(http_request, http::query_http_request());
|
||||
}
|
||||
}
|
||||
|
@ -20,25 +20,17 @@ use std::collections::HashMap;
|
||||
|
||||
use crate::core::common::{Pos, SourceInfo};
|
||||
use crate::core::common::Value;
|
||||
use crate::http::libcurl;
|
||||
use crate::http;
|
||||
use crate::runner::core::RunnerError;
|
||||
|
||||
use super::core::*;
|
||||
use super::core::Error;
|
||||
use super::super::core::ast::*;
|
||||
|
||||
impl libcurl::core::Response {
|
||||
pub fn get_header(&self, name: String) -> Vec<String> {
|
||||
self.headers
|
||||
.iter()
|
||||
.filter(|&h| h.name.to_lowercase() == name.to_lowercase())
|
||||
.map(|h| h.value.clone())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Response {
|
||||
pub fn eval_asserts(self, variables: &HashMap<String, Value>, http_response: libcurl::core::Response, context_dir: String) -> Vec<AssertResult> {
|
||||
pub fn eval_asserts(self, variables: &HashMap<String, Value>, http_response: http::Response, context_dir: String) -> Vec<AssertResult> {
|
||||
let mut asserts = vec![];
|
||||
|
||||
let version = self.clone().version;
|
||||
@ -200,7 +192,7 @@ impl Response {
|
||||
asserts
|
||||
}
|
||||
|
||||
pub fn eval_captures(self, http_response: libcurl::core::Response, variables: &HashMap<String, Value>) -> Result<Vec<CaptureResult>, Error> {
|
||||
pub fn eval_captures(self, http_response: http::Response, variables: &HashMap<String, Value>) -> Result<Vec<CaptureResult>, Error> {
|
||||
let mut captures = vec![];
|
||||
for capture in self.captures() {
|
||||
let capture_result = capture.eval(variables, http_response.clone())?;
|
||||
@ -268,7 +260,7 @@ mod tests {
|
||||
let variables = HashMap::new();
|
||||
let context_dir = "undefined".to_string();
|
||||
assert_eq!(
|
||||
user_response().eval_asserts(&variables, libcurl::core::tests::xml_two_users_http_response(), context_dir),
|
||||
user_response().eval_asserts(&variables, http::xml_two_users_http_response(), context_dir),
|
||||
vec![
|
||||
AssertResult::Version {
|
||||
actual: String::from("1.0"),
|
||||
@ -301,7 +293,7 @@ mod tests {
|
||||
pub fn test_eval_captures() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
user_response().eval_captures(libcurl::core::tests::xml_two_users_http_response(), &variables).unwrap(),
|
||||
user_response().eval_captures(http::xml_two_users_http_response(), &variables).unwrap(),
|
||||
vec![
|
||||
CaptureResult {
|
||||
name: "UserCount".to_string(),
|
||||
|
@ -3,9 +3,10 @@ use std::io::prelude::*;
|
||||
|
||||
use curl::easy::Easy;
|
||||
|
||||
use hurl::http::libcurl;
|
||||
use hurl::http::libcurl::client::ClientOptions;
|
||||
use hurl::http::libcurl::core::*;
|
||||
|
||||
use hurl::http::*;
|
||||
|
||||
|
||||
use server::Server;
|
||||
|
||||
macro_rules! t {
|
||||
@ -55,7 +56,7 @@ fn get_easy() {
|
||||
}
|
||||
|
||||
|
||||
fn default_client() -> libcurl::client::Client {
|
||||
fn default_client() -> Client {
|
||||
let options = ClientOptions {
|
||||
follow_location: false,
|
||||
max_redirect: None,
|
||||
@ -65,7 +66,7 @@ fn default_client() -> libcurl::client::Client {
|
||||
verbose: true,
|
||||
insecure: false,
|
||||
};
|
||||
libcurl::client::Client::init(options)
|
||||
Client::init(options)
|
||||
}
|
||||
|
||||
fn default_get_request(url: String) -> Request {
|
||||
@ -121,12 +122,10 @@ fn test_put() {
|
||||
let response = client.execute(&request, 0).unwrap();
|
||||
assert_eq!(response.status, 200);
|
||||
assert!(response.body.is_empty());
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_patch() {
|
||||
|
||||
let mut client = default_client();
|
||||
let request = Request {
|
||||
method: Method::Patch,
|
||||
@ -146,7 +145,6 @@ fn test_patch() {
|
||||
let response = client.execute(&request, 0).unwrap();
|
||||
assert_eq!(response.status, 204);
|
||||
assert!(response.body.is_empty());
|
||||
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -176,7 +174,6 @@ fn test_custom_headers() {
|
||||
let response = client.execute(&request, 0).unwrap();
|
||||
assert_eq!(response.status, 200);
|
||||
assert!(response.body.is_empty());
|
||||
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -205,7 +202,6 @@ fn test_querystring_params() {
|
||||
let response = client.execute(&request, 0).unwrap();
|
||||
assert_eq!(response.status, 200);
|
||||
assert!(response.body.is_empty());
|
||||
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -241,7 +237,6 @@ fn test_form_params() {
|
||||
let response = client.execute(&request, 0).unwrap();
|
||||
assert_eq!(response.status, 200);
|
||||
assert_eq!(response.body, b"Hello World!".to_vec());
|
||||
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -266,9 +261,9 @@ fn test_follow_location() {
|
||||
proxy: None,
|
||||
no_proxy: None,
|
||||
verbose: false,
|
||||
insecure: false
|
||||
insecure: false,
|
||||
};
|
||||
let mut client = libcurl::client::Client::init(options);
|
||||
let mut 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");
|
||||
@ -295,7 +290,7 @@ fn test_max_redirect() {
|
||||
verbose: false,
|
||||
insecure: false,
|
||||
};
|
||||
let mut client = libcurl::client::Client::init(options);
|
||||
let mut 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);
|
||||
@ -303,7 +298,6 @@ fn test_max_redirect() {
|
||||
|
||||
let error = client.execute(&request, 11).err().unwrap();
|
||||
assert_eq!(error, HttpError::TooManyRedirect);
|
||||
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -312,7 +306,6 @@ fn test_max_redirect() {
|
||||
|
||||
#[test]
|
||||
fn test_multipart_form_data() {
|
||||
|
||||
let mut client = default_client();
|
||||
let request = Request {
|
||||
method: Method::Post,
|
||||
@ -321,27 +314,27 @@ fn test_multipart_form_data() {
|
||||
querystring: vec![],
|
||||
form: vec![],
|
||||
multipart: vec![
|
||||
MultipartParam::Param(Param{
|
||||
MultipartParam::Param(Param {
|
||||
name: "key1".to_string(),
|
||||
value: "value1".to_string()
|
||||
value: "value1".to_string(),
|
||||
}),
|
||||
MultipartParam::FileParam(FileParam{
|
||||
MultipartParam::FileParam(FileParam {
|
||||
name: "upload1".to_string(),
|
||||
filename: "hello.txt".to_string(),
|
||||
data: b"Hello World!".to_vec(),
|
||||
content_type: "text/plain".to_string()
|
||||
content_type: "text/plain".to_string(),
|
||||
}),
|
||||
MultipartParam::FileParam(FileParam{
|
||||
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()
|
||||
content_type: "text/html".to_string(),
|
||||
}),
|
||||
MultipartParam::FileParam(FileParam{
|
||||
MultipartParam::FileParam(FileParam {
|
||||
name: "upload3".to_string(),
|
||||
filename: "hello.txt".to_string(),
|
||||
data: b"Hello World!".to_vec(),
|
||||
content_type: "text/html".to_string()
|
||||
content_type: "text/html".to_string(),
|
||||
}),
|
||||
],
|
||||
cookies: vec![],
|
||||
@ -358,7 +351,6 @@ fn test_multipart_form_data() {
|
||||
let response = client.execute(&request, 0).unwrap();
|
||||
assert_eq!(response.status, 200);
|
||||
assert_eq!(response.body, b"Hello World!".to_vec());
|
||||
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -367,7 +359,6 @@ fn test_multipart_form_data() {
|
||||
|
||||
#[test]
|
||||
fn test_post_bytes() {
|
||||
|
||||
let mut client = default_client();
|
||||
let request = Request {
|
||||
method: Method::Post,
|
||||
@ -378,12 +369,11 @@ fn test_post_bytes() {
|
||||
multipart: vec![],
|
||||
cookies: vec![],
|
||||
body: b"Hello World!".to_vec(),
|
||||
content_type: None
|
||||
content_type: None,
|
||||
};
|
||||
let response = client.execute(&request, 0).unwrap();
|
||||
assert_eq!(response.status, 200);
|
||||
assert!(response.body.is_empty());
|
||||
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -394,7 +384,7 @@ fn test_post_bytes() {
|
||||
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();
|
||||
let error = client.execute(&request, 0).err().unwrap();
|
||||
|
||||
assert_eq!(error, HttpError::CouldNotResolveHost);
|
||||
}
|
||||
@ -403,7 +393,7 @@ fn test_error_could_not_resolve_host() {
|
||||
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();
|
||||
let error = client.execute(&request, 0).err().unwrap();
|
||||
assert_eq!(error, HttpError::FailToConnect);
|
||||
|
||||
|
||||
@ -416,11 +406,10 @@ fn test_error_fail_to_connect() {
|
||||
verbose: true,
|
||||
insecure: false,
|
||||
};
|
||||
let mut client = libcurl::client::Client::init(options);
|
||||
let mut client = Client::init(options);
|
||||
let request = default_get_request("http://localhost:8000/hello".to_string());
|
||||
let error = client.execute(&request, 0).err().unwrap();
|
||||
let error = client.execute(&request, 0).err().unwrap();
|
||||
assert_eq!(error, HttpError::FailToConnect);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -435,7 +424,7 @@ fn test_error_could_not_resolve_proxy_name() {
|
||||
verbose: false,
|
||||
insecure: false,
|
||||
};
|
||||
let mut client = libcurl::client::Client::init(options);
|
||||
let mut 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);
|
||||
@ -486,7 +475,6 @@ fn test_cookie() {
|
||||
// let response = client.execute(&request, 0).unwrap();
|
||||
// assert_eq!(response.status, 200);
|
||||
// assert!(response.body.is_empty());
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -512,7 +500,6 @@ fn test_cookie_storage() {
|
||||
let response = client.execute(&request, 0).unwrap();
|
||||
assert_eq!(response.status, 200);
|
||||
assert!(response.body.is_empty());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -531,12 +518,11 @@ fn test_cookie_file() {
|
||||
verbose: false,
|
||||
insecure: false,
|
||||
};
|
||||
let mut client = libcurl::client::Client::init(options);
|
||||
let mut 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
|
||||
@ -555,7 +541,7 @@ fn test_proxy() {
|
||||
verbose: false,
|
||||
insecure: false,
|
||||
};
|
||||
let mut client = libcurl::client::Client::init(options);
|
||||
let mut 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);
|
||||
|
@ -21,7 +21,7 @@ use hurl::core::ast;
|
||||
use hurl::core::common::{Pos, SourceInfo};
|
||||
use hurl::runner;
|
||||
use hurl::format;
|
||||
use hurl::http::libcurl;
|
||||
use hurl::http;
|
||||
use std::collections::HashMap;
|
||||
use hurl::core::ast::{Template, TemplateElement, EncodedString};
|
||||
use hurl::runner::core::RunnerOptions;
|
||||
@ -37,7 +37,7 @@ fn test_hurl_file() {
|
||||
let content = std::fs::read_to_string(filename).expect("Something went wrong reading the file");
|
||||
let hurl_file = hurl::parser::parse_hurl_file(content.as_str()).unwrap();
|
||||
let variables = HashMap::new();
|
||||
let options = libcurl::client::ClientOptions {
|
||||
let options = http::ClientOptions {
|
||||
follow_location: false,
|
||||
max_redirect: None,
|
||||
cookie_input_file: None,
|
||||
@ -46,7 +46,7 @@ fn test_hurl_file() {
|
||||
verbose: false,
|
||||
insecure: false,
|
||||
};
|
||||
let mut client = libcurl::client::Client::init(options);
|
||||
let mut client = http::Client::init(options);
|
||||
let mut lines: Vec<&str> = regex::Regex::new(r"\n|\r\n")
|
||||
.unwrap()
|
||||
.split(&content)
|
||||
@ -144,7 +144,7 @@ fn hello_request() -> ast::Request {
|
||||
|
||||
#[test]
|
||||
fn test_hello() {
|
||||
let options = libcurl::client::ClientOptions {
|
||||
let options = http::ClientOptions {
|
||||
follow_location: false,
|
||||
max_redirect: None,
|
||||
cookie_input_file: None,
|
||||
@ -153,7 +153,7 @@ fn test_hello() {
|
||||
verbose: false,
|
||||
insecure: false,
|
||||
};
|
||||
let mut client = libcurl::client::Client::init(options);
|
||||
let mut client = http::Client::init(options);
|
||||
let source_info = SourceInfo {
|
||||
start: Pos { line: 1, column: 1 },
|
||||
end: Pos { line: 1, column: 1 },
|
||||
|
Loading…
Reference in New Issue
Block a user