blob: ba689d8b3241a0aebcbf7190a293045aca4ee4e0 [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:shelf/shelf.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'src/web_socket_handler.dart';
typedef _BinaryFunction = void Function(Null, Null);
/// Creates a Shelf handler that upgrades HTTP requests to WebSocket
/// connections.
///
/// Only valid WebSocket upgrade requests are upgraded. If a request doesn't
/// look like a WebSocket upgrade request, a 404 Not Found is returned; if a
/// request looks like an upgrade request but is invalid, a 400 Bad Request is
/// returned; and if a request is a valid upgrade request but has an origin that
/// doesn't match [allowedOrigins] (see below), a 403 Forbidden is returned.
/// This means that this can be placed first in a [Cascade] and only upgrade
/// requests will be handled.
///
/// The [onConnection] must take a [WebSocketChannel] as its first argument. It
/// may also take a string, the [WebSocket subprotocol][], as its second
/// argument. The subprotocol is determined by looking at the client's
/// `Sec-WebSocket-Protocol` header and selecting the first entry that also
/// appears in [protocols]. If no subprotocols are shared between the client and
/// the server, `null` will be passed instead. Note that if [onConnection] takes
/// two arguments, [protocols] must be passed.
///
/// [WebSocket subprotocol]: https://tools.ietf.org/html/rfc6455#section-1.9
///
/// If [allowedOrigins] is passed, browser connections will only be accepted if
/// they're made by a script from one of the given origins. This ensures that
/// malicious scripts running in the browser are unable to fake a WebSocket
/// handshake. Note that non-browser programs can still make connections freely.
/// See also the WebSocket spec's discussion of [origin considerations][].
///
/// [origin considerations]: https://tools.ietf.org/html/rfc6455#section-10.2
///
/// If [pingInterval] is specified, it will get passed to the created
/// channel instance, enabling round-trip disconnect detection.
/// See [WebSocketChannel] for more details.
Handler webSocketHandler(Function onConnection,
{Iterable<String> protocols,
Iterable<String> allowedOrigins,
Duration pingInterval}) {
if (onConnection is! _BinaryFunction) {
if (protocols != null) {
throw ArgumentError("If protocols is non-null, onConnection must "
"take two arguments, the WebSocket and the protocol.");
}
var innerOnConnection = onConnection;
onConnection = (webSocket, _) => innerOnConnection(webSocket);
}
return WebSocketHandler(
onConnection,
protocols?.toSet(),
allowedOrigins?.map((origin) => origin.toLowerCase())?.toSet(),
pingInterval,
).handle;
}