How to request an available port to OS in Rust? - rust

I have many same programs and they run in different ports.
Configuring them manually is difficult so I want to apply for some available ports from OS.
But I can't find a way to implement this operation cuz I'm new to rust.
(Extra condition is that I have an external program have to know all ports of them so specifying the port to zero will can't achieve this)

I got it.
specify the port to zero let OS assign an available port for me and expose it by SocketAddr in Actix. (raw TCP in a similar way with SocketAddrV4)
#[actix_web::main]
async fn main() -> std::io::Result<()> {
set_var("PWNXY_CLIENT_PORT", "8080").unwrap();
let srv = HttpServer::new(|| {
App::new()
.service(hello)
.service(echo)
.route("/hey", web::get().to(manual_hello))
})
.bind(("127.0.0.1", 0))?;
println!("{:#?}", srv.addrs()[0].port());
srv.run().await
}

From the documentation of bind:
Binding with a port number of 0 will request that the OS assigns a port to this listener. The port allocated can be queried via the TcpListener::local_addr method.

Related

Tokio thread is not starting / spawning

I'm trying to start a new task to read from a socket client. I'm using the following same method on both the websocket server and client to receive from the connection.
The problem is, on the server side, the thread is started (2 log lines printed), but on the client side the thread is not starting (only the first line printed).
If I await on the spawn(), I can receive from the client. But then the parent task cannot proceed.
Any pointers for solving this problem?
pub async fn receive_message_from_peer(
mut receiver: PeerReceiver,
sender: Sender<IoEvent>,
peer_index: u64,
) {
debug!("starting new task for reading from peer : {:?}", peer_index);
tokio::task::spawn(async move {
debug!("new thread started for peer receiving");
// ....
}); // not awaiting or join!()
Do you use tokio::TcpListener::from_std(...) to create a listener object this way?
I had the same problem as you, my std_listener object was created based on net2. So there is a scheduling incompatibility problem.
From the description in the newer official documentation https://docs.rs/tokio/latest/tokio/net/struct.TcpListener.html#method.from_std, it seems that tokio currently has better support for socket2.
So I think the issue was I was using std::thread::sleep() in async code in some places. after using tokio::time::sleep() didn't need to yield the thread.

Resolve host of `http::uri::Uri` to IP addresses

I have an http::uri::Uri value and I want to get the IpAddrs of the host. In case the host is an IP address (e.g. http://127.0.0.1:8080), it just needs to be parsed. But if it's a hostname, it needs to be DNS resolved. How do I best do that?
It seems like std only has one public way to resolve hosts: ToSocketAddrs. Unfortunately, it includes the port in the API which is irrelevant for DNS resolving. But oh well. But with this, my first attempt was thus:
use std::net::ToSocketAddr;
let host = uri.authority().unwrap().host();
let addresses = (host, 0).to_socket_addrs()?
.map(|socket_addr| socket_addr.ip());
(I am using a dummy port here since it's irrelevant for the DNS resolution.)
This attempt works for many cases, but not for the square-bracket IPv6 form: http://[::1]:8080. Here, host() is [::1] and is not parsable as IpAddr, thus to_socket_addrs() tries to resolve [::1] as host, which fails. So I would need to check manually whether the host is an IPv6 within square-brackets. Not nice, especially since I don't know what grammars are allowed here exactly.
I had several other ideas, like changing the Authority to always have a dummy port, because then I could call authority.as_str().to_socket_addrs(). The <&str as ToSocketAddr> does support the [::1]:8080 syntax! But that impl NEEDS a port and fails if there is none.
So yes, I have not found an easy way to do that. But it seems this should not be that hard! Any ideas?
Try parsing it as std::net::IpAddr first, then look up the hostname if that fails. You have to handle the square bracket notation yourself, but this isn't too bad:
use std::net::IpAddr;
fn parse_ip_from_uri_host(host: &str) -> Option<IpAddr> {
host.parse::<IpAddr>().ok().or_else(||
// Parsing failed, try as bracketed IPv6
host.strip_prefix("[")?
.strip_suffix("]")?
.parse::<IpAddr>().ok()
)
}
(Playground)
If this returns None then parsing failed, and the next step is to attempt to resolve the string as a hostname through DNS.

Why can I not recieve serial data using Rust?

I've been trying to read serial data from a Feather M0, but for some reason I can't read the data into a buffer. This device is for sure outputting serial data, and both PlatformIO and the Arduino IDE show serial data in their respective serial monitors. However, it will timeout when I'm reading it in Rust, every single time, no matter what timeout value I have it set to. Here is my code:
// First, find the serial port
let port_info = find_receiver();
// If we didn't find a port, then we can't continue
if port_info.is_none() {
panic!("Could not find a serial port");
}
let mut port = serialport::new(port_info.unwrap().port_name, 9600)
.timeout(Duration::from_millis(1000))
.open()
.expect("Could not open serial port");
let mut serial_buf: Vec<u8> = vec![0; 8];
loop {
let n = port.read_exact(serial_buf.as_mut_slice()).unwrap();
println!("Buffer is {:?}", serial_buf);
}
The find_reciever() function simply scans the open ports and returns the one that I want to connect to. I'm on Windows, so in this case it's usually COM9 or COM12. I would like this to be a cross-platform application, so I'm not using the open_native() function that serialport provides.
I've tried varrying the size of the buffer from 1 byte to 1000, I've trying different versions of read into the port, I've tried skipping over timeout errors, and I've tried directly outputting the read bytes to io::Stdout. Any ideas on what to do?
Apparently, the serialport crate that I was using requires you to set the command
port.write_data_terminal_ready(true);
in order for it to start reading data. On Linux this works perfectly fine without it. Rip 4 hours trying to change what IO reader I was using.

how to get current thread(worker) in rust rocket

Now I am using rust rocket rocket = { version = "0.5.0-rc.1", features = ["json"] } as a web server, I am facing a problem when the request quickly, some request may turn to time out, my server side code look like this:
#[post("/v1",data = "<record>")]
pub fn word_search(record: Json<WordSearchRequest>, login_user_info: LoginUserInfo) -> content::Json<String> {
// some logic to fetch data from database
}
I was wonder why the requst turned to time out, so I want to print the server side thread and handle request time. is it possible to get current thread id in rust rocket? I am seriously doubt the server only have one thread.
I finnally found the server only have one worker from the log ouput, then I add more workers in the Rocket.toml config file fix the timeout problem.
[release]
workers = 12
log_level = "normal"
keep_alive = 5
port = 8000

Periodic tasks in Rust Rocket

I try to build a REST with Rocket, that allows the caller to configure times at which he will be unavailable. Therefore it can either happen, that the unavailability has to be started immediately or I have to store it in a database for later execution.
I can implement all this with a normal set up of Rocket, rocket_sync_db_pools and Diesel:
#[database("postgres_diesel")]
pub struct DbConn(diesel::PgConnection);
#[rocket::main]
async fn main() {
rocket::build()
.attach(DbConn::fairing())
.mount(
"/v1",
routes![ post_block ],
)
.launch()
.await
.expect("could not launch rocket");
}
Within the handler function for the incoming requests I can get the database connection:
#[post("/block", data = "<new_block>")]
pub async fn post_block(
new_block: Json<NewBlock>,
conn: DbConn,
) -> Result<Json<Block>, BadRequest<&'static str>> {
Ok(Json(conn.run(move |c| configure_block(new_block.0, c)).await))
}
Within the implementation of configure_block() I either execute the configuration immediately or write it as a job to the database.
But how can I execute the configuration changes written to the database at a later moment? I need something like a cronjob within Rocket within that I can check the database, if there are pending jobs.
I tried to do it using tokio::time::interval which will run my task repeatedly. But I cannot get access to by DbPool within this tokio timer. I cannot use DbPool::get_one(&Rocket) as I don't have access to my rocket instance.
So I wonder: how can I run a repeated task within rocket?

Resources