This is the fifth iteration of my tracking pixel experiment:
AWS Lambda function to return a tracking pixel
AWS Lambda Tracking Pixel with Elasticsearch
AWS Lambda, Kinesis Firehose and Elasticsearch
AWS Lambda Detect Android or iOS device node.js
In this tutorial I show you how to create a node.js project, include node_modules, zip the project and deploy the project to AWS Lambda.
I’m going to take the code from the first tutorial and use the node-cuid module to create a unique cookie id.
First I create an empty directory mkdir lambda-test
, then cd lambda-test
and install the aws-sdk and node-cuid modules.
mkdir lambda-test
cd lambda-test
npm init
npm install --save aws-sdk
npm install --save cuid
Once the modules are installed I can create index.js and copy this code to it
index.js
'use strict';
var cuid = require('cuid');
exports.handler = function(event, context, callback) {
const cookie_name = "_cl";
var date = new Date();
var cookieVal = cuid(); // Generate a random cookie string
var cookieString = cookie_name+"="+cookieVal;
var cookie = '';
if (event.hasOwnProperty('params') && event.params.hasOwnProperty('header') && event.params.header.hasOwnProperty('Cookie')) {
cookie = getCookie(cookie_name, event.params.header.Cookie);
if (!cookie) {
cookieString = cookie_name + "=" + cookieVal;
} else {
cookieString = cookie_name + "=" + cookie;
}
}
// Get Unix milliseconds at current time plus 365 days
date.setTime(+ date + (365 * 86400000)); //24 \* 60 \* 60 \* 100
cookieString = cookieString+"; "+"expires="+date.toGMTString()+";";
var imageHex = "\x42\x4d\x3c\x00\x00\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00"+
"\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00"+
"\x00\x00\x06\x00\x00\x00\x27\x00\x00\x00\x27\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00";
callback(null, { "body":imageHex, "Cookie":cookieString });
};
function getCookie(name, cookies) {
var value = "; " + cookies;
var parts = value.split("; " + name + "=");
if (parts.length >= 2) return parts.pop().split(";").shift();
}
At this point I could zip the node.js project and use the AWS GUI to upload and deploy. Instead of testing on AWS, I like to do a quick test on my local machine.
Install node-lambda globally. This module makes it easy to test AWS Lambda functions locally.
npm install -g node-lambda
Then run $ node-lambda setup
in the lambda-test directory
This will create 3 files: deploy.env, event.json, and context.json
Open and edit event.json
to match the test events you may already have defined for your AWS Lambda functions
event.json
{
"params": {
"path": "",
"querystring": "",
"header": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, sdch, br",
"Accept-Language": "en-US,en;q=0.8",
"Cache-Control": "no-cache"
}
},
"context": {
"http-method": "GET",
"resource-path": "/test.gif"
}
}
Save event.json and run $ node-lambda run
on the console. This will simulate a lambda test and use event.json as the input. If everything works you will see a success in the console and a print out of the pixel and cookie values.
example response from node-lambda run
Success:
{"body":"BM<\u0000\u0000\u0000\u0000\u0000\u0000\u00006\u0000\u0000\u0000(\u0000\u0000\u0000\u0001\u0000\u0000\u0000\u0001\u0000\u0000\u0000\u0001\u0000\u0018\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000'\u0000\u0000\u0000'\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000ÿÿÿÿ\u0000\u0000","Cookie":"_cl=cizqc3be800008ouxn5l6vdw6; expires=Thu, 01 Mar 2018 02:19:26 GMT;"}
Now to create a zip: run $ node-lambda package
this will create a zip file that will be located in the build folder. In the AWS Lambda Console upload this zip file in the function package. If the main file is named index.js (as I show above) then the default Lambda values are fine. Then save and test. For more details checkout the AWS Tutorial - Deployment Package (Node.js).
API Gateway
At this point the Lambda function should be successfully deployed and tested. Now I integrate it with AWS API Gateway. I setup the API Gateway as I did in the other tutorials. In addition, I added Set-Cookie to the Response Header in the Method Response.
Once Set-Cookie is added to the Method Response it is available to edit in the Integration Response. In the Integration Response I expand the table, then expand the Header Mappings table. In the Header Mappings table I edit the Set-Cookie and add the value integration.response.body.Cookie
, click the tiny checkmark and click save.
Also in the Integration Response, I expand the Body Mappings Templates and verify I only have image/gif
set in the Content-Type table.
Integration Response -> Body Mappings Templates -> Content-Type -> ‘image/gif’
#set($inputRoot = $input.path('$'))
$inputRoot.body
Now I deploy the API Gateway to my dev stage and visit the endpoint with Chrome. The response should be an invisible GIF and a cookie should be set. If I visit the endpoint again the cookie should remain the same. If I clear the cookie then visit the endpoint I should have a new cookie value set.