SOLUTION:
I needed to add "use std::io::prelude::*;" to my code. I do not know why.
I am trying to read from an std::net::TcpStream but I recieve this error when calling stream.read(&buf).unwrap;
the method read exists for struct std::net::TcpStream, but its
trait bounds were not satisfied method cannot be called on
std::net::TcpStream due to unsatisfied trait bounds note: the
following trait bounds were not satisfied:
std::net::TcpStream: futures::AsyncRead
which is required by std::net::TcpStream: futures::AsyncReadExt help: items from traits can only be used if the
trait is in scoperustc(E0599) main.rs(31, 16): method cannot be called
on std::net::TcpStream due to unsatisfied trait bounds tcp.rs(49,
1): doesn't satisfy std::net::TcpStream: futures::AsyncReadExt
tcp.rs(49, 1): doesn't satisfy std::net::TcpStream: futures::AsyncRead mod.rs(580, 8): the method is available for
std::boxed::Box<std::net::TcpStream> here
Code:
use irc::client::prelude::*;
use futures::prelude::*;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
use std::io;
use futures::{AsyncRead, AsyncReadExt};
const NAME: &str = "nickname";
#[derive(Debug)]
struct DCC {
ip: IpAddr,
port: u16,
}
impl DCC {
fn from_msg(msg: &str) -> Result<DCC, std::num::ParseIntError> {
let msg_split: Vec<&str> = msg.split_whitespace().collect();
let ip: u32 = msg_split[3].parse()?;
let ip_addr: IpAddr = IpAddr::V4(Ipv4Addr::from(ip));
let port_num: u16 = msg_split[4].parse()?;
let dcc = DCC{
ip: ip_addr,
port: port_num,
};
return Ok(dcc);
}
async fn connect(&self) -> Result<(), io::Error>{
let socket_addr = SocketAddr::new(self.ip, self.port);
let mut socket = TcpStream::connect(socket_addr)?;
let mut buf = vec![];
socket.read(&buf).unwrap();
return Err(io::Error::new(io::ErrorKind::Other, "oh no!"));
}
}
#[tokio::main]
async fn irc_get(name: &str) -> Result<String, irc::error::Error>{
let config = Config {
nickname: Some(NAME.to_owned()),
server: Some("irc.irchighway.net".to_owned()),
port: Some(6667),
use_tls: Some(false),
channels: vec!["#ebooks".to_owned()],
..Config::default()
};
let mut client = Client::from_config(config).await?;
client.identify()?;
let mut stream = client.stream()?;
//waits for server to log us in and then sends the search request
loop{
let m = match stream.next().await{
Some(v) => v,
None => panic!("at the disco")
};
let message = match &m {
Ok(message) => match &message.command {Command::NOTICE(_s1, s2)=> {print!("{:?} \n", s2); message}, _ => message},
Err(_e) => panic!("at the disco")};
match &message.command{
Command::NOTICE(_s, msg) => { if msg.contains("Welcome to #ebooks"){break}},
_=> ()
}
}
client.send_privmsg("#ebooks", format!["#Search {}", name])?;
loop{
let m = match stream.next().await.transpose()?{
Some(m) => m,
None => panic!("at the disco")
};
match &m.command{
Command::PRIVMSG(nm, msg) => if nm == NAME {println!("{:?}",m); return Ok(String::from(msg))},
_ => ()
}
}
}
fn main() {
let dcc = DCC::from_msg(&irc_get(&"romeo and juliet").unwrap()[..]);
println!("{:?}", dcc);
}
I'm fairly new at rust and based on all of the examples in the documentation I think I'm using .read correctly. My only thought is that maybe it's because I'm trying to write the code in the impl, but I don't know if rust treats that differently. It also fails with "async fn connect..." and with "fn connect...".
The compiler was telling you the solution:
help: items from traits can only be used if the trait is in scope
You need to import the traits in order to use them:
use futures::{AsyncRead, AsyncReadExt};
Also you would probably want to use tokio::TcpStream which is async and not the std one.
Related
I'm facing compiler errors with dynamic traits in a scenario where I either have a std::net::TcpStream or an native_tls::TlsStream<TcpStream>. The idea for my algorithm is the following:
create TCP stream in var "tcp"
if scheme is https => replace same var with new TlsStream<TcpStream>.
Write and receive data on stream.
The important traits I need are std::io::Read and std::io::Write. Therefore I thought, I just model my "tcp" var as Box<dyn Read + Write>. But no matter how I tried, I always got compilation errors. Below you can see a minimal example, that doesn't compile.
use std::net::{IpAddr, Ipv4Addr, TcpStream};
use std::io::{Write as IoWrite, Read as IoRead};
use native_tls::TlsConnector;
fn main() {
let use_https = true;
// IP of "example.com".
let ip_addr = std::net::IpAddr::V4(Ipv4Addr::new(85, 13, 155, 159));
let port = if use_https { 443 } else { 80 };
let tcp_stream = TcpStream::connect((ip_addr, port)).unwrap();
let tcp_stream = maybe_connect_to_tls(tcp_stream, use_https);
}
fn maybe_connect_to_tls(tcp: TcpStream, use_https: bool) -> Box<dyn IoRead + IoWrite> {
if use_https {
let tls = TlsConnector::new().unwrap();
let tcp = tls.connect("example.com", tcp).unwrap();
Box::new(tcp)
} else {
Box::new(tcp)
}
}
Playground link
This is the error I get when I try to compile it:
error[E0225]: only auto traits can be used as additional traits in a trait object
--> src/main.rs:14:78
|
14 | fn maybe_connect_to_tls(tcp: TcpStream, use_https: bool) -> Box<dyn IoRead + IoWrite> {
| ------ ^^^^^^^ additional non-auto trait
| |
| first non-auto trait
|
= help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: std::io::Read + std::io::Write {}`
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
I also tried maybe_connect_to_tls<T>(tcp: TcpStream, use_https: bool) -> Box<T> where T: IoWrite + IoRead + ?Sized but whatever I try leads to other compilation failures..
Any idea how I can solve this? I want a common stream object and it should be invisible to the application what the specific implementation is.
For Rust internal reasons, your trait objects can contain no more than one non-auto trait. However, you can simply create a trait which depends on the Read and Write traits:
use std::net::{IpAddr, Ipv4Addr, TcpStream};
use std::io::{Write as IoWrite, Read as IoRead};
use native_tls::TlsConnector;
trait ReadAndWrite: IoRead + IoWrite {
}
impl <T: IoRead + IoWrite> ReadAndWrite for T {
}
fn main() {
let use_https = true;
// IP of "example.com".
let ip_addr = std::net::IpAddr::V4(Ipv4Addr::new(85, 13, 155, 159));
let port = if use_https { 443 } else { 80 };
let tcp_stream = TcpStream::connect((ip_addr, port)).unwrap();
let tcp_stream = maybe_connect_to_tls(tcp_stream, use_https);
}
fn maybe_connect_to_tls(tcp: TcpStream, use_https: bool) -> Box<dyn ReadAndWrite> {
if use_https {
let tls = TlsConnector::new().unwrap();
let tcp = tls.connect("example.com", tcp).unwrap();
Box::new(tcp)
} else {
Box::new(tcp)
}
}
Playground link
I am writing a server using rustls and hyper and wish to peek and then parse the TcpStream to then accept the corresponding tokio_rustls::TlsAcceptor I want. However, this leads me to use both async and non async functions (tokio::net::TcpStream::peek and tokio_rustls::TlsAcceptor::accept) on the stream, which as been causing me trouble. Simply adding an async block for the peek function gives me an "unused implementer of `core::future::future::Future` that must be used" error and changing move to async move does not work.
I'm wondering if there is some way to get around this, perhaps by not using and_then()?
// Dependencies: futures-util = "0.3.1", rustls = "0.18"
// tokio = {version = "0.2", features = ["full"]}, tokio-rustls = "0.14.0"
use tokio::net::{TcpListener, TcpStream};
use tokio_rustls::server::TlsStream;
use tokio_rustls::TlsAcceptor;
use std::{sync, io};
use futures_util::{
future::TryFutureExt,
stream::{StreamExt, TryStreamExt},
};
#[tokio::main]
async fn run_server() -> Result<(), Box<dyn std::error::Error + Send + Sync>>{
let addr = format!("127.0.0.1:{}", 8000);
let mut tcp = TcpListener::bind(&addr).await?;
let tls_config = sync::Arc::new(rustls::ServerConfig::new(rustls::NoClientAuth::new()));
let tls_acceptor = TlsAcceptor::from(tls_config);
let mut v = vec![0u8; 16 * 1024];
// main focus of question
let incoming_tls_stream = tcp
.incoming()
.map_err(|e| error(format!("Incoming failed: {:?}", e)))
.and_then(move |mut s: TcpStream| {
let n: usize = s.peek(&mut v).await.unwrap();
println!("{:}", n);
// parse something from stream
let parsed = do_something(&v[..n]);
println!("{:}", parsed);
tls_acceptor.accept(s).map_err(|e| {
println!("Client-connection error...");
error(format!("TLS Error: {:?}", e))
})
})
.boxed();
// ...
return Ok(());
}
fn main() {
if let Err(e) = run_server() {
eprintln!("FAILED: {}", e);
std::process::exit(1);
}
}
fn error(err: String) -> io::Error {
io::Error::new(io::ErrorKind::Other, err)
}
fn do_something(bytes: &[u8]) -> &str {
return "test";
}
I'm trying to write a Rocket / Juniper / Rust based GraphQL Server using PickleDB - an in-memory key/value store.
The pickle db is created / loaded at the start and given to rocket to manage:
fn rocket() -> Rocket {
let pickle_path = var_os(String::from("PICKLE_PATH")).unwrap_or(OsString::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Bin;
let pickle_db: PickleDb = match Path::new(&pickle_path).exists() {
false => PickleDb::new(pickle_path, pickle_db_dump_policy, pickle_serialization_method),
true => PickleDb::load(pickle_path, pickle_db_dump_policy, pickle_serialization_method).unwrap(),
};
rocket::ignite()
.manage(Schema::new(Query, Mutation))
.manage(pickle_db)
.mount(
"/",
routes![graphiql, get_graphql_handler, post_graphql_handler],
)
}
And I want to retrieve the PickleDb instance from the Rocket State in my Guard:
pub struct Context {
pickle_db: PickleDb,
}
impl juniper::Context for Context {}
impl<'a, 'r> FromRequest<'a, 'r> for Context {
type Error = ();
fn from_request(_request: &'a Request<'r>) -> request::Outcome<Context, ()> {
let pickle_db = _request.guard::<State<PickleDb>>()?.inner();
Outcome::Success(Context { pickle_db })
}
}
This does not work because the State only gives me a reference:
26 | Outcome::Success(Context { pickle_db })
| ^^^^^^^^^ expected struct `pickledb::pickledb::PickleDb`, found `&pickledb::pickledb::PickleDb`
When I change my Context struct to contain a reference I get lifetime issues which I'm not yet familiar with:
15 | pickle_db: &PickleDb,
| ^ expected named lifetime parameter
I tried using 'static which does make rust quite unhappy and I tried to use the request lifetime (?) 'r of the FromRequest, but that does not really work either...
How do I get this to work? As I'm quite new in rust, is this the right way to do things?
I finally have a solution, although the need for unsafe indicates it is sub-optimal :)
#![allow(unsafe_code)]
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::env;
use std::path::Path;
use std::time::Duration;
pub static mut PICKLE_DB: Option<PickleDb> = None;
pub fn cache_init() {
let pickle_path = env::var(String::from("PICKLE_PATH")).unwrap_or(String::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Json;
let pickle_db = match Path::new(&pickle_path).exists() {
false => PickleDb::new(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
),
true => PickleDb::load(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
)
.unwrap(),
};
unsafe {
PICKLE_DB = Some(pickle_db);
}
}
pub fn cache_get<V>(key: &str) -> Option<V>
where
V: DeserializeOwned + std::fmt::Debug,
{
unsafe {
let pickle_db = PICKLE_DB
.as_ref()
.expect("cache uninitialized - call cache_init()");
pickle_db.get::<V>(key)
}
}
pub fn cache_set<V>(key: &str, value: &V) -> Result<(), pickledb::error::Error>
where
V: Serialize,
{
unsafe {
let pickle_db = PICKLE_DB
.as_mut()
.expect("cache uninitialized - call cache_init()");
pickle_db.set::<V>(key, value)?;
Ok(())
}
}
This can be simply imported and used as expected, but I think I'll run into issues when the load gets to high...
I want to use the async function to parse the inbound stream progressively, but actix-web requires impl Future<Item = HttpResponse, Error = Error> as the return value.
How can I convert the future returned by async function to what actix-web requires?
I'm using Rust 1.39 nightly and actix-web 1.0.7.
http_srv.rs :
use futures::compat::Stream01CompatExt;
use futures::future::{FutureExt, TryFutureExt};
use futures::stream::TryStreamExt;
use futures01::future::Future;
use futures01::stream::Stream;
use futures01::sync::mpsc; // for `try_next`
use actix_web::*;
use bytes::Bytes;
use futures_timer::Delay;
use std::time::Duration;
fn inbound(
req: HttpRequest,
stream: web::Payload,
) -> impl Future<Item = HttpResponse, Error = Error> {
let fut = async_inbound(&req, &stream);
fut.unit_error().boxed_local().compat() // <--- compliation error here.
}
async fn async_inbound(req: &HttpRequest, stream: &web::Payload) -> HttpResponse {
let mut compat_stream = stream.compat();
loop {
let result = compat_stream.try_next().await;
if let Err(e) = result {
warn!("Failed to read stream from {} : {}", req.path(), e);
break;
}
if let Ok(option) = result {
match option {
None => {
info!("Request ends");
break;
}
Some(data) => {
println!("{:?}", data);
}
}
}
}
HttpResponse::Ok().content_type("text/html").body("RESP")
}
pub fn start(port: u16) {
info!("Starting HTTP server listening at port {} ...", port);
let _ = HttpServer::new(|| {
App::new()
.wrap(middleware::DefaultHeaders::new().header(http::header::CACHE_CONTROL, "no-cache"))
.wrap(middleware::Logger::default())
.service(web::resource("/").route(web::put().to_async(inbound)))
})
.bind(format!("0.0.0.0:{}", port))
.expect(&format!("Unable to bind on port {}", port))
.run()
.expect("Failed to start HTTP server");
}
Cargo.toml:
dependencies]
log = "0.4.8"
env_logger = "0.6.2"
chrono = "0.4.8"
actix = "0.8.3"
bytes = "0.4.12"
actix-utils = "0.4.5"
futures-timer = "0.3"
futures01 = { package = "futures", version = "0.1", optional = false }
[dependencies.actix-web]
version = "1.0.7"
features = ["ssl"]
# https://rust-lang-nursery.github.io/futures-rs/blog/2019/04/18/compatibility-layer.html
# Rust’s futures ecosystem is currently split in two:
# On the one hand we have the vibrant ecosystem built around futures#0.1 with its many libraries working on stable Rust
# and on the other hand there’s std::future ecosystem with support for the ergonomic and powerful async/await language feature.
# To bridge the gap between these two worlds we have introduced a compatibility layer as part of the futures#0.3 extension to std::future.
[dependencies.futures-preview]
version = "0.3.0-alpha.18"
default-features = false
features = ["compat", "async-await", "nightly"]
Compilation Error:
error[E0271]: type mismatch resolving `<std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = std::result::Result<actix_http::response::Response, ()>>>> as core::future::future::Future>::Output == std::result::Result<_, actix_http::error::Error>`
--> src/http_server.rs:39:55
|
39 | fn inbound(req: HttpRequest, stream: web::Payload) -> impl Future<Item=HttpResponse, Error=Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found struct `actix_http::error::Error`
|
= note: expected type `std::result::Result<actix_http::response::Response, ()>`
found type `std::result::Result<_, actix_http::error::Error>`
= note: required because of the requirements on the impl of `futures_core::future::TryFuture` for `std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = std::result::Result<actix_http::response::Response, ()>>>>`
= note: the return type of a function must have a statically known size
std::future -> future#0.1 conversion steps:
The future needs to be TryFuture (Output = Result<T, E>)
The future needs to be Unpin (you can use boxed combinator)
Finally, you can call the compat combinator
Your inbound function:
fn inbound(
req: HttpRequest,
stream: web::Payload,
) -> impl Future<Item = HttpResponse, Error = Error> {
let fut = async_inbound(&req, &stream);
fut.unit_error().boxed_local().compat()
}
The inbound function signature is fine, but the conversion isn't.
The async_inbound function isn't TryFuture (because of -> HttpResponse). You're trying to convert it with the unit_error combinator, but the result is Result<HttpResponse, ()> and you want Result<HttpResponse, Error>. Fixed inbound function:
fn inbound(
req: HttpRequest,
stream: web::Payload,
) -> impl Future<Item = HttpResponse, Error = Error> {
let fut = async_inbound(req, stream);
fut.boxed_local().compat()
}
Your async_inbound function:
async fn async_inbound(req: &HttpRequest, stream: &web::Payload) -> HttpResponse {
// ...
}
The first issue here is to replace -> HttpResponse with -> Result<HttpResponse>. Another problem is that you're passing reg and stream by reference. Move them as there's no need to take a reference and you'll need 'static. Fixed async_inbound function:
async fn async_inbound(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse> {
let mut compat_stream = stream.compat();
while let Some(data) = compat_stream.try_next().await? {
println!("{:?}", data);
}
Ok(HttpResponse::Ok().content_type("text/html").body("RESP"))
}
I am looking to set the headers of a Iron Response with the following code:
extern crate iron; // 0.3.0
extern crate hyper; // 0.8.1
use iron::prelude::*;
use iron::status;
use hyper::header::{Headers, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
fn main() {
fn hello_world(_: &mut Request) -> IronResult<Response> {
let mut headers = Headers::new();
let string = getFileAsString("./public/index.html");
headers.set(
ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![]))
);
Ok(Response::with((status::Ok, string, headers)))
}
Iron::new(hello_world).http("localhost:3000").unwrap();
println!("On 3000");
}
fn getFileAsString(fileStr: &str) -> String {
let path = Path::new(fileStr);
let display = path.display();
let mut fileContents = String::new();
let mut file = match File::open(&path) {
Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
Ok(file) => file,
};
match file.read_to_string(&mut fileContents) {
Err(why) => panic!("couldn't read {}: {}", display, Error::description(&why)),
Ok(_) => fileContents
}
}
However I get the error:
error[E0277]: the trait bound `iron::Headers: iron::modifier::Modifier<iron::Response>` is not satisfied
--> src/main.rs:24:12
|
24 | Ok(Response::with((status::Ok, string, headers)))
| ^^^^^^^^^^^^^^ the trait `iron::modifier::Modifier<iron::Response>` is not implemented for `iron::Headers`
|
= note: required because of the requirements on the impl of `iron::modifier::Modifier<iron::Response>` for `(hyper::status::StatusCode, std::string::String, iron::Headers)`
= note: required by `iron::Response::with`
Why am I not able to pass headers into this tuple to be modified by the Request builder?
You can modify the headers on the Response object:
fn hello_world(_: &mut Request) -> IronResult<Response> {
let string = get_file_as_string("./public/index.html");
let mut resp = Response::with((status::Ok, string));
resp.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
Ok(resp)
}
To figure out the original error, let's check the error message:
error[E0277]: the trait bound `iron::Headers: iron::modifier::Modifier<iron::Response>` is not satisfied
--> src/main.rs:24:12
|
24 | Ok(Response::with((status::Ok, string, headers)))
| ^^^^^^^^^^^^^^ the trait `iron::modifier::Modifier<iron::Response>` is not implemented for `iron::Headers`
|
= note: required because of the requirements on the impl of `iron::modifier::Modifier<iron::Response>` for `(hyper::status::StatusCode, std::string::String, iron::Headers)`
= note: required by `iron::Response::with`
The first line tells us the immediate issue: iron::Headers does not implement the trait iron::modifier::Modifier<iron::Response>. If we check the documentation for Headers, we can see under the Trait Implementations section that it indeed does not implement Modifier.
We can then look at the problem from the other end: what does implement Modifier? The docs for Modifier, when built in conjunction with Iron, answer that question. One thing we can see is:
impl<H> Modifier<Response> for Header<H>
where
H: Header + HeaderFormat,
This leads to an alternate possibility:
use iron::modifiers::Header;
fn hello_world(_: &mut Request) -> IronResult<Response> {
let string = get_file_as_string("./public/index.html");
let content_type = Header(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
Ok(Response::with((status::Ok, string, content_type)))
}
And if we look at the implementation of Modifier for Header:
fn modify(self, res: &mut Response) {
res.headers.set(self.0);
}
It just sets the headers as we did above.
FYI, Rust style is snake_case for variables and methods and Error::description(&why) is normally written why.description().