Updated websocket
This commit is contained in:
parent
0d294adb20
commit
62336e0d53
25 changed files with 158 additions and 223 deletions
|
@ -52,7 +52,7 @@ abstract class BaseAngelClient extends Angel {
|
|||
final List<Service> _services = [];
|
||||
final http.BaseClient client;
|
||||
|
||||
final _p = Context(style: Style.url);
|
||||
final Context _p = Context(style: Style.url);
|
||||
|
||||
@override
|
||||
Stream<AngelAuthResult> get onAuthenticated => _onAuthenticated.stream;
|
||||
|
@ -212,7 +212,7 @@ class BaseAngelService<Id, Data> extends Service<Id, Data?> {
|
|||
final http.BaseClient client;
|
||||
final AngelDeserializer<Data>? deserializer;
|
||||
|
||||
final _p = Context(style: Style.url);
|
||||
final Context _p = Context(style: Style.url);
|
||||
|
||||
final StreamController<List<Data?>> _onIndexed = StreamController();
|
||||
final StreamController<Data?> _onRead = StreamController(),
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<includedPredefinedLibrary name="ECMAScript 6" />
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
<component name="ProjectInspectionProfilesVisibleTreeState">
|
||||
<entry key="Project Default">
|
||||
<profile-state>
|
||||
<expanded-state>
|
||||
<State>
|
||||
<id />
|
||||
</State>
|
||||
<State>
|
||||
<id>General</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>XPath</id>
|
||||
</State>
|
||||
</expanded-state>
|
||||
<selected-state>
|
||||
<State>
|
||||
<id>AngularJS</id>
|
||||
</State>
|
||||
</selected-state>
|
||||
</profile-state>
|
||||
</entry>
|
||||
</component>
|
||||
</project>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/angel_proxy.iml" filepath="$PROJECT_DIR$/.idea/angel_proxy.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -1,8 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$" />
|
||||
<option name="scope" value="FOLDER" />
|
||||
<option name="testRunnerOptions" value="-j 4" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,6 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Basic Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/test/basic_test.dart" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,6 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Pub Serve Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/test/pub_serve_test.dart" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,7 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="multiple.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true" nameIsGenerated="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/example/multiple.dart" />
|
||||
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
11
packages/proxy/.vscode/launch.json
vendored
11
packages/proxy/.vscode/launch.json
vendored
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Multiple Proxies",
|
||||
"type": "dart-cli",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/example/multiple.dart"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,32 +1,43 @@
|
|||
# 4.0.0
|
||||
# Change Log
|
||||
|
||||
## 4.0.1
|
||||
|
||||
* Updated README
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* Migrated to support Dart SDK 2.12.x NNBD
|
||||
|
||||
# 3.0.0
|
||||
## 3.0.0
|
||||
|
||||
* Migrated to work with Dart SDK 2.12.x Non NNBD
|
||||
|
||||
# 2.2.0
|
||||
## 2.2.0
|
||||
|
||||
* Use `http.Client` instead of `http.BaseClient`, and make it an
|
||||
optional parameter.
|
||||
* Allow `baseUrl` to accept `Uri` or `String`.
|
||||
* Add `Proxy.pushState`.
|
||||
|
||||
# 2.1.2
|
||||
## 2.1.2
|
||||
|
||||
* Apply lints.
|
||||
|
||||
# 2.1.1
|
||||
## 2.1.1
|
||||
|
||||
* Update for framework@2.0.0-alpha.15
|
||||
|
||||
# 2.1.0
|
||||
## 2.1.0
|
||||
|
||||
- Use `Uri` instead of archaic `host`, `port`, and `mapTo`. Also cleaner + safer + easier.
|
||||
* Use `Uri` instead of archaic `host`, `port`, and `mapTo`. Also cleaner + safer + easier.
|
||||
|
||||
* Enable WebSocket proxying.
|
||||
|
||||
# 2.0.0
|
||||
## 2.0.0
|
||||
|
||||
- Updates for Angel 2. Big thanks to @denkuy!
|
||||
- Use `package:path` for better path resolution.
|
||||
* Updates for Angel 2. Big thanks to @denkuy!
|
||||
* Use `package:path` for better path resolution.
|
||||
|
||||
# 1.1.1
|
||||
## 1.1.1
|
||||
|
||||
- Removed reference to `io`; now works with HTTP/2. Thanks to @daniel-v!
|
||||
* Removed reference to `io`; now works with HTTP/2. Thanks to @daniel-v!
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
# angel3_proxy
|
||||
[![version](https://img.shields.io/badge/pub-v4.0.0-brightgreen)](https://pub.dartlang.org/packages/angel3_proxy)
|
||||
# Angel3 Proxy
|
||||
|
||||
[![version](https://img.shields.io/badge/pub-v4.0.1-brightgreen)](https://pub.dartlang.org/packages/angel3_proxy)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||
|
||||
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/proxy/LICENSE)
|
||||
|
||||
Angel middleware to forward requests to another server (i.e. `webdev serve`).
|
||||
Also supports WebSockets.
|
||||
Angel middleware to forward requests to another server (i.e. `webdev serve`). Also supports WebSockets.
|
||||
|
||||
```dart
|
||||
import 'package:angel3_proxy/angel3_proxy.dart';
|
||||
|
@ -23,15 +23,17 @@ void main() async {
|
|||
```
|
||||
|
||||
You can also restrict the proxy to serving only from a specific root:
|
||||
|
||||
```dart
|
||||
Proxy(baseUrl, publicPath: '/remote');
|
||||
```
|
||||
|
||||
Also, you can map requests to a root path on the remote server:
|
||||
|
||||
```dart
|
||||
Proxy(baseUrl.replace(path: '/path'));
|
||||
```
|
||||
|
||||
Request bodies will be forwarded as well, if they are not empty. This allows things like POST requests to function.
|
||||
|
||||
For a request body to be forwarded, the body must not have already been parsed.
|
||||
For a request body to be forwarded, the body must not have already been parsed.
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:angel3_framework/angel3_framework.dart';
|
|||
import 'package:angel3_framework/http.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path/path.dart';
|
||||
|
||||
final RegExp _straySlashes = RegExp(r'(^/+)|(/+$)');
|
||||
final MediaType _fallbackMediaType = MediaType('application', 'octet-stream');
|
||||
|
@ -16,6 +16,8 @@ final MediaType _fallbackMediaType = MediaType('application', 'octet-stream');
|
|||
class Proxy {
|
||||
String? _prefix;
|
||||
|
||||
final Context _p = Context(style: Style.url);
|
||||
|
||||
/// The underlying [Client] to use.
|
||||
final http.Client httpClient;
|
||||
|
||||
|
@ -82,12 +84,12 @@ class Proxy {
|
|||
Future<bool> handleRequest(RequestContext req, ResponseContext res) {
|
||||
var path = req.path.replaceAll(_straySlashes, '');
|
||||
|
||||
if (_prefix!.isNotEmpty) {
|
||||
if (!p.isWithin(_prefix!, path) && !p.equals(_prefix!, path)) {
|
||||
if (_prefix?.isNotEmpty == true) {
|
||||
if (!_p.isWithin(_prefix!, path) && !_p.equals(_prefix!, path)) {
|
||||
return Future<bool>.value(true);
|
||||
}
|
||||
|
||||
path = p.relative(path, from: _prefix);
|
||||
path = _p.relative(path, from: _prefix);
|
||||
}
|
||||
|
||||
return servePath(path, req, res);
|
||||
|
@ -98,7 +100,7 @@ class Proxy {
|
|||
String path, RequestContext req, ResponseContext res) async {
|
||||
http.StreamedResponse rs;
|
||||
|
||||
var uri = baseUrl.replace(path: p.join(baseUrl.path, path));
|
||||
var uri = baseUrl.replace(path: _p.join(baseUrl.path, path));
|
||||
|
||||
try {
|
||||
if (req is HttpRequestContext &&
|
||||
|
@ -123,13 +125,13 @@ class Proxy {
|
|||
var headers = <String, String>{
|
||||
'host': uri.authority,
|
||||
'x-forwarded-for': req.remoteAddress.address,
|
||||
'x-forwarded-port': req.uri!.port.toString(),
|
||||
'x-forwarded-port': req.uri?.port.toString() ?? '',
|
||||
'x-forwarded-host':
|
||||
req.headers!.host ?? req.headers!.value('host') ?? 'none',
|
||||
req.headers?.host ?? req.headers?.value('host') ?? 'none',
|
||||
'x-forwarded-proto': uri.scheme,
|
||||
};
|
||||
|
||||
req.headers!.forEach((name, values) {
|
||||
req.headers?.forEach((name, values) {
|
||||
headers[name] = values.join(',');
|
||||
});
|
||||
|
||||
|
@ -139,8 +141,8 @@ class Proxy {
|
|||
List<int>? body;
|
||||
|
||||
if (!req.hasParsedBody) {
|
||||
body = await req.body!
|
||||
.fold<BytesBuilder>(BytesBuilder(), (bb, buf) => bb..add(buf))
|
||||
body = await req.body
|
||||
?.fold<BytesBuilder>(BytesBuilder(), (bb, buf) => bb..add(buf))
|
||||
.then((bb) => bb.takeBytes());
|
||||
}
|
||||
|
||||
|
@ -149,13 +151,17 @@ class Proxy {
|
|||
rq.headers['host'] = rq.url.host;
|
||||
rq.encoding = Utf8Codec(allowMalformed: true);
|
||||
|
||||
if (body != null) rq.bodyBytes = body;
|
||||
if (body != null) {
|
||||
rq.bodyBytes = body;
|
||||
}
|
||||
|
||||
return httpClient.send(rq);
|
||||
}
|
||||
|
||||
var future = accessRemote();
|
||||
if (timeout != null) future = future.timeout(timeout!);
|
||||
if (timeout != null) {
|
||||
future = future.timeout(timeout!);
|
||||
}
|
||||
rs = await future;
|
||||
} on TimeoutException catch (e, st) {
|
||||
if (recoverFromDead) return true;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
name: angel3_proxy
|
||||
version: 4.0.0
|
||||
version: 4.0.1
|
||||
description: Angel middleware to forward requests to another server (i.e. pub serve).
|
||||
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/proxy
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/proxy
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
|
|
|
@ -9,7 +9,7 @@ import 'package:test/test.dart';
|
|||
import 'common.dart';
|
||||
|
||||
void main() {
|
||||
Angel? app;
|
||||
late Angel app;
|
||||
var client = http.IOClient();
|
||||
//late HttpServer server;
|
||||
late HttpServer testServer;
|
||||
|
@ -17,7 +17,7 @@ void main() {
|
|||
|
||||
setUp(() async {
|
||||
app = Angel();
|
||||
var appHttp = AngelHttp(app!);
|
||||
var appHttp = AngelHttp(app);
|
||||
|
||||
testServer = await startTestServer();
|
||||
|
||||
|
@ -33,15 +33,15 @@ void main() {
|
|||
print('Proxy 1 on: ${proxy1.baseUrl}');
|
||||
print('Proxy 2 on: ${proxy2.baseUrl}');
|
||||
|
||||
app!.all('/proxy/*', proxy1.handleRequest);
|
||||
app!.all('*', proxy2.handleRequest);
|
||||
app.all('/proxy/*', proxy1.handleRequest);
|
||||
app.all('*', proxy2.handleRequest);
|
||||
|
||||
app!.fallback((req, res) {
|
||||
app.fallback((req, res) {
|
||||
print('Intercepting empty from ${req.uri}');
|
||||
res.write('intercept empty');
|
||||
});
|
||||
|
||||
app!.logger = Logger('angel');
|
||||
app.logger = Logger('angel');
|
||||
|
||||
Logger.root.onRecord.listen((rec) {
|
||||
print(rec);
|
||||
|
@ -56,7 +56,6 @@ void main() {
|
|||
tearDown(() async {
|
||||
await testServer.close(force: true);
|
||||
//await server.close(force: true);
|
||||
app = null;
|
||||
url = null;
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
name: angel3_static
|
||||
description: This library provides a virtual directory to serve static files for Angel3 framework.
|
||||
version: 4.0.2
|
||||
homepage: https://github.com/dukefirehawk/angel
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/static
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
|
|
|
@ -1,63 +1,88 @@
|
|||
# 4.0.0
|
||||
# Change Log
|
||||
|
||||
## 4.0.1
|
||||
|
||||
* Updated README
|
||||
* Fixed NNBD issues
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* Migrated to support Dart SDK 2.12.x NNBD
|
||||
|
||||
# 3.0.0
|
||||
## 3.0.0
|
||||
|
||||
* Migrated to work with Dart SDK 2.12.x Non NNBD
|
||||
|
||||
# 2.0.3
|
||||
## 2.0.3
|
||||
|
||||
* Remove `WebSocketController.plugin`.
|
||||
* Remove any unawaited futures.
|
||||
|
||||
# 2.0.2
|
||||
## 2.0.2
|
||||
|
||||
* Update `stream_channel` to `2.0.0`.
|
||||
* Use `angel_framework^@2.0.0-rc.0`.
|
||||
|
||||
# 2.0.1
|
||||
## 2.0.1
|
||||
|
||||
* Add `reconnectOnClose` and `reconnectinterval` parameters in top-level `WebSockets` constructors.
|
||||
* Close `WebSocketExtraneousEventHandler`.
|
||||
* Add onAuthenticated to server-side.
|
||||
|
||||
# 2.0.0
|
||||
## 2.0.0
|
||||
|
||||
* Update to work with `client@2.0.0`.
|
||||
|
||||
# 2.0.0-alpha.8
|
||||
## 2.0.0-alpha.8
|
||||
|
||||
* Support for WebSockets over HTTP/2 (though in practice this doesn't often happen, if ever).
|
||||
|
||||
# 2.0.0-alpha.7
|
||||
## 2.0.0-alpha.7
|
||||
|
||||
* Replace `WebSocketSynchronizer` with `StreamChannel<WebSocketEvent>`.
|
||||
|
||||
# 2.0.0-alpha.6
|
||||
## 2.0.0-alpha.6
|
||||
|
||||
* Explicit import of `import 'package:http/io_client.dart' as http;`
|
||||
|
||||
# 2.0.0-alpha.5
|
||||
## 2.0.0-alpha.5
|
||||
|
||||
* Update `http` dependency.
|
||||
|
||||
# 2.0.0-alpha.4
|
||||
## 2.0.0-alpha.4
|
||||
|
||||
* Remove `package:json_god`.
|
||||
* Make `WebSocketContext` take any `StreamChannel`.
|
||||
* Strong typing updates.
|
||||
|
||||
# 2.0.0-alpha.3
|
||||
## 2.0.0-alpha.3
|
||||
|
||||
* Directly import Angel HTTP.
|
||||
|
||||
# 2.0.0-alpha.2
|
||||
## 2.0.0-alpha.2
|
||||
|
||||
* Updated for the next version of `angel_client`.
|
||||
|
||||
# 2.0.0-alpha.1
|
||||
## 2.0.0-alpha.1
|
||||
|
||||
* Refactorings for updated Angel 2 versions.
|
||||
* Remove `package:dart2_constant`.
|
||||
|
||||
# 2.0.0-alpha
|
||||
## 2.0.0-alpha
|
||||
|
||||
* Depend on Dart 2 and Angel 2.
|
||||
|
||||
# 1.1.2
|
||||
## 1.1.2
|
||||
|
||||
* Dart 2 updates.
|
||||
* Added `handleClient`, which is nice for external implementations
|
||||
that plug into `AngelWebSocket`.
|
||||
|
||||
# 1.1.1
|
||||
## 1.1.1
|
||||
|
||||
* Deprecated `unwrap`.
|
||||
* Service streams now pump out `e.data`, rather than the actual event.
|
||||
|
||||
# 1.1.0+1
|
||||
* Added `unwrap`.
|
||||
## 1.1.0+1
|
||||
|
||||
* Added `unwrap`.
|
||||
|
|
|
@ -1,29 +1,24 @@
|
|||
# angel3_websocket
|
||||
[![version](https://img.shields.io/badge/pub-v4.0.0-brightgreen)](https://pub.dartlang.org/packages/angel3_websocket)
|
||||
# Angel3 Websocket Library
|
||||
|
||||
[![version](https://img.shields.io/badge/pub-v4.0.1-brightgreen)](https://pub.dartlang.org/packages/angel3_websocket)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||
|
||||
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/websocket/LICENSE)
|
||||
|
||||
WebSocket plugin for Angel.
|
||||
|
||||
This plugin broadcasts events from hooked services via WebSockets.
|
||||
|
||||
In addition, it adds itself to the app's IoC container as `AngelWebSocket`, so that it can be used
|
||||
in controllers as well.
|
||||
WebSocket plugin for Angel3. This plugin broadcasts events from hooked services via WebSockets. In addition, it adds itself to the app's IoC container as `AngelWebSocket`, so that it can be used in controllers as well.
|
||||
|
||||
WebSocket contexts are add to `req.properties` as `'socket'`.
|
||||
|
||||
## Usage
|
||||
|
||||
# Usage
|
||||
|
||||
**Server-side**
|
||||
### Server-side
|
||||
|
||||
```dart
|
||||
import "package:angel3_framework/angel3_framework.dart";
|
||||
import "package:angel3_websocket/server.dart";
|
||||
|
||||
main() async {
|
||||
void main() async {
|
||||
var app = Angel();
|
||||
|
||||
var ws = AngelWebSocket();
|
||||
|
@ -38,8 +33,7 @@ main() async {
|
|||
|
||||
```
|
||||
|
||||
Filtering events is easy with hooked services. Just return a `bool`, whether
|
||||
synchronously or asynchronously.
|
||||
Filtering events is easy with hooked services. Just return a `bool`, whether synchronously or asynchronously.
|
||||
|
||||
```dart
|
||||
myService.properties['ws:filter'] = (HookedServiceEvent e, WebSocketContext socket) async {
|
||||
|
@ -51,14 +45,14 @@ myService.index({
|
|||
});
|
||||
```
|
||||
|
||||
**Adding Handlers within a Controller**
|
||||
#### Adding Handlers within a Controller
|
||||
|
||||
`WebSocketController` extends a normal `Controller`, but also listens to WebSockets.
|
||||
|
||||
```dart
|
||||
import 'dart:async';
|
||||
import "package:angel_framework/angel_framework.dart";
|
||||
import "package:angel_websocket/server.dart";
|
||||
import "package:angel3_framework/angel3_framework.dart";
|
||||
import "package:angel3_websocket/server.dart";
|
||||
|
||||
@Expose("/")
|
||||
class MyController extends WebSocketController {
|
||||
|
@ -86,25 +80,22 @@ class MyController extends WebSocketController {
|
|||
}
|
||||
```
|
||||
|
||||
**Client Use**
|
||||
### Client Use
|
||||
|
||||
This repo also provides two client libraries `browser` and `io` that extend the base
|
||||
`angel3_client` interface, and allow you to use a very similar API on the client to that of
|
||||
the server.
|
||||
This repo also provides two client libraries `browser` and `io` that extend the base `angel3_client` interface, and allow you to use a very similar API on the client to that of the server.
|
||||
|
||||
The provided clients also automatically try to reconnect their WebSockets when disconnected,
|
||||
which means you can restart your development server without having to reload browser windows.
|
||||
The provided clients also automatically try to reconnect their WebSockets when disconnected, which means you can restart your development server without having to reload browser windows.
|
||||
|
||||
They also provide streams of data that pump out filtered data as it comes in from the server.
|
||||
|
||||
Clients can even perform authentication over WebSockets.
|
||||
|
||||
**In the Browser**
|
||||
#### In the Browser
|
||||
|
||||
```dart
|
||||
import "package:angel3_websocket/browser.dart";
|
||||
|
||||
main() async {
|
||||
void main() async {
|
||||
Angel app = WebSockets("/ws");
|
||||
await app.connect();
|
||||
|
||||
|
@ -127,7 +118,7 @@ main() async {
|
|||
}
|
||||
```
|
||||
|
||||
**CLI Client**
|
||||
#### CLI Client
|
||||
|
||||
```dart
|
||||
import "package:angel3_framework/common.dart";
|
||||
|
@ -163,4 +154,4 @@ main() async {
|
|||
|
||||
// Authenticate a WebSocket, if you were not already authenticated...
|
||||
app.authenticateViaJwt('<some-jwt>');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@ abstract class BaseWebSocketClient extends BaseAngelClient {
|
|||
if (_socket == null) {
|
||||
_queue.addLast(action);
|
||||
} else {
|
||||
socket!.sink.add(serialize(action));
|
||||
socket?.sink.add(serialize(action));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class WebSockets extends BaseWebSocketClient {
|
|||
StreamSubscription<Event>? sub;
|
||||
t = Timer.periodic(Duration(milliseconds: 500), (timer) {
|
||||
if (!ctrl.isClosed) {
|
||||
if (wnd.closed!) {
|
||||
if (wnd.closed == true) {
|
||||
ctrl.addError(AngelHttpException.notAuthenticated(
|
||||
message:
|
||||
errorMessage ?? 'Authentication via popup window failed.'));
|
||||
|
@ -60,7 +60,7 @@ class WebSockets extends BaseWebSocketClient {
|
|||
ctrl.add((e as CustomEvent).detail.toString());
|
||||
t.cancel();
|
||||
ctrl.close();
|
||||
sub!.cancel();
|
||||
sub?.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:angel3_framework/angel3_framework.dart';
|
|||
HookedServiceEventListener doNotBroadcast([provider]) {
|
||||
return (HookedServiceEvent e) {
|
||||
if (e.params != null && e.params!.containsKey('provider')) {
|
||||
var eParam = e.params!;
|
||||
var deny = false;
|
||||
var providers = provider is Iterable ? provider : [provider];
|
||||
|
||||
|
@ -14,18 +15,15 @@ HookedServiceEventListener doNotBroadcast([provider]) {
|
|||
if (deny) break;
|
||||
|
||||
if (p is Providers) {
|
||||
deny = deny ||
|
||||
p == e.params!['provider'] ||
|
||||
e.params!['provider'] == p.via;
|
||||
deny = deny || p == eParam['provider'] || eParam['provider'] == p.via;
|
||||
} else if (p == null) {
|
||||
deny = true;
|
||||
} else {
|
||||
deny =
|
||||
deny || (e.params!['provider'] as Providers).via == p.toString();
|
||||
deny = deny || (eParam['provider'] as Providers).via == p.toString();
|
||||
}
|
||||
}
|
||||
|
||||
e.params!['broadcast'] = false;
|
||||
eParam['broadcast'] = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:stream_channel/stream_channel.dart';
|
|||
import 'package:web_socket_channel/io.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:logging/logging.dart';
|
||||
import 'angel3_websocket.dart';
|
||||
import 'constants.dart';
|
||||
export 'angel3_websocket.dart';
|
||||
|
@ -26,6 +27,8 @@ typedef WebSocketResponseSerializer = String Function(dynamic data);
|
|||
|
||||
/// Broadcasts events from [HookedService]s, and handles incoming [WebSocketAction]s.
|
||||
class AngelWebSocket {
|
||||
final _log = Logger('AngelWebSocket');
|
||||
|
||||
final List<WebSocketContext> _clients = <WebSocketContext>[];
|
||||
final List<String> _servicesAlreadyWired = [];
|
||||
|
||||
|
@ -258,6 +261,7 @@ class AngelWebSocket {
|
|||
return null;
|
||||
}
|
||||
} catch (e, st) {
|
||||
_log.severe('Unable to handle unknown action');
|
||||
catchError(e, st, socket);
|
||||
}
|
||||
}
|
||||
|
@ -266,15 +270,15 @@ class AngelWebSocket {
|
|||
Future handleAuth(WebSocketAction action, WebSocketContext socket) async {
|
||||
if (allowAuth != false &&
|
||||
action.eventName == authenticateAction &&
|
||||
action.params!['query'] is Map &&
|
||||
action.params!['query']['jwt'] is String) {
|
||||
action.params?['query'] is Map &&
|
||||
action.params?['query']['jwt'] is String) {
|
||||
try {
|
||||
var auth = socket.request.container!.make<AngelAuth>()!;
|
||||
var jwt = action.params!['query']['jwt'] as String;
|
||||
AuthToken token;
|
||||
|
||||
token = AuthToken.validate(jwt, auth.hmac);
|
||||
var user = await auth.deserializer!(token.userId as Object);
|
||||
var user = await auth.deserializer(token.userId as Object);
|
||||
socket.request
|
||||
..container!.registerSingleton<AuthToken>(token)
|
||||
..container!.registerSingleton(user, as: user.runtimeType);
|
||||
|
@ -282,6 +286,7 @@ class AngelWebSocket {
|
|||
socket.send(authenticatedEvent,
|
||||
{'token': token.serialize(auth.hmac), 'data': user});
|
||||
} catch (e, st) {
|
||||
_log.severe('Authentication failed');
|
||||
catchError(e, st, socket);
|
||||
}
|
||||
} else {
|
||||
|
@ -345,6 +350,7 @@ class AngelWebSocket {
|
|||
}
|
||||
}
|
||||
} catch (e, st) {
|
||||
_log.severe('Invalid data');
|
||||
catchError(e, st, socket);
|
||||
}
|
||||
}
|
||||
|
@ -353,16 +359,16 @@ class AngelWebSocket {
|
|||
// Send an error
|
||||
if (e is AngelHttpException) {
|
||||
socket.sendError(e);
|
||||
app!.logger?.severe(e.message, e.error ?? e, e.stackTrace);
|
||||
app?.logger?.severe(e.message, e.error ?? e, e.stackTrace);
|
||||
} else if (sendErrors) {
|
||||
var err = AngelHttpException(e,
|
||||
message: e.toString(), stackTrace: st, errors: [st.toString()]);
|
||||
socket.sendError(err);
|
||||
app!.logger?.severe(err.message, e, st);
|
||||
app?.logger?.severe(err.message, e, st);
|
||||
} else {
|
||||
var err = AngelHttpException(e);
|
||||
socket.sendError(err);
|
||||
app!.logger?.severe(e.toString(), e, st);
|
||||
app?.logger?.severe(e.toString(), e, st);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,10 +389,10 @@ class AngelWebSocket {
|
|||
|
||||
/// Configures an [Angel] instance to listen for WebSocket connections.
|
||||
Future configureServer(Angel app) async {
|
||||
app.container!.registerSingleton(this);
|
||||
app.container?.registerSingleton(this);
|
||||
|
||||
if (runtimeType != AngelWebSocket) {
|
||||
app.container!.registerSingleton<AngelWebSocket>(this);
|
||||
app.container?.registerSingleton<AngelWebSocket>(this);
|
||||
}
|
||||
|
||||
// Set up services
|
||||
|
@ -397,7 +403,7 @@ class AngelWebSocket {
|
|||
});
|
||||
|
||||
if (synchronizationChannel != null) {
|
||||
synchronizationChannel!.stream
|
||||
synchronizationChannel?.stream
|
||||
.listen((e) => batchEvent(e, notify: false));
|
||||
}
|
||||
|
||||
|
@ -406,7 +412,7 @@ class AngelWebSocket {
|
|||
|
||||
/// Handles an incoming [WebSocketContext].
|
||||
Future<void> handleClient(WebSocketContext socket) async {
|
||||
var origin = socket.request.headers!.value('origin');
|
||||
var origin = socket.request.headers?.value('origin');
|
||||
if (allowedOrigins != null && !allowedOrigins!.contains(origin)) {
|
||||
throw AngelHttpException.forbidden(
|
||||
message:
|
||||
|
@ -418,7 +424,7 @@ class AngelWebSocket {
|
|||
|
||||
_onConnection.add(socket);
|
||||
|
||||
socket.request.container!.registerSingleton<WebSocketContext>(socket);
|
||||
socket.request.container?.registerSingleton<WebSocketContext>(socket);
|
||||
|
||||
socket.channel.stream.listen(
|
||||
(data) {
|
||||
|
@ -451,11 +457,11 @@ class AngelWebSocket {
|
|||
return false;
|
||||
} else if (req is Http2RequestContext && res is Http2ResponseContext) {
|
||||
var connection =
|
||||
req.headers!['connection']?.map((s) => s.toLowerCase().trim());
|
||||
var upgrade = req.headers!.value('upgrade')?.toLowerCase();
|
||||
var version = req.headers!.value('sec-websocket-version');
|
||||
var key = req.headers!.value('sec-websocket-key');
|
||||
var protocol = req.headers!.value('sec-websocket-protocol');
|
||||
req.headers?['connection']?.map((s) => s.toLowerCase().trim());
|
||||
var upgrade = req.headers?.value('upgrade')?.toLowerCase();
|
||||
var version = req.headers?.value('sec-websocket-version');
|
||||
var key = req.headers?.value('sec-websocket-key');
|
||||
var protocol = req.headers?.value('sec-websocket-protocol');
|
||||
|
||||
if (connection == null) {
|
||||
throw AngelHttpException.badRequest(
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
name: angel3_websocket
|
||||
description: Support for using pkg:angel_client with WebSockets. Designed for Angel.
|
||||
version: 4.0.0
|
||||
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/websocket
|
||||
description: This library provides WebSockets support for Angel3 framework.
|
||||
version: 4.0.1
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/websocket
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
|
@ -15,10 +16,10 @@ dependencies:
|
|||
stream_channel: ^2.1.0
|
||||
web_socket_channel: ^2.1.0
|
||||
collection: ^1.15.0
|
||||
logging: ^1.0.1
|
||||
dev_dependencies:
|
||||
angel3_container: ^3.0.0
|
||||
angel3_model: ^3.0.0
|
||||
logging: ^1.0.1
|
||||
pedantic: ^1.11.0
|
||||
test: ^1.17.4
|
||||
|
|
@ -18,10 +18,8 @@ void main() {
|
|||
setUp(() async {
|
||||
app = Angel();
|
||||
http = AngelHttp(app, useZone: false);
|
||||
var auth = AngelAuth();
|
||||
|
||||
auth.serializer = (_) async => 'baz';
|
||||
auth.deserializer = (_) async => USER;
|
||||
var auth = AngelAuth(
|
||||
serializer: (_) async => 'baz', deserializer: (_) async => USER);
|
||||
|
||||
auth.strategies['local'] = LocalAuthStrategy(
|
||||
(username, password) async {
|
||||
|
|
Loading…
Reference in a new issue