question

Matt Kruse avatar image
Matt Kruse asked

How To Use AWS Lambda as a Proxy for non-SSL Server Endpoints!

After some discussion here, I implemented a generic http proxy in AWS Lambda. This allows you to host your Alexa App on any public-facing HTTP server, rather than going through the hassle of setting up an HTTPS server. It will also allow responses that have chunked encoding. In short, it lowers the barrier to entry for getting published apps up and running. For my POC, I created the Lambda function below. The COOL part is that I can use the SAME lambda function for all of my Alexa Skills! As long as I edit it to map the assigned applicationId to my destination url, it will work. So I can maintain a single Lambda function for all my Alexa Skills, and point them to any http url I wish. Very convenient. Check out the example proxy code below. It can be entered directly into the Lambda editor, no zip file or dependencies required. What do you think? [code] var http = require('http'); var URLParser = require('url'); exports.handler = function (json, context) { try { // A list of URL's to call for each applicationId var handlers = { 'appId':'url', 'amzn1.echo-sdk-ams.app.999999-d0ed-9999-ad00-999999d00ebe':' http://alexa-app.com/helloworld' }; // Look up the url to call based on the appId var url = handlers[json.session.application.applicationId]; if (!url) { context.fail("No url found for application id"); } var parts = URLParser.parse(url); var post_data = JSON.stringify(json); // An object of options to indicate where to post to var post_options = { host: parts.hostname, port: (parts.port || 80), path: parts.path, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': post_data.length } }; // Initiate the request to the HTTP endpoint var req = http.request(post_options,function(res) { var body = ""; // Data may be chunked res.on('data', function(chunk) { body += chunk; }); res.on('end', function() { // When data is done, finish the request context.succeed(JSON.parse(body)); }); }); req.on('error', function(e) { context.fail('problem with request: ' + e.message); }); // Send the JSON data req.write(post_data); req.end(); } catch (e) { context.fail("Exception: " + e); } }; [/code]
alexa skills kitshowcase
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Michael F. Flynn avatar image
Michael F. Flynn answered
Well done and this type of approach (not using Lambda, but one of the other SSL web app services) but everything you proxy through this will be in the clear from Lambda to your service (as well as the response). It's better than nothing as at least the initial request is secure and you'd have to sniff the proxied requests, but it's something to think about especially for any apps with private information (calendaring for example).
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

jjaquinta avatar image
jjaquinta answered
You make a good point about being able to painless redirect. I can see it providing a useful way to move people from test to production, or from one version to another simply with no user change.
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

benrady avatar image
benrady answered
This is a great idea. Do you have a sense of the additional latency introduced by Lambda? I know some people on this forum have complained about 3 second response times using Lambda and I would be a bit concerned about adding that much overhead.
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Matt Kruse avatar image
Matt Kruse answered
> Do you have a sense of the additional latency > introduced by Lambda? I haven't compared against a non-Lambda server (I still haven't gotten SSL to work on Google Cloud) but there is no additional latency compared to a normal Lambda function. It executes quickly for me.
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Greg Laabs avatar image
Greg Laabs answered
Keep in mind that there are very good reasons that Amazon requires all requests to be HTTPS, and it would be irresponsible to publish a public app that used a simple proxy to turn what should be an end-to-end encrypted communication in to an unencrypted one. This has awesome potential for development environments and personal apps though!
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

benrady avatar image
benrady answered
You could proxy this to a server with a self-signed certificate though, right?
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Anil avatar image
Anil answered
Or to get around the lack of (hopefully temporary) support for https SNI.
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Robert Salita avatar image
Robert Salita answered
Using Mark's code as a starting point, I've create a Visual Studio C# project which receives Amazon Lambda proxies: https://github.com/BSalita/ProxyIntent
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

fat ninja avatar image
fat ninja answered
Hello I am having difficulty understanding Mark's proxy code. I am not at all familiar with node.js, or java script... If anyone could help enlighten me. I would like to have a aws lambda function send a url command to a device on my network that I have placed in my DMZ. the url is: http://XXX.XXX.XXX.XXX:8080/remote/processKey?key=guide&hold=keyPress Could someone much smarter than I alter Mark's proxy to make this fire off this url? It is also very possible I am completely misunderstanding what this example is even doing. Somebody able to help fill in the blanks? Thanks!!! var http = require('http'); var URLParser = require('url'); exports.handler = function (json, context) { try { // A list of URL's to call for each applicationId var handlers = { 'appId':'url', 'amzn1.echo-sdk-ams.app.999999-d0ed-9999-ad00-999999d00ebe':' http://alexa-app.com/helloworld' }; // Look up the url to call based on the appId var url = handlers[json.session.application.applicationId]; if (!url) { context.fail("No url found for application id"); } var parts = URLParser.parse(url); var post_data = JSON.stringify(json); // An object of options to indicate where to post to var post_options = { host: parts.hostname, port: (parts.port || 80), path: parts.path, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': post_data.length } }; // Initiate the request to the HTTP endpoint var req = http.request(post_options,function(res) { var body = ""; // Data may be chunked res.on('data', function(chunk) { body += chunk; }); res.on('end', function() { // When data is done, finish the request context.succeed(JSON.parse(body)); }); }); req.on('error', function(e) { context.fail('problem with request: ' + e.message); }); // Send the JSON data req.write(post_data); req.end(); } catch (e) { context.fail("Exception: " + e); } };
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

jjaquinta avatar image
jjaquinta answered
What language [i]do[/i] you know? If it's Java, I have the equivalent thing in Java, if that's what you are coding in. Otherwise, it looks pretty simple. You need to put in your appId and url into the structure references: [code] var http = require('http'); var URLParser = require('url'); exports.handler = function (json, context) { try { // A list of URL's to call for each applicationId var handlers = { 'appId':'url', 'amzn1.echo-sdk-ams.app.999999-d0ed-9999-ad00-999999d00ebe':' http://alexa-app.com/helloworld', 'yourappid': 'http://XXX.XXX.XXX.XXX:8080/remote/processKey?key=guide&hold=keyPress' }; [/code] You are going to have to work out what your appId is.
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.