Skip to content

Commit

Permalink
feat: add keep-alive settings (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicobritos authored Sep 17, 2024
1 parent 7378b88 commit a7a5521
Show file tree
Hide file tree
Showing 13 changed files with 421 additions and 64 deletions.
27 changes: 25 additions & 2 deletions rhttp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,27 @@ This should only be called during app start to avoid blocking the UI thread.
final client = RhttpClient.createSync();
```

#### ➤ Keep-Alive

By default, connections are not kept alive. On HTTP2, the same connection
is reused for multiple requests that are done on the same time, but the socket
is closed immediately after the last request is finished.

Setting `keepAliveTimeout` to a value greater than `0` will keep the socket
open when idle for the specified duration, both in HTTP/1.1 and HTTP/2.

```dart
final client = await RhttpClient.create(
settings: const ClientSettings(
timeoutSettings: TimeoutSettings(
keepAliveTimeout: Duration(seconds: 60),
keepAlivePing: Duration(seconds: 30),
),
),
);
```


### ➤ Cancel Requests

You can cancel a request by providing a `CancelToken`:
Expand Down Expand Up @@ -334,8 +355,10 @@ You can specify the timeout for the request:
await Rhttp.get(
'https://example.com',
settings: const ClientSettings(
timeout: Duration(seconds: 10),
connectTimeout: Duration(seconds: 5),
timeoutSettings: TimeoutSettings(
timeout: Duration(seconds: 10),
connectTimeout: Duration(seconds: 5),
),
),
);
```
Expand Down
2 changes: 2 additions & 0 deletions rhttp/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ include: package:flutter_lints/flutter.yaml
# https://dart.dev/guides/language/analysis-options

analyzer:
errors:
deprecated_member_use_from_same_package: ignore
exclude:
- 'benchmark/**'
- 'cargokit/**'
Expand Down
7 changes: 5 additions & 2 deletions rhttp/example/lib/cancellation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ class _MyAppState extends State<MyApp> {
final cancelToken = CancelToken();
client ??= await RhttpClient.create(
settings: const ClientSettings(
timeout: Duration(seconds: 10),
timeoutSettings: TimeoutSettings(
timeout: Duration(seconds: 10),
),
),
);
final resFuture = client!.requestBytes(
method: HttpMethod.get,
url: 'https://github.com/localsend/localsend/releases/download/v1.15.3/LocalSend-1.15.3-linux-x86-64.AppImage',
url:
'https://github.com/localsend/localsend/releases/download/v1.15.3/LocalSend-1.15.3-linux-x86-64.AppImage',
cancelToken: cancelToken,
);

Expand Down
2 changes: 1 addition & 1 deletion rhttp/example/lib/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ class _MyAppState extends State<MyApp> {
),
);
}
}
}
10 changes: 5 additions & 5 deletions rhttp/example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ packages:
dependency: "direct main"
description:
name: dio
sha256: "0dfb6b6a1979dac1c1245e17cef824d7b452ea29bd33d3467269f9bef3715fb0"
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
url: "https://pub.dev"
source: hosted
version: "5.6.0"
version: "5.7.0"
dio_compatibility_layer:
dependency: "direct main"
description:
Expand Down Expand Up @@ -272,7 +272,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.7.0"
version: "0.7.1"
riverpod:
dependency: transitive
description:
Expand Down Expand Up @@ -378,10 +378,10 @@ packages:
dependency: transitive
description:
name: web
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062
url: "https://pub.dev"
source: hosted
version: "0.5.1"
version: "1.0.0"
webdriver:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions rhttp/lib/rhttp.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export 'src/model/request.dart'
export 'src/model/settings.dart'
show
ClientSettings,
TimeoutSettings,
ProxySettings,
RedirectSettings,
TlsSettings,
Expand Down
65 changes: 59 additions & 6 deletions rhttp/lib/src/model/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const _keepDuration = Duration(microseconds: -9999);
const _keepProxySettings = ProxySettings.noProxy();
const _keepRedirectSettings = RedirectSettings.limited(-9999);
const _keepTlsSettings = TlsSettings();
const _keepTimeoutSettings = TimeoutSettings();

class ClientSettings {
/// Base URL to be prefixed to all requests.
Expand All @@ -21,12 +22,17 @@ class ClientSettings {
final HttpVersionPref httpVersionPref;

/// The timeout for the request including time to establish a connection.
@Deprecated('Use timeoutSettings')
final Duration? timeout;

/// The timeout for establishing a connection.
/// See [timeout] for the total timeout.
@Deprecated('Use timeoutSettings')
final Duration? connectTimeout;

/// Timeout and keep alive settings.
final TimeoutSettings? timeoutSettings;

/// Throws an exception if the status code is 4xx or 5xx.
final bool throwOnStatusCode;

Expand All @@ -44,19 +50,31 @@ class ClientSettings {
const ClientSettings({
this.baseUrl,
this.httpVersionPref = HttpVersionPref.all,
this.timeout,
this.connectTimeout,
@Deprecated('Use timeoutSettings') this.timeout,
@Deprecated('Use timeoutSettings') this.connectTimeout,
this.timeoutSettings,
this.throwOnStatusCode = true,
this.proxySettings,
this.redirectSettings,
this.tlsSettings,
});

TimeoutSettings? get _timeoutSettings {
if (timeoutSettings != null) {
return timeoutSettings;
}
if (timeout != null || connectTimeout != null) {
return TimeoutSettings(timeout: timeout, connectTimeout: connectTimeout);
}
return null;
}

ClientSettings copyWith({
String? baseUrl = _keepBaseUrl,
HttpVersionPref? httpVersionPref,
Duration? timeout = _keepDuration,
Duration? connectTimeout = _keepDuration,
@Deprecated('Use timeoutSettings') Duration? timeout = _keepDuration,
@Deprecated('Use timeoutSettings') Duration? connectTimeout = _keepDuration,
TimeoutSettings? timeoutSettings = _keepTimeoutSettings,
bool? throwOnStatusCode,
ProxySettings? proxySettings = _keepProxySettings,
RedirectSettings? redirectSettings = _keepRedirectSettings,
Expand All @@ -69,6 +87,9 @@ class ClientSettings {
connectTimeout: identical(connectTimeout, _keepDuration)
? this.connectTimeout
: connectTimeout,
timeoutSettings: identical(timeoutSettings, _keepTimeoutSettings)
? this.timeoutSettings
: timeoutSettings,
throwOnStatusCode: throwOnStatusCode ?? this.throwOnStatusCode,
proxySettings: identical(proxySettings, _keepProxySettings)
? this.proxySettings
Expand Down Expand Up @@ -168,13 +189,34 @@ class ClientCertificate {
});
}

class TimeoutSettings {
/// The timeout for the request including time to establish a connection.
final Duration? timeout;

/// The timeout for establishing a connection.
/// See [timeout] for the total timeout.
final Duration? connectTimeout;

/// Keep alive idle timeout. If not set, keep alive is disabled.
final Duration? keepAliveTimeout;

/// Keep alive ping interval, only valid if keepAliveTimeout is set and http2.
final Duration keepAlivePing;

const TimeoutSettings({
this.timeout,
this.connectTimeout,
this.keepAliveTimeout,
this.keepAlivePing = const Duration(seconds: 30),
});
}

@internal
extension ClientSettingsExt on ClientSettings {
rust_client.ClientSettings toRustType() {
return rust_client.ClientSettings(
httpVersionPref: httpVersionPref._toRustType(),
timeout: timeout,
connectTimeout: connectTimeout,
timeoutSettings: _timeoutSettings?._toRustType(),
throwOnStatusCode: throwOnStatusCode,
proxySettings: proxySettings?._toRustType(),
redirectSettings: redirectSettings?._toRustType(),
Expand All @@ -201,6 +243,17 @@ extension on RedirectSettings {
}
}

extension on TimeoutSettings {
rust_client.TimeoutSettings _toRustType() {
return rust_client.TimeoutSettings(
timeout: timeout,
connectTimeout: connectTimeout,
keepAliveTimeout: keepAliveTimeout,
keepAlivePing: keepAlivePing,
);
}
}

extension on TlsSettings {
rust_client.TlsSettings _toRustType() {
return rust_client.TlsSettings(
Expand Down
43 changes: 35 additions & 8 deletions rhttp/lib/src/rust/api/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,15 @@ class ClientCertificate {

class ClientSettings {
final HttpVersionPref httpVersionPref;
final Duration? timeout;
final Duration? connectTimeout;
final TimeoutSettings? timeoutSettings;
final bool throwOnStatusCode;
final ProxySettings? proxySettings;
final RedirectSettings? redirectSettings;
final TlsSettings? tlsSettings;

const ClientSettings({
required this.httpVersionPref,
this.timeout,
this.connectTimeout,
this.timeoutSettings,
required this.throwOnStatusCode,
this.proxySettings,
this.redirectSettings,
Expand All @@ -61,8 +59,7 @@ class ClientSettings {
@override
int get hashCode =>
httpVersionPref.hashCode ^
timeout.hashCode ^
connectTimeout.hashCode ^
timeoutSettings.hashCode ^
throwOnStatusCode.hashCode ^
proxySettings.hashCode ^
redirectSettings.hashCode ^
Expand All @@ -74,8 +71,7 @@ class ClientSettings {
other is ClientSettings &&
runtimeType == other.runtimeType &&
httpVersionPref == other.httpVersionPref &&
timeout == other.timeout &&
connectTimeout == other.connectTimeout &&
timeoutSettings == other.timeoutSettings &&
throwOnStatusCode == other.throwOnStatusCode &&
proxySettings == other.proxySettings &&
redirectSettings == other.redirectSettings &&
Expand All @@ -97,6 +93,37 @@ sealed class RedirectSettings with _$RedirectSettings {
) = RedirectSettings_LimitedRedirects;
}

class TimeoutSettings {
final Duration? timeout;
final Duration? connectTimeout;
final Duration? keepAliveTimeout;
final Duration? keepAlivePing;

const TimeoutSettings({
this.timeout,
this.connectTimeout,
this.keepAliveTimeout,
this.keepAlivePing,
});

@override
int get hashCode =>
timeout.hashCode ^
connectTimeout.hashCode ^
keepAliveTimeout.hashCode ^
keepAlivePing.hashCode;

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TimeoutSettings &&
runtimeType == other.runtimeType &&
timeout == other.timeout &&
connectTimeout == other.connectTimeout &&
keepAliveTimeout == other.keepAliveTimeout &&
keepAlivePing == other.keepAlivePing;
}

class TlsSettings {
final bool trustRootCertificates;
final List<Uint8List> trustedRootCertificates;
Expand Down
Loading

0 comments on commit a7a5521

Please sign in to comment.