Getting a valid session for a Socket.IO app
-
With v1.0.0, Socket.IO now requires a valid session cookie. My apps no longer work since they are not in a browser and I don't know how/if I can get a valid cookie? I'm using socket.io-client-java.
-
I think I just need to set a header with the nodebb secret in it.
-
Okay! That made express add
signedCookies
to my request, but I'm not sure what cookie to send with it. The only cookie in the response isio
, and nodebb is looking forexpress.sid
. Nevermind I don't think that actually did anything. -
Hmm, I'm stuck. I have a feeling there's no way to connect to the forum using socket.io only now from my Java app. Maybe it will work if I send an initial connection via http... but this is a bit inconvenient since I only send messages to my
plugins.*
namespace. -
I'm working on a similar project in NodeJS and i came up with a fairly simple solution that involves making an HTTP request to get the session cookie and then telling the socket to use that session
demonstration code (JS)
'use strict'; const request = require('request'); const io = require('socket.io-client'); const jar = request.jar(); const forum = 'https://community.nodebb.org'; request.get({ url: forum, jar: jar }, function () { const cookies = jar.getCookieString(forum); const socket = io(forum, { extraHeaders: { 'Cookie': cookies } }); socket.on('connect', () => { console.log('connected'); socket.on('pong', (ms) => console.log('pong', ms)); socket.emit("topics.loadMore", { "tid": 8046, "after": 1, "direction": 1 }, (e, result) => console.log(result)); }); });
Assuming you're using the official socket.io-client for Java it looks like you'll be able to get at the cookies in Java a bit of a different way:
https://github.com/socketio/socket.io-client-java#transports-and-http-headers -
Thanks! Yeah, figured I would have to go this way. It's just kind of a bummer because before now I could just make requests to my socket namespace without the extra hassle.
-
@yariplus no problem,
It's a bit annoying, yes. but at least the way to do the header fiddling was actually documented for you in the Java client.
I had to trace the code to figure out that that
extraHeaders
object was even a thing for the javascript client.All I was able to find through googling was workarounds that involved monkey patching XMLHttpRequest in ways that were either extremely version specific or just didn't work.
I literally headdesked when i found out it was really as simple as adding an as yet undocumented key to the socket configuration.
-
Okay! It works perfect now that I GET the Cookie first. It was pretty easy using the OkHttp3 library, which was already a dependent of SocketIO, so I didn't have to add any more dependencies.
I'm not sure if this is useful to anyone else, but here is my test app.
package com.radiofreederp.nodebbapp; import io.socket.client.Ack; import io.socket.client.IO; import io.socket.client.Manager; import io.socket.client.Socket; import io.socket.emitter.Emitter; import io.socket.engineio.client.Transport; import okhttp3.*; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.net.URISyntaxException; import java.util.Arrays; import java.util.List; import java.util.Map; public class NodeBBApp { private String url; private OkHttpClient client = new OkHttpClient(); private String cookie; private Socket socket; NodeBBApp(String url) { this.url = url; } private void getCookie() throws IOException { Request request = new Request.Builder().url(url).build(); Response response = client.newCall(request).execute(); cookie = response.headers().get("Set-Cookie"); } private void connectSocket() throws URISyntaxException { socket = IO.socket(url); // Send the session cookie with requests. socket.io().on(Manager.EVENT_TRANSPORT, new Emitter.Listener() { @Override public void call(Object... args) { Transport transport = (Transport)args[0]; transport.on(Transport.EVENT_REQUEST_HEADERS, new Emitter.Listener() { @Override public void call(Object... args) { @SuppressWarnings("unchecked") Map<String, List<String>> headers = (Map<String, List<String>>)args[0]; headers.put("Cookie", Arrays.asList(cookie)); } }); } }); socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() { @Override public void call(Object... objects) { JSONObject data = new JSONObject(); try { data.put("tid", 45); data.put("after", 1); data.put("direction", 1); } catch (JSONException e) { e.printStackTrace(); } socket.emit("topics.loadMore", data, new Ack() { @Override public void call(Object... objects) { System.out.println("Response:"); if (objects.length == 2 && objects[0] == null) { JSONObject data = (JSONObject)objects[1]; try { JSONArray posts = data.getJSONArray("posts"); for (int i = 0; i < posts.length(); i++) { System.out.println(((JSONObject)posts.get(i)).get("content")); } } catch (JSONException e) { e.printStackTrace(); } } socket.close(); } }); } }); socket.connect(); } public static void main(String [] args) { NodeBBApp app = new NodeBBApp("http://www.yaricraft.com/"); try { app.getCookie(); app.connectSocket(); } catch (IOException e) { e.printStackTrace(); } catch (URISyntaxException e) { e.printStackTrace(); } } }