Commit 5702769a authored by Kenton Varda's avatar Kenton Varda

Add slides for meetup talk.

parent 8db916de
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
<meta name="viewport" content="width=480">
<link rel="stylesheet" type="text/css" media="screen" href="{{ site.baseurl }}stylesheets/stylesheet.css">
<link rel="alternate" type="application/rss+xml" title="Cap'n Proto News" href="{{site.baseurl}}feed.xml">
<title>Cap'n Proto: {{ page.title }}</title>
<script type="text/javascript" src="{{ site.baseurl }}javascripts/main.js"></script>
</head>
<body class="slides">
<header>
<img src="{{ site.baseurl }}images/logo.png">
<h1 class="title"></h1>
</header>
<!-- MAIN CONTENT -->
<main>
{{ content }}
</main>
<!-- FOOTER -->
<footer>
<p class="url">
<button class="back">&#9664;</button>
<button class="forward">&#9654;</button>
https://capnproto.org{{ page.url }}<span id="slide-num"></span>
</p>
<h2>Kenton Varda</h2>
<p>May 18, 2017</p>
</footer>
<script type="text/javascript">setupSlides();</script>
<!-- Google Analytics. -->
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-39711112-1");
pageTracker._trackPageview();
} catch(err) {}
</script>
</body>
</html>
......@@ -139,3 +139,62 @@ function setupNewsSidebar(items) {
}
}
}
function setupSlides() {
var slides = document.querySelectorAll("body.slides main section");
var headerTitle = document.querySelector("body.slides header .title");
var slideNum = document.querySelector("#slide-num");
var current = 0;
var hash = document.location.hash;
if (hash) {
current = parseInt(hash.slice(1)) - 1;
}
slides[current].className = "current";
headerTitle.textContent = slides[current].dataset.title || "";
slideNum.textContent = window.location.hash;
function navSlide(diff) {
slides[current].className = "";
current = Math.min(slides.length - 1, Math.max(0, current + diff));
slides[current].className = "current";
headerTitle.textContent = slides[current].dataset.title || "";
if (current) {
history.replaceState({}, "", "#" + (current + 1));
slideNum.textContent = "#" + (current + 1);
} else {
history.replaceState({}, "", window.location.pathname);
slideNum.textContent = "";
}
}
document.body.addEventListener("keydown", event => {
if (event.keyCode == 39) {
navSlide(1);
} else if (event.keyCode == 37) {
navSlide(-1);
}
});
document.querySelector("body.slides footer button.back").addEventListener("click", event => {
navSlide(-1);
});
document.querySelector("body.slides footer button.forward").addEventListener("click", event => {
navSlide(1);
});
if (document.location.hostname === "localhost") {
var lastModified = new Date(document.lastModified);
setInterval(function () {
var req = new Request(".", {headers: {
"If-Modified-Since": lastModified.toUTCString()}});
fetch(req).then(response => {
if (response.status == 200 &&
new Date(response.headers.get("Last-Modified")) > lastModified) {
document.location.reload();
}
});
}, 1000);
}
}
---
layout: slides
title: "Slides: What's Next for Cap'n Proto"
---
<!--===================================================================================-->
<section markdown="1" id="slides-cover">
What's Next for Cap'n Proto?
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Streaming">
Cap'n Proto supports streaming!
{% highlight capnp %}
interface FileStore {
get @0 (name :Text, stream :Stream);
put @1 (name :Text) -> (stream :Stream);
}
interface Stream {
write @0 (data :Data);
end @1 ();
}
{% endhighlight %}
But flow control is up to the app.
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Flow Control">
Let's build it in.
{% highlight capnp %}
interface Stream {
write @0 (data :Data) -> bulk;
end @1 ();
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Realtime">
What about realtime streams?
{% highlight capnp %}
interface VideoCallStream {
sendFrame @0 (frame :Frame) -> realtime;
}
{% endhighlight %}
<br>Best served on a UDP transport...
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Three-Party Handoff">
<img class="ph3" src="3ph.png">
Forwarded request.
Where does response go?
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Three-Party Handoff">
<img class="ph3" src="3ph-proxy.png">
Classic solution:
Proxy
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Three-Party Handoff">
<img class="ph3" src="3ph-redirect.png">
Classic solution:
Redirect
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Three-Party Handoff">
<img class="ph3" src="3ph-0rt.png">
Cap'n Proto:
3-Party Handoff
(aka 3PH)
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Three-Party Handoff">
<img class="ph3" src="3ph-0rt.png">
Cap'n Proto:
3-Party Handoff
(aka 3PH)
... gonna need UDP
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Three-Party Handoff">
<img class="ph3" src="3ph-0rt.png">
Cap'n Proto:
3-Party Handoff
(aka 3PH)
... gonna need UDP
... and 0-RT crypto
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Three-Party Handoff">
API: "Tail call"
{% highlight c++ %}
kj::Promise<void> myRpc(MyRpcContext context) override {
// Begin sub-request.
auto subRequest = someCapability.someRpcRequest();
subRequest.setSomeParam(someValue);
// Send as a tail call.
return context.tailCall(kj::mv(subRequest));
}
{% endhighlight %}
Today: Will proxy<br>Future: 3PH
</section>
<!--===================================================================================-->
<section markdown="1" data-title="KJ TLS Bindings">
KJ client networking, no TLS:
{% highlight c++ %}
void send() {
auto io = kj::setupAsyncIo();
auto& network = io.provider->getNetwork();
auto addr = network.parseAddress("capnproto.org", 80)
.wait(io.waitScope);
auto connection = addr->connect().wait(io.waitScope);
connection->write("GET /", 5).wait(io.waitScope);
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="KJ TLS Bindings">
KJ client networking with TLS:
{% highlight c++ %}
void send() {
auto io = kj::setupAsyncIo();
kj::TlsContext tls;
auto network = tls.wrapNetwork(io.provider->getNetwork());
auto addr = network->parseAddress("capnproto.org", 443)
.wait(io.waitScope);
auto connection = addr->connect().wait(io.waitScope);
connection->write("GET /", 5).wait(io.waitScope);
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="KJ TLS Bindings">
Diff:
{% highlight c++ %}
void send() {
kj::TlsContext tls;
tls.wrapNetwork( );
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="KJ TLS Bindings">
{% highlight c++ %}
void receive() {
auto io = kj::setupAsyncIo();
auto& network = io.provider->getNetwork();
auto addr = network.parseAddress("*", 80)
.wait(io.waitScope);
auto listener = addr->listen();
auto connection = listener->accept().wait(io.waitScope);
connection->write("HTTP/1.1 404 Not Found\r\n\r\n", 26)
.wait(io.waitScope);
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="KJ TLS Bindings">
{% highlight c++ %}
void receive() {
auto io = kj::setupAsyncIo();
kj::TlsKeypair keypair { KEY_PEM_TEXT, CERT_PEM_TEXT };
kj::TlsContext::Options options;
options.defaultKeypair = keypair;
kj::TlsContext tls(options);
auto& network = io.provider->getNetwork();
auto addr = network.parseAddress("*", 443).wait(io.waitScope);
auto listener = tls.wrapPort(addr->listen());
auto connection = listener->accept().wait(io.waitScope);
connection->write("HTTP/1.1 404 Not Found\r\n\r\n", 26)
.wait(io.waitScope);
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="KJ TLS Bindings">
{% highlight c++ %}
void receive() {
kj::TlsKeypair keypair { KEY_PEM_TEXT, CERT_PEM_TEXT };
kj::TlsContext::Options options;
options.defaultKeypair = keypair;
kj::TlsContext tls(options);
tls.wrapPort( );
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="KJ HTTP Library">
{% highlight c++ %}
auto io = kj::setupAsyncIo();
kj::HttpHeaderTable headerTable;
auto client = kj::newHttpClient(
*headerTable, io.provider->getNetwork());
kj::HttpHeaders headers(*headerTable);
auto response = client->request(
kj::HttpMethod::GET, "http://capnproto.org", headers)
.response.wait(io.waitScope);
KJ_ASSERT(response.statusCode == 200);
KJ_LOG(INFO, response.body->readAllText().wait(io.waitScope));
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="KJ HTTP Library">
Headers identified by small numbers.
{% highlight c++ %}
kj::HttpHeaderTable::Builder builder;
kj::HttpHeaderId userAgent = builder.add("User-Agent");
auto headerTable = builder.build();
kj::HttpHeaders headers(*headerTable);
headers.set(kj::HttpHeaderId::HOST, "capnproto.org");
headers.set(userAgent, "kj-http/0.6");
{% endhighlight %}
Header parsing is zero-copy.
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Designated Initializers">
Old and busted:
{% highlight c++ %}
capnp::MallocMessageBuilder message;
auto root = message.initRoot<MyStruct>();
root.setInt32Field(123);
root.setTextField("foo");
auto inner = root.initStructField();
inner.setBoolField(true);
capnp::writeMessageToFd(fd, message);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Designated Initializers">
New hotness:
{% highlight c++ %}
using namespace capnp::init;
capnp::MallocMessageBuilder message;
message.initRoot<MyStruct>(
$int32Field = 123,
$textField = "foo",
$structField(
$boolField = true
)
);
capnp::writeMessageToFd(fd, message);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Designated Initializers">
Even better:
{% highlight c++ %}
using namespace capnp::init;
capnp::writeMessageToFd<MyStruct>(fd,
$int32Field = 123,
$textField = "foo",
$structField(
$boolField = true
)
);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Designated Initializers">
{% highlight c++ %}
struct {
template <typename T>
struct Setter {
T value;
template <typename U> void operator()(U& target) {
target.setInt32Field(kj::fwd<T>(value));
}
};
template <typename T>
Setter<T> operator=(T&& value) {
return { kj::fwd<T>(value) };
}
} $int32Field;
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="POCS">
Kind of painful:
{% highlight c++ %}
capnp::MallocMessageBuilder message;
MyStruct::Builder root = message.initRoot<MyStruct>();
root.setInt32Field(123);
root.setTextField("foo");
InnerStruct::Builder inner = root.initStructField();
inner.setBoolField(true);
capnp::writeMessageToFd(fd, message);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="POCS">
Plain Old C++ Structs?
{% highlight c++ %}
MyStruct root;
root.int32Field = 123;
root.textField = "foo";
InnerStruct inner;
inner.boolField = true;
root.structField = kj::mv(inner);
capnp::writeMessageToFd(fd, message);
{% endhighlight %}
Caveat: No longer zero-copy.
</section>
<!--===================================================================================-->
<section markdown="1" data-title="POCS">
{% highlight c++ %}
capnp::MallocMessageBuilder message;
capnp::readMessageCopy(input, message);
auto root = message.getRoot<MyStruct>();
auto oldListOrphan = root.disownStructList();
auto oldList = oldListOrphan.getReader();
auto newList = root.initStructList(oldList.size() - 1);
for (auto i: kj::indices(newList)) {
newList.setWithCaveats(i,
oldList[i < indexToRemove ? i : i + 1]);
}
capnp::MallocMessageBuilder message2;
message2.setRoot(root.asReader());
capnp::writeMessage(output, message2);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="POCS">
{% highlight c++ %}
auto root = capnp::readMessageCopy<MyStruct>(input);
root.structList.erase(indexToRemove);
capnp::writeMessageCopy(output, root);
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="Threads">
Cap'n Proto RPC between threads!
Actor model!
... that's all I have to say about that.
</section>
<!--===================================================================================-->
<section markdown="1" data-title="JSON-HTTP Bridge">
{% highlight capnp %}
interface AddressBook {
getPerson @0 (id :UInt32 $httpPath)
-> (person :Person $httpBody(type = json))
$http(method = get, route = "person");
# GET /person/<id>
# JSON response body
updatePerson @1 (id :UInt32 $httpPath,
person :Person $httpBody(type = json));
$http(method = put, route = "person");
# PUT /person/<id>
# JSON request body
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="JSON-HTTP Bridge">
{% highlight capnp %}
addPerson @2 (person :Person $httpBody(type = json))
-> (id :UInt32 $httpBody(type = jsonField));
$http(method = post, route = "person");
# POST /person
# JSON request body
# JSON response body (object containing field `id`)
getAll @3 (page :UInt32 = 0 $httpQuery)
-> (people: List(Person) $httpBody(type = json));
$http(method = get);
# GET /?page=<num>
# Query is optional.
# JSAN (JSON array) repsonse body.
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" data-title="JSON-HTTP Bridge">
{% highlight capnp %}
interface AddressBookService {
getAddressBook @0 (key :String $httpPath)
-> (result :AddressBook $httpPipeline);
$http(route = "book");
# GET /book/JrpmUduyHd8uW3x3TOXn2g/person/123
# Becomes:
# service.getAddressBook("JrpmUduyHd8uW3x3TOXn2g").send()
# .getResult().getPerson(123).send()
#
# GET /book/JrpmUduyHd8uW3x3TOXn2g
# Becomes:
# service.getAddressBook("JrpmUduyHd8uW3x3TOXn2g").send()
# .getResult().getAll().send()
}
{% endhighlight %}
</section>
<!--===================================================================================-->
<section markdown="1" id="slides-cover">
Questions?
</section>
......@@ -23,29 +23,29 @@
.highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #006699 } /* Keyword.Pseudo */
.highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #FF6600 } /* Literal.Number */
.highlight .s { color: #CC3300 } /* Literal.String */
.highlight .kt { color: #009900; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000FF } /* Literal.Number */
.highlight .s { color: #0000FF } /* Literal.String */
.highlight .na { color: #330099 } /* Name.Attribute */
.highlight .nb { color: #336666 } /* Name.Builtin */
.highlight .nb { color: #0000FF } /* Name.Builtin */
.highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */
.highlight .no { color: #336600 } /* Name.Constant */
.highlight .nd { color: #9999FF } /* Name.Decorator */
.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #CC00FF } /* Name.Function */
.highlight .nl { color: #9999FF } /* Name.Label */
.highlight .nf { color: inherit } /* Name.Function */
.highlight .nl { color: inherit } /* Name.Label */
.highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #003333 } /* Name.Variable */
.highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #FF6600 } /* Literal.Number.Float */
.highlight .mh { color: #FF6600 } /* Literal.Number.Hex */
.highlight .mi { color: #FF6600 } /* Literal.Number.Integer */
.highlight .mo { color: #FF6600 } /* Literal.Number.Oct */
.highlight .mf { color: #0000FF } /* Literal.Number.Float */
.highlight .mh { color: #0000FF } /* Literal.Number.Hex */
.highlight .mi { color: #0000FF } /* Literal.Number.Integer */
.highlight .mo { color: #0000FF } /* Literal.Number.Oct */
.highlight .sb { color: #CC3300 } /* Literal.String.Backtick */
.highlight .sc { color: #CC3300 } /* Literal.String.Char */
.highlight .sc { color: #0000FF } /* Literal.String.Char */
.highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #CC3300 } /* Literal.String.Double */
.highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */
......
......@@ -709,3 +709,147 @@ table.pass-fail td.pass { background-color: #8f8; }
table.pass-fail td.fail { background-color: #f88; }
table.pass-fail td.warn { background-color: #ff8; }
/*******************************************************************************
Slides
*******************************************************************************/
body.slides * {
box-sizing: border-box;
}
body.slides header {
background: #C42727;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 20%;
}
body.slides header>img {
float: right;
height: 20vh;
padding: 2vh;
width: auto;
}
body.slides header>.title {
color: white;
font-size: 8vh;
text-align: left;
line-height: 20vh;
padding-left: 5vh;
}
body.slides main {
font-size: 4vh;
}
body.slides main section {
border-top: 1px solid #111;
border-bottom: 1px solid #111;
position: fixed;
top: 20%;
left: 0;
width: 100%;
bottom: 10%;
background-color: #f2f2f2;
position: absolute;
z-index: -1;
padding: 5vh;
}
body.slides main section.current {
z-index: 1;
}
body.slides main section#slides-cover {
background: url(/images/logo.png) center/75% no-repeat #C42727;
top: 0;
border-top: none;
}
body.slides main section#slides-cover p {
color: white;
position: absolute;
bottom: 0;
left: 0;
right: 0;
text-align: center;
font-size: 6vh;
font-weight: bold;
}
body.slides main img.ph3 {
background-color: white;
padding: 4vh;
height: 60vh;
width: auto;
box-shadow: 0 0 10px rgba(0,0,0,.1);
float: left;
margin-right: 2vh;
}
body.slides pre {
margin: 0 0 2em;
}
body.slides pre code {
font-size: 3vh;
}
body.slides footer {
background: #212121;
position: fixed;
width: 100%;
height: 10%;
left: 0;
bottom: 0;
color: white;
text-align: right;
padding: 2vh;
}
body.slides footer h2 {
font-size: 4vh;
height: 4vh;
margin: 0;
padding: 0;
background: none;
color: inherit;
line-height: 4vh;
}
body.slides footer p {
font-size: 2vh;
height: 2vh;
line-height: 2vh;
margin: 0;
}
body.slides footer p.url {
position: absolute;
left: 5vh;
top: 0;
bottom: 0;
font-size: 4vh;
line-height: 10vh;
margin: 0;
}
body.slides footer button {
width: 5vh;
height: 5vh;
font-size: 4vh;
padding: 0 0 0.5vh 0;
border: none;
border-radius: 0.5vh;
background-color: white;
}
body.slides footer button:last-of-type {
margin-right: 1vh;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment