I'm writing a "hello world" HTTP server with Hyper, however I am not able to find the Server and rt modules when trying to import them.
When invoking cargo run, then I see this error message:
26 | let server = hyper::Server::bind(&addr).serve(router);
| ^^^^^^ could not find `Server` in `hyper`
I must be missing something obvious about Rust and Hyper. What I am trying to do is writing something as dry/simple as possible with just the HTTP layer and some basic routes. I would like to include as little as possible 3rd party dependencies e.g avoiding Tokio which I think involves async behavior, but I am not sure about the context as I am new to Rust.
Looks like I must use futures, so I included this dependency and perhaps futures only work with the async reserved word (which I am not sure if it comes from Tokio or Rust itself).
What confuses me is that in the Hyper examples I do see imports like use hyper::{Body, Request, Response, Server};, so that Server thing must be there, somewhere.
These are the dependencies in Cargo.toml:
hyper = "0.14.12"
serde_json = "1.0.67"
futures = "0.3.17"
This is the code in main.rs:
use futures::future;
use hyper::service::service_fn;
use hyper::{Body, Method, Response, StatusCode};
use serde_json::json;
fn main() {
let router = || {
service_fn(|req| match (req.method(), req.uri().path()) {
(&Method::GET, "/foo") => {
let mut res = Response::new(
Body::from(json!({"message": "bar"}).to_string())
);
future::ok(res)
},
(_, _) => {
let mut res = Response::new(
Body::from(json!({"content": "route not found"}).to_string())
);
*res.status_mut() = StatusCode::NOT_FOUND;
future::ok(res)
}
})
};
let addr = "127.0.0.1:8080".parse::<std::net::SocketAddr>().unwrap();
let server = hyper::Server::bind(&addr).serve(router); // <== this line fails to compile
// hyper::rt::run(server.map_err(|e| {
// eprintln!("server error: {}", e);
// }));
}
How do I make the code above compile and run?
According to documentation, you are missing one module namespace in your call hyper::server::Server:
let server = hyper::server::Server::bind(&addr).serve(router)
In order to use server you need to activate the feature flag in cargo:
hyper = { version = "0.14.12", features = ["server"] }
Related
Issue
I have a client that needs to send the following custom data structure to an API:
#[derive(Serialize, Deserialize)]
pub struct FheSum {
pub server_keys: ServerKey,
pub value1: FheUint8,
pub value2: FheUint8,
}
The code for the client is the following:
let fhe_post: FheSum = FheSum {
server_keys: server_keys.to_owned(),
value1: value_api.to_owned(),
value2: value_api_2.to_owned(),
};
let client = reqwest::blocking::Client::builder()
.timeout(None)
.build().unwrap();
let response = client
.post("http://127.0.0.1:8000/computesum")
.json(&fhe_post)
.send().unwrap();
let response_json: Result<FheSumResult, reqwest::Error> = response.json();
match response_json {
Ok(j) => {
let result_api: u8 = FheUint8::decrypt(&j.result, &client_keys);
println!("Final Result: {}", result_api)
},
Err(e) => println!("{:}", e),
};
In the API, I have the following definition of an HttpServer:
HttpServer::new(|| {
let json_cfg = actix_web::web::JsonConfig::default()
.limit(std::usize::MAX);
App::new()
.app_data(json_cfg)
.service(integers::computesum)
})
.client_disconnect_timeout(std::time::Duration::from_secs(3000))
.client_request_timeout(std::time::Duration::from_secs(3000))
.max_connection_rate(std::usize::MAX)
.bind(("127.0.0.1", 8000))?
.run()
.await
And the associated endpoint the client is trying to access:
#[post("/computesum")]
pub async fn computesum(req: Json<FheSum>) -> HttpResponse {
let req: FheSum = req.into_inner();
let recovered: FheSum = FheSum::new(
req.server_keys,
req.value1,
req.value2,
);
set_server_key(recovered.server_keys);
let result_api_enc: FheSumResult = FheSumResult::new(recovered.value1 + recovered.value2);
HttpResponse::Ok()
.content_type(ContentType::json())
.json(&result_api_enc)
}
Problem
The structs are the same in both the client and the server. This code works when using common data types such as Strings. The issue is when using this data structures. The memory occupied, obtained with mem::size_of_val which returns the size in bytes, is the following:
Size 1: 2488
Size 2: 32
Size 3: 32
The result has been obtained in bytes, so, given the limit established in the HttpServer, this shouldn't be an issue. Timeouts have also been set at much higher values than commonly needed.
Even with this changes, the client always shows Killed, and doesn't display the answer from the server, not giving any clues on what the problem might be.
The client is killing the process before being able to process the server's response. I want to find a way to send these custom data types to the HTTP server without the connection closing before the operation has finished.
I have already tried different libraries for the client such as the acw crate, apart from reqwest and the result is the same. I have also tried not using reqwest in blocking mode, and the error persists.
The documentation for https://docs.rs/reqwest/latest/reqwest/blocking/multipart/struct.Form.html shows this example to create a multipart::Form from a file.
let file = reqwest::blocking::multipart::Form::new().file("key", "/path/to/file")?;
let response = reqwest::blocking::Client::new()
.post("https:://test.com.br/send")
.multipart(file)
.send()
.unwrap();
But the function ".file" is only available on the blocking version of reqwest (reqwest::blocking::multipart::Form).
I've tested the blocking version, and i was able to send a form. But i can't find a way to do this using the async version (reqwest::multipart::Form).
Is there a alternative way to make this call using the async version?
If you want to post just a file using async, please check this answer. If you want to construct a multipart form, try to use reqwest::multipart::Part to construct a body, while wrap your file into a stream.
i m successfully update file with multipart with this code(you could change patch to post)
pub async fn update_form(&self, con: &Collection, id: &str, path: &str) -> String {
let url = [&self.url_struct(con), "/", id].concat();
let file = fs::read(path).unwrap();
let file_part = reqwest::multipart::Part::bytes(file)
.file_name("bg.jpg")
.mime_str("image/jpg")
.unwrap();
let form = reqwest::multipart::Form::new().part("img", file_part);
let client = reqwest::Client::new();
match client
.patch(url)
.headers(construct_headers_form())
.multipart(form)
.send()
.await
{
Ok(res) => res.text().await.unwrap_or("no message".to_string()),
Err(_) => "{\"error\":400}".to_string(),
}
}
I tried the example of actix-multipart with actix-web v3.3.2 and actix-multipart v0.3.0.
For a minimal example,
use actix_multipart::Multipart;
use actix_web::{post, web, App, HttpResponse, HttpServer};
use futures::{StreamExt, TryStreamExt};
#[post("/")]
async fn save_file(mut payload: Multipart) -> HttpResponse {
while let Ok(Some(mut field)) = payload.try_next().await {
let content_type = field.content_disposition().unwrap();
let filename = content_type.get_filename().unwrap();
println!("filename = {}", filename);
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
println!("Read a chunk.");
}
println!("Done");
}
HttpResponse::Ok().finish()
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(save_file))
.bind("0.0.0.0:8080")?
.run()
.await
}
This works well, but I want to do with form data asynchronously. So I tried instead:
use actix_multipart::Multipart;
use actix_web::{post, web, App, HttpResponse, HttpServer};
use futures::{StreamExt, TryStreamExt};
#[post("/")]
async fn save_file(mut payload: Multipart) -> HttpResponse {
actix_web::rt::spawn(async move {
while let Ok(Some(mut field)) = payload.try_next().await {
let content_type = field.content_disposition().unwrap();
let filename = content_type.get_filename().unwrap();
println!("filename = {}", filename);
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
println!("Read a chunk.");
}
println!("Done");
}
});
HttpResponse::Ok().finish()
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(save_file))
.bind("0.0.0.0:8080")?
.run()
.await
}
(Added actix_web::rt::spawn to save_file.)
But this did not work -- the message "Done" never printed. The number of "Read a chunk" displayed in the second case was less than the first case, so I guess that field.next().await cannot terminate for some reason before completing reading all data.
I do not know so much about asynchronous programming, so I am not sure why field.next() did not work in actix_web::rt::spawn.
My question are: why is it, and how can I do with actix_web::rt::spawn?
When you make this call:
actix_web::rt::spawn(async move {
// do things...
});
spawn returns a JoinHandle which is used to poll the task. When you drop that handle (by not binding it to anything), the task is "detached", i.e., it runs in the background.
The actix documentation is not particularly helpful here, but actix uses the tokio runtime under the hood. A key issue is that in tokio, spawned tasks are not guaranteed to complete. The executor needs to know, somehow, that it should perform work on that future. In your second example, the spawned task is never .awaited, nor does it communicate with any other task via channels.
Most likely, the spawned task is never polled and does not make any progress. In order to ensure that it completes, you can either .await the JoinHandle (which will drive the task to completion) or .await some other Future that depends on work in the spawned task (usually by using a channel).
As for your more general goal, the work is already being performed asynchronously! Most likely, actix is doing roughly what you tried to do in your second example: upon receiving a request, it spawns a task to handle the request and polls it repeatedly (as well as the other active requests) until it completes, then sends a response.
How to get OS name while writing gnome-extensions..
for example:
GLib.get_real_name()
I have gone through this post How can my GNOME Shell extension detect the GNOME version?
In case of getting the operating system name as found in /etc/os-release, this not particularly related to GJS or extensions.
You could just open the /etc/os-release file directly, but since GKeyFile is not introspectable in GJS you would have to parse it manually. Alternatively you could use the org.freedesktop.hostname1 DBus interface to get the "pretty name", although I don't know if that's guaranteed to be available on all distributions.
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
let osName = 'Unknown';
try {
// NOTE: this is a synchronous call that will block the main thread
// until it completes. Using `Gio.DBus.system.call()` would be
// better, but I don't know if that works for your use case.
let reply = Gio.DBus.system.call_sync(
'org.freedesktop.hostname1',
'/org/freedesktop/hostname1',
'org.freedesktop.DBus.Properties',
'Get',
new GLib.Variant('(ss)', [
'org.freedesktop.hostname1',
'OperatingSystemPrettyName'
]),
null,
Gio.DBusCallFlags.NONE,
-1,
null
);
let value = reply.deep_unpack()[0];
osName = value.unpack();
} catch (e) {
logError(e, 'Fetching OS name');
}
// Example Output: "Fedora 32 (Workstation Edition)" or "Unknown" on failure
log(osName);
I'm currently experimenting with Webassembly, and one thing I'm trying to do here is with a Webassembly to Fetch data from a JSON file, compile that into a .wasm module, and use that module in Javascript to read the result of the fetch.
I've tried following the code on https://kripken.github.io/emscripten-site/docs/api_reference/fetch.html but the resulting .wasm code is confusing to me because I can't find how to properly load that .wasm module in Javascript.
In case I'm going about this the wrong way, I really need some help with this.
started with this fetch.c file that is supposed to fetch JSON data from a file.
#include <stdio.h>
#include <string.h>
#include <emscripten/fetch.h>
/*////////////////////////
// This file contains the code for fetching
// -> Compiled to .wasm file with emscripten <-
*////////////////////////
void downloadSucceeded(emscripten_fetch_t *fetch) {
printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);
// The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1];
emscripten_fetch_close(fetch); // Free data associated with the fetch.
}
void downloadFailed(emscripten_fetch_t *fetch) {
printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status);
emscripten_fetch_close(fetch); // Also free data on failure.
}
int main() {
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "GET");
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY | EMSCRIPTEN_FETCH_PERSIST_FILE;
attr.onsuccess = downloadSucceeded;
attr.onerror = downloadFailed;
emscripten_fetch(&attr, "./json/bol_list1.json");
}
I compiled this with : emcc wasm/fetch.c -Os -s WASM=1 -s FETCH=1 -s SIDE_MODULE=1 -s BINARYEN_ASYNC_COMPILATION=0 -o wasm/fetch.wasm
fetch.wasm: https://pastebin.com/cHYpgazy
So, now with that module I'm supposed to read it in Javascript and get the result, but this is where I'm stuck, because as opposed to other examples this .wasm module doesn't have an obvious export/import thing and my previous methods of trying to load it failed.
wasmbyfile.js:
Method 1:
let obj;
loadWebAssembly('./wasm/fetch.wasm') //Testing function
.then(instance => {
obj = instance.exports._main;
console.log(obj);
});
function loadWebAssembly(fileName) {
return fetch(fileName)
.then(response => response.arrayBuffer())
.then(bits => WebAssembly.compile(bits))
.then(module => { return new WebAssembly.Instance(module) });
};
error result: wasmbyfile.js:64 Uncaught (in promise) TypeError: WebAssembly Instantiation: Imports argument must be present and must be an object
at fetch.then.then.then.module (wasmbyfile.js:64)
Method 2:
(async () => {
const fetchPromise = fetch('./wasm/fetch.wasm');
const { instance } = await WebAssembly.instantiateStreaming(fetchPromise);
const result = instance.exports._main;
console.log(result);
})();
error result: Uncaught (in promise) TypeError: WebAssembly Instantiation: Imports argument must be present and must be an object
So I'm stuck at this point, and not really sure how to load the module correctly in JS. I need some help for this, or am I doing this the wrong way from the beginning and is there a better way for me to do this?
You are getting an error because your WASM has import statements, while your call to instantiateStreaming does not send an importObject.
But the basic way to use WASM from Javascript is much simpler than: Just define a function in WASM that you can call from JS, and then you do the "fetch" from JS, for instance ("add.wasm"):
(module
(type $t0 (func (param i32 i32) (result i32)))
(func $add (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
get_local $p0
get_local $p1
i32.add)
(export "add" (func $add)))
And then call it from Javascript:
const wasmInstanceFromFile = await WebAssembly.instantiateStreaming(await fetch('add.wasm'));
let sum = wasmInstanceFromFile.instance.exports.add(1,2);