Cloudfront Lambda @ edge在查看器请求上设置cookie [英] Cloudfront Lambda@edge set cookie on Viewer Request

查看:119
本文介绍了Cloudfront Lambda @ edge在查看器请求上设置cookie的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:更好地收集了我的想法



我正在为查看器请求Lambda中的每个用户生成一个唯一标识符(UUID),然后根据该UUID选择要返回的缓存页面。这可行。



理想情况下,该用户将始终具有相同的UUID。



我必须在查看器请求中生成该UUID ,如果该UUID不存在于该查看器请求的Cookie中。我还需要将UUID设置为cookie,这当然发生在响应中而不是请求中。



没有缓存,我的服务器只处理获取自定义标头并在响应标头中创建Set-Cookie。



如果要缓存页面,我没有找到一种处理方法。我可以忽略用于缓存的请求标头,并提供正确的缓存页面,但是由于没有将Cookie设置为在下一个请求中使用,因此用户不会坚持使用UUID。



有人做到了吗?



我正在尝试的事情



我有几个角度正在对此进行处理,但尚未开始工作:


  1. Cloudfront中的某些设置尚未意识到该消息会处理从Viewer Request到Viewer Response的标头或其他数据传递,而这些数据可能在Cloudfront中的第二个lambda中使用。


  2. 修改查看者请求中抢先的响应对象标头。我认为这是不可能的,因为还没有创建返回标头,除非缺少某些内置的Cloudfront方法。


  3. 现有的某种直通标头,我什至都不知道这是因为我对请求响应处理的这一方面并不十分熟悉,但是值得一试。


  4. 可能(虽然尚未尝试),我可以在Client Request lambda中创建整个响应对象,然后以某种方式从该处提供缓存的页面,修改然后将响应标头传递给回调方法。


托宾的答案确实有效,但不是一个可靠的解决方案。如果用户不存储或提供他们的cookie,它将成为一个无限循环,而且如果我可以避免的话,我宁愿不要在所有页面前进行重定向






有些工作原理


  1. 查看器请求Lambda ,当cookie中不存在UUID时,会生成UUID

  2. 查看器请求Lambda在请求对象的标头中的cookie中设置UUID。使用传入的更新请求对象进行回调

  3. 存在UUID cookie破坏Cloudfront缓存

  4. 在存在UUID的情况下触发原始请求Lambda

  5. 来源请求Lambda通过设置了UUID cookie的 http.get 再次调用原始请求URL(限制为40KB,这在查看器请求Lambda中是不切实际的)

  6. 查看器请求Lambda的第二种情况,看到现在存在UUID,剥离UUID cookie,然后正常继续该请求

  7. 第二个原始请求(如果尚未缓存)-缓存的响应如果已缓存,则因为不存在破坏缓存的UUID-将实际页面HTML返回到第一原始请求

  8. 第一原始请求接收到来自 http.get 包含HTML

  9. 第一个原始请求创建一个包含来自 http.get 和Set-Cookie标头的响应主体的自定义响应对象我们原来的UUID

随后的通话,具有UUID已经设置的代码,将从cookie中剥离UUID(以防止缓存破坏),并直接跳到查看器请求Lambda中的第二种情况,后者将直接加载页面的缓存版本。



我说有点是因为当我尝试击中端点时,我下载了一个二进制文件。



编辑



这是因为我没有设置 content-type 标头。我现在只有一个302重定向问题...如果我克服了这个问题,我会给出完整的答案。






原始问题



我在查看器请求上具有一个函数,该函数可以选择一个选项并在从缓存或服务器中检索请求之前设置请求中的某些内容。



这行得通,但我希望它记住将来用户的选择。想法是简单地设置一个cookie,以便下次用户访问时可以阅读。由于这是在查看器请求而非查看器响应上,因此我还没有弄清楚该如何实现,或者甚至可能是通过Lambda本身实现的。

 查看器请求-> 
Lambda选择选项(需要设置Cookie)->
获得相应的内容->
完整设置set-cookie标头返回到Viewer

我有看到示例,并能够通过Lambda。这对我没有多大帮助,因为需要根据请求做出决定。毫不奇怪,将此代码添加到查看器请求中不会在响应中显示任何内容。

解决方案

我认为设置不存在Cookie的真正正确方法是使用以下方法返回指向相同URI的302重定向: Set-Cookie ,然后让浏览器重做请求。这可能不会有太大影响,因为浏览器可以重用相同的连接来跟随重定向。



但是,如果您坚持不这样做,那么您可以使用Viewer Request触发器将Cookie注入请求中,然后在Viewer Response触发器中发出具有相同值的 Set-Cookie



在查看器 response 事件中的 request 对象可以在原始 request中的同一位置找到事件, event.Records [0] .cf.request



在观众响应触发器中,结构的这一部分包含 请求CloudFront从查看器接收到的请求,并且该请求可能已被触发的Lambda函数修改查看器请求事件。



请务必确保正确处理Cookie标头。 Cookie 请求标头需要仔细且准确的操作,因为当存在多个Cookie时,浏览器可以使用多种格式。 p>

有时,必须将Cookie作为单个请求标头发送。

  Cookie:foo = bar; buzz = fizz 

通过拆分上的值来解析这些内容; 后跟< space>



但是浏览器也可以使用多个标头将它们拆分,例如

  Cookie:foo = bar 
Cookie:buzz = fizz

在后一种情况下,数组 event.Records [0] .cf.request.headers.cookie 将包含多个成员。您需要检查该数组中每个对象的 value 属性,检查每个对象中是否有多个值,并适应该数组将完全未定义(不为空)的事实。 )如果没有cookie。






奖金:这是我写的一个函数,我认为它可以正确处理所有情况,包括案件没有Cookie的地方。它将提取您要查找的名称的cookie。 饼干名称区分大小写

  //从cookie标头
中提取请求标头中的cookie值// const my_cookie_value = extract_cookie(event.Records [0] .cf.request.headers,' MYCOOKIENAME);
//如果找不到cookie,则返回null
// https://stackoverflow.com/a/55436033/1695906

函数extract_cookie(headers,cname) {

const cookies = headers ['cookie'];
if(!cookies)
{
console.log( extract_cookie():请求中没有 Cookie:标头);
返回null;
}

//遍历请求中的每个Cookie头,最后到第一个

为(var n = cookies.length; n--;)
{
//检查每个标头值中的所有值,最后到第一个

const cval = cookies [n] .value.split(/; \ /);
const vlen = cval.length;

for(var m = vlen; m--;)
{
const cookie_kv = cval [m] .split(’=’);
if(cookie_kv [0] === cname)
{
return cookie_kv [1];
}
} //对于m(每个值)
} //对于n(每个标头)

//如果达到这一点,我们将不匹配
console.log('extract_cookie():找到了cookie,但是没有指定的cookie');
返回null;

}


Update: Collected my thoughts better

I'm generating a unique identifier (UUID) for each user in the Viewer Request Lambda, and then selecting a cached page to return based upon that UUID. This works.

Ideally, this user would always have the same UUID.

I must generate that UUID in the Viewer Request if it is not present in a cookie on that Viewer Request. I also need that UUID to be set as a cookie, which of course happens in the response not the request.

Without caching, my server simply handles taking a custom header and creating a Set-Cookie in the response header.

I am not finding a way to handle this if I want to cache the page. I can ignore the request header for caching and serve the correct cached page, but then the user does not persist that UUID as no cookie is set to be utilized in their next request.

Has anyone accomplished something like this?

Things I'm trying

There are a few angles I'm working on with this, but haven't been able to get to work yet:

  1. Some sort of setting in Cloudfront I'm unaware of that handles the header or other data pass-through from Viewer Request to Viewer Response, which could be used in a second lambda in Cloudfront.

  2. Modify the response object headers preemptively in the Viewer Request. I don't think this is possible, as they return headers are not yet created, unless there's some built-in Cloudfront methodology I'm missing.

  3. An existing pass-through header of some sort, I don't know if that's even a thing since I'm not intimately familiar with this aspect of request-response handling, but worth a shot.

  4. Possibly (haven't tried yet though) I could create the entire response object in the Client Request lambda and somehow serve the cached page from there, modifying the response headers then passing it into the callback method.

Tobin's answer actually works, but is not a solid solution. If the user is not storing or serving their cookies it becomes an infinite loop, plus I'd rather not throw a redirect up in front of all of my pages if I can avoid it


Somewhat-working concept

  1. Viewer Request Lambda, when UUID not present in cookies, generates UUID
  2. Viewer Request Lambda sets UUID in cookies on header in request object. Callback with updated request object passed in
  3. Presence of UUID cookie busts Cloudfront cache
  4. Origin Request Lambda is triggered with UUID present
  5. Origin Request Lambda calls original request URL again via http.get with UUID cookie set (40KB limit makes doing this in the Viewer Request Lambda impractical)
  6. Second scenario for Viewer Request Lambda, seeing UUID now present, strips the UUID cookie then continues the request normally
  7. Second Origin Request if not yet cached - Cached response if cached, as cache-busting UUID is not present - returns actual page HTML to First Origin Request
  8. First Origin Request receives response from http.get containing HTML
  9. First Origin Request creates custom response object containing response body from http.get and Set-Cookie header set with our original UUID

Subsequent calls, having the UUID already set, will strip the UUID from the cookie (to prevent cache busting) and skip directly to the second-scenario in the Viewer Request Lambda which will directly load the cached version of the page.

I say "somewhat" because when I try to hit my endpoint, I get a binary file downloaded.

EDIT

This is because I was not setting the content-type header. I now have only a 302 redirect problem... if I overcome this I'll post a full answer.


Original question

I have a function on the Viewer Request that picks an option and sets some things in the request before it's retrieved from the cache or server.

That works, but I want it to remember that choice for future users. The thought is to simply set a cookie I can read the next time that user comes through. As this is on the Viewer Request and not the Viewer Response I haven't figured out how to make that happen, or if it even is possible via the Lambda itself.

Viewer Request -> 
  Lambda picks options (needs to set cookie) -> 
    gets corresponding content -> 
      returns to Viewer with set-cookie header intact

I have seen the examples and been able to set cookies successfully in the Viewer Response via a Lambda. That doesn't help me much as the decision needs to be made on the request. Quite unsurprisingly adding this code into the Viewer Request shows nothing in the response.

解决方案

I would argue that the really correct way to set a nonexistent cookie would be to return a 302 redirect to the same URI with Set-Cookie, and let the browser redo the request. This probably would not have much of an impact since the browser can reuse the same connection to "follow" the redirect.

But if you insist on not doing it that way, then you can inject the cookie into the request with your Viewer Request trigger and then emit a Set-Cookie with the same value in your Viewer Response trigger.

The request object, in a viewer response event, can be found at the same place where it's found in the original request event, event.Records[0].cf.request.

In a viewer-response trigger, this part of the structure contains the "request that CloudFront received from the viewer and that might have been modified by the Lambda function that was triggered by a viewer request event."

Use caution to ensure that you handle the cookie header correctly. The Cookie request header requires careful and accurate manipulation because the browser can use multiple formats when multiple cookies exist.

Once upon a time, cookies were required to be sent as a single request header.

Cookie: foo=bar; buzz=fizz

Parse these by splitting the values on ; followed by <space>.

But the browser may also split them with multiple headers, like this:

Cookie: foo=bar
Cookie: buzz=fizz

In the latter case, the array event.Records[0].cf.request.headers.cookie will contain multiple members. You need to examine the value attribute of each object in that array, check for multiple values within each, as well as accommodating the fact that the array will be completely undefined (not empty) if no cookies exist.


Bonus: Here's a function I wrote, that I believe correctly handles all cases including the case where there are no cookies. It will extract the cookie with the name you are looking for. Cookie names are case-sensitive.

// extract a cookie value from request headers, by cookie name
// const my_cookie_value = extract_cookie(event.Records[0].cf.request.headers,'MYCOOKIENAME');
// returns null if the cookie can't be found
// https://stackoverflow.com/a/55436033/1695906

function extract_cookie(headers, cname) {

    const cookies = headers['cookie'];
    if(!cookies)
    {
        console.log("extract_cookie(): no 'Cookie:' headers in request");
        return null;
    }

    // iterate through each Cookie header in the request, last to first

    for (var n = cookies.length; n--;)
    {
        // examine all values within each header value, last to first

        const cval = cookies[n].value.split(/;\ /);
        const vlen = cval.length;

        for (var m = vlen; m--;)
        {
            const cookie_kv = cval[m].split('=');
            if(cookie_kv[0] === cname)
            {
                return cookie_kv[1];
            }            
        } // for m (each value)    
    } // for n (each header)

    // we have no match if we reach this point
    console.log('extract_cookie(): cookies were found, but the specified cookie is absent');
    return null;

}

这篇关于Cloudfront Lambda @ edge在查看器请求上设置cookie的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆