Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkgs/ok_http: JNIgen fixes and added WebSocket support #1257

Merged
merged 29 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
dbf7d1d
ok_http: upgrade jni and regenerate bindings
Anikate-De Jun 30, 2024
bd97d77
ok_http: jnigen fixes
Anikate-De Jun 30, 2024
4cb89e8
generate bindings for okhttp websocket classes
Anikate-De Jul 1, 2024
daf5d3a
add websocket dependency
Anikate-De Jul 4, 2024
87f7595
create proxy and interceptor; generate bindings
Anikate-De Jul 4, 2024
dc6984b
first websocket impl
Anikate-De Jul 4, 2024
78b6637
create an example section for websocket demo
Anikate-De Jul 4, 2024
7b93fb0
add integration tests
Anikate-De Jul 4, 2024
bb68b2b
add helper comments and docs to kotlin files
Anikate-De Jul 7, 2024
5cea12b
add more docs
Anikate-De Jul 7, 2024
3c14e5d
Squashed commit of the following:
Anikate-De Jul 7, 2024
91c9378
add more and more docs
Anikate-De Jul 7, 2024
9968a76
update changelog
Anikate-De Jul 7, 2024
84cd97b
Merge branch 'master' into okhttp_websockets
Anikate-De Jul 7, 2024
add63c5
Revert "ok_http: jnigen fixes"
Anikate-De Jul 7, 2024
8a22001
Merge branch 'okhttp_websockets' of https://github.com/Anikate-De/htt…
Anikate-De Jul 7, 2024
280b727
jnigen fixes
Anikate-De Jul 7, 2024
1f8de0c
fix duplicate sections in the readme
Anikate-De Jul 7, 2024
6e64458
minor text fixes
Anikate-De Jul 7, 2024
0bd1a73
fix JArray extensions
Anikate-De Jul 14, 2024
4efbfad
remove websocket example
Anikate-De Jul 14, 2024
b2711bd
fix ok_http_web_socket.dart nits
Anikate-De Jul 14, 2024
d0d1961
rename 'WSInterceptor' to 'WebSocketInterceptor'
Anikate-De Jul 14, 2024
7bc6d26
refer to deadlock issue in package: jni
Anikate-De Jul 14, 2024
8871562
added usage notes to readme and docs
Anikate-De Jul 14, 2024
57fe25f
documentation improvements
Anikate-De Jul 15, 2024
05400b5
bump up version from `0.1.0-wip` to `0.1.0`
Anikate-De Jul 15, 2024
1d0ef4c
increase step `test` timeout to 1200s to prevent workflow failures
Anikate-De Jul 15, 2024
260c2a2
upgrade jni versions and regenerate bindings
Anikate-De Jul 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions pkgs/ok_http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,43 @@ An Android Flutter plugin that provides access to the

This size of the [example application][] APK file using different packages:

| Package | APK Size (MiB) |
| ----------------------------------------------------------------------------------------- | -------------- |
| **`ok_http`** | **20.3** |
| [`cronet_http`](https://pub.dev/packages/cronet_http) [^1] | 20.6 |
| [`cronet_http` (embedded)](https://pub.dev/packages/cronet_http#use-embedded-cronet) [^2] | 34.4 |
| `dart:io` [^3] | 20.4 |
| Package | APK Size (MiB) |
|-|-|
| **`ok_http`** | **20.3** |
| [`cronet_http`](https://pub.dev/packages/cronet_http) [^1] | 20.6 |
| [`cronet_http` (embedded)](https://pub.dev/packages/cronet_http#use-embedded-cronet) [^2] | 34.4 |
| `dart:io` [^3] | 20.4 |

[^1]: Requires [Google Play Services][], which are not available on all devices.
[^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, allowing you to use it in your Flutter app with ease.
`package:ok_http` wraps the OkHttp [WebSocket][] API which supports:

See the [OkHttp Example App][] for usage notes.
- Configured System Proxy on Android
- HTTP/2

```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

Expand All @@ -48,4 +69,3 @@ feedback, suggestions, and comments, please file an issue in the
[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
[OkHttp Example App]: https://github.com/dart-lang/http/tree/master/pkgs/ok_http/example/
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ 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 WSInterceptor {
class WebSocketInterceptor {
companion object {
fun addWSInterceptor(
clientBuilder: OkHttpClient.Builder
Expand Down
182 changes: 17 additions & 165 deletions pkgs/ok_http/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// 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 'dart:async';
import 'dart:convert';
import 'dart:io';

Expand All @@ -12,7 +11,6 @@ import 'package:http/io_client.dart';
import 'package:http_image_provider/http_image_provider.dart';
import 'package:ok_http/ok_http.dart';
import 'package:provider/provider.dart';
import 'package:web_socket/web_socket.dart';

import 'book.dart';

Expand Down Expand Up @@ -50,46 +48,6 @@ class HomePage extends StatefulWidget {
}

class _HomePageState extends State<HomePage> {
int _selectedIndex = 0;
final _labels = ['HTTP', 'WebSocket'];

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title:
Text(_selectedIndex == 0 ? 'Book Search' : 'WebSocket Echo')),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (value) => setState(() {
_selectedIndex = value;
}),
items: [
BottomNavigationBarItem(
icon: const Icon(Icons.cloud_outlined),
activeIcon: const Icon(Icons.cloud),
label: _labels[0],
),
BottomNavigationBarItem(
icon: const Icon(Icons.swap_vert_circle_outlined),
activeIcon: const Icon(Icons.swap_vert_circle),
label: _labels[1],
),
],
),
body: _selectedIndex == 0 ? const BookSearch() : const WebSocketEcho());
}
}

/// Example to demonstrate HTTP Calls with [OkHttpClient].
class BookSearch extends StatefulWidget {
const BookSearch({super.key});

@override
State<BookSearch> createState() => _BookSearchState();
}

class _BookSearchState extends State<BookSearch> {
List<Book>? _books;
String? _lastQuery;
late Client _client;
Expand Down Expand Up @@ -141,21 +99,24 @@ class _BookSearchState extends State<BookSearch> {
? BookList(_books!)
: const Text('No results found', style: TextStyle(fontSize: 24));

return Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
const SizedBox(height: 20),
TextField(
onChanged: _runSearch,
decoration: const InputDecoration(
labelText: 'Search',
suffixIcon: Icon(Icons.search),
return Scaffold(
appBar: AppBar(title: const Text('Book Search')),
body: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
const SizedBox(height: 20),
TextField(
onChanged: _runSearch,
decoration: const InputDecoration(
labelText: 'Search',
suffixIcon: Icon(Icons.search),
),
),
),
const SizedBox(height: 20),
Expanded(child: searchResult),
],
const SizedBox(height: 20),
Expanded(child: searchResult),
],
),
),
);
}
Expand Down Expand Up @@ -186,112 +147,3 @@ class _BookListState extends State<BookList> {
),
);
}

/// Example to demonstrate WebSocket communication with [OkHttpWebSocket],
/// using the echo server at `wss://echo.websocket.org`.
class WebSocketEcho extends StatefulWidget {
const WebSocketEcho({super.key});

@override
State<WebSocketEcho> createState() => _WebSocketEchoState();
}

class _WebSocketEchoState extends State<WebSocketEcho> {
// Contains a list of pairs [String message, bool isSent].
final List<List> _messages = [];
final msgController = TextEditingController();

late WebSocket _webSocket;

@override
void initState() {
super.initState();
}

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () async {
try {
_webSocket = await OkHttpWebSocket.connect(
Uri.parse('wss://echo.websocket.org'));
} on WebSocketException catch (e) {
print('Error connecting to WebSocket: ${e.message}');
return;
}

_webSocket.events.listen((event) {
setState(() {
switch (event) {
case TextDataReceived(text: final text):
_messages.add([text, false]);
break;
case CloseReceived():
_messages.add(['Connection closed', false]);
// For this example, binary data is not expected.
default:
_messages.add(
['Unknown message received $event', false]);
}
});
});
},
child: const Text('Connect'))),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: () {
try {
_webSocket.close();
} on WebSocketException catch (e) {
print('Error closing WebSocket: ${e.message}');
}
},
child: const Text('Disconnect'))),
],
),
Row(
children: [
Expanded(
child: TextField(
controller: msgController,
decoration: const InputDecoration(
labelText: 'Message',
),
),
),
ElevatedButton(
onPressed: () {
_webSocket.sendText(msgController.text);
setState(() {
_messages.add([msgController.text, true]);
});
},
child: const Text('Send')),
],
),
const SizedBox(height: 20),
Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(_messages[index][0] as String),
trailing: Icon(_messages[index][1] as bool
? Icons.upload
: Icons.download),
);
},
itemCount: _messages.length,
)),
],
),
);
}
}
2 changes: 1 addition & 1 deletion pkgs/ok_http/jnigen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ classes:
- "okhttp3.WebSocket"
- "com.example.ok_http.WebSocketListenerProxy"
- "okio.ByteString"
- "com.example.ok_http.WSInterceptor"
- "com.example.ok_http.WebSocketInterceptor"

# Exclude the deprecated methods listed below
# They cause syntax errors during the `dart format` step of JNIGen.
Expand Down
Loading