Skip to content

Commit 981b63b

Browse files
authored
Support the NSURLSession WebSocket API (#921)
1 parent db84d12 commit 981b63b

File tree

9 files changed

+66571
-67022
lines changed

9 files changed

+66571
-67022
lines changed

pkgs/cupertino_http/example/integration_test/url_session_task_test.dart

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,139 @@ import 'package:flutter/foundation.dart';
99
import 'package:integration_test/integration_test.dart';
1010
import 'package:test/test.dart';
1111

12+
void testWebSocketTask() {
13+
group('websocket', () {
14+
late HttpServer server;
15+
int? lastCloseCode;
16+
String? lastCloseReason;
17+
18+
setUp(() async {
19+
lastCloseCode = null;
20+
lastCloseReason = null;
21+
server = await HttpServer.bind('localhost', 0)
22+
..listen((request) {
23+
if (request.uri.path.endsWith('error')) {
24+
request.response.statusCode = 500;
25+
request.response.close();
26+
} else {
27+
WebSocketTransformer.upgrade(request)
28+
.then((websocket) => websocket.listen((event) {
29+
final code = request.uri.queryParameters['code'];
30+
final reason = request.uri.queryParameters['reason'];
31+
32+
websocket.add(event);
33+
if (!request.uri.queryParameters.containsKey('noclose')) {
34+
websocket.close(
35+
code == null ? null : int.parse(code), reason);
36+
}
37+
}, onDone: () {
38+
lastCloseCode = websocket.closeCode;
39+
lastCloseReason = websocket.closeReason;
40+
}));
41+
}
42+
});
43+
});
44+
45+
tearDown(() async {
46+
await server.close();
47+
});
48+
49+
test('client code and reason', () async {
50+
final session = URLSession.sharedSession();
51+
final task = session.webSocketTaskWithRequest(URLRequest.fromUrl(
52+
Uri.parse('ws://localhost:${server.port}/?noclose')))
53+
..resume();
54+
await task
55+
.sendMessage(URLSessionWebSocketMessage.fromString('Hello World!'));
56+
await task.receiveMessage();
57+
task.cancelWithCloseCode(
58+
4998, Data.fromUint8List(Uint8List.fromList('Bye'.codeUnits)));
59+
60+
// Allow the server to run and save the close code.
61+
while (lastCloseCode == null) {
62+
await Future<void>.delayed(const Duration(milliseconds: 10));
63+
}
64+
expect(lastCloseCode, 4998);
65+
expect(lastCloseReason, 'Bye');
66+
});
67+
68+
test('server code and reason', () async {
69+
final session = URLSession.sharedSession();
70+
final task = session.webSocketTaskWithRequest(URLRequest.fromUrl(
71+
Uri.parse('ws://localhost:${server.port}/?code=4999&reason=fun')))
72+
..resume();
73+
await task
74+
.sendMessage(URLSessionWebSocketMessage.fromString('Hello World!'));
75+
await task.receiveMessage();
76+
await expectLater(task.receiveMessage(),
77+
throwsA(isA<Error>().having((e) => e.code, 'code', 57 // NOT_CONNECTED
78+
)));
79+
80+
expect(task.closeCode, 4999);
81+
expect(task.closeReason!.bytes, 'fun'.codeUnits);
82+
task.cancel();
83+
});
84+
85+
test('data message', () async {
86+
final session = URLSession.sharedSession();
87+
final task = session.webSocketTaskWithRequest(
88+
URLRequest.fromUrl(Uri.parse('ws://localhost:${server.port}')))
89+
..resume();
90+
await task.sendMessage(URLSessionWebSocketMessage.fromData(
91+
Data.fromUint8List(Uint8List.fromList([1, 2, 3]))));
92+
final receivedMessage = await task.receiveMessage();
93+
expect(receivedMessage.type,
94+
URLSessionWebSocketMessageType.urlSessionWebSocketMessageTypeData);
95+
expect(receivedMessage.data!.bytes, [1, 2, 3]);
96+
expect(receivedMessage.string, null);
97+
task.cancel();
98+
});
99+
100+
test('text message', () async {
101+
final session = URLSession.sharedSession();
102+
final task = session.webSocketTaskWithRequest(
103+
URLRequest.fromUrl(Uri.parse('ws://localhost:${server.port}')))
104+
..resume();
105+
await task
106+
.sendMessage(URLSessionWebSocketMessage.fromString('Hello World!'));
107+
final receivedMessage = await task.receiveMessage();
108+
expect(receivedMessage.type,
109+
URLSessionWebSocketMessageType.urlSessionWebSocketMessageTypeString);
110+
expect(receivedMessage.data, null);
111+
expect(receivedMessage.string, 'Hello World!');
112+
task.cancel();
113+
});
114+
115+
test('send failure', () async {
116+
final session = URLSession.sharedSession();
117+
final task = session.webSocketTaskWithRequest(
118+
URLRequest.fromUrl(Uri.parse('ws://localhost:${server.port}/error')))
119+
..resume();
120+
await expectLater(
121+
task.sendMessage(
122+
URLSessionWebSocketMessage.fromString('Hello World!')),
123+
throwsA(isA<Error>().having(
124+
(e) => e.code, 'code', -1011 // NSURLErrorBadServerResponse
125+
)));
126+
task.cancel();
127+
});
128+
129+
test('receive failure', () async {
130+
final session = URLSession.sharedSession();
131+
final task = session.webSocketTaskWithRequest(
132+
URLRequest.fromUrl(Uri.parse('ws://localhost:${server.port}')))
133+
..resume();
134+
await task
135+
.sendMessage(URLSessionWebSocketMessage.fromString('Hello World!'));
136+
await task.receiveMessage();
137+
await expectLater(task.receiveMessage(),
138+
throwsA(isA<Error>().having((e) => e.code, 'code', 57 // NOT_CONNECTED
139+
)));
140+
task.cancel();
141+
});
142+
});
143+
}
144+
12145
void testURLSessionTask(
13146
URLSessionTask Function(URLSession session, Uri url) f) {
14147
group('task states', () {
@@ -231,4 +364,6 @@ void main() {
231364
testURLSessionTask((session, uri) =>
232365
session.downloadTaskWithRequest(URLRequest.fromUrl(uri)));
233366
});
367+
368+
testWebSocketTask();
234369
}

pkgs/cupertino_http/ffigen.yaml

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@ language: 'objc'
88
output: 'lib/src/native_cupertino_bindings.dart'
99
headers:
1010
entry-points:
11-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSArray.h'
12-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSData.h'
13-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSDictionary.h'
14-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSURLRequest.h'
15-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSURLSession.h'
16-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSURL.h'
17-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSProgress.h'
18-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSURLResponse.h'
19-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSHTTPCookieStorage.h'
20-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSOperation.h'
21-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSError.h'
22-
- '/Library/Developer/CommandLineTools/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSDictionary.h'
11+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSArray.h'
12+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSData.h'
13+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSDictionary.h'
14+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSURLRequest.h'
15+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSURLSession.h'
16+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSURL.h'
17+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSProgress.h'
18+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSURLResponse.h'
19+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSHTTPCookieStorage.h'
20+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSOperation.h'
21+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSError.h'
22+
- '/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSDictionary.h'
2323
- 'src/CUPHTTPClientDelegate.h'
2424
- 'src/CUPHTTPForwardedDelegate.h'
25+
- 'src/CUPHTTPCompletionHelper.h'
2526
preamble: |
2627
// ignore_for_file: always_specify_types
2728
// ignore_for_file: camel_case_types
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include "../../src/CUPHTTPCompletionHelper.m"

0 commit comments

Comments
 (0)