Skip to content

Commit

Permalink
pkgs/ok_http: JNIgen fixes and added WebSocket support (#1257)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Anikate-De authored Jul 17, 2024
1 parent 757438e commit a0781c5
Show file tree
Hide file tree
Showing 14 changed files with 4,702 additions and 1,083 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/okhttp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ jobs:
# - pkgs/ok_http/example/android/app/build.gradle
api-level: 21
arch: x86_64
script: cd pkgs/ok_http/example && flutter test --timeout=120s integration_test/
script: cd pkgs/ok_http/example && flutter test --timeout=1200s integration_test/
3 changes: 2 additions & 1 deletion pkgs/ok_http/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 0.1.0-wip
## 0.1.0

- 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)
- `ok_http` can now send asynchronous requests and stream response bodies.
- Add [DevTools Network View](https://docs.flutter.dev/tools/devtools/network) support.
- 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).
34 changes: 32 additions & 2 deletions pkgs/ok_http/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[![pub package](https://img.shields.io/pub/v/cronet_http.svg)](https://pub.dev/packages/ok_http)
[![pub package](https://img.shields.io/pub/v/ok_http.svg)](https://pub.dev/packages/ok_http)
[![package publisher](https://img.shields.io/pub/publisher/ok_http.svg)](https://pub.dev/packages/ok_http/publisher)

An Android Flutter plugin that provides access to the
[OkHttp][] HTTP client.
[OkHttp][] HTTP client and the OkHttp [WebSocket][] API.

## Why use `package:ok_http`?

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

### 🔌 Supports WebSockets out of the box

`package:ok_http` wraps the OkHttp [WebSocket][] API which supports:

- Configured System Proxy on Android
- HTTP/2

**Example Usage of `OkHttpWebSocket`:**

```dart
import 'package:ok_http/ok_http.dart';
import 'package:web_socket/web_socket.dart';
void main() async {
final socket = await OkHttpWebSocket.connect(
Uri.parse('wss://ws.postman-echo.com/raw'));
socket.events.listen((e) async {
switch (e) {
case TextDataReceived(text: final text):
print('Received Text: $text');
await socket.close();
case BinaryDataReceived(data: final data):
print('Received Binary: $data');
case CloseReceived(code: final code, reason: final reason):
print('Connection to server closed: $code [$reason]');
}
});
}
```

## Status: experimental

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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2024, 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.

package com.example.ok_http

import okhttp3.Interceptor
import okhttp3.OkHttpClient

/**
* Usage of `chain.proceed(...)` via JNI Bindings leads to threading issues. This is a workaround
* to intercept the response before it is parsed by the WebSocketReader, to prevent response parsing errors.
*
* https://github.com/dart-lang/native/issues/1337
*/
class WebSocketInterceptor {
companion object {
fun addWSInterceptor(
clientBuilder: OkHttpClient.Builder
): OkHttpClient.Builder {
return clientBuilder.addInterceptor(Interceptor { chain ->
val request = chain.request()
val response = chain.proceed(request)

response.newBuilder()
// Removing this header to ensure that OkHttp does not fail due to unexpected values.
.removeHeader("sec-websocket-extensions")
// Adding the header to ensure successful parsing of the response.
.addHeader("sec-websocket-extensions", "permessage-deflate").build()
})
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) 2024, 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.

package com.example.ok_http

import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okio.ByteString

/**
* `OkHttp` expects a subclass of the abstract class [`WebSocketListener`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-web-socket-listener/index.html)
* to be passed to the `newWebSocket` method.
*
* `package:jnigen` does not support the ability to subclass abstract Java classes in Dart
* (see https://github.com/dart-lang/jnigen/issues/348).
*
* This file provides an interface `WebSocketListener`, which can
* be implemented in Dart and a wrapper class `WebSocketListenerProxy`, which
* can be passed to the OkHttp API.
*/
class WebSocketListenerProxy(private val listener: WebSocketListener) : WebSocketListener() {
interface WebSocketListener {
fun onOpen(webSocket: WebSocket, response: Response)
fun onMessage(webSocket: WebSocket, text: String)
fun onMessage(webSocket: WebSocket, bytes: ByteString)
fun onClosing(webSocket: WebSocket, code: Int, reason: String)
fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?)
}

override fun onOpen(webSocket: WebSocket, response: Response) {
listener.onOpen(webSocket, response)
}

override fun onMessage(webSocket: WebSocket, text: String) {
listener.onMessage(webSocket, text)
}

override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
listener.onMessage(webSocket, bytes)
}

override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
listener.onClosing(webSocket, code, reason)
}

override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
listener.onFailure(webSocket, t, response)
}
}
10 changes: 10 additions & 0 deletions pkgs/ok_http/example/integration_test/web_socket_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) 2024, 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:ok_http/ok_http.dart';
import 'package:web_socket_conformance_tests/web_socket_conformance_tests.dart';

void main() {
testAll(OkHttpWebSocket.connect);
}
3 changes: 3 additions & 0 deletions pkgs/ok_http/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies:
ok_http:
path: ../
provider: ^6.1.1
web_socket: ^0.1.5

dev_dependencies:
dart_flutter_team_lints: ^3.0.0
Expand All @@ -27,6 +28,8 @@ dev_dependencies:
integration_test:
sdk: flutter
test: ^1.23.1
web_socket_conformance_tests:
path: ../../web_socket_conformance_tests/

flutter:
uses-material-design: true
21 changes: 21 additions & 0 deletions pkgs/ok_http/jnigen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,16 @@ classes:
- "okhttp3.Callback"
- "okhttp3.ConnectionPool"
- "okhttp3.Dispatcher"
- "java.util.concurrent.ExecutorService"
- "okhttp3.Cache"
- "com.example.ok_http.RedirectReceivedCallback"
- "com.example.ok_http.RedirectInterceptor"
- "com.example.ok_http.AsyncInputStreamReader"
- "com.example.ok_http.DataCallback"
- "okhttp3.WebSocket"
- "com.example.ok_http.WebSocketListenerProxy"
- "okio.ByteString"
- "com.example.ok_http.WebSocketInterceptor"

# Exclude the deprecated methods listed below
# They cause syntax errors during the `dart format` step of JNIGen.
Expand Down Expand Up @@ -86,3 +91,19 @@ exclude:
- "okhttp3.Headers#-deprecated_size"
- "okhttp3.Dispatcher#-deprecated_executorService"
- "okhttp3.Cache#-deprecated_directory"
- "java.util.concurrent.ExecutorService#invokeAll"
- "java.util.concurrent.ExecutorService#invokeAny"
- "java.util.concurrent.ExecutorService#submit"
- "okio.ByteString$Companion#-deprecated_getByte"
- "okio.ByteString$Companion#-deprecated_size"
- 'okio.ByteString\$Companion#-deprecated_decodeBase64'
- 'okio.ByteString\$Companion#-deprecated_decodeHex'
- 'okio.ByteString\$Companion#-deprecated_encodeString'
- 'okio.ByteString\$Companion#-deprecated_encodeUtf8'
- 'okio.ByteString\$Companion#-deprecated_of'
- 'okio.ByteString\$Companion#-deprecated_read'
- "okio.ByteString#-deprecated_getByte"
- "okio.ByteString#-deprecated_size"

preamble: |
// ignore_for_file: prefer_expression_function_bodies
5 changes: 4 additions & 1 deletion pkgs/ok_http/lib/ok_http.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
// BSD-style license that can be found in the LICENSE file.

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

export 'src/ok_http_client.dart';
export 'src/ok_http_web_socket.dart';
Loading

0 comments on commit a0781c5

Please sign in to comment.