Skip to content

Commit a0781c5

Browse files
authored
pkgs/ok_http: JNIgen fixes and added WebSocket support (#1257)
* ok_http: upgrade jni and regenerate bindings * ok_http: jnigen fixes * generate bindings for okhttp websocket classes * add websocket dependency * create proxy and interceptor; generate bindings * first websocket impl * create an example section for websocket demo * add integration tests * add helper comments and docs to kotlin files * remove websocket example * rename 'WSInterceptor' to 'WebSocketInterceptor' * refer to deadlock issue in package: jni * bump up version from `0.1.0-wip` to `0.1.0` * increase step `test` timeout to 1200s to prevent workflow failures
1 parent 757438e commit a0781c5

File tree

14 files changed

+4702
-1083
lines changed

14 files changed

+4702
-1083
lines changed

.github/workflows/okhttp.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ jobs:
5555
# - pkgs/ok_http/example/android/app/build.gradle
5656
api-level: 21
5757
arch: x86_64
58-
script: cd pkgs/ok_http/example && flutter test --timeout=120s integration_test/
58+
script: cd pkgs/ok_http/example && flutter test --timeout=1200s integration_test/

pkgs/ok_http/CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## 0.1.0-wip
1+
## 0.1.0
22

33
- Implementation of [`BaseClient`](https://pub.dev/documentation/http/latest/http/BaseClient-class.html) and `send()` method using [`enqueue()` API](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-call/enqueue.html)
44
- `ok_http` can now send asynchronous requests and stream response bodies.
55
- Add [DevTools Network View](https://docs.flutter.dev/tools/devtools/network) support.
6+
- WebSockets support is now available in the `ok_http` package. Wraps around the OkHttp [WebSocket API](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-web-socket/index.html).

pkgs/ok_http/README.md

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
[![pub package](https://img.shields.io/pub/v/cronet_http.svg)](https://pub.dev/packages/ok_http)
1+
[![pub package](https://img.shields.io/pub/v/ok_http.svg)](https://pub.dev/packages/ok_http)
22
[![package publisher](https://img.shields.io/pub/publisher/ok_http.svg)](https://pub.dev/packages/ok_http/publisher)
33

44
An Android Flutter plugin that provides access to the
5-
[OkHttp][] HTTP client.
5+
[OkHttp][] HTTP client and the OkHttp [WebSocket][] API.
66

77
## Why use `package:ok_http`?
88

@@ -23,6 +23,35 @@ This size of the [example application][] APK file using different packages:
2323
[^2]: Embeds the Cronet HTTP library.
2424
[^3]: Accessed through [`IOClient`](https://pub.dev/documentation/http/latest/io_client/IOClient-class.html).
2525

26+
### 🔌 Supports WebSockets out of the box
27+
28+
`package:ok_http` wraps the OkHttp [WebSocket][] API which supports:
29+
30+
- Configured System Proxy on Android
31+
- HTTP/2
32+
33+
**Example Usage of `OkHttpWebSocket`:**
34+
35+
```dart
36+
import 'package:ok_http/ok_http.dart';
37+
import 'package:web_socket/web_socket.dart';
38+
void main() async {
39+
final socket = await OkHttpWebSocket.connect(
40+
Uri.parse('wss://ws.postman-echo.com/raw'));
41+
socket.events.listen((e) async {
42+
switch (e) {
43+
case TextDataReceived(text: final text):
44+
print('Received Text: $text');
45+
await socket.close();
46+
case BinaryDataReceived(data: final data):
47+
print('Received Binary: $data');
48+
case CloseReceived(code: final code, reason: final reason):
49+
print('Connection to server closed: $code [$reason]');
50+
}
51+
});
52+
}
53+
```
54+
2655
## Status: experimental
2756

2857
**NOTE**: This package is currently experimental and published under the
@@ -41,3 +70,4 @@ feedback, suggestions, and comments, please file an issue in the
4170
[example application]: https://github.com/dart-lang/http/tree/master/pkgs/flutter_http_example
4271
[OkHttp]: https://square.github.io/okhttp/
4372
[Google Play Services]: https://developers.google.com/android/guides/overview
73+
[WebSocket]: https://square.github.io/okhttp/5.x/okhttp/okhttp3/-web-socket/index.html

pkgs/ok_http/analysis_options.yaml

-5
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
package com.example.ok_http
6+
7+
import okhttp3.Interceptor
8+
import okhttp3.OkHttpClient
9+
10+
/**
11+
* Usage of `chain.proceed(...)` via JNI Bindings leads to threading issues. This is a workaround
12+
* to intercept the response before it is parsed by the WebSocketReader, to prevent response parsing errors.
13+
*
14+
* https://github.com/dart-lang/native/issues/1337
15+
*/
16+
class WebSocketInterceptor {
17+
companion object {
18+
fun addWSInterceptor(
19+
clientBuilder: OkHttpClient.Builder
20+
): OkHttpClient.Builder {
21+
return clientBuilder.addInterceptor(Interceptor { chain ->
22+
val request = chain.request()
23+
val response = chain.proceed(request)
24+
25+
response.newBuilder()
26+
// Removing this header to ensure that OkHttp does not fail due to unexpected values.
27+
.removeHeader("sec-websocket-extensions")
28+
// Adding the header to ensure successful parsing of the response.
29+
.addHeader("sec-websocket-extensions", "permessage-deflate").build()
30+
})
31+
}
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
package com.example.ok_http
6+
7+
import okhttp3.Response
8+
import okhttp3.WebSocket
9+
import okhttp3.WebSocketListener
10+
import okio.ByteString
11+
12+
/**
13+
* `OkHttp` expects a subclass of the abstract class [`WebSocketListener`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-web-socket-listener/index.html)
14+
* to be passed to the `newWebSocket` method.
15+
*
16+
* `package:jnigen` does not support the ability to subclass abstract Java classes in Dart
17+
* (see https://github.com/dart-lang/jnigen/issues/348).
18+
*
19+
* This file provides an interface `WebSocketListener`, which can
20+
* be implemented in Dart and a wrapper class `WebSocketListenerProxy`, which
21+
* can be passed to the OkHttp API.
22+
*/
23+
class WebSocketListenerProxy(private val listener: WebSocketListener) : WebSocketListener() {
24+
interface WebSocketListener {
25+
fun onOpen(webSocket: WebSocket, response: Response)
26+
fun onMessage(webSocket: WebSocket, text: String)
27+
fun onMessage(webSocket: WebSocket, bytes: ByteString)
28+
fun onClosing(webSocket: WebSocket, code: Int, reason: String)
29+
fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?)
30+
}
31+
32+
override fun onOpen(webSocket: WebSocket, response: Response) {
33+
listener.onOpen(webSocket, response)
34+
}
35+
36+
override fun onMessage(webSocket: WebSocket, text: String) {
37+
listener.onMessage(webSocket, text)
38+
}
39+
40+
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
41+
listener.onMessage(webSocket, bytes)
42+
}
43+
44+
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
45+
listener.onClosing(webSocket, code, reason)
46+
}
47+
48+
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
49+
listener.onFailure(webSocket, t, response)
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:ok_http/ok_http.dart';
6+
import 'package:web_socket_conformance_tests/web_socket_conformance_tests.dart';
7+
8+
void main() {
9+
testAll(OkHttpWebSocket.connect);
10+
}

pkgs/ok_http/example/pubspec.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ dependencies:
1616
ok_http:
1717
path: ../
1818
provider: ^6.1.1
19+
web_socket: ^0.1.5
1920

2021
dev_dependencies:
2122
dart_flutter_team_lints: ^3.0.0
@@ -27,6 +28,8 @@ dev_dependencies:
2728
integration_test:
2829
sdk: flutter
2930
test: ^1.23.1
31+
web_socket_conformance_tests:
32+
path: ../../web_socket_conformance_tests/
3033

3134
flutter:
3235
uses-material-design: true

pkgs/ok_http/jnigen.yaml

+21
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,16 @@ classes:
2626
- "okhttp3.Callback"
2727
- "okhttp3.ConnectionPool"
2828
- "okhttp3.Dispatcher"
29+
- "java.util.concurrent.ExecutorService"
2930
- "okhttp3.Cache"
3031
- "com.example.ok_http.RedirectReceivedCallback"
3132
- "com.example.ok_http.RedirectInterceptor"
3233
- "com.example.ok_http.AsyncInputStreamReader"
3334
- "com.example.ok_http.DataCallback"
35+
- "okhttp3.WebSocket"
36+
- "com.example.ok_http.WebSocketListenerProxy"
37+
- "okio.ByteString"
38+
- "com.example.ok_http.WebSocketInterceptor"
3439

3540
# Exclude the deprecated methods listed below
3641
# They cause syntax errors during the `dart format` step of JNIGen.
@@ -86,3 +91,19 @@ exclude:
8691
- "okhttp3.Headers#-deprecated_size"
8792
- "okhttp3.Dispatcher#-deprecated_executorService"
8893
- "okhttp3.Cache#-deprecated_directory"
94+
- "java.util.concurrent.ExecutorService#invokeAll"
95+
- "java.util.concurrent.ExecutorService#invokeAny"
96+
- "java.util.concurrent.ExecutorService#submit"
97+
- "okio.ByteString$Companion#-deprecated_getByte"
98+
- "okio.ByteString$Companion#-deprecated_size"
99+
- 'okio.ByteString\$Companion#-deprecated_decodeBase64'
100+
- 'okio.ByteString\$Companion#-deprecated_decodeHex'
101+
- 'okio.ByteString\$Companion#-deprecated_encodeString'
102+
- 'okio.ByteString\$Companion#-deprecated_encodeUtf8'
103+
- 'okio.ByteString\$Companion#-deprecated_of'
104+
- 'okio.ByteString\$Companion#-deprecated_read'
105+
- "okio.ByteString#-deprecated_getByte"
106+
- "okio.ByteString#-deprecated_size"
107+
108+
preamble: |
109+
// ignore_for_file: prefer_expression_function_bodies

pkgs/ok_http/lib/ok_http.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
/// An Android Flutter plugin that provides access to the
6-
/// [OkHttp](https://square.github.io/okhttp/) HTTP client.
6+
/// [OkHttp](https://square.github.io/okhttp/) HTTP client, and the OkHttp
7+
/// [WebSocket](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-web-socket/index.html)
8+
/// API
79
///
810
/// ```
911
/// import 'package:ok_http/ok_http.dart';
@@ -53,3 +55,4 @@ import 'package:http/http.dart';
5355
import 'src/ok_http_client.dart';
5456

5557
export 'src/ok_http_client.dart';
58+
export 'src/ok_http_web_socket.dart';

0 commit comments

Comments
 (0)