Lambda@Edge function for handling redirects and rewrites

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
2
down vote

favorite












We're converting the City of Philadelphia's website to be served as static files. In order to support redirects and rewrites (URL masking), we decided to use a Lambda@edge function, which modifies (or responds to) the inbound request. I expect this will be maintained by other people so readability is very important. I would love your feedback on how this could be easier to understand and maintain.



const URL = require('url')
const inspect = require('util')
const TRAILING_SLASH = //$/
let compiledRegexes =

exports.handler = createHandler(require('../rules.json')) // expose to lambda
exports.createHandler = createHandler // expose for testing

function createHandler (rules)
return async function (event)
const request = event.Records[0].cf.request
const cleanPath = request.uri.toLowerCase().replace(TRAILING_SLASH, '')
log(event)

for (const rule of rules)
let newPath = null

if (rule.regex)
if (!compiledRegexes.hasOwnProperty(rule.pattern))
compiledRegexes[rule.pattern] = new RegExp(rule.pattern)


const regex = compiledRegexes[rule.pattern]
if (regex.test(cleanPath))
newPath = cleanPath.replace(regex, rule.replacement)

else if (cleanPath === rule.pattern)
newPath = rule.replacement


if (newPath !== null)
if (rule.type === 'rewrite') else
const response = createRedirect(newPath)
log(response)
return response




// If no matches, return unmodified request
log('no match')
return request



// Mutates request object
function setOrigin (request, origin)
const url = new URL(origin)
const protocol = url.protocol.slice(0, -1) // remove trailing colon
const path = url.pathname.replace(TRAILING_SLASH, '')

request.origin =
custom:
domainName: url.hostname,
protocol,
port: (protocol === 'https') ? 443 : 80,
path,
sslProtocols: ['TLSv1.2', 'TLSv1.1'],
readTimeout: 5,
keepaliveTimeout: 5,
customHeaders:


request.headers.host = [
key: 'host', value: url.hostname
]


function createRedirect (newUri)
return
status: '301',
statusDescription: 'Moved Permanently',
headers:
location: [
key: 'Location', value: newUri
]




function log (data)
if (process.env.NODE_ENV === 'test') return
// use util.inspect so objects aren't collapsed
console.log(inspect(data, false, 10))



A few example rules from rules.json:



redirects




"pattern": "/old",
"replacement": "https://example.com/new",
"type": "redirect"



rewrites




"pattern": "/old(/.+)?",
"regex": true,
"replacement": "$1",
"origin": "https://example.com/new",
"type": "rewrite"



(I definitely don't love the "API" of how these rules are configured, and I think they could be simplified or merged. Would love any ideas here too!)



Thanks so much for your time.







share|improve this question

























    up vote
    2
    down vote

    favorite












    We're converting the City of Philadelphia's website to be served as static files. In order to support redirects and rewrites (URL masking), we decided to use a Lambda@edge function, which modifies (or responds to) the inbound request. I expect this will be maintained by other people so readability is very important. I would love your feedback on how this could be easier to understand and maintain.



    const URL = require('url')
    const inspect = require('util')
    const TRAILING_SLASH = //$/
    let compiledRegexes =

    exports.handler = createHandler(require('../rules.json')) // expose to lambda
    exports.createHandler = createHandler // expose for testing

    function createHandler (rules)
    return async function (event)
    const request = event.Records[0].cf.request
    const cleanPath = request.uri.toLowerCase().replace(TRAILING_SLASH, '')
    log(event)

    for (const rule of rules)
    let newPath = null

    if (rule.regex)
    if (!compiledRegexes.hasOwnProperty(rule.pattern))
    compiledRegexes[rule.pattern] = new RegExp(rule.pattern)


    const regex = compiledRegexes[rule.pattern]
    if (regex.test(cleanPath))
    newPath = cleanPath.replace(regex, rule.replacement)

    else if (cleanPath === rule.pattern)
    newPath = rule.replacement


    if (newPath !== null)
    if (rule.type === 'rewrite') else
    const response = createRedirect(newPath)
    log(response)
    return response




    // If no matches, return unmodified request
    log('no match')
    return request



    // Mutates request object
    function setOrigin (request, origin)
    const url = new URL(origin)
    const protocol = url.protocol.slice(0, -1) // remove trailing colon
    const path = url.pathname.replace(TRAILING_SLASH, '')

    request.origin =
    custom:
    domainName: url.hostname,
    protocol,
    port: (protocol === 'https') ? 443 : 80,
    path,
    sslProtocols: ['TLSv1.2', 'TLSv1.1'],
    readTimeout: 5,
    keepaliveTimeout: 5,
    customHeaders:


    request.headers.host = [
    key: 'host', value: url.hostname
    ]


    function createRedirect (newUri)
    return
    status: '301',
    statusDescription: 'Moved Permanently',
    headers:
    location: [
    key: 'Location', value: newUri
    ]




    function log (data)
    if (process.env.NODE_ENV === 'test') return
    // use util.inspect so objects aren't collapsed
    console.log(inspect(data, false, 10))



    A few example rules from rules.json:



    redirects




    "pattern": "/old",
    "replacement": "https://example.com/new",
    "type": "redirect"



    rewrites




    "pattern": "/old(/.+)?",
    "regex": true,
    "replacement": "$1",
    "origin": "https://example.com/new",
    "type": "rewrite"



    (I definitely don't love the "API" of how these rules are configured, and I think they could be simplified or merged. Would love any ideas here too!)



    Thanks so much for your time.







    share|improve this question





















      up vote
      2
      down vote

      favorite









      up vote
      2
      down vote

      favorite











      We're converting the City of Philadelphia's website to be served as static files. In order to support redirects and rewrites (URL masking), we decided to use a Lambda@edge function, which modifies (or responds to) the inbound request. I expect this will be maintained by other people so readability is very important. I would love your feedback on how this could be easier to understand and maintain.



      const URL = require('url')
      const inspect = require('util')
      const TRAILING_SLASH = //$/
      let compiledRegexes =

      exports.handler = createHandler(require('../rules.json')) // expose to lambda
      exports.createHandler = createHandler // expose for testing

      function createHandler (rules)
      return async function (event)
      const request = event.Records[0].cf.request
      const cleanPath = request.uri.toLowerCase().replace(TRAILING_SLASH, '')
      log(event)

      for (const rule of rules)
      let newPath = null

      if (rule.regex)
      if (!compiledRegexes.hasOwnProperty(rule.pattern))
      compiledRegexes[rule.pattern] = new RegExp(rule.pattern)


      const regex = compiledRegexes[rule.pattern]
      if (regex.test(cleanPath))
      newPath = cleanPath.replace(regex, rule.replacement)

      else if (cleanPath === rule.pattern)
      newPath = rule.replacement


      if (newPath !== null)
      if (rule.type === 'rewrite') else
      const response = createRedirect(newPath)
      log(response)
      return response




      // If no matches, return unmodified request
      log('no match')
      return request



      // Mutates request object
      function setOrigin (request, origin)
      const url = new URL(origin)
      const protocol = url.protocol.slice(0, -1) // remove trailing colon
      const path = url.pathname.replace(TRAILING_SLASH, '')

      request.origin =
      custom:
      domainName: url.hostname,
      protocol,
      port: (protocol === 'https') ? 443 : 80,
      path,
      sslProtocols: ['TLSv1.2', 'TLSv1.1'],
      readTimeout: 5,
      keepaliveTimeout: 5,
      customHeaders:


      request.headers.host = [
      key: 'host', value: url.hostname
      ]


      function createRedirect (newUri)
      return
      status: '301',
      statusDescription: 'Moved Permanently',
      headers:
      location: [
      key: 'Location', value: newUri
      ]




      function log (data)
      if (process.env.NODE_ENV === 'test') return
      // use util.inspect so objects aren't collapsed
      console.log(inspect(data, false, 10))



      A few example rules from rules.json:



      redirects




      "pattern": "/old",
      "replacement": "https://example.com/new",
      "type": "redirect"



      rewrites




      "pattern": "/old(/.+)?",
      "regex": true,
      "replacement": "$1",
      "origin": "https://example.com/new",
      "type": "rewrite"



      (I definitely don't love the "API" of how these rules are configured, and I think they could be simplified or merged. Would love any ideas here too!)



      Thanks so much for your time.







      share|improve this question











      We're converting the City of Philadelphia's website to be served as static files. In order to support redirects and rewrites (URL masking), we decided to use a Lambda@edge function, which modifies (or responds to) the inbound request. I expect this will be maintained by other people so readability is very important. I would love your feedback on how this could be easier to understand and maintain.



      const URL = require('url')
      const inspect = require('util')
      const TRAILING_SLASH = //$/
      let compiledRegexes =

      exports.handler = createHandler(require('../rules.json')) // expose to lambda
      exports.createHandler = createHandler // expose for testing

      function createHandler (rules)
      return async function (event)
      const request = event.Records[0].cf.request
      const cleanPath = request.uri.toLowerCase().replace(TRAILING_SLASH, '')
      log(event)

      for (const rule of rules)
      let newPath = null

      if (rule.regex)
      if (!compiledRegexes.hasOwnProperty(rule.pattern))
      compiledRegexes[rule.pattern] = new RegExp(rule.pattern)


      const regex = compiledRegexes[rule.pattern]
      if (regex.test(cleanPath))
      newPath = cleanPath.replace(regex, rule.replacement)

      else if (cleanPath === rule.pattern)
      newPath = rule.replacement


      if (newPath !== null)
      if (rule.type === 'rewrite') else
      const response = createRedirect(newPath)
      log(response)
      return response




      // If no matches, return unmodified request
      log('no match')
      return request



      // Mutates request object
      function setOrigin (request, origin)
      const url = new URL(origin)
      const protocol = url.protocol.slice(0, -1) // remove trailing colon
      const path = url.pathname.replace(TRAILING_SLASH, '')

      request.origin =
      custom:
      domainName: url.hostname,
      protocol,
      port: (protocol === 'https') ? 443 : 80,
      path,
      sslProtocols: ['TLSv1.2', 'TLSv1.1'],
      readTimeout: 5,
      keepaliveTimeout: 5,
      customHeaders:


      request.headers.host = [
      key: 'host', value: url.hostname
      ]


      function createRedirect (newUri)
      return
      status: '301',
      statusDescription: 'Moved Permanently',
      headers:
      location: [
      key: 'Location', value: newUri
      ]




      function log (data)
      if (process.env.NODE_ENV === 'test') return
      // use util.inspect so objects aren't collapsed
      console.log(inspect(data, false, 10))



      A few example rules from rules.json:



      redirects




      "pattern": "/old",
      "replacement": "https://example.com/new",
      "type": "redirect"



      rewrites




      "pattern": "/old(/.+)?",
      "regex": true,
      "replacement": "$1",
      "origin": "https://example.com/new",
      "type": "rewrite"



      (I definitely don't love the "API" of how these rules are configured, and I think they could be simplified or merged. Would love any ideas here too!)



      Thanks so much for your time.









      share|improve this question










      share|improve this question




      share|improve this question









      asked Jun 8 at 21:51









      Tobias Fünke

      1485




      1485

























          active

          oldest

          votes











          Your Answer




          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "196"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f196141%2flambdaedge-function-for-handling-redirects-and-rewrites%23new-answer', 'question_page');

          );

          Post as a guest



































          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes










           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f196141%2flambdaedge-function-for-handling-redirects-and-rewrites%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Greedy Best First Search implementation in Rust

          Function to Return a JSON Like Objects Using VBA Collections and Arrays

          C++11 CLH Lock Implementation