-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make get/post more conveniently by using closure #179
Comments
Hey there! Thanks for the write up! Looks like there's possibly two issues here, so I'll try to separate them:
|
Not CompilableBy reading docs of reqwest::get, let mut cookies = Cookie::new();
cookies.append("ISecer", "s:0:\"\";");
let resp = reqwest::get("http://192.168.96.11:88/web-master/web100-4/index.php")?
.header(cookies)
.send()?; Focus on business, Make API as simple as possible and with less Error handlingFor cooperations whose main business is not software developing, there is a special type of developer who is focus on business logic develop. They really want API as simple as possible.
If reqwest provide get/post/other api in closure style, a client developer can call a request like writing protocal code: get("http://yyy/xxx", |r|{ // GET xxx HTTP/1.1 Host: yyy
r.header(Cookies(vec![("a", "b")]) // Cookie: a=b
.header(Accept(zzz)) // Accept: zzzz
//.header(more headers)
})? The same function in Client style will be a bit complex(developer have to Client::new()? // Not concerned
.get("http://yyy/xxx")?
.header(Cookies)
.header(Accept)
.header(more headers)
.send()? // Not concerned Current InconvenienceFor a batch of requests with user default headers, current lazy_static! {
// Follow Header shared by whole project.
static ref DEFAULT: Mutex<Headers> = Mutex::new({
let mut cookies = Cookie::new();
cookies.append("department", "sgit.sgcc");
cookies.append("collector", "liuan");
let mut headers = Headers::new();
headers.set(UserAgent::new("firefox/55.0"));
headers.set(cookies);
headers.set(Accept(vec![qitem(mime::TEXT_HTML)]));
headers
});
}
fn information_collect() -> Result<String, Box<Error>> {
let default = {DEFAULT.lock()?.clone()};
let login = post("http://httpbin.org/post", |rb|{
rb.headers(default.clone())
.header(ContentType(mime::APPLICATION_WWW_FORM_URLENCODED))
.body("user=admin&passwd=admin")
})?;
let r1 = if login.status() == StatusCode::Ok {
let mut logined = Cookie::new();
logined.append("sid", "!!Sorry, I still don't know how to support HTTP session!!");
logined.append("comment", "!!;;;after reading API docs;;;;");
get2("http://httpbin.org/get", |rb|{
rb.headers(default.clone())
.header(logined.clone())
//.header(Other when login)
})
} else {
get2("http://httpbin.org/get", |rb|{rb.headers(default.clone())})
};
match r1 {
Ok(mut r) => r.text(),
_ => Ok(String::from("0")),
}
} If I write this code in Client::new()....send() style, I'll have to write many error handling, and many Client::new()....send() looks strange for readers. Futuremore when reqwest decided to add other supports (i.e. add session support by setting reqwest env, higher level api can stay unchanged, such as code below // in the future
reqwest::request::env().enable_conn_reuse(true) // enable connection reuse for same server
let login = reqwest::request::post("http://xxx/login", |rb| {
rb.headers(default.clone())
.body(...)
})?;
let resp = if login.status() == StatusCode::OK {
//set the cookie
reqwest::request::get("http://xxx/realtime", |rb| {
rb.headers(default.clone())
.header(cookie)
})
} else {
reqwest::request::get("http://xxx/delayed" |rb| {rb.headers(default.clone())})
}?; If I have different flow cause by different environments. A closure(callback) will make it easy to change the outer flow. //In the future
let s = reqwest::request::Session::new();
let login = s.post("http://xxx/login", |rb| {
rb.headers(default.clone())
.body(...)
})?;
let resp = if login.status() == StatusCode::Ok {
s.get("http://xxx/realtime", |rb| {rb.headers(default.clone())})
} else {
reqwest::request::get("http://xxx/delayed" |rb| {rb.headers(default.clone())})
}?; Actually, s.get(), s.post(), etc. is coding like below impl Session {
pub fn get(&self, url &str, set_header: F) -> Result<Response>
where F: Fn(&mut RequestBuilder) -> &mut RequestBuilder {
let resp = set_header(
&mut Client::new()?
.get(url)?
).header(self.load_cookie()) // Append cookies stored in &self
.send()?
self.save_cookie(resp);
Ok(cookie)
}
pub fn post(...)...;
pub fn put(...)...;
fn load_cookie()...;
fn save_cookie()...;
} If we use style without callback closure, Session.get() cannot keep style uniform with Non session mode API. My implsFollowing code are helper functions I often use. trait Text {
fn text(&mut self) -> Result<String, Box<Error>>;
}
// I implement this because most html/xml/json docs are utf-8 streams,
// provide this method call will make code more simple in response analysis.
impl Text for Response {
fn text(&mut self) -> Result<String, Box<Error>> {
let mut s = String::new();
self.read_to_string(&mut s)?;
Ok(s)
}
}
pub fn get2<F>(url: &str, set_header: F) -> Result<Response, Box<Error>>
where F: Fn(&mut RequestBuilder) -> &mut RequestBuilder {
let response = set_header(
&mut Client::new()?
.get(url)?
).send()?;
Ok(response)
}
pub fn post<F>(url: &str, set_header: F) -> Result<Response, Box<Error>>
where F: Fn(&mut RequestBuilder) -> &mut RequestBuilder {
let response = set_header(
&mut Client::new()?
.post(url)?
).send()?;
Ok(response)
} Pack get/get2/post/put/etc. to reqwest::request?These API are higher level API for user in reqwest(needn't learn how to use Client), maybe these can be packed into request? So user can quickly start by RequestBuilder.headers - Borrow or Move?When I write LastProvide API like below is also a good news. But current released version(0.7.2) still not provided yet. reqwest::request::get2(url)?
.headers(default)
.header(Cookie(...))
//...
.send()?
reqwest::request::post(url)?
//...
.body(...)
.send()?
reqwest::request::put(url)?
//...
.send()? But consider twice before implement these. Closure callback maybe a better choice to compat features in the future |
Advantages of using closured high-level API
let r = get(url);
let r = get2(url, |r|{r.headers(default.clone())});
let r = post(url, |r|{r.body("username=admin&passwd=admin")});
let r = put(url, |r|{r.body(file)});
let r = options(url, |r|{r});
let s: Session = Session::new();
let r = s.post(url, |r|{r.body("username=admin&passwd=admin")});
let r = s.get(url);
// let r = etc.
//mostly, attribute in ClientBuilder are seldom changed, consider put it in session env or guard env
let s = Session::new();
s.env().gzip(flase);
let login = s.post(url, |r|{r.body("u=admin&p=1")});
// thread local env()
let guard = reqwest::request::env();
guard.gzip(false);
let login = post(url, |r|{r.body("u=admin&p=1")});//post() setting a ClientBuilder
//guard.clean();
// To support keep_alive need to maintaince a map in env,
// since Client to different hostname is differed,
// but high-level developer needn't to concered this.
// Here reqwest library is acting as a connection pool.
// **Using callback closure can help to keep calling style no changed**
let env = reqwest::request::env();
env.keep_alive(true);//Change a switch in post/get/put/etc.
let login = post(url, |r|{r.body("u=admin&p=1")});
loop {
let r = get(url2); //get() reuse Client created by post()
//sleep some seconds
}
//env.clean();
// use async traits to support and_then
// Following code is a password brute force, But I don't know how to do async programming.
// Consider it a persedo code, since there may sth. wrong.
let async = reqwest::request::Async::new();
async.max_parallel(100); // default -1 unlimited, If connections over 100, new Request will be blocked.
let cont = Mutex::new(true); // true until passwd cracked.
while {*cont.lock()} == true {
let s = async.new_session();
//s.get(vcodeurl).and_then
s.post(url, |rb|{rb.body("passwd=" + dict.next())})
.and_then(|r|{
cont.lock() = false;
});
//async.get(xxx)
} |
Thanks for writing in detail your points, I think there's some valid things in here.
I wouldn't suggest writing If a bunch of The let res = client.post(url)?
.json(&map)
.response()?; // send()
Good point, perhaps
I've considered adding such a method before, there is #86 for discussing the merit. |
Yes, you are right, Reuse client makes brute force very fast. |
reqwest is a convenient library for rapid http development. We can add 2 api to make it more convenient by using closure. The feature is usable in contests like CTF.
For example:
Add
pub fn get2()
andpub fn post()
So when I do get with cookie by simply call:
Hope reqwest can be used as simply as Python requests.
Future more, we can add a
reqwest::header::Cookies
to support multi key-value cookie.So that we can write CTF code with cookies as simply as below.
which is as simple as requests code in Python
The text was updated successfully, but these errors were encountered: