Slide 1

Slide 1 text

Web API for Perl 6 Sterling Hanenkamp Software Engineer 1

Slide 2

Slide 2 text

Yes, we're hiring. We’re also happy to help you find a job somewhere else…
 https:/ /www.ziprecruiter.com Thanks for the plane ticket! 2

Slide 3

Slide 3 text

P6W:Perl6::PSGI:Perl 3

Slide 4

Slide 4 text

Easy Peasey Just Translate from P5 to P6 WRONG! 4

Slide 5

Slide 5 text

hello.p6w use v6; sub app(%env) { start { 200, [ Content-Type => 'text/plain' ], [ 'Hello World' ] } } 5

Slide 6

Slide 6 text

Small Print All code samples in this presentation are non-normative and may not reflect the reality of the current specification or any other revision to the specification. They are for demonstration purposes only and they may not even compile. 6

Slide 7

Slide 7 text

Perl 6: Making the World a Better Place Since Christmas CONCURRENCY Composable Concurrency Objects Promise Supply Automatic Type Coercion 7

Slide 8

Slide 8 text

Goals Standardize the app/server API No 6PAN requirements for apps Simplicity Flexibility to optimize Flexibility to extend 8

Slide 9

Slide 9 text

Implied Objectives Common tasks should be easy Unusual/unanticipated tasks should be possible and even portable Low-level API within a high-level language Making the policy choices required to solve the problem and no others. 9

Slide 10

Slide 10 text

Layers Layer 1: Middleware Layer 2: Application Layer 0: Server All Running Concurrently 10

Slide 11

Slide 11 text

Environment %env passed to the application as an Associative Contains all the CGI-ish keys as well as custom p6w.* ones. Similar to PSGI but Asynchronous (maybe synchronous too). 11

Slide 12

Slide 12 text

A Response, I Promise use v6; sub app(%env) { start { 200, [ Content-Type => 'text/plain' ], [ 'Hello World' ] } } 12

Slide 13

Slide 13 text

A Response-ish, I Promise use v6; sub app(%env) { supply { emit [ 200, [ Content-Type => 'text/plain' ], [ 'Hello World' ] ]; }.Promise; } 13

Slide 14

Slide 14 text

Status/Headers Int (or coerces to an Int) List of Pairs (or something listy that contains something pairy) 14

Slide 15

Slide 15 text

Message Payload Supply .emit('blah blah blah HTML blah'); .done; .quit(X::Adhoc.new(:message('bad stuff'));
 OR within on, die "bad stuff"; Or anything else that can .Supply! 15

Slide 16

Slide 16 text

Message Payload use v6; sub app(%env) { start { 200, [ Content-Type => 'text/plain' ], [ 'Hello World' ] } } 16

Slide 17

Slide 17 text

Message Payload use v6; sub app(%env) { start { 200, [ Content-Type => 'text/plain' ], Supply.from-list(['Hello World' ]) } } 17

Slide 18

Slide 18 text

Message Payload use v6; sub app(%env) { start { 200, [ Content-Type => 'text/plain' ], ['Hello World' ].Supply } } 18

Slide 19

Slide 19 text

Stringify-Encode Blobs are passed through as-is Arrays are treated as headers, encoded using ISO-8859-1 Anything else: stringify and encode using p6w.encode OR via detected Content-Type charset 19

Slide 20

Slide 20 text

P6W > PSGI 20

Slide 21

Slide 21 text

P6W < PSGI (more accurately) 21

Slide 22

Slide 22 text

PSGI Delayed Response my $app = sub { my $env = shift; # Delays response until it fetches content from the network return sub { my $responder = shift; fetch_content_from_server(sub { my $content = shift; $responder->([ 200, $headers, [ $content ] ]); }); }; }; 22

Slide 23

Slide 23 text

P6SGI Delayed Response my &app = sub (%env) { # Delays response until it fetches content from the network start { my $content = fetch-content-from-server(); 200, $headers, [ $content ] } } 23

Slide 24

Slide 24 text

PSGI Streaming my $app = sub { my $env = shift; # immediately starts the response and stream the content return sub { my $responder = shift; my $writer = $responder->( [ 200, [ 'Content-Type', 'application/json' ]]); wait_for_events(sub { my $new_event = shift; if ($new_event) { $writer->write($new_event->as_json . "\n"); } else { $writer->close; } }); }; }; 24

Slide 25

Slide 25 text

P6W Streaming my &app = sub (%env) { # immediately starts the response and stream the content start { my $events = wait-for-events(); 200, [ 'Content-Type' => 'application/json' ], $events.map({ .as-json ~ "\n" }); } } 25

Slide 26

Slide 26 text

PSGI Middleware # $app is a simple PSGI application my $app = sub { my $env = shift; return [ '200', [ 'Content-Type' => 'text/plain' ], [ "Hello World" ] ]; }; # $xheader is a piece of middleware that wraps $app my $xheader = sub { my $env = shift; my $res = $app->($env); push @{$res->[1]}, 'X-PSGI-Used' => 1; return $res; }; 26

Slide 27

Slide 27 text

P6W Middleware # &app is a simple PSGI application my &app = sub (%env) { 200, [ Content-Type => 'text/plain' ], [ "Hello World" ] } # &xheader is a piece of middleware that wraps some &app my &xheader = sub (%env) { nextsame.then(-> $r { my ($s, @h, $p) = $r.result; @h.push: 'P6SGI-Used' => 1; $s, @h, $p }); } &app.wrap(&xheader); 27

Slide 28

Slide 28 text

Middleware: Payload sub debug-line-lengths(%env) { nextsame.then({ my ($s, $h, Supply() $p) = .result; $s, $h, $p.lines.map({ .chars ~ "\n" }) }); } 28

Slide 29

Slide 29 text

Input: Supplies! use v6; sub app(%env) { start { my $form-str = ''; %env.tap: -> $v { $form-str ~= $v }; %env.wait; # Yeah, this is naïve. Get over it. my %params = |$form-str.split('&')».split('='); 200, [ Content-Type => 'text/html' ], do-search(%params); } } 29

Slide 30

Slide 30 text

Request Life Cycle Server MUST start the app ASAP The Server MUST begin sending the response from the App ASAP It is generally assumed that the application and server must run concurrently. (This is Perl 6, not some neolithic language without a decent threading model, after all.) Request Srv Application Response 30

Slide 31

Slide 31 text

Request Life Cycle my $expect-continue = %env ~~ 'HTTP/1.1'|'HTTP/2' && %env eq '100-continue'; if $expect-continue { 100, [ ], on -> $out { my ($s, @h, Supply() $b) = process-input(); @h.push: ':status' => $s; $out.emit(@h); $b.do: -> $v { $out.emit($v) {; }; } 31

Slide 32

Slide 32 text

Raw HTTP/2 # Look, ma, no p6w.io! sub app(%env) { start { if %env ~~ /\/ && %env eq 'h2c' { 101, [ Connection => 'Upgrade', Upgrade => 'h2c' ], supply { # beginning reading HTTP/2 frames and output HTTP/2 frames # using p6w.input and p6w.output }; } else { ... } } } 32

Slide 33

Slide 33 text

HTTP/2 Extension if %env eq 'HTTP/1.1' && %env ~~ /\/ && %env eq 'h2c' && any(|%env) ~~ 'h2c' { 101, [ P6SGIx-Upgrade => 'h2c' ], [] } if %env eq '/index.html' { if %env eq 'HTTP/2' { %env.emit([ ':method' => 'GET', ':scheme' => 'http', ':path' => 'foo.png', ]); } 200, [ 'Content-Type' => 'text/html' ], ... } elsif %env eq '/foo.png' { 200, [ 'Content-Type' => 'image/png' ], ... } 33

Slide 34

Slide 34 text

WebSocket Extension if %env eq 'HTTP/1.1' && %env ~~ /\/ && %env eq 'websocket' && any(|%env) ~~ 'websocket' { 101, [ P6Wx-Upgrade => 'websocket' ], [] } # echo service elsif %env eq 'websocket' { supply { whenever %env -> $v { emit $v; LAST { done } }; }; } 34

Slide 35

Slide 35 text

Protocol Handling The overall specification assumes HTTP/1.x So far, provides special case handling for others: HTTP/2 (adds p6sgix.h2c.push-promise) WebSocket 35

Slide 36

Slide 36 text

Custom Protocol Implementation New protocols can be implemented at any level They can be implemented according to specified extension They can be implemented some other way 36

Slide 37

Slide 37 text

Ready Check sub app(%env) { start { 200, [ Content-Type => 'text/plain' ], supply { await %env; for 0 .. * -> $n { emit $n ~ "\n" } }; } } 37

Slide 38

Slide 38 text

Header/Body Done Extensions sub app(%env) { start { 200, [ Content-Type => 'text/plain' ], supply { await %env; emit("HEADER WRITTEN OUT YAY!"); done; await %env; %env.emit("BODY WRITTEN OUT YAY!"); # TODO Lame. Come up with something better. } } } 38

Slide 39

Slide 39 text

Output Block Detection Extension sub app(%env) { start { my $fh = "really-big-file.txt".IO.open(:r); 200, [ Content-Type => 'text/plain' ], supply { whenever %env -> $yes { sleep 10 if $yes; } whenever $fh -> $chars { # throws an exception when this promise is broken %env.result if %env.status ~~ Broken; emit $chars; } } } } 39

Slide 40

Slide 40 text

PSGI Extensions Sessions Logger Harikiri Cleanup 40

Slide 41

Slide 41 text

More Details https:/ /github.com/zostay/P6W/ https:/ /github.com/zostay/P6W/tree/ master/eg https:/ /speakerdeck.com/zostay/perl-6- standard-gateway-interface-ppw-2015 41

Slide 42

Slide 42 text

Contributing https:/ /github.com/zostay/P6W/ https:/ /github.com/zostay/Smack/ #p6sgi on Freenode #perl6 on Freenode My name is zostay on Freenode/irc.perl.org 42

Slide 43

Slide 43 text

Questions? 43