How to perform a web request with ntml authentication - rust

I tried to perform a web post request in rust with the ntlm authentification but I can't figure out.
Here is my actual code:
let mut map = HashMap::new();
map.insert("test", "test");
let client = reqwest::Client::new();
let res = client.post(format!("{}/create", URL))
.json(&map)
.send()
.await?;
So to add the ntlm authentication, I tried the curl crate with the "ntlm" feature enabled:
let mut easy = Easy::new();
easy.url(format!("{}/create", URL)).unwrap();
easy.post(true).unwrap();
easy.post_field_size(map.len() as u64).unwrap();
let mut transfer = easy.transfer();
transfer.read_function(|buf| {
Ok(map.read(buf).unwrap_or(0))
}).unwrap();
transfer.perform().unwrap();
Sadly, I got this error:
Ok(map.read(buf).unwrap_or(0))
| ^^^^ method not found in `HashMap<&str, String>`
I understand that I need to convert my HashMap as bytes, but I can't find an easy way to do that, I tried "as_bytes()" but it doesn't work also.
My last question, this crate is a good way to do my ntlm authentication automatically?

Related

Rust Http Request with reqwest for specific website returns error

I'm trying to send http GET request with Rust using reqwest crate. Following code works:
extern crate reqwest;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::blocking::get("https://en.wikipedia.org/wiki/Rust_(programming_language)")?
.text()?;
println!("{:#?}", resp);
Ok(())
}
but when I change the URL to https://www.mongolbank.mn/
response body html shows the following error and not the content I want
...Description: </b>An application error occurred on the server. The current custom error settings for this application prevent the details of the application error from being viewed remotely (for security reasons). It could, however, be viewed by browsers running on the local server machine...
What's happening?
How can I fix it?
Use tokio runtime and user agent to bypass the error. User agent you can go and grab it from your browser using browser's debugging toolkit
use reqwest::{self, header};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>>{
let mut headers = header::HeaderMap::new();
headers.insert(header::USER_AGENT,
header::HeaderValue::from_static("Mozilla/5.0...."));
let client = reqwest::Client::builder()
.default_headers(headers)
.build()?;
let res = client.get("https://www.mongolbank.mn").send().await?;
println!("{:#?}", res);
Ok(())
}

How to run both server and client using Tonic in Rust?

I am trying to send a request to a grpc server from another grpc server. But the problem is I don't seem to get any ideas about how to access share the the data.
Lets say I have 3 apps, app1, app2, and app3
app2 and app3 are both written in rust using tonic.
So app1 sends a grpc request to app2 - this is working
app2 receives the request and processes the data - this is working as well
app2 needs to send a grpc request to app3 - I don't know how to do this
app3 receives the requests and sends back the reply - this is working.
Here is the code where I am trying to send the data:
async fn list_modules(
&self,
request: Request<ListModulesRequest>,
) -> Result<Response<ListModulesResponse>, Status> {
println!("Got a request: {:?}", request);
let request_data = request.into_inner();
let request = tonic::Request::new(DalListModulesRequest {
request_id: Uuid::new_v4().to_string(),
app_id: APP_ID.to_string(),
});
let mut dal_client = ModulesDalManager::connect("http://[::1]:50052").await?;
let mut response = dal_client.list_modules(request).await?;
let reply = ListModulesResponse {
request_id: response.request_id,
modules: response.modules,
};
Ok(Response::new(reply))
}
Here I guess I shouldn't be creating the dal_client as below as it is giving me errors. But I don't understand where to create it.
let mut dal_client = ModulesDalManager::connect("http://[::1]:50052").await?;
Can someone please give me any ideas on how to proceed?
Here is the error I get:
error[E0599]: no function or associated item named `connect` found for trait object `(dyn ModulesDalManager + 'static)` in the current scope
--> src/bal/services/module_manager.rs:48:49
|
48 | let mut dal_client = ModulesDalManager::connect("http://[::1]:50052").await?;
| ^^^^^^^ function or associated item not found in `(dyn ModulesDalManager + 'static)`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following traits define an item `connect`, perhaps you need to implement one of them:
candidate #1: `hyper::client::connect::sealed::Connect`
candidate #2: `hyper::client::connect::sealed::ConnectSvc`
ModulesDalManager didn't have connect. I should have used ModulesDalManagerClient instead. So the line becomes
let mut dal_client = ModulesDalManager::connect("http://[::1]:50052").await?;
PS: jbg from rust unofficial IRC channel helped me in figuring out the issue.

Why can't I send multiple requests in parallel using rust tonic?

I implemented the tonic helloworld tutorial. I then tried to change the client code so that I could send multiple requests before awaiting any.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let num_requests = 10;
let mut client = GreeterClient::connect("http://[::1]:50051").await?;
let mut responses = Vec::with_capacity(num_requests);
for _ in 0..num_requests {
let request = tonic::Request::new(HelloRequest {
name: "Tonic".into(),
});
responses.push(client.say_hello(request));
}
for resp in responses {
assert!(resp.await.is_ok());
}
Ok(())
}
This results in a compilation error:
error[E0499]: cannot borrow `client` as mutable more than once at a time
--> src/client.rs:19:24
|
19 | responses.push(client.say_hello(request));
| ^^^^^^ mutable borrow starts here in previous iteration of loop
Does that mean 'client.say_hello()' returns a type which still references client, and therefore I can't make another call to 'say_hello', which itself requires '&mut self'? Is there a way to continue to make requests before calling to 'await'?
From the Tonic documentation:
Sending a request on a channel requires a &mut self and thus can only send one request in flight. This is intentional and is required to follow the Service contract from the tower library which this channel implementation is built on top of.
...
To work around this and to ease the use of the channel, Channel provides a Clone implementation that is cheap. This is because at the very top level the channel is backed by a tower_buffer::Buffer which runs the connection in a background task and provides a mpsc channel interface. Due to this cloning the Channel type is cheap and encouraged.
Therefore, you can clone the client for each concurrent request you make. This eliminates the possibility of a single client being mutably borrowed more than once at any given time, so the borrow checker is appeased.
let num_requests = 10;
let client = GreeterClient::connect("http://[::1]:50051").await?;
let mut responses = Vec::with_capacity(num_requests);
for _ in 0..num_requests {
let mut client = client.clone();
let request = tonic::Request::new(HelloRequest {
name: "Tonic".into(),
});
responses.push(tokio::spawn(async move {
client.say_hello(request).await
}));
}
for resp in responses {
let resp = resp.await;
assert!(resp.is_ok());
assert!(resp.unwrap().is_ok());
}

Client certificate in Rust using Hyper

I have been writing a client in Rust which makes a request to a server with a client certificate (Pkcs12). Although this has been answered How to make a request with client certificate in Rust, the code doesn't compile as it is. If I make some modifications like replacing '?' by call to unwrap() function,
Code:
let tls_conn = TlsConnector::builder().unwrap()
.identity(cert).unwrap()
.build().unwrap();
Error:
let tls_conn = TlsConnector::builder().unwrap()
| ____________________^
18 | | .identity(cert).unwrap()
| |________________________________^ cannot move out of borrowed content.
I rewrote the above line of code and broke it down into multiple lines for debugging:
let ref mut init_tls_conn_builder = TlsConnector::builder().unwrap();
let ref mut tls_conn_builder = init_tls_conn_builder.identity(cert).unwrap();
let tls_conn = tls_conn_builder.build().unwrap();
I get the error as follows:
let tls_conn = tls_conn_builder.build().unwrap();
| ^^^^^^^^^^^^^^^^ cannot move out of borrowed content.
I am new to Rust and seeking help on this, can anyone please share an example code which compiles?
You don't need any mut references here. The builder pattern is create smth mutable (TlsConnector::builder().unwrap()), mutate it (tls_conn_builder.identity(cert)) and then get the result (build). Try this code
let mut tls_conn_builder = TlsConnector::builder().unwrap();
tls_conn_builder.identity(cert);
let tls_conn = tls_conn_builder.build().unwrap();

How do I start a web server in Rust with hyper?

I want to learn Rust by writing a reverse proxy with the hyper framework. My complete project is on GitHub. I'm stuck at starting a listener as explained in the documentation:
extern crate hyper;
use hyper::Client;
use hyper::server::{Server, Request, Response};
use std::io::Read;
fn pipe_through(req: Request, res: Response) {
let client = Client::new();
// Why does the response have to be mutable here? We never need to modify it, so we should be
// able to remove "mut"?
let mut response = client.get("http://drupal-8.localhost/").send().unwrap();
// Print out all the headers first.
for header in response.headers.iter() {
println!("{}", header);
}
// Now the body. This is ugly, why do I have to create an intermediary string variable? I want
// to push the response directly to stdout.
let mut body = String::new();
response.read_to_string(&mut body).unwrap();
print!("{}", body);
}
Server::http("127.0.0.1:9090").unwrap().handle(pipe_through).unwrap();
That does not work and fails with the following compile error:
error: expected one of `!` or `::`, found `(`
--> src/main.rs:23:13
|
23 | Server::http("127.0.0.1:9090").unwrap().handle(pipe_through).unwrap();
| ^
Why is my call to http() not correct? Shouldn't it create a new server as indicated in the documentation?
All expressions in Rust must be inside a function, so I need to start my server in fn main(). Then it works!

Resources