Read set-cookie for sticky sessions from WebSocket handshake in meteorrn

Hi,

if you have your meteor app running behind a load balancer, you typically have sticky sessions implemented by a set-cookie header, so that following request can connect to the same backend.

I need to access this header/cookie in meteorrn so that I can for instance initiate downloads via meteor-files; it’s not possible to send this request to a different instance than the current meteor session, since in meteor-files the user associated to a download request is determined by the meteor sessionId.

From how far I understand the “set-cookie” header from the server should be send during the WebSocket handshake; I assume that in the “normal” meteor client this is handled somehow. Since the meteorrn client is also based on WebSocket this should be possible in principle i guess, but I can’t figure it out… Any ideas?

Hello @bratelefant, I’ll check both within our community and with our internal team to find the best solution to your inquiry. I’ll get back to you as soon as I have more information or a potential solution. Please give me more details or clarify any other points while you wait. This can help us better understand your situation and find a more accurate answer.

Thanks @TatiBarros , I’ll try to add some informations and ways that I already tried to accomplish what I want to achieve.

I have several files in my meteor-files collection, that I’d like to serve via http. I would like to add an access protection mechanism via meteor-files method protected, passed to the constructor of the FilesCollection (cf. https://github.com/veliovgroup/Meteor-Files/blob/master/docs/constructor.md).

The access control mechanism I try to implement is based on the userId in this method. In order to determine the userId in an http request, meteor-files relies on a cookie x_mtok. That cookie stores the Meteor.connection._lastSessionId in order to determine the user for that request.

Now, if you use multiple instances of your meteor server behind a load balancer, you need to make sure that the http (file)-requests is forwarded to the correct server instance. This is the instance to which the client is connected to via ddp.

I’m hosting my app on scalingo. They send a set-cookie in the header, which allows them to forward requests to the same backend, aka a sticky-session. So in order to send the http request to the correct backend I need to set the sticky-session cookie. I managed to get the sessionId in the meteor-react-native client (cf. the commit 6fd0e17 in my PR)

The problem now is that if I do not send the correct “sticky-session-cookie”, my request might end at the wrong instance (i.e. any instance where my meteor-session is not running).

Right now I try to work around this by first sending a HEAD request prior to the download request, read out the set-cookie value for the sticky-session and then send the download request. I have minimum two instances running behind the balancer, so if I send my requests without that sticky-session-cookie I get auth failures at around ~50%, which is what you would expect. With my workaround I get success at around 90% of the time, so maybe this could be ok, but I’m really not sure, if this is the exact sticky-session-cookie that I get from the WebSocket handshake on when the ddp session begins.

So I try to catch the set-cookie header at the first occurence, which I guess is the WebSocket http handshake.

Maybe @jkuester or @therealnate can help out with this? How does meteor-react-native handle sticky session?

1 Like

Meteor react native doesn’t handle sticky sessions at all, to get this to work you will probably have to implement a new solution to pass headers that your load balancer to connect it to the same session. I believe the actual load balancer part will be service dependent and you will have to look at your service provider. I know heroku adds its own affinity header that you have to manage.

But assuming you get it on the same box as your other messages I believe the header that Meteor files uses is a x-mtok: Meteor.connect._lastSessionId

if (http.request.headers['x-mtok']) {
        mtok = http.request.headers['x-mtok'];
      } else {
        const cookie = http.request.Cookies;
        if (cookie.has('x_mtok')) {
          mtok = cookie.get('x_mtok');
        }
      }

Right now meteor react native doesn’t save the _lastSessionId in anyway I can see, so you will have see if it is stored on the ddp connection there or not, but doesn’t look like it is.

Meteor react native isn’t super well supported by the Meteor team, and has some problems, but would probably welcome a PR to save the session Id if you want to jump into that code.

Personally, depending on how deep you are getting I would suggest finding a new solution other than Meteor files, its not looking promising to support Meteor 3.0 any time soon and has some limitations if you are growing a lot. I recently moved to a pure http request solution instead of the package.

Hey, thanks for bringing up all these points. It’s important to note that all your points are valuable, and I appreciate how you address issues and bring technical clarity. I’m currently putting together a technical document with all the forum information, and I’ve already discussed the problem with the technical team. We’re all fully dedicated to finding solutions. Let’s keep this brainstorming going and create something extraordinary together. Thanks for the points!

Ok, I suspected that the meteor rn client does not support sticky sessions.

Could it be a strategy to enhance meteorrn like this:

  • fetch HEAD request with backend to pick up sticky session set-cookie headers
  • initiate rn WebSocket (there seems to be a third extra parameter that allows to set headers)

Edit: actually did some testing with my local clone of meteor-react-native. Could be quite easy to accomplish the way I stated. This way you can
1.) have meteor-react-native support sticky sessions.
2.) Save sticky session cookies for later use (like follow up http requests)

Biggest change would be to make the Socket open method async. In my app this results in several INVALID_STATE_ERR’s right now, guess because WebSocket messages are send before the async fetch HEAD stuff is done

1 Like

I don’t think that is a generalized solution since an extra “HEAD” request will be unnecessary for most apps.

I would probably be good to allow the react native package to pass headers to the ddp connection, at the moment I don’t think its possible

You would have to be able to pass the headers to this point https://github.com/meteorrn/meteor-react-native/blob/cb96f79ff3144a99e93cb6bbe71a2d4ea73e2504/lib/socket.js#L36C3-L36C64

Definitely would be a worthwhile PR on that repo to deal with headers

Got my branch here where I did exactly that:

There’s obviously a way to pass headers to WebSocket via the third parameter (even throws warning if you have malformed object there…)

If I look at my montiapm I get quite a bunch of HEAD requests already, so I assume that the “normal” meteor client handles cookies similarly. Anyway, picking up cookies by the server prior to upgrading websocket seems quite canonical to me though…