<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="https://graham.cool/feed.xml" rel="self" type="application/atom+xml" /><link href="https://graham.cool/" rel="alternate" type="text/html" /><updated>2021-08-26T10:33:13+00:00</updated><id>https://graham.cool/feed.xml</id><title type="html">Graham’s Cool Site</title><subtitle>Miscellaneous ramblings from one specific developer.</subtitle><entry><title type="html">Rocket Lamb - Run your Rocket webserver in AWS Lambda</title><link href="https://graham.cool/blog/2019/08/25/rocket-lamb/" rel="alternate" type="text/html" title="Rocket Lamb - Run your Rocket webserver in AWS Lambda" /><published>2019-08-25T15:00:00+00:00</published><updated>2019-08-25T15:00:00+00:00</updated><id>https://graham.cool/blog/2019/08/25/rocket-lamb</id><content type="html" xml:base="https://graham.cool/blog/2019/08/25/rocket-lamb/">&lt;p&gt;I’ve been very interested in running web applications with serverless technologies for a while, and as I’m a primarily a C# developer, I’ve mostly been using &lt;a href=&quot;https://github.com/aws/aws-lambda-dotnet/tree/master/Libraries/src/Amazon.Lambda.AspNetCoreServer&quot;&gt;Amazon.Lambda.AspNetCoreServer&lt;/a&gt;. This allows me to write my actual application using the standard ASP.NET Core framework and easily plug it into Lambda. But one of the biggest problems I’ve experienced with serverless is the slow cold starts, particularly with .NET Core.&lt;/p&gt;

&lt;p&gt;Since I’ve been meaning to get into Rust, I thought I’d try it out with Lambda and see how it compares to what I already know. From some limited googling, Rocket looked like the most ergonomic and “familiar” web framework, but I couldn’t find any easy way to hook it up to Lambda. So I made one! It’s built on the official &lt;a href=&quot;https://github.com/awslabs/aws-lambda-rust-runtime&quot;&gt;lambda rust runtime&lt;/a&gt; and dispatches requests to Rocket using a &lt;a href=&quot;https://api.rocket.rs/v0.4/rocket/local/struct.Client.html&quot;&gt;local Client&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/GREsau/rocket-lamb&quot;&gt;Check it out on GitHub here.&lt;/a&gt;&lt;/strong&gt; I was very pleased to find that (from what little testing I’ve done) cold starts are barely noticeable!&lt;/p&gt;

&lt;h2 id=&quot;basic-usage&quot;&gt;Basic Usage&lt;/h2&gt;

&lt;p&gt;Let’s say you have a simple Rocket application that has a couple of endpoints configured:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[macro_use]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;rocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ignite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.mount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;routes!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;All that’s needed to hook it up to Lambda to enable it to serve requests from AWS API Gateway (or an Application Load Balancer) is to add Rocket Lamb’s &lt;a href=&quot;https://docs.rs/rocket_lamb/0.5.0/rocket_lamb/trait.RocketExt.html#tymethod.lambda&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lambda()&lt;/code&gt;&lt;/a&gt; extension method on your instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rocket&lt;/code&gt;, like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[macro_use]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rocket_lamb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RocketExt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;rocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ignite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.mount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;routes!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is really just sugar for creating an implementation of a &lt;a href=&quot;https://docs.rs/lambda_http/0.1.1/lambda_http/trait.Handler.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lambda_http::Handler&lt;/code&gt;&lt;/a&gt; and passing it to the &lt;a href=&quot;https://docs.rs/lambda_http/0.1.1/lambda_http/macro.lambda.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lambda_http::lambda!&lt;/code&gt;&lt;/a&gt; macro. This slightly more verbose implementation would behave identically:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[macro_use]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;lambda_http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rocket_lamb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RocketHandlerBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rocket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ignite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.mount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;routes!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;RocketHandlerBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.into_handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;lambda!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;binary-responses&quot;&gt;Binary Responses&lt;/h2&gt;
&lt;p&gt;Lambda’s runtime API involves passing all data (including response bodies) around inside JSON documents. This generally means everything is encoded as UTF-8 text, but it also accepts base64-encoded binary content. Rocket Lamb will automatically base-64 encode any response body which is not valid UTF-8, but in order for this to be handled correctly by AWS, you must enable binary responses on your API Gateway.&lt;/p&gt;

&lt;p&gt;The easiest way to do this is by adding a single catch-all binary media type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*/*&lt;/code&gt;, which can be added to your API Gateway in &lt;a href=&quot;https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings-configure-with-console.html&quot;&gt;the AWS console&lt;/a&gt;. If you’re deploying to AWS using the AWS Severless Application Model, then you can enable binary responses by adding this global setting to your SAM template:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;Globals&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;BinaryMediaTypes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*/*&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You should only need to enable binary responses if you’re using AWS API Gateway - if you’re instead using an Application Load Balancer, it should just work immediately.&lt;/p&gt;

&lt;h2 id=&quot;base-path-handling&quot;&gt;Base Path Handling&lt;/h2&gt;
&lt;p&gt;When you deploy to API Gateway, you’re given a URL like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://abcd1234.execute-api.eu-west-1.amazonaws.com/Prod&lt;/code&gt;, where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Prod&lt;/code&gt; is the API Gateway deployment stage. Because the stage is part of the path, the server must be aware of it in order to generate correct absolute URLs in responses (e.g. in the Location response header for redirects). This problem also occurs when using an &lt;a href=&quot;https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains.html&quot;&gt;API Gateway custom domain&lt;/a&gt; with a set base path.&lt;/p&gt;

&lt;p&gt;To prevent you from having to handle this base path in the application, Rocket Lamb will automatically figure out the base path from the first request received, and then clone all routes of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rocket&lt;/code&gt;, prepending the base path onto them. If you’d rather handle the base path yourself, you can change this behaviour by setting a different &lt;a href=&quot;https://docs.rs/rocket_lamb/0.6.0/rocket_lamb/struct.RocketHandlerBuilder.html#method.base_path_behaviour&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base_path_behaviour&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;building-and-deploying-to-aws&quot;&gt;Building and Deploying to AWS&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/awslabs/aws-lambda-rust-runtime#deployment&quot;&gt;AWS Lambda Rust Runtime readme&lt;/a&gt; goes over deployment in detail, so I don’t seen any need to repeat it - go check that out!&lt;/p&gt;

&lt;p&gt;You may also want to take a look at the &lt;a href=&quot;https://github.com/GREsau/example-rocket-lamb-api#deploying-to-aws-lambda&quot;&gt;Example Rocket Lamb API&lt;/a&gt;, which uses docker-compose as a cross-platform way to produce a ZIP file ready to be uploaded to lambda. It also has a &lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html&quot;&gt;Serverless Application Model&lt;/a&gt; template file to make it easy to deploy to AWS using CloudFormation.&lt;/p&gt;

&lt;h2 id=&quot;running-locally&quot;&gt;Running Locally&lt;/h2&gt;
&lt;p&gt;There are a couple of ways I’ve used to run a Rocket Lamb application locally:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Output two binaries - one for running in lambda, and one for just running locally. The local one can just run the Rocket server normally, without using Rocket Lamb at all. The &lt;a href=&quot;https://github.com/GREsau/example-rocket-lamb-api&quot;&gt;Example Rocket Lamb API&lt;/a&gt; shows how this can be done.&lt;/li&gt;
  &lt;li&gt;Use AWS SAM CLI’s &lt;a href=&quot;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-start-api.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local start-api&lt;/code&gt;&lt;/a&gt; - which is a more realistic environment, but pretty slow as it spins up a new docker container for each request (although &lt;a href=&quot;https://github.com/awslabs/aws-sam-cli/pull/1305&quot;&gt;this is being improved&lt;/a&gt;).&lt;/li&gt;
&lt;/ol&gt;</content><author><name></name></author><category term="rust" /><category term="lambda" /><category term="rocket" /><category term="aws" /><summary type="html">I’ve been very interested in running web applications with serverless technologies for a while, and as I’m a primarily a C# developer, I’ve mostly been using Amazon.Lambda.AspNetCoreServer. This allows me to write my actual application using the standard ASP.NET Core framework and easily plug it into Lambda. But one of the biggest problems I’ve experienced with serverless is the slow cold starts, particularly with .NET Core.</summary></entry></feed>