I want to connect to the Nats server using Rust. To do this I tried to use crate async_nats. That's the documentation.
And here's my code:
use futures::StreamExt;
#[tokio::main]
async fn main() -> Result<(), async_nats::Error> {
let client = async_nats::connect("nats://127.0.0.1:4222").await?;
// let client = async_nats::connect("127.0.0.1:4222").await?;
// let client = async_nats::connect("127.0.0.1").await?;
let mut subscriber = client.subscribe("messages".into()).await?.take(10);
for _ in 0..10 {
client.publish("messages".into(), "data".into()).await?;
}
while let Some(message) = subscriber.next().await {
println!("Received message {:?}", message);
}
Ok(())
}
It looks very similar to the example but it doesn't work. Rust panics with the message
Error: Custom { kind: Other, error: "failed to read root certificates: MissingOrMalformedExtensions" }
error: process didn't exit successfully: `target\debug\program.exe` (exit code: 1)
I thought the address might be a problem, so I tried a few variants, but it didn't help.
Could you please tell me what to do to eliminate this panic?
This has been fixed in
https://github.com/nats-io/nats.rs/pull/788
And will be part of next release.
Related
Taking the base example for the final project on The Book:
use std::net::TcpListener;
mod server {
fn run() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
println!("Connection established!");
}
}
}
I am trying to write an integration test for that piece of code.
Obviously, my test it's running to the infinite and beyond because the event loop of the TCP stream provided by the std.
// tests/server.rs
#[test]
fn run() {
server::run();
// the rest of the code's test...
}
What would be a good way to test that the server stands up correctly (without need to receive any request) without change the public interface for just testing purposes?
NOTE: There's no assertions neither Result<T, E> of any type because I didn't even know how to set up the test case for something that it's running endless.
I wouldn't test the entire server from within Rust, I'd instead test components of it. The highest component I would test is a single connection.
Dependency injection in Rust usually works like this:
Use traits for parameters instead of specific object types
Create a mock of the object that also implements the desired trait
Use the mock to create the desired behaviour during tests
In our case, I will use io::Read + io::Write to abstract TcpStream, as that is all the funcionality we use. If you need further functionality instead of just those two, you might have to implement your own NetworkStream: Send + Sync trait or similar, in which you can proxy further functionality of TcpStream.
The Mock I will be using is SyncMockStream from the mockstream crate.
For the following examples you need to add mockstream to your Cargo.toml:
[dev-dependencies]
mockstream = "0.0.3"
First, here is the simpler version with just io::Read + io::Write:
mod server {
use std::{io, net::TcpListener};
fn handle_connection(
mut stream: impl io::Read + io::Write,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("Connection established!");
// Read 'hello'
let mut buf = [0u8; 5];
stream.read_exact(&mut buf)?;
if &buf != b"hello" {
return Err(format!("Received incorrect data: '{:?}'", buf).into());
}
println!("Received 'hello'. Sending 'world!' ...");
// Respond with 'world!'
stream.write_all(b"world!\n")?;
stream.flush()?;
println!("Communication finished. Closing connection ...");
Ok(())
}
pub fn run(addr: &str) {
let listener = TcpListener::bind(addr).unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
std::thread::spawn(move || {
if let Err(e) = handle_connection(stream) {
println!("Connection closed with error: {}", e);
} else {
println!("Connection closed.");
}
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockstream::SyncMockStream;
use std::time::Duration;
#[test]
fn hello_world_handshake() {
// Arrange
let mut stream = SyncMockStream::new();
let connection = stream.clone();
let connection_thread = std::thread::spawn(move || handle_connection(connection));
// Act
stream.push_bytes_to_read(b"hello");
std::thread::sleep(Duration::from_millis(100));
// Assert
assert_eq!(stream.pop_bytes_written(), b"world!\n");
connection_thread.join().unwrap().unwrap();
}
}
}
fn main() {
server::run("127.0.0.1:7878");
}
> nc localhost 7878
hello
world!
> cargo test
running 1 test
test server::tests::hello_world_handshake ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s
Now if we need further functionality, like the sender address, we can introduce our own trait NetworkStream:
mod traits {
use std::io;
pub trait NetworkStream: io::Read + io::Write {
fn peer_addr_str(&self) -> io::Result<String>;
}
impl NetworkStream for std::net::TcpStream {
fn peer_addr_str(&self) -> io::Result<String> {
self.peer_addr().map(|addr| addr.to_string())
}
}
}
mod server {
use crate::traits::NetworkStream;
use std::net::TcpListener;
fn handle_connection(
mut stream: impl NetworkStream,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("Connection established!");
// Read 'hello'
let mut buf = [0u8; 5];
stream.read_exact(&mut buf)?;
if &buf != b"hello" {
return Err(format!("Received incorrect data: '{:?}'", buf).into());
}
println!("Received 'hello'. Sending response ...");
// Respond with 'world!'
stream.write_all(format!("hello, {}!\n", stream.peer_addr_str()?).as_bytes())?;
stream.flush()?;
println!("Communication finished. Closing connection ...");
Ok(())
}
pub fn run(addr: &str) {
let listener = TcpListener::bind(addr).unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
std::thread::spawn(move || {
if let Err(e) = handle_connection(stream) {
println!("Connection closed with error: {}", e);
} else {
println!("Connection closed.");
}
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockstream::SyncMockStream;
use std::time::Duration;
impl crate::traits::NetworkStream for SyncMockStream {
fn peer_addr_str(&self) -> std::io::Result<String> {
Ok("mock".to_string())
}
}
#[test]
fn hello_world_handshake() {
// Arrange
let mut stream = SyncMockStream::new();
let connection = stream.clone();
let connection_thread = std::thread::spawn(move || handle_connection(connection));
// Act
stream.push_bytes_to_read(b"hello");
std::thread::sleep(Duration::from_millis(100));
// Assert
assert_eq!(stream.pop_bytes_written(), b"hello, mock!\n");
connection_thread.join().unwrap().unwrap();
}
}
}
fn main() {
server::run("127.0.0.1:7878");
}
> nc localhost 7878
hello
hello, 127.0.0.1:50718!
> cargo test
running 1 test
test server::tests::hello_world_handshake ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.12s
Note that in both cases, the mockstream dependency is only needed as a dev-dependency. The actual cargo build does not require it.
Integration testing
If you want to go further up and test the entire server, I would treat the server as a black box instead and test it with an external tool like behave.
Behave is a behaviour test framework based on Python and Gherkin which is great for black box integration tests.
With it, you can run the actual, unmocked executable that cargo build produces, and then test actual functionality with a real connection. behave is excellent with that, especially in the regard that it bridges the gap between programmers and requirement engineers, as the actual test cases are in written, non-programmer-readable form.
I stuck with proper handling panics in case of tokio::net::TcpStream
use tokio::*;
use tokio::{net::{ TcpStream }, io::AsyncWriteExt};
#[tokio::main]
async fn main() {
let mut stream = TcpStream::connect("10.20.30.40:6142").await.unwrap();
println!("created stream");
let result = stream.write(b"hello world\n").await;
println!("wrote to stream; success={:?}", result.is_ok());
}
or in playground
Can guru teach me how to catch these errors like
thread 'main' panicked at 'called Result::unwrap() on an Err
value: Os { code: 101, kind: NetworkUnreachable, message: "Network is
unreachable" }', src/main.rs:6:67
You'll want to change main() to handle errors, and use the ? operator instead of unwrap() to propagate them.
type SomeResult<T> = Result<T, Box<dyn std::error::Error>>;
#[tokio::main]
async fn main() -> SomeResult<()> {
let mut stream = TcpStream::connect("10.20.30.40:6142").await?;
println!("created stream");
let result = stream.write(b"hello world\n").await;
println!("wrote to stream; success={:?}", result.is_ok());
Ok(())
}
The last line (Ok(())) is because main() now expects a result returned. I also added an alias so you can reuse the SomeResult for other functions from which you might want to propagate errors. Here is a playground.
I have four modules. The client is sending messages and the server is receiving messages. Once the server receives the message, it tries to send the message to the MPSC channel. I put the receiver in the other .rs file where I intend to receive the message.
I am not getting any message on the receiver side.
Maybe an infinite loop on the server side creates a problem, but is there a way to make this channel communication working?
client.rs
use std::io::prelude::*;
use std::os::unix::net::UnixDatagram;
use std::path::Path;
use std::sync::mpsc;
pub fn tcp_datagram_client() {
pub static FILE_PATH: &'static str = "/tmp/datagram.sock";
let socket = UnixDatagram::unbound().unwrap();
match socket.connect(FILE_PATH) {
Ok(socket) => socket,
Err(e) => {
println!("Couldn't connect: {:?}", e);
return;
}
};
println!("TCP client Connected to TCP Server {:?}", socket);
loop {
socket
.send(b"Hello from client to server")
.expect("recv function failed");
}
}
fn main() {
tcp_datagram_client();
}
server.rs
use std::os::unix::net::UnixDatagram;
use std::path::Path;
use std::str::from_utf8;
use std::sync::mpsc::Sender;
fn unlink_socket(path: impl AsRef<Path>) {
let path = path.as_ref();
if path.exists() {
if let Err(e) = std::fs::remove_file(path) {
eprintln!("Couldn't remove the file: {:?}", e);
}
}
}
static FILE_PATH: &'static str = "/tmp/datagram.sock";
pub fn tcp_datagram_server(tx: Sender<String>) {
unlink_socket(FILE_PATH);
let socket = match UnixDatagram::bind(FILE_PATH) {
Ok(socket) => socket,
Err(e) => {
eprintln!("Couldn't bind: {:?}", e);
return;
}
};
let mut buf = vec![0; 1024];
println!("Waiting for client to connect...");
loop {
let received_bytes = socket.recv(&mut buf).expect("recv function failed");
println!("Received {:?}", received_bytes);
let received_message = from_utf8(&buf).expect("utf-8 convert failed");
tx.send(received_message.to_string());
}
}
message_receiver.rs
use crate::server;
use std::sync::mpsc;
pub fn handle_messages() {
let (tx, rx) = mpsc::channel();
server::tcp_datagram_server(tx);
let message_from_tcp_server = rx.recv().unwrap();
println!("{:?}", message_from_tcp_server);
}
main.rs
mod server;
mod message_receiver;
fn main() {
message_receiver::handle_messages();
}
Once the TCP client is connected:
TCP client Connected to TCP Server UnixDatagram { fd: 3, local: (unnamed), peer: "/tmp/datagram.sock" (pathname) }
I receive no messages on the channel receiver end:
Waiting for client to connect...
Maybe an infinite loop on the server side creates a problem
Yes, quite literally, your server code does an infinite loop to handle continuously messages from the client(s). So the call to tcp_datagram_server never returns.
but is there a way to make this channel communication working?
Of course, it seems you are simply missing a second thread for your message_receiver. Wrapping your tcp_datagram_server(tx) in std::thread::spawn should do it. You could also add a loop to keep processing requests to match the one in tcp_datagram_server:
pub fn handle_messages() {
let (tx, rx) = mpsc::channel();
std::thread::spawn(|| tcp_datagram_server(tx));
loop {
let message_from_tcp_server = rx.recv().unwrap();
println!("{}", message_from_tcp_server);
}
}
The docs for the hyper crate have a simple example to start a web server:
extern crate hyper;
use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};
fn main() {
// Construct our SocketAddr to listen on...
let addr = ([127, 0, 0, 1], 3000).into();
// And a NewService to handle each connection...
let new_service = || service_fn_ok(|_req| Response::new(Body::from("Hello World")));
// Then bind and serve...
let server = Server::bind(&addr).serve(new_service);
// Finally, spawn `server` onto an Executor...
hyper::rt::run(server.map_err(|e| {
eprintln!("server error: {}", e);
}));
}
I want to embed this web server in a procedure that returns an error:
// Executes a web server. Returns a result object with an error object
// if the server stopped unexpectedly
fn run_and_stop_web_server(addr: std::net::SocketAddr) -> Result<(), Error> {
let server_builder = Server::try_bind(&addr)?;
let server = server_builder.serve(move || {
let handler: Handler = Handler::new();
hyper::service::service_fn(move |req| handler.serve(req))
});
// Run the server
// HERE: I don't know how to handle the error so I can return it from
// the run_and_stop_web_server() function
hyper::rt::run(server.map_err(|e| eprintln!("Ignored Server error: {}", e)));
Result::Ok(())
}
I don't know how I can handle the error. Instead of using eprintln!() I want to return the error from the run_and_stop_web_server function.
I believe I found a solution using the tokio runtime directly... It compiles. Please anyone tell me if I'm wrong here.
The idea is to use the tokio Runtime directly (docs have many examples):
use tokio::runtime::Runtime;
// Executes a web server. Returns a result object with an error object
// if the server stopped unexpectedly
fn run_and_stop_web_server(addr: std::net::SocketAddr) -> Result<(), Error> {
let server_builder = Server::try_bind(&addr)?;
let server = server_builder.serve(move || {
let handler: Handler = Handler::new();
hyper::service::service_fn(move |req| handler.serve(req))
});
let mut rt = Runtime::new()?;
rt.block_on(server)?;
rt.shutdown_now();
Result::Ok(())
}
Maybe you can store the error into a local variable and return that:
let mut result = Result::Ok(());
hyper::rt::run(server.map_err(|e| { result = Result::Error (e); });
result
I can not understand this error regarding lifetimes. Proxy is a struct:
impl Proxy {
pub fn new<A: ip::ToSocketAddr>(address: A) -> Proxy {
Proxy {
address: address.to_socket_addr().unwrap()
}
}
pub fn listen_and_proxy(&self) {
println!("listening {}", self.address);
for stream in TcpListener::bind(self.address).listen().incoming() {
let stream = stream.unwrap();
spawn(move || { // <- this causes an error
let mut stream = stream;
let name = stream.peer_name();
println!("client: {} -> {}", name, self.handle(stream));
});
}
}
}
Output error:
cannot infer an appropriate lifetime due to conflicting requirements
What is wrong in the code?
You are trying to call self.handle(stream) which would require moving self into the newly spawned thread. This cannot be done.