Подтвердить что ты не робот

Идентификатор socket.io с данными сеанса совместного доступа, как работает io.use()

Вдохновленный Как обмениваться сеансами с Socket.IO 1.x и Express 4.x?, я реализовал аутентификацию сокетов в некотором "чистом" виде, где нет необходимости использовать cookie-синтаксический анализатор и читать файлы cookie из заголовков, но для меня осталось неясным. Пример использования последней стабильной версии 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>

Итак, io('http://localhost:8080/abc'); с клиентской стороны отправит исходный запрос на подтверждение HTTP на сервер, откуда сервер может собирать файлы cookie и многие другие запрашивать информацию. Таким образом, сервер имеет доступ к этому первоначальному запросу через socket.request.

Мой первый вопрос заключается в том, почему запрос на управление рукопожатием не входит в область промежуточного программного обеспечения экспресс-сессии? (В общем, в области app.use middlewares?) В некотором роде я ожидал этого app.use(sessionMiddleware);, чтобы запустить этот первоначальный запрос, а затем легко получить доступ к socket.request.session

Во-вторых, каковы сценарии, в которых будут срабатывать middlewares, определенные с помощью io.use()? Только для первоначального запроса на квитирование HTTP? Похоже, что io.use() используется для сокетов stuff (вопрос: какой материал), а app.use - для стандартных запросов.

Я не совсем уверен, почему в приведенном выше примере io.use() запускается до io.of('/abc').use(). Преднамеренно я написал, что порядок, ставящий io.of('/abc').use(), сначала увидеть, будет ли он работать, и он работает. Должно быть написано наоборот.

Наконец, socket.request.res, как указано также у некоторых людей в связанном вопросе, иногда undefined заставляет приложение сломаться, проблему можно решить, предоставив пустой объект вместо socket.request.res, например: sessionMiddleware(socket.request, {}, next);, который кажется мне грязным взломом. По каким причинам socket.request.res дает undefined?

4b9b3361

Ответ 1

Несмотря на то, что @oLeduc является правильным, есть еще несколько вещей, чтобы объяснить.

Почему запрос квитирования не входит в область промежуточного программного обеспечения экспресс-сессии?

Самая большая причина здесь в том, что промежуточное ПО в экспресс-среде предназначено для обработки конкретных запросов. Не все, но большинство обработчиков используют стандартный синтаксис req, res, next. И сокеты "без запроса", если можно сказать. Тот факт, что у вас есть socket.request, связано с тем, как делается рукопожатие, и что для этого используется HTTP. Итак, ребята из socket.io взломали этот первый запрос в ваш класс сокетов, чтобы вы могли его использовать. Эксплуатационная команда не была разработана, чтобы работать с сокетами и TCP.

Каковы сценарии, в которых будут срабатывать middlewares, определенные с помощью io.use()?

io.use является близким представлением пути экспресс-связи use. В express, промежуточное программное обеспечение выполняется по каждому запросу, не так ли? Но сокеты не имеют запросов, и будет неудобно использовать промежуточное программное обеспечение для каждого сокета, чтобы они запускали его для каждого подключения. Но так же, как и экспресс-промежуточное программное обеспечение укладывается в стек и используется до того, как обрабатывается фактический запрос (и отвечает), Socket.IO использует промежуточное программное обеспечение при подключении и даже до фактическое рукопожатие! Вы можете перехватить рукопожатие, если хотите, используя такое промежуточное программное обеспечение, которое очень удобно (чтобы защитить ваш сервер от спама). Подробнее об этом можно узнать в коде паспорт-сокето

Почему io.use() срабатывает перед io.of('/abc'). use()?

Реальное объяснение этого можно найти здесь, который является этим кодом:

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];
};

И в начале кода есть эта строка кода:

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

Итак, существует пространство имен по умолчанию, созданное с самого начала. И прямо там, вы можете видеть, что он сразу же подключился к приложению connect. Позже каждое пространство имен получает тот же самый прослушиватель connect, но поскольку Namespace - EventEmitter, слушатели добавляются один за другим, поэтому они запускаются один за другим. Другими словами, пространство имен по умолчанию имеет его слушатель на первом месте, поэтому он запускается первым.

Я не думаю, что это предназначено специально, но это случилось именно так:)

Почему socket.request.res undefined?

Честно говоря, я не совсем уверен в этом. Это из-за того, как реализовано engine.io - вы можете прочитать немного больше здесь. Он прикрепляется к обычному серверу и отправляет запросы для установления рукопожатия. Я могу только предположить, что иногда по ошибкам заголовки отделены от ответа и почему вы их не получите. В любом случае, все еще просто гадать.

Полезная информация помогает.

Ответ 2

Почему запрос квитирования не входит в область промежуточного программного обеспечения экспресс-сессии?

Потому что socket.io будет прикрепляться к http.Server, который является слоем под экспресс. Он упоминается в комментарии в источнике socket.io.

Причиной этого является то, что первый запрос является обычным HTTP-запросом, используемым для обновления HTTP-соединения с постоянным статусом без подключения к сети с полным доступом к сети. Поэтому для него не было бы смысла использовать всю логику, применимую к обычным HTTP-запросам.

Каковы сценарии, в которых будут срабатывать middlewares, определенные с помощью io.use()?

Всякий раз, когда создается новое соединение сокета.

Таким образом, каждый раз, когда клиент подключается, он вызывает вызовы middlewares с помощью io.use(). Однако, когда клиент подключен, он не вызывается, когда пакет получен от клиента. Не имеет значения, инициируется ли соединение в пользовательском пространстве имен или в основном пространстве имен, он всегда будет вызываться.

Почему io.use() срабатывает перед io.of('/abc'). use()?

Пространства имен - это деталь реализации socket.io, в действительности, веб-порты всегда будут попадать в основное пространство имен.

Чтобы проиллюстрировать ситуацию, посмотрите на этот фрагмент и вывод, который он производит:

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!')
});

Вывод:

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

См. предупреждение о том, что команда socket.io добавлена ​​в свою документацию:

Важное примечание. Пространство имен является деталью реализации протокола Socket.IO и не связано с фактическим URL-адресом базового транспорта, который по умолчанию соответствует /socket.io/....

Почему socket.request.res undefined?

Насколько я знаю, он никогда не должен быть undefined. Это может быть связано с вашей конкретной реализацией.