Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use shelf_web_socket to listen for http and ws requests on the same port

https://pub.dartlang.org/packages/shelf_web_socket shows this example

import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_web_socket/shelf_web_socket.dart';

void main() {
  var handler = webSocketHandler((webSocket) {
    webSocket.listen((message) {
      webSocket.add("echo $message");
    });
  });

  shelf_io.serve(handler, 'localhost', 8080).then((server) {
    print('Serving at ws://${server.address.host}:${server.port}');
  });
}

I would like to know how to combine this with my HTTP server initialization

import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as sIo;
import 'package:shelf_auth/shelf_auth.dart' as sAuth;
import 'package:shelf_auth/src/authentication.dart' as sAuth2;
import 'package:option/option.dart';
import 'package:shelf_web_socket/shelf_web_socket.dart' as sWs;

...

var authMiddleware = sAuth.authenticate(
    [new MyAuthenticator()],
    sessionHandler: new sAuth.JwtSessionHandler('bla', 'blub', new UserLookup()),
    allowHttp: true,
    allowAnonymousAccess: false);

var handler = const shelf.Pipeline()
    .addMiddleware(shelf.logRequests())
    .addMiddleware(authMiddleware)
    .addHandler(_handleHttpRequest);

// var wsHandler = sWs.webSocketHandler(_handleWebSocketConnect);

sIo.serve(handler, '0.0.0.0', servePort).then((server) {
  _log.finest('Serving at http://${server.address.host}:${server.port}');
});

What needs to be done so that wsHandler gets called for WebSocket connects and handler keeps handling HTTP requests (if possible on the same port) and if possible uses the configured authentication and session management.

I tried it on a different port but with the authentication/session middleware (no idea if this is supposed to be used together)

   var authMiddleware = sAuth.authenticate(
        [new MyAuthenticator()],
        sessionHandler: new sAuth.JwtSessionHandler('bla', 'blub', new UserLookup()),
        allowHttp: true,
        allowAnonymousAccess: false);

    var handler = const shelf.Pipeline()
        .addMiddleware(shelf.logRequests())
        .addMiddleware(authMiddleware)
        .addHandler(_handleHttpRequest);

    sIo.serve(handler, '0.0.0.0', servePort).then((server) {
      _log.finest('Serving at http://${server.address.host}:${server.port}');
    });


    var wsHandler = const shelf.Pipeline()
      .addMiddleware(shelf.logRequests())
      .addMiddleware(authMiddleware)
      .addHandler(sWs.webSocketHandler(_handleWebSocketConnect));

    sIo.serve(wsHandler, '0.0.0.0', servePort + 1).then((server) {
      _log.finest('Serving at ws://${server.address.host}:${server.port}');
    });

and got

Illegal argument(s): webSocketHandler may only be used with a server that supports request hijacking.
like image 899
Günter Zöchbauer Avatar asked Oct 03 '14 06:10

Günter Zöchbauer


People also ask

How many WebSockets can a server handle?

By default, a single server can handle 65,536 socket connections just because it's the max number of TCP ports available.

What is WebSocket handshake?

WebSockets - Overview In computer science, handshaking is a process that ensures the server is in sync with its clients. Handshaking is the basic concept of Web Socket protocol. The following diagram shows the server handshake with various clients −

What port do WebSockets use?

By default the WebSocket protocol uses port 80 for regular WebSocket connections and port 443 for WebSocket connections tunneled over TLS [RFC2818].

Can server initiate WebSocket connection?

No. The server can't initiate a connection.


Video Answer


1 Answers

At the moment your root handler is the http handler. You'll need to set up a handler that conditionally sends requests to the ws handler or another handler for your http requests. Eg

/ws -> your ws handler

/rest -> your other handler

The easiest way to do that is to use a router like shelf_route.

However someone recently tried this and hit a bug in shelf that stopped this working. Which as you noted below is fixed but not merged.

Once the issue is fixed you should be able to do

import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_route/shelf_route.dart' as route;
import 'package:shelf_web_socket/shelf_web_socket.dart' as sWs;
import 'package:shelf_auth/shelf_auth.dart' as sAuth;
import 'dart:async';
import 'package:option/option.dart';
import 'package:shelf_exception_response/exception_response.dart';

void main(List<String> arguments) {

  var authMiddleware = sAuth.authenticate(
        [new MyAuthenticator()],
        sessionHandler: new sAuth.JwtSessionHandler('bla', 'blub', new UserLookup()),
        allowHttp: true,
        allowAnonymousAccess: false);


  var router = (route.router()
      ..get('/rest', _handleHttpRequest)
      ..get('/ws', sWs.webSocketHandler(_handleWebSocketConnect)));

  var handler = const shelf.Pipeline()
      .addMiddleware(exceptionResponse())
      .addMiddleware(shelf.logRequests())
      .addMiddleware(authMiddleware)
      .addHandler(router.handler);

  route.printRoutes(router);

  io.serve(handler, '127.0.0.1', 8080).then((server) {
    print('Serving at http://${server.address.host}:${server.port}');
  });
}

Until the issue is fixed you can replace the router.handler with

var hackyRouterHandler = (shelf.Request request) {
  var path = request.url.path;
  if (path.startsWith('/rest')) {
    return _handleHttpRequest(request);
  }
  else if (path.startsWith('/ws')) {
    return sWs.webSocketHandler(_handleWebSocketConnect)(request);
  }
  else {
    throw new NotFoundException();
  }
};
like image 119
Anders Avatar answered Oct 27 '22 11:10

Anders