blob: 599e295c60ac92923dc620303830b986b8522f96 [file] [log] [blame]
// Copyright (c) 2013, 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.
part of dart.io;
/**
* WebSocket status codes used when closing a WebSocket connection.
*/
abstract class WebSocketStatus {
static const int NORMAL_CLOSURE = 1000;
static const int GOING_AWAY = 1001;
static const int PROTOCOL_ERROR = 1002;
static const int UNSUPPORTED_DATA = 1003;
static const int RESERVED_1004 = 1004;
static const int NO_STATUS_RECEIVED = 1005;
static const int ABNORMAL_CLOSURE = 1006;
static const int INVALID_FRAME_PAYLOAD_DATA = 1007;
static const int POLICY_VIOLATION = 1008;
static const int MESSAGE_TOO_BIG = 1009;
static const int MISSING_MANDATORY_EXTENSION = 1010;
static const int INTERNAL_SERVER_ERROR = 1011;
static const int RESERVED_1015 = 1015;
}
/**
* The [WebSocketTransformer] provides the ability to upgrade a
* [HttpRequest] to a [WebSocket] connection. It supports both
* upgrading a single [HttpRequest] and upgrading a stream of
* [HttpRequest]s.
*
* To upgrade a single [HttpRequest] use the static [upgrade] method.
*
* HttpServer server;
* server.listen((request) {
* if (...) {
* WebSocketTransformer.upgrade(request).then((websocket) {
* ...
* });
* } else {
* // Do normal HTTP request processing.
* }
* });
*
* To transform a stream of [HttpRequest] events as it implements a
* stream transformer that transforms a stream of HttpRequest into a
* stream of WebSockets by upgrading each HttpRequest from the HTTP or
* HTTPS server, to the WebSocket protocol.
*
* server.transform(new WebSocketTransformer()).listen((webSocket) => ...);
*
* This transformer strives to implement WebSockets as specified by RFC6455.
*/
abstract class WebSocketTransformer
implements StreamTransformer<HttpRequest, WebSocket> {
/**
* Create a new [WebSocketTransformer].
*
* If [protocolSelector] is provided, [protocolSelector] will be called to
* select what protocol to use, if any were provided by the client.
* [protocolSelector] is should return either a [String] or a [Future]
* completing with a [String]. The [String] must exist in the list of
* protocols.
*/
factory WebSocketTransformer({protocolSelector(List<String> protocols)})
=> new _WebSocketTransformerImpl(protocolSelector);
/**
* Upgrades a [HttpRequest] to a [WebSocket] connection. If the
* request is not a valid WebSocket upgrade request an HTTP response
* with status code 500 will be returned. Otherwise the returned
* future will complete with the [WebSocket] when the upgrade pocess
* is complete.
*
* If [protocolSelector] is provided, [protocolSelector] will be called to
* select what protocol to use, if any were provided by the client.
* [protocolSelector] is should return either a [String] or a [Future]
* completing with a [String]. The [String] must exist in the list of
* protocols.
*/
static Future<WebSocket> upgrade(HttpRequest request,
{protocolSelector(List<String> protocols)}) {
return _WebSocketTransformerImpl._upgrade(request, protocolSelector);
}
/**
* Checks whether the request is a valid WebSocket upgrade request.
*/
static bool isUpgradeRequest(HttpRequest request) {
return _WebSocketTransformerImpl._isUpgradeRequest(request);
}
}
/**
* A two-way HTTP communication object for client or server applications.
*
* The stream exposes the messages received. A text message will be of type
* [:String:] and a binary message will be of type [:List<int>:].
*/
abstract class WebSocket implements Stream, StreamSink {
/**
* Possible states of the connection.
*/
static const int CONNECTING = 0;
static const int OPEN = 1;
static const int CLOSING = 2;
static const int CLOSED = 3;
/**
* Set and get the interval for sending ping signals. If a ping message is not
* answered by a pong message from the peer, the `WebSocket` is assumed
* disconnected and the connection is closed with a
* [WebSocketStatus.GOING_AWAY] close code. When a ping signal is sent, the
* pong message must be received within [pingInterval].
*
* There are never two outstanding pings at any given time, and the next ping
* timer starts when the pong is received.
*
* Set the [pingInterval] to `null` to disable sending ping messages.
*
* The default value is `null`.
*/
Duration pingInterval;
/**
* Create a new WebSocket connection. The URL supplied in [url]
* must use the scheme `ws` or `wss`.
*
* The [protocols] argument is specifying the subprotocols the
* client is willing to speak.
*
* The [headers] argument is specifying additional HTTP headers for
* setting up the connection. This would typically be the `Origin`
* header and potentially cookies. The keys of the map are the header
* fields and the values are either String or List<String>.
*
* If [headers] is provided, there are a number of headers
* which are controlled by the WebSocket connection process. These
* headers are:
*
* - `connection`
* - `sec-websocket-key`
* - `sec-websocket-protocol`
* - `sec-websocket-version`
* - `upgrade`
*
* If any of these are passed in the `headers` map they will be ignored.
*
* If the `url` contains user information this will be passed as basic
* authentication when setting up the connection.
*/
static Future<WebSocket> connect(String url,
{Iterable<String> protocols,
Map<String, dynamic> headers}) =>
_WebSocketImpl.connect(url, protocols, headers);
@Deprecated('This constructor will be removed in Dart 2.0. Use `implements`'
' instead of `extends` if implementing this abstract class.')
WebSocket();
/**
* Creates a WebSocket from an already-upgraded socket.
*
* The initial WebSocket handshake must have occurred prior to this call. A
* WebSocket client can automatically perform the handshake using
* [WebSocket.connect], while a server can do so using
* [WebSocketTransformer.upgrade]. To manually upgrade an [HttpRequest],
* [HttpRequest.detachSocket] may be called.
*
* [protocol] should be the protocol negotiated by this handshake, if any.
*
* [serverSide] must be passed explicitly. If it's `false`, the WebSocket will
* act as the client and mask the messages it sends. If it's `true`, it will
* act as the server and will not mask its messages.
*/
factory WebSocket.fromUpgradedSocket(Socket socket, {String protocol,
bool serverSide}) {
if (serverSide == null) {
throw new ArgumentError("The serverSide argument must be passed "
"explicitly to WebSocket.fromUpgradedSocket.");
}
return new _WebSocketImpl._fromSocket(socket, protocol, serverSide);
}
/**
* Returns the current state of the connection.
*/
int get readyState;
/**
* The extensions property is initially the empty string. After the
* WebSocket connection is established this string reflects the
* extensions used by the server.
*/
String get extensions;
/**
* The protocol property is initially the empty string. After the
* WebSocket connection is established the value is the subprotocol
* selected by the server. If no subprotocol is negotiated the
* value will remain [:null:].
*/
String get protocol;
/**
* The close code set when the WebSocket connection is closed. If
* there is no close code available this property will be [:null:]
*/
int get closeCode;
/**
* The close reason set when the WebSocket connection is closed. If
* there is no close reason available this property will be [:null:]
*/
String get closeReason;
/**
* Closes the WebSocket connection. Set the optional [code] and [reason]
* arguments to send close information to the remote peer. If they are
* omitted, the peer will see [WebSocketStatus.NO_STATUS_RECEIVED] code
* with no reason.
*/
Future close([int code, String reason]);
/**
* Sends data on the WebSocket connection. The data in [data] must
* be either a [:String:], or a [:List<int>:] holding bytes.
*/
void add(data);
/**
* Sends data from a stream on WebSocket connection. Each data event from
* [stream] will be send as a single WebSocket frame. The data from [stream]
* must be either [:String:]s, or [:List<int>:]s holding bytes.
*/
Future addStream(Stream stream);
}
class WebSocketException implements IOException {
final String message;
const WebSocketException([this.message = ""]);
String toString() => "WebSocketException: $message";
}