Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cookies #73

Closed
arcticShadow opened this issue Feb 26, 2013 · 8 comments
Closed

cookies #73

arcticShadow opened this issue Feb 26, 2013 · 8 comments

Comments

@arcticShadow
Copy link

Hi,
I'm working with a custom but of code in a header file to build a country switcher, that changes between the sites and remembers the decision for future visits. This works outside of varnish, but when we tried to deploy it, cookies were stripped.

I realise that cookies are stripped, however, I need to allow one through.

My setup is:

  • 3 magento stores
  • turpentine (From github)
  • when a user first visits any site, a geoip lookup is done, and a cookie is set based on there location (For future redirection)
  • I.E. User visit .com and GEOIP lookup says they are NZ then they get an nz cookie, and the nz store is loaded, causing the url to change. Now a new cookie is set on the nz domain, and the user stays there. If a user hits 'com' from the switcher, it sets a cookie on nz side to say 'com' and then redirects with a url param (?mystore=com) to com. The index picks up the flag and sets the com domain cookie to com.
  • I'm thinking that i need a rule to exclude ?mystore=* urls from caching, and
  • to allow the mystore cookie to be used as part of the varnish page hash

Any pointers in the right direction would be much appreciated.

@aheadley
Copy link
Contributor

Doing something like this is... complicated. To start with, the current behaviour of passing a visitor's initial request to the backend is going away in the next release. Instead Varnish will generate the frontend cookie token and serve the response from cache without talking to the backend at all, so this:

when a user first visits any site, a geoip lookup is done, and a cookie is set based on there location (For future redirection)

won't work in the (very) near future if it relies on that initial pass through.

User visit .com and GEOIP lookup says they are NZ then they get an nz cookie, and the nz store is loaded, causing the url to change. Now a new cookie is set on the nz domain, and the user stays there. If a user hits 'com' from the switcher, it sets a cookie on nz side to say 'com' and then redirects with a url param (?mystore=com) to com. The index picks up the flag and sets the com domain cookie to com.

While it wouldn't be trivial, you could probably use the one of the GeoIP vmods:

and manually setting the Cookie header in vcl_recv and Set-Cookie header in vcl_deliver, and doing the redirect if you just set the cookie. You can put your additions in app/code/community/Nexcessnet/Turpentine/misc/custom_include.vcl and they will be included in the generated VCL automatically.

@arcticShadow
Copy link
Author

Thanks for the insightful response. I had no idea that varnish could have plugins! I will have to look into this.

Can you please give an example use of custom_include.vcl Do I define the vcl_rev and vcl_deliver the same as if it was 'default.vcl'?

@aheadley
Copy link
Contributor

Sort of, yes. You will need to define the functions but Varnish will append them all together so you don't need to directly copy them, just do the processing that you need to do and don't do a return to let it fall through to the later methods. For example, you could write:

sub vcl_recv {
     if (req.http.Host ~ "example.com.nz" && req.http.Cookie ~ "region=us") {
         # do redirect stuff
     }
}

Then the later vcl_recv function would just be appended to that.

@aheadley aheadley closed this as completed Mar 6, 2013
@arcticShadow
Copy link
Author

Hi,
This cookie setting business is becoming quite a handful.

I have identified what I need to acheive:
My cookie is named 'mystore'.

A) If mystore cookie does not exist: #1 create cookie for current domain (if not .com) #2 redirect to .com (if not already there) #3 create cookie on .com

B) If mystore cookie exists, and url indicates the same store as in the cookie value, then load store

C) If mystore cookie exists, and url indicates a different store than is in the cookie value, then redirect to correct store.

D) The other thing that i can see needs to happen, is the cookie needs to be passed to the backend for any backend requests.

So far, I have achieved B and C. I'm stuck with the rest.

Are you able to provide paid support to implement this for us? If so what kind of time frame and costs.

Thanks in advance.
Cole

@aheadley
Copy link
Contributor

aheadley commented Mar 7, 2013

So far, I have achieved B and C. I'm stuck with the rest.

D should happen automatically once the client has the cookie (sent the Set-Cookie header with the cookie), with 0.5.x at least, not sure about lower versions.

For A, you would probably want to do something like this:

sub set_store_cookie {
    # set a flag so we know to send the Set-Cookie header in the response
    set req.http.X-MyStoreCookie-Flag = "1";
    if (req.http.Cookie) {
        set req.http.Cookie = req.http.Cookie + "; mystore=" + req.http.Host;
    } else {
        set req.http.Cookie = "mystore=" + req.http.Host;
    }
}
sub vcl_recv {
    if (req.http.Cookie !~ "mystore=") {
        call set_store_cookie;
        if (req.http.Host !~ ".com$") {
            # redirect to .com domain
        }
    }
}
sub vcl_deliver {
    if (req.http.X-MyStoreCookie-Flag) {
        set resp.http.Set-Cookie = regsub(req.http.Cookie, ".*(mystore=[^;]+).*", "\1");
    }
}

Are you able to provide paid support to implement this for us? If so what kind of time frame and costs.

Unfortunately no, I don't do custom development like that.

@aheadley aheadley reopened this Mar 7, 2013
@arcticShadow
Copy link
Author

Wow thanks heaps. It appears that I have got it working to our specification. I will report back on progress.

@arcticShadow
Copy link
Author

So I thought that I had it all working, but it's still only half working. I guess the easiest thing to do is post my current config.

I had to change the regex slightly provided in a previous post it apeared that it was using a ; as a cookie separator, where as I needed extra cookie values so I have changed that to a comma instead.

  1. I've noticed that I cant set the cookie at the same time as the frontend cookie. it seems to overwrite it.
  2. When I change stores I have a on click JavaScript event to update the cookie on this domain, and I go to a new domain with a ?mystore=value - Based on my code below i'm trying to use this url as a mannual overide of the automatic process. But it dosnt seem to set the cooke.

for reference the site (mentioned below) is hosted at 119.224.138.120 - DNS is not currently pointing there.

#
# Custom Additions in this file are merged into teh default.vcl that turpintine spits out.
#

sub vcl_recv {
    if (req.http.Host == "phpmyadmin.europa.netactive.co.nz") {
         # stop all forms of caching - this is phpmyadmin
         return (pass);
     }
#force all assets to use same cached version
    if (req.url ~ ".*\.(?:css|js|jpe?g|png|gif|ico|swf)(?=\?|&|$)") {
#    set req.http.Host = "www.charlesparsonsinteriors.com";
    }


    if (req.url ~ "admin") {
    }
    # try get the url override to work.
    else if( req.http.Host ~ "com\.au" && req.url ~ "mystore=comau"){
        call set_store_cookie;
    }
    else if( req.http.Host ~ "co\.nz" && req.url ~ "mystore=conz"){
        call set_store_cookie;
    }
    else if( req.http.Host ~ "com" && req.url ~ "mystore=com"){
        call set_store_cookie;
    }
    #if cookie says au but domain is not au - redirect
    else if(req.http.Cookie ~ "mystore=www\.charlesparsonsinteriors\.com\.aupad" && req.http.Host !~ "com\.au$") {
        error 330 req.http.host;
    }
    #if cookie says nz but domain is not nz - redirect
    else if(req.http.Cookie ~ "mystore=www\.charlesparsonsinteriors\.co\.nzpad" && req.http.Host !~ "co\.nz$") {
        error 331 req.http.host;
    }
    #if cookie says com but domain is not com - redirect
    else if(req.http.Cookie ~ "mystore=www\.charlesparsonsinteriors\.compad" && req.http.Host !~ "\.com$") {
        error 332 req.http.host;
    }
    #if no cookie -create it
    else if(req.http.Cookie !~ "mystore=" ) {
        if (req.http.Host !~ ".com$") {
            error 333 req.http.host;
        }else{
            call set_store_cookie;
        }
    }

}

sub set_store_cookie {
    # set a flag so we know to send the Set-Cookie header in the response
    set req.http.X-MyStoreCookie-Flag = "1";
    if (req.http.Cookie) {
        set req.http.Cookie = req.http.Cookie + ", mystore=" + req.http.Host + "pad ; domain=."+req.http.Host+" ; expires=31536000";
    } else {
        set req.http.Cookie = "mystore=" + req.http.Host + "pad ; domain=."+req.http.Host + "; expires=31536000 ";
    }
}
sub vcl_deliver {
    if (req.http.X-MyStoreCookie-Flag) {
        set resp.http.Set-Cookie = regsub(req.http.Cookie, ".*(mystore=[^,]+).*", "\1");
    }
}
sub vcl_error {
    #cookie said au but domain is not au - redirect
    if (obj.status == 330){
        set obj.http.Location = "http://www.charlesparsonsinteriors.com.au/?mystore=comau";
        set obj.status = 302;
        return(deliver);
    }
    #cookie said nz but domain is not nz - redirect
    if (obj.status == 331) {
        set obj.http.Location = "http://www.charlesparsonsinteriors.co.nz/?mystore=conz";
        set obj.status = 302;
        return(deliver);
    }
    #cookie said com but domain is not com - redirect
    if (obj.status == 332){
        set obj.http.Location = "http://www.charlesparsonsinteriors.com/?mystore=com";
        set obj.status = 302;
        return(deliver);
    }
    #no cookie and not com domain - redirect
    if (obj.status == 333){
        #set obj.http.Set-Cookie = "mystore=com; expires: Session; path=/; domain=.www.charlesparsonsinteriors.com" ;
        set obj.http.Location = "http://www.charlesparsonsinteriors.com";
        set obj.status = 302;
        return(deliver);
    }
}

@aheadley
Copy link
Contributor

I've noticed that I cant set the cookie at the same time as the frontend cookie. it seems to overwrite it.

This is expected, unfortunately Varnish doesn't really handle multiple Set-Cookie headers in a response very well (and browser support for setting multiple cookies in a single Set-Cookie header is spotty). You can only really manipulate one (the last sent I believe) and doing so will remove any others. Sorry, I should have mentioned that before. Off the top of my head, I don't think it will cause any problems and the frontend cookie should get set on the request after the mystore cookie is set.

Unless you meant that the frontend cookie is overwriting the mystore cookie? That makes things a little more difficult. You'll need to "trick" the frontend cookie generation logic in vcl_deliver into not messing with the Set-Cookie header if you are setting a mystore cookie by doing something like this:

sub vcl_deliver {
    if (req.http.X-MyStoreCookie-Flag) {
        # prevent the frontend cookie set-cookie header
        unset req.http.X-Varnish-Faked-Session;
        set resp.http.Set-Cookie = regsub(req.http.Cookie, ".*(mystore=[^,]+).*", "\1");
    }
}

When I change stores I have a on click JavaScript event to update the cookie on this domain, and I go to a new domain with a ?mystore=value - Based on my code below i'm trying to use this url as a mannual overide of the automatic process. But it dosnt seem to set the cooke.

If the frontend cookie generation is overwriting the mystore cookie then the above change should fix this (I think).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants