WebSockets with actors vs Akka streams directly, which one should I use?

Viewed 320

I am very new to play framework. What I'd like to do is a client/server application where

  1. Client side stream audio in real time to the server (User has to push a button to talk)
  2. Server does recognition
  3. Server send back in real-time the result of what is currently being said by user.

I read quite a bit about play framework, and I think WebSocket is what I need. But is it not clear yet for me if I should use actors OR akka stream directly?

I did some test with play chatroom example (using actors) and and with play stock market example (akka stream), but there are a lot a magic in there, not sure to understand.

By the way, as a side note, I thought that chatroom example was more suitable for my needs. But I am not even able to understand how I could get the data on my server :( When a use click "send" button to send the string, I'd like my HomeController to get this string... I can't even do this...

/**
* A very simple chat client using websockets.
*/
public class HomeController extends Controller {

private final Flow<String, String, NotUsed> userFlow;
private final WebJarsUtil webJarsUtil;


@Inject
public HomeController(ActorSystem actorSystem,
                      Materializer mat,
                      WebJarsUtil webJarsUtil) {
    org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
    LoggingAdapter logging = Logging.getLogger(actorSystem.eventStream(), logger.getName());

    //noinspection unchecked
    Source<String, Sink<String, NotUsed>> source = MergeHub.of(String.class)
            .log("source", logging)
            .recoverWithRetries(-1, new PFBuilder().match(Throwable.class, e -> Source.empty()).build());
    Sink<String, Source<String, NotUsed>> sink = BroadcastHub.of(String.class);

    Pair<Sink<String, NotUsed>, Source<String, NotUsed>> sinkSourcePair = source.toMat(sink, Keep.both()).run(mat);
    Sink<String, NotUsed> chatSink = sinkSourcePair.first();
    Source<String, NotUsed> chatSource = sinkSourcePair.second();
    this.userFlow = Flow.fromSinkAndSource(chatSink, chatSource).log("userFlow", logging);

    this.webJarsUtil = webJarsUtil;
}

public Result index() {
    Http.Request request = request();
    String url = routes.HomeController.chat().webSocketURL(request);
    return Results.ok(views.html.index.render(url, webJarsUtil));
}

public WebSocket chat() {
    return WebSocket.Text.acceptOrResult(request -> {
        if (sameOriginCheck(request)) {
            return CompletableFuture.completedFuture(F.Either.Right(userFlow));
        } else {
            return CompletableFuture.completedFuture(F.Either.Left(forbidden()));
        }
    });
}

/**
 * Checks that the WebSocket comes from the same origin.  This is necessary to protect
 * against Cross-Site WebSocket Hijacking as WebSocket does not implement Same Origin Policy.
 *
 * See https://tools.ietf.org/html/rfc6455#section-1.3 and
 * http://blog.dewhurstsecurity.com/2013/08/30/security-testing-html5-websockets.html
 */
private boolean sameOriginCheck(Http.RequestHeader request) {
    String[] origins = request.headers().get("Origin");
    if (origins.length > 1) {
        // more than one origin found
        return false;
    }
    String origin = origins[0];
    return originMatches(origin);
}

private boolean originMatches(String origin) {
    if (origin == null) return false;
    try {
        URI url = new URI(origin);
        return url.getHost().equals("localhost")
                && (url.getPort() == 9000 || url.getPort() == 19001);
    } catch (Exception e ) {
        return false;
    }
}

}
0 Answers
Related