I'm trying to test some code that uses a hyper::Client by implementing my own hyper::client::Connect using a static response. I've got the types figured out, but can't figure out a runtime issue where tokio-proto complains saying request / response mismatch. Here's a simplified version of my code that demonstrates the failure:
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate tokio_io;
use futures::{future, Future, Stream};
use std::str::from_utf8;
use std::io::Cursor;
struct Client<'a, C: 'a> {
client: &'a hyper::Client<C>,
url: &'a str,
}
impl<'a, C: hyper::client::Connect> Client<'a, C> {
fn get(&self) -> Box<Future<Item = String, Error = hyper::Error>> {
Box::new(self.client.get(self.url.parse().unwrap()).and_then(|res| {
let body = Vec::new();
res.body()
.fold(body, |mut acc, chunk| {
acc.extend_from_slice(chunk.as_ref());
Ok::<_, hyper::Error>(acc)
})
.and_then(move |value| Ok(String::from(from_utf8(&value).unwrap())))
}))
}
}
struct StaticConnector<'a> {
body: &'a [u8],
}
impl<'a> StaticConnector<'a> {
fn new(body: &'a [u8]) -> StaticConnector {
StaticConnector { body: body }
}
}
impl<'a> hyper::server::Service for StaticConnector<'a> {
type Request = hyper::Uri;
type Response = Cursor<Vec<u8>>;
type Error = std::io::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, _: Self::Request) -> Self::Future {
Box::new(future::ok(Cursor::new(self.body.to_vec())))
}
}
fn main() {
let mut core = tokio_core::reactor::Core::new().unwrap();
let handle = core.handle();
// My StaticConnector for testing
let hyper_client = hyper::Client::configure()
.connector(StaticConnector::new(
b"\
HTTP/1.1 200 OK\r\n\
Content-Length: 8\r\n\
\r\n\
Maldives\
",
))
.build(&handle);
// Real Connector
/*
let hyper_client = hyper::Client::configure().build(&handle);
*/
let client = Client {
client: &hyper_client,
url: "http://ifconfig.co/country",
};
let result = core.run(client.get()).unwrap();
println!("{}", result);
}
Playground
I'm guessing it's my use of the Cursor for Io that is incomplete in some way, but I'm failing to debug and make progress. One thought is that the writes to this Cursor the hyper::Client presumably makes are not working as expected. Maybe I need a combination of a sink for the writes and the static content for the reads? All ideas I've failed to make progress using!
The reason the original code didn't work was because the reader side provided the response before the client sent the request, so tokio-proto errored out with request / response mismatch. The fix turns out to be non trivial in that first we need to arrange for the reader to block, or more specifically error out with std::io::ErrorKind::WouldBlock to indicate to the event loop that there isn't anything yet, but don't consider it an EOF. Additionally once we get the write which indicates the request has been sent and the tokio-proto machinery is waiting for a response, we use futures::task::current.notify to unblock the read. Here's an updated implementation that works as expected:
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate tokio_io;
use futures::{future, Future, Stream, task, Poll};
use std::str::from_utf8;
use std::io::{self, Cursor, Read, Write};
use tokio_io::{AsyncRead, AsyncWrite};
struct Client<'a, C: 'a> {
client: &'a hyper::Client<C>,
url: &'a str,
}
impl<'a, C: hyper::client::Connect> Client<'a, C> {
fn get(&self) -> Box<Future<Item = String, Error = hyper::Error>> {
Box::new(self.client.get(self.url.parse().unwrap()).and_then(|res| {
let body = Vec::new();
res.body()
.fold(body, |mut acc, chunk| {
acc.extend_from_slice(chunk.as_ref());
Ok::<_, hyper::Error>(acc)
})
.and_then(move |value| Ok(String::from(from_utf8(&value).unwrap())))
}))
}
}
struct StaticStream {
wrote: bool,
body: Cursor<Vec<u8>>,
}
impl Read for StaticStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.wrote {
self.body.read(buf)
} else {
Err(io::ErrorKind::WouldBlock.into())
}
}
}
impl Write for StaticStream {
fn write<'a>(&mut self, buf: &'a [u8]) -> io::Result<usize> {
self.wrote = true;
task::current().notify();
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl AsyncRead for StaticStream {}
impl AsyncWrite for StaticStream {
fn shutdown(&mut self) -> Poll<(), io::Error> {
Ok(().into())
}
}
struct StaticConnector<'a> {
body: &'a [u8],
}
impl<'a> StaticConnector<'a> {
fn new(body: &'a [u8]) -> StaticConnector {
StaticConnector { body: body }
}
}
impl<'a> hyper::server::Service for StaticConnector<'a> {
type Request = hyper::Uri;
type Response = StaticStream;
type Error = std::io::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, _: Self::Request) -> Self::Future {
Box::new(future::ok(StaticStream {
wrote: false,
body: Cursor::new(self.body.to_vec()),
}))
}
}
fn main() {
let mut core = tokio_core::reactor::Core::new().unwrap();
let handle = core.handle();
// My StaticConnector for testing
let hyper_client = hyper::Client::configure()
.connector(StaticConnector::new(
b"\
HTTP/1.1 200 OK\r\n\
Content-Length: 8\r\n\
\r\n\
Maldives\
",
))
.build(&handle);
// Real Connector
/*
let hyper_client = hyper::Client::configure().build(&handle);
*/
let client = Client {
client: &hyper_client,
url: "http://ifconfig.co/country",
};
let result = core.run(client.get()).unwrap();
println!("{}", result);
}
Playground
Note: This implementation works for simple cases, but I haven't tested more complex scenarios. For example one thing I'm unsure of is how large request/responses behave as they may involve more than 1 read/write call.
Related
If I have an optional method that has to be called many many times what is better if I want to skip it: have an empty body and call it or check the bool/Option before calling it?
The following benchmark make no sense. It gave zeroes.
#![feature(test)]
extern crate test;
trait OptTrait: 'static {
fn cheap_call(&mut self, data: u8);
fn expensive_call(&mut self, data: u8);
}
type ExpensiveFnOf<T> = &'static dyn Fn(&mut T, u8);
struct Container<T: OptTrait> {
inner: T,
expensive_fn: Option<ExpensiveFnOf<T>>,
}
impl<T: OptTrait> Container<T> {
fn new(inner: T, expensive: bool) -> Self {
let expensive_fn = {
if expensive {
Some(&T::expensive_call as ExpensiveFnOf<T>)
} else {
None
}
};
Self {
inner,
expensive_fn,
}
}
}
struct MyStruct;
impl OptTrait for MyStruct {
fn cheap_call(&mut self, _data: u8) {
}
fn expensive_call(&mut self, _data: u8) {
}
}
#[cfg(test)]
mod tests {
use super::*;
use test::Bencher;
#[bench]
fn bench_always_call_empty(b: &mut Bencher) {
let mut cont = Container::new(MyStruct, false);
b.iter(|| {
cont.inner.cheap_call(0);
cont.inner.expensive_call(1);
});
}
#[bench]
fn bench_alwaws_skip_empty(b: &mut Bencher) {
let mut cont = Container::new(MyStruct, false);
b.iter(|| {
cont.inner.cheap_call(0);
if let Some(func) = cont.expensive_fn {
func(&mut cont.inner, 1);
}
});
}
}
I want to use state within my guards. I want to have routes that required authentication with an api key which I want to define in my Rocket.toml. But running this code I get the following error:
the trait From<(Status, ())> is not implemented for (Status, ApiKeyError)
for this line of code let config_state = try_outcome!(req.guard::<State<'_, Config>>().await);
How do I implement this trait? Or is there even a better solution to manage the api token in Rocket.
I am using the 0.5.0-devversion of Rocket.
#[macro_use] extern crate rocket;
use rocket::http::Status;
use rocket::request::{Outcome, Request, FromRequest};
use rocket::State;
use rocket::fairing::AdHoc;
use serde::Deserialize;
#[derive(Deserialize)]
struct Config {
api_key: String,
}
struct ApiKey<'r>(&'r str);
#[derive(Debug)]
enum ApiKeyError {
Missing,
Invalid,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for ApiKey<'r> {
type Error = ApiKeyError;
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let config_state = try_outcome!(req.guard::<State<'_, Config>>().await);
/// Returns true if `key` is a valid API key string.
fn is_valid(key: &str, api_key: String) -> bool {
key == api_key
}
match req.headers().get_one("Authorization") {
None => Outcome::Failure((Status::Unauthorized, ApiKeyError::Missing)),
Some(key) if is_valid(key, config_state.api_key) => Outcome::Success(ApiKey(key)),
Some(_) => Outcome::Failure((Status::Unauthorized, ApiKeyError::Invalid)),
}
}
}
#[get("/")]
async fn index(config: State<'_, Config>, key: ApiKey<'_>) -> &'static str {
"Hello, world!"
}
fn rocket() -> rocket::Rocket {
let rocket = rocket::ignite();
let figment = rocket.figment();
let config: Config = figment.extract().expect("config");
rocket
.mount("/", routes![index])
.attach(AdHoc::config::<Config>())
}
#[rocket::main]
async fn main() {
rocket()
.launch()
.await;
}
I already stored the config with AdHoch::config() but to retrieve it within the guard I need to use request.rocket().state::<Config>(). The corrected source code is below:
#[macro_use] extern crate rocket;
use rocket::http::Status;
use rocket::request::{Outcome, Request, FromRequest};
use rocket::State;
use rocket::fairing::AdHoc;
use serde::Deserialize;
#[derive(Deserialize)]
struct Config {
api_key: String,
}
struct ApiKey<'r>(&'r str);
#[derive(Debug)]
enum ApiKeyError {
Missing,
Invalid,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for ApiKey<'r> {
type Error = ApiKeyError;
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
// Retrieve the config state like this
let config = req.rocket().state::<Config>().unwrap();
/// Returns true if `key` is a valid API key string.
fn is_valid(key: &str, api_key: &str) -> bool {
key == api_key
}
match req.headers().get_one("Authorization") {
None => Outcome::Failure((Status::Unauthorized, ApiKeyError::Missing)),
Some(key) if is_valid(key, &config.api_key) => Outcome::Success(ApiKey(key)),
Some(_) => Outcome::Failure((Status::Unauthorized, ApiKeyError::Invalid)),
}
}
}
#[get("/")]
async fn index(config: State<'_, Config>, key: ApiKey<'_>) -> &'static str {
"Hello, world!"
}
fn rocket() -> rocket::Rocket {
let rocket = rocket::ignite();
let figment = rocket.figment();
let config: Config = figment.extract().expect("config");
rocket
.mount("/", routes![index])
.attach(AdHoc::config::<Config>())
}
#[rocket::main]
async fn main() {
rocket()
.launch()
.await;
}
I'm currently trying to wrap a C library in rust that has a few requirements. The C library can only be run on a single thread, and can only be initialized / cleaned up once on the same thread. I want something something like the following.
extern "C" {
fn init_lib() -> *mut c_void;
fn cleanup_lib(ctx: *mut c_void);
}
// This line doesn't work.
static mut CTX: Option<(ThreadId, Rc<Context>)> = None;
struct Context(*mut c_void);
impl Context {
fn acquire() -> Result<Rc<Context>, Error> {
// If CTX has a reference on the current thread, clone and return it.
// Otherwise initialize the library and set CTX.
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe { cleanup_lib(self.0); }
}
}
Anyone have a good way to achieve something like this? Every solution I try to come up with involves creating a Mutex / Arc and making the Context type Send and Sync which I don't want as I want it to remain single threaded.
A working solution I came up with was to just implement the reference counting myself, removing the need for Rc entirely.
#![feature(once_cell)]
use std::{error::Error, ffi::c_void, fmt, lazy::SyncLazy, sync::Mutex, thread::ThreadId};
extern "C" {
fn init_lib() -> *mut c_void;
fn cleanup_lib(ctx: *mut c_void);
}
#[derive(Debug)]
pub enum ContextError {
InitOnOtherThread,
}
impl fmt::Display for ContextError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ContextError::InitOnOtherThread => {
write!(f, "Context already initialized on a different thread")
}
}
}
}
impl Error for ContextError {}
struct StaticPtr(*mut c_void);
unsafe impl Send for StaticPtr {}
static CTX: SyncLazy<Mutex<Option<(ThreadId, usize, StaticPtr)>>> =
SyncLazy::new(|| Mutex::new(None));
pub struct Context(*mut c_void);
impl Context {
pub fn acquire() -> Result<Context, ContextError> {
let mut ctx = CTX.lock().unwrap();
if let Some((id, ref_count, ptr)) = ctx.as_mut() {
if *id == std::thread::current().id() {
*ref_count += 1;
return Ok(Context(ptr.0));
}
Err(ContextError::InitOnOtherThread)
} else {
let ptr = unsafe { init_lib() };
*ctx = Some((std::thread::current().id(), 1, StaticPtr(ptr)));
Ok(Context(ptr))
}
}
}
impl Drop for Context {
fn drop(&mut self) {
let mut ctx = CTX.lock().unwrap();
let (_, ref_count, ptr) = ctx.as_mut().unwrap();
*ref_count -= 1;
if *ref_count == 0 {
unsafe {
cleanup_lib(ptr.0);
}
*ctx = None;
}
}
}
I think the most 'rustic' way to do this is with std::sync::mpsc::sync_channel and an enum describing library operations.
The only public-facing elements of this module are launch_lib(), the SafeLibRef struct (but not its internals), and the pub fn that are part of the impl SafeLibRef.
Also, this example strongly represents the philosophy that the best way to deal with global state is to not have any.
I have played fast and loose with the Result::unwrap() calls. It would be more responsible to handle error conditions better.
use std::sync::{ atomic::{ AtomicBool, Ordering }, mpsc::{ SyncSender, Receiver, sync_channel } };
use std::ffi::c_void;
extern "C" {
fn init_lib() -> *mut c_void;
fn do_op_1(ctx: *mut c_void, a: u16, b: u32, c: u64) -> f64;
fn do_op_2(ctx: *mut c_void, a: f64) -> bool;
fn cleanup_lib(ctx: *mut c_void);
}
enum LibOperation {
Op1(u16,u32,u64,SyncSender<f64>),
Op2(f64, SyncSender<bool>),
Terminate(SyncSender<()>),
}
#[derive(Clone)]
pub struct SafeLibRef(SyncSender<LibOperation>);
fn lib_thread(rx: Receiver<LibOperation>) {
static LIB_INITIALIZED: AtomicBool = AtomicBool::new(false);
if LIB_INITIALIZED.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_err() {
panic!("Tried to double-initialize library!");
}
let libptr = unsafe { init_lib() };
loop {
let op = rx.recv();
if op.is_err() {
unsafe { cleanup_lib(libptr) };
break;
}
match op.unwrap() {
LibOperation::Op1(a,b,c,tx_res) => {
let res: f64 = unsafe { do_op_1(libptr, a, b, c) };
tx_res.send(res).unwrap();
},
LibOperation::Op2(a, tx_res) => {
let res: bool = unsafe { do_op_2(libptr, a) };
tx_res.send(res).unwrap();
}
LibOperation::Terminate(tx_res) => {
unsafe { cleanup_lib(libptr) };
tx_res.send(()).unwrap();
break;
}
}
}
}
/// This needs to be called no more than once.
/// The resulting SafeLibRef can be cloned and passed around.
pub fn launch_lib() -> SafeLibRef {
let (tx,rx) = sync_channel(0);
std::thread::spawn(|| lib_thread(rx));
SafeLibRef(tx)
}
// This is the interface that most of your code will use
impl SafeLibRef {
pub fn op_1(&self, a: u16, b: u32, c: u64) -> f64 {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Op1(a, b, c, res_tx)).unwrap();
res_rx.recv().unwrap()
}
pub fn op_2(&self, a: f64) -> bool {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Op2(a, res_tx)).unwrap();
res_rx.recv().unwrap()
}
pub fn terminate(&self) {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Terminate(res_tx)).unwrap();
res_rx.recv().unwrap();
}
}
I am writing a server that allocates some compressed data on startup. Now when I serve a hyper response I do not want to copy these bytes, but I cannot figure out a way to do this with hyper.
I have tried implementing HttpBody for my own type, but the lifetime restriction on the trait blocks me from doing this.
Am I missing something? Here is a minimal example of what I am trying to do:
use hyper::{service::Service, Body, Request, Response, Server};
use std::net::SocketAddr;
use std::sync::Arc;
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
fn main() {
let addr = SocketAddr::new("0.0.0.0".parse().unwrap(), 8080);
println!("Server startup...");
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let app = MakeSvc::new().await;
let ret = app.clone();
let server = Server::bind(&addr).serve(app);
println!("Running on {}", &addr);
server.await.unwrap();
})
}
#[derive(Debug, Clone)]
pub struct WrapperApp {
pub cache: Arc<Vec<u8>>,
}
impl WrapperApp {
//Let's say I allocate some bytes here.
async fn new() -> Self {
Self {
cache: Arc::new(Vec::new()),
}
}
}
impl Service<Request<Body>> for WrapperApp {
type Response = Response<Body>;
type Error = hyper::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
let a = Arc::clone(&self.cache);
return Box::pin(async { Ok(Response::builder().body(Body::from(a)).unwrap()) });
}
}
#[derive(Debug, Clone)]
pub struct MakeSvc {
app: WrapperApp,
}
impl MakeSvc {
pub async fn new() -> Self {
Self {
app: WrapperApp::new().await,
}
}
}
impl<T> Service<T> for MakeSvc {
type Response = WrapperApp;
type Error = hyper::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: T) -> Self::Future {
let app = self.app.clone();
let fut = async move { Ok(app) };
Box::pin(fut)
}
}
error[E0277]: the trait bound `Body: From<Arc<Vec<u8>>>` is not satisfied
--> src/main.rs:50:61
|
50 | return Box::pin(async { Ok(Response::builder().body(Body::from(a)).unwrap()) });
| ^^^^^^^^^^ the trait `From<Arc<Vec<u8>>>` is not implemented for `Body`
|
= help: the following implementations were found:
<Body as From<&'static [u8]>>
<Body as From<&'static str>>
<Body as From<Box<(dyn futures_core::stream::Stream<Item = std::result::Result<hyper::body::Bytes, Box<(dyn std::error::Error + Send + Sync + 'static)>>> + Send + 'static)>>>
<Body as From<Cow<'static, [u8]>>>
and 4 others
= note: required by `from`
The Cargo.toml that goes with this. The example breaks at the place where I am trying to use the ref:
[package]
name = "hyper-ptr"
version = "0.1.0"
authors = ["Pierre Laas <lanklaas123#gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
hyper={version="0.14", features=["server","http1","tcp"]}
tokio={version="1.0.0", features=["macros","io-util","rt-multi-thread"]}
serde={version="*", features=["derive","rc"]}
serde_json="*"
flate2="*"
openssl="*"
rand="*"
From the documentation, there is no From implementation that doesn't require either 'static references or ownership of the body.
Instead, you can clone the cache. Body::From's implementation requires ownership of the Vec<u8> or static data (if that is useful for your case).
Notice that you need to dereference it first:
use hyper::Body; // 0.14.2
use std::sync::Arc;
const FOO: [u8; 1] = [8u8];
fn main() {
let cache = Arc::new(vec![0u8]);
let body = Body::from((*cache).clone());
let other_body = Body::from(&FOO[..]);
println!("{:?}", cache);
}
Playground
I'm trying to send a closure which will generate a structure to a thread, however when I try to do it I get a Sized error. I understand the error (the size is indeed not known at compile time), however adding Boxes and other such tricks does not seem to solve it.
I've tried to look into how to implement the Sized trait, however it seems to be quite special and honestly above my understanding.
I've written a minimal reproducible example:
use std::thread;
trait DataProcess {
fn start(&self);
fn run(&self);
fn stop(&self);
}
struct SomeDP {
name: String,
}
impl DataProcess for SomeDP {
fn start(&self) {
println!("Started");
}
fn run(&self) {
println!("Running");
}
fn stop(&self) {
println!("Stopped");
}
}
fn thread_maker(builder: Box<dyn Fn() -> (dyn DataProcess + Send)>) {
let thread_builder = thread::Builder::new();
let handle = thread_builder.spawn(move || {
let dp = builder();
dp.start();
});
}
fn main() {
let dp_builder = || SomeDP {
name: "nice".to_string(),
};
thread_maker(Box::new(dp_builder));
}
Which you can also find on the playground here
This works
use std::thread;
trait DataProcess{
fn start(&self);
fn run(&self);
fn stop(&self);
}
struct SomeDP{
name: String
}
impl DataProcess for SomeDP{
fn start(&self){println!("Started");}
fn run(&self){println!("Running");}
fn stop(&self){println!("Stopped");}
}
fn thread_maker<F>(builder: F)
where
F: Fn() -> Box<dyn DataProcess>,
F: Send + 'static {
let thread_builder = thread::Builder::new();
let handle = thread_builder.spawn(
move ||{
let dp = builder();
dp.start();
}
);
}
fn main(){
let dp_builder = || -> Box<dyn DataProcess> {
Box::new(SomeDP{name: "nice".to_string()})
};
thread_maker(dp_builder);
}