Pirmin Kalberer @implgeo
Rust Zurich, 11. Juli 2018
Basic
Assembler (6502)
(Turbo) Pascal
Modula
C
C++
Java
Eiffel
Perl
Ruby
Javascript
Python
Rust (2016)
Performance high-water marks for trivial exercises of framework functionality (routing, ORM, templates, etc.).
Real world apps will be substantially more complex with far lower RPS.
JSON serialization of a freshly-instantiated object.
{"message":"Hello, World!"}
Fetching a single row from a simple database table and serializing as a JSON response.
{"id":3217,"randomNumber":2149}
Fetching multiple rows from a simple database table and serializing these rows as a JSON response.
The test is run multiple times: testing 1, 5, 10, 15, and 20 queries per request. All tests are run at 256 concurrency.
The framework’s ORM is used to fetch all rows from a database table containing an unknown number of Unix fortune cookie messages. An additional fortune cookie message is inserted into the list at runtime and then the list is sorted by the message text. Finally, the list is delivered to the client using a server-side HTML template.
Fetching multiple rows from a simple database table, converting the rows to in-memory objects, modifying one attribute of each object in memory, updating each associated row in the database individually, and then serializing the list of objects as a JSON response.
“Hello, World” message rendered as plain text.
Hello, World!
Single Threaded:
Multi-Threaded:
https://codewala.net/2015/07/29/concurrency-vs-multi-threading-vs-asynchronous-programming-explained/
Single Threaded:
Multi-Threaded:
An actor is a computational entity that contains state information and can send, receive and handle messages.
https://www.brianstorti.com/the-actor-model/
fn main() {
let system = System::new("test");
// start new actor
let addr: Addr<Unsync, _> = MyActor{count: 10}.start();
// send message and get future for result
let res = addr.send(Ping(10));
Arbiter::handle().spawn(
res.map(|res| {
println!("RESULT: {}", res == 20);
})
.map_err(|_| ()));
system.run();
}
https://actix.rs
struct AppState {
counter: Cell<usize>,
}
fn index(req: HttpRequest<AppState>) -> String {
let count = req.state().counter.get() + 1; // <- get count
req.state().counter.set(count); // <- store new count in state
format!("Request number: {}", count) // <- response with count
}
// State initialization:
App::with_state(AppState { counter: Cell::new(0) })
.resource("/", |r| r.method(http::Method::GET).f(index))
.finish()
Application state is shared with all routes and resources within the same application (App
).
Note: Http server constructs an application instance for each thread, thus application state must be constructed multiple times.
Handler
trait: A request handler accepts an HttpRequest
instance as parameter and returns a type that can be converted into HttpResponse
(Responder
trait).
/// extract path info from "/{username}/{count}/index.html" url
/// {username} - deserializes to a String
/// {count} - - deserializes to a u32
fn index(info: Path<(String, u32)>) -> Result<String> {
Ok(format!("Welcome {}! {}", info.0, info.1))
}
fn main() {
let app = App::new().resource(
"/{username}/{count}/index.html", // <- define path parameters
|r| r.method(http::Method::GET).with(index) // <- use `with` extractor
.finish();
);
}
Option 2: Access by calling extract() on the extractor
#[derive(Deserialize)]
struct Info {
username: String,
}
// this handler get called only if request's query contains `username` field
fn index(info: Query<Info>) -> String {
format!("Welcome {}!", info.username)
}
fn main() {
let app = App::new().resource(
"/index.html",
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
}
#[derive(Deserialize)]
struct Info {
username: String,
}
/// deserialize `Info` from request's body
fn index(info: Json<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
let app = App::new().resource(
"/index.html",
|r| r.method(http::Method::POST).with(index)); // <- use `with` extractor
}
#[derive(Deserialize)]
struct FormData {
username: String,
}
/// extract form data using serde
/// this handler gets called only if the content type is *x-www-form-urlencoded*
/// and the content of the request could be deserialized to a `FormData` struct
fn index(form: Form<FormData>) -> Result<String> {
Ok(format!("Welcome {}!", form.username))
}
fn index((path, query): (Path<(u32, String)>, Query<Info>)) -> String {
format!("Welcome {}!", query.username)
}
fn main() {
let app = App::new().resource(
"/users/{userid}/{friend}", // <- define path parameters
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
}
Actix provides extractor implementations for tuples (up to 10 elements) whose elements implement FromRequest.
fn index(req: HttpRequest) -> impl Responder {
"Hello from the index page"
}
fn hello(path: Path<String>) -> impl Responder {
format!("Hello {}!", *path)
}
fn main() {
App::new()
.resource("/", |r| r.method(Method::Get).with(index))
.resource("/hello/{name}", |r| r.method(Method::Get).with(hello))
.finish();
}
let app = App::new().configure(|app| {
Cors::for_app(app) // <- Construct CORS middleware builder
.allowed_origin("https://www.rust-lang.org/")
.allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
.allowed_header(http::header::CONTENT_TYPE)
.max_age(3600)
.resource(/* ... */)
.register()
});
let app = App::new()
.middleware(
csrf::CsrfFilter::new().allowed_origin("https://www.example.com"),
)
.resource("/", |r| {
Origin Header based.
fn main() {
actix::System::run(|| {
server::new(
|| App::new().middleware(
SessionStorage::new( // <- create session middleware
CookieSessionBackend::signed(&[0; 32]) // <- create signed cookie session backend
.secure(false)
)))
.bind("127.0.0.1:59880").unwrap()
.start();
});
}
SessionBackend
fn index(req: HttpRequest) -> Result<String> {
// access request identity
if let Some(id) = req.identity() {
Ok(format!("Welcome! {}", id))
} else {
Ok("Welcome Anonymous!".to_owned())
}
}
fn login(mut req: HttpRequest) -> HttpResponse {
req.remember("User1".to_owned()); // <- remember identity
HttpResponse::Ok().finish()
}
fn logout(mut req: HttpRequest) -> HttpResponse {
req.forget(); // <- remove identity
HttpResponse::Ok().finish()
}
fn main() {
let app = App::new().middleware(IdentityService::new(
// <- create identity middleware
CookieIdentityPolicy::new(&[0; 32]) // <- create cookie session backend
.name("auth-cookie")
.secure(false),
));
}
RequestIdentity
fn index(req: HttpRequest) -> HttpResponse {
if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) {
HttpResponse::Ok().into()
} else {
HttpResponse::BadRequest().into()
}
}
fn main() {
let resp = TestRequest::with_header("content-type", "text/plain")
.run(index)
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let resp = TestRequest::default().run(index).unwrap();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
use actix_web::client;
fn main() {
tokio::run({
client::get("http://www.rust-lang.org") // <- Create request builder
.header("User-Agent", "Actix-web")
.finish().unwrap()
.send() // <- Send http request
.map_err(|_| ())
.and_then(|response| { // <- server http response
println!("Response: {:?}", response);
Ok(())
})
});
}
https://t-rex.tileserver.ch/