tutorial socket rooms example javascript node.js express socket.io express-session

javascript - rooms - autenticación socket.io con datos de sesión compartidos, cómo funciona io.use()



socket.io chat rooms example (2)

Inspirado en ¿Cómo compartir sesiones con Socket.IO 1.xy Express 4.x? Implementé la autenticación de socket de alguna manera "limpia" donde no es necesario usar el analizador de cookies y leer cookies de los encabezados, pero pocos elementos siguen sin estar claros para mí. Ejemplo de uso de la última versión estable de socket.io 1.3.6.

var express = require(''express''), session = require(''express-session''), RedisStore = require(''connect-redis'')(session), sessionStore = new RedisStore(), io = require(''socket.io'').listen(server); var sessionMiddleware = session({ store : sessionStore, secret : "blabla", cookie : { ... } }); function socketAuthentication(socket, next) { var sessionID = socket.request.sessionID; sessionStore.get(sessionID, function(err, session) { if(err) { return next(err); } if(typeof session === "undefined") { return next( new Error(''Session cannot be found'') ); } console.log(''Socket authenticated successfully''); next(); }); } io.of(''/abc'').use(socketAuthentication).on(''connection'', function(socket) { // setup events and stuff }); io.use(function(socket, next) { sessionMiddleware(socket.request, socket.request.res, next); }); app.use(sessionMiddleware); app.get(''/'', function(req, res) { res.render(''index''); }); server.listen(8080);

index.html

<body> ... <script src="socket.io/socket.io.js"></script> <script> var socket = io(''http://localhost:8080/abc''); </script> </body>

So io(''http://localhost:8080/abc''); del lado del cliente enviará una solicitud inicial de protocolo de enlace HTTP al servidor, desde donde el servidor puede recopilar cookies y muchos otros solicitan información. Así que el servidor tiene acceso a esa solicitud inicial a través de socket.request .

Mi primera pregunta es ¿por qué la solicitud de protocolo de enlace no está en el alcance del middleware de sesión rápida? (¿Más generalmente en el alcance de middleware de app.use ?) De alguna manera esperaba esta app.use (sessionMiddleware); para disparar antes de esa solicitud inicial, y luego acceder fácilmente a socket.request.session

En segundo lugar, ¿cuáles son los escenarios en los que los io.use() definidos con io.use() activarán? ¿Solo para la solicitud inicial de protocolo de enlace HTTP? Parece que io.use() se usa para cosas relacionadas con sockets (la pregunta es: qué cosas ), mientras que app.use para solicitudes estándar.

No estoy seguro de por qué en el ejemplo anterior io.use() se io.of(''/abc'').use() antes de io.of(''/abc'').use() . Intencionalmente escribí esa orden poniendo io.of(''/abc'').use() primero para ver si funcionará y funcionará. Debería haber sido escrito a la inversa.

Por último, socket.request.res como señaló también a algunas personas vinculadas con la pregunta, a veces undefined está undefined provoca que la aplicación se rompa , el problema se puede resolver al proporcionar un objeto vacío en lugar de socket.request.res , como: sessionMiddleware(socket.request, {}, next); Lo que me parece un truco sucio. ¿Por qué razones socket.request.res cede a undefined ?


A pesar de que @oLeduc es un poco correcto, hay algunas cosas más que explicar ...

¿Por qué la solicitud del protocolo de enlace no está en el alcance del middleware de sesión rápida?

La razón principal aquí es que el middleware en Express está diseñado para manejar tareas específicas de solicitud. No todos, pero la mayoría de los manejadores usan la req, res, next sintaxis: req, res, next . Y los enchufes son "petición-menos" si puedo decir. El hecho de que tengas socket.request se debe a la forma en que se realiza el protocolo de enlace y al uso de HTTP para eso. Así que los chicos de socket.io piratearon esa primera solicitud en su clase de socket para que pueda usarla. No fue diseñado por el equipo expreso para trabajar con sockets y TCP.

¿Cuáles son los escenarios en los que los middlewares definidos con io.use () se activarán?

io.use es una representación cercana del modo de use expreso de middleware. En expreso, el middleware se ejecuta en cada solicitud, ¿verdad? Pero los sockets no tienen solicitudes y será incómodo usar middleware en cada socket que se emita, por lo que han sido ejecutados en cada conexión. Pero así como el middleware expreso se apila y utiliza antes de que se maneje (y responda) la solicitud real, Socket.IO usa el middleware en la conexión e incluso antes del protocolo de enlace real. Puede interceptar el protocolo de enlace si lo desea, utilizando ese tipo de middleware, que es muy útil (para proteger su servidor contra el spam). Más sobre esto se puede encontrar en el código de passport-socketio

¿Por qué io.use () se dispara antes de io.of (''/ abc''). Use ()?

La explicación real sobre esto se puede encontrar here , que es este código:

Server.prototype.of = function(name, fn){ if (String(name)[0] !== ''/'') name = ''/'' + name; if (!this.nsps[name]) { debug(''initializing namespace %s'', name); var nsp = new Namespace(this, name); this.nsps[name] = nsp; } if (fn) this.nsps[name].on(''connect'', fn); return this.nsps[name]; };

Y al principio del código, está esta línea de código:

this.sockets = this.of(''/'');

Por lo tanto, hay un espacio de nombres predeterminado creado al principio. Y allí mismo, puedes ver que tiene un oyente de connect conectado inmediatamente. Más adelante, cada espacio de nombres obtiene el mismo escucha de connect , pero como el Namespace es EventEmitter , los oyentes se agregan uno tras otro, de modo que se disparan uno tras otro. En otras palabras, el espacio de nombres predeterminado tiene su escucha en primer lugar, por lo que se dispara primero.

No creo que esto esté diseñado a propósito, pero resultó ser así :)

¿Por qué socket.request.res no está definido?

Para ser honesto, no estoy muy seguro de eso. Esto se debe a la forma en que se implementa engine.io: puede leer un poco más here . Se adjunta al servidor normal y envía solicitudes para hacer un saludo. Solo puedo imaginar que a veces, en los errores, los encabezados están separados de la respuesta y es por eso que no obtendrás ninguno. De todos modos, todavía estoy adivinando.

Espero que la información ayude.


¿Por qué la solicitud del protocolo de enlace no está en el alcance del middleware de sesión rápida?

Porque socket.io se adjuntará a un http.Server que es la capa debajo de Express. Se menciona en un comentario en la source de socket.io.

El motivo de esto es que la primera solicitud es una solicitud http regular que se utiliza para actualizar la conexión http sin estado y sin licencia a una conexión websocket completa en todo el estado. Por lo tanto, no tendría mucho sentido que tuviera que pasar por toda la lógica que se aplica a las solicitudes http regulares.

¿Cuáles son los escenarios en los que los middlewares definidos con io.use () se activarán?

Cada vez que se crea una nueva conexión de socket.

Por lo tanto, cada vez que un cliente se conecte, llamará a los middlewares registrados utilizando io.use (). Sin embargo, una vez que el cliente está conectado, no se llama cuando se recibe un paquete del cliente. No importa si la conexión se inicia en un espacio de nombres personalizado o en el espacio de nombres principal, siempre se llamará.

¿Por qué io.use () se dispara antes de io.of (''/ abc''). Use ()?

Los espacios de nombres son un detalle de la implementación de socket.io; en realidad, los websockets siempre golpearán primero el espacio de nombres principal.

Para ilustrar la situación, mira este fragmento y la salida que produce:

var customeNamespace = io.of(''/abc''); customeNamespace.use(function(socket, next){ console.log(''Use -> /abc''); return next(); }); io.of(''/abc'').on(''connection'', function (socket) { console.log(''Connected to namespace!'') }); io.use(function(socket, next){ console.log(''Use -> /''); return next(); }); io.on(''connection'', function (socket) { console.log(''Connected to namespace!'') });

Salida:

Use -> / Main namespace Use -> /abc Connected to namespace!

Vea la advertencia que el equipo de socket.io agregó a su documentación:

Nota importante: el espacio de nombres es un detalle de implementación del protocolo Socket.IO, y no está relacionado con la URL real del transporte subyacente, que de forma predeterminada es /socket.io/….

¿Por qué socket.request.res no está definido?

Por lo que sé, nunca debe estar indefinido. Puede estar relacionado con su implementación específica.