2017-02-28 19:05:00 -0600 -

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.