dart_http2_flutter_sse 1.0.2 copy "dart_http2_flutter_sse: ^1.0.2" to clipboard
dart_http2_flutter_sse: ^1.0.2 copied to clipboard

A production-ready HTTP/2 client package for Dart and Flutter with interceptors, retry, timeouts, cancellation, streaming, and connection pooling.

dart_http2_flutter_sse #

pub package License: MIT

A production-ready HTTP/2 client package for Dart and Flutter. Provides a clean, developer-friendly API similar to dio or http, with robust HTTP/2 support.

Features #

  • HTTP/2 requests: GET, POST, PUT, PATCH, DELETE
  • Request/response headers, query parameters, JSON body, form body, raw bytes, and streaming body
  • Response streaming for large downloads
  • Connection reuse, multiplexing, and connection pooling
  • TLS/ALPN where available (native platforms)
  • Timeout support: connect, send, receive timeouts
  • Cancellation tokens for cancelling active requests
  • Retry policy with exponential backoff and jitter
  • Interceptors for request, response, and error handling
  • Logging interceptor with safe redaction for tokens/passwords
  • Certificate pinning support (native platforms)
  • Graceful fallback — HTTP/1.1 on Web, with clear platform documentation
  • Strong typing, null safety, no unnecessary dependencies
  • Fully tested with unit and integration tests

Platform Support #

Platform HTTP/2 Support Notes
Android ✅ Native Via dart:io HttpClient
iOS ✅ Native Via dart:io HttpClient
macOS ✅ Native Via dart:io HttpClient
Linux ✅ Native Via dart:io HttpClient
Windows ✅ Native Via dart:io HttpClient
Web ⚠️ Limited Uses browser's fetch (HTTP/1.1). Browser may negotiate HTTP/2 but no direct control. No certificate pinning, no streaming uploads.

Installation #

Add to your pubspec.yaml:

dependencies:
  dart_http2_flutter_sse: ^1.0.0

Then run:

flutter pub get

Quick Start #

import 'package:dart_http2_flutter_sse/dart_http2_flutter_sse.dart';

final client = Http2Client();

final response = await client.get(
  'https://api.example.com/users',
  queryParameters: {'page': 1},
);

print(response.statusCode); // 200
print(response.data);        // Decoded JSON body

Usage #

Basic Requests #

// GET
final getRes = await client.get('https://api.example.com/items');

// POST with JSON body
final postRes = await client.post(
  'https://api.example.com/items',
  body: {'name': 'New Item', 'price': 29.99},
);

// POST with form data
final formRes = await client.post(
  'https://api.example.com/login',
  body: {'username': 'john', 'password': 'secret'},
  bodyType: BodyType.form,
);

// PUT
final putRes = await client.put(
  'https://api.example.com/items/1',
  body: {'name': 'Updated Item'},
);

// PATCH
final patchRes = await client.patch(
  'https://api.example.com/items/1',
  body: {'price': 19.99},
);

// DELETE
final deleteRes = await client.delete('https://api.example.com/items/1');

Query Parameters & Headers #

final response = await client.get(
  'https://api.example.com/search',
  queryParameters: {
    'q': 'flutter',
    'page': 1,
    'limit': 20,
  },
  headers: {
    'Authorization': 'Bearer your-token',
    'X-Custom': 'value',
  },
);

Response Handling #

final response = await client.get('https://api.example.com/data');

print(response.statusCode);    // 200
print(response.isOk);          // true
print(response.bodyString);    // Raw body string
print(response.data);          // Auto-decoded JSON (Map, List, or String)
print(response.dataAsMap);     // Null-safe JSON map access
print(response.headers);       // Response headers
print(response.contentLength); // Content-Length from headers
print(response.duration);      // Request duration

Streaming Download #

final request = Http2Request.get('https://example.com/large-file.zip');

int totalBytes = 0;
await for (final chunk in client.streamResponse(request)) {
  totalBytes += chunk.bodyBytes?.length ?? 0;
  print('Received $totalBytes bytes so far...');
}
print('Download complete: $totalBytes bytes');

Streaming Upload #

final fileStream = File('large-file.txt').openRead();

final response = await client.post(
  'https://api.example.com/upload',
  body: fileStream,
  bodyType: BodyType.stream,
  onSendProgress: (sent, total) {
    print('Uploaded $sent of $total bytes');
  },
);

Timeouts #

final client = Http2Client(
  timeoutConfiguration: const TimeoutConfiguration(
    connectTimeout: Duration(seconds: 10),
    sendTimeout: Duration(seconds: 30),
    receiveTimeout: Duration(seconds: 30),
  ),
);

// Or use the factory for all-equal timeouts:
final client2 = Http2Client(
  timeoutConfiguration: TimeoutConfiguration.all(
    Duration(seconds: 15),
  ),
);

Cancellation #

final cts = CancellationTokenSource();

// Start a request
final future = client.get(
  'https://api.example.com/slow-endpoint',
  cancellationToken: cts.token,
);

// Cancel it later
cts.cancel();

try {
  await future;
} on CancelledException catch (e) {
  print('Request was cancelled: $e');
}

Retry with Exponential Backoff #

final client = Http2Client(
  retryConfig: const RetryConfig(
    maxRetries: 3,
    baseDelay: Duration(seconds: 1),
    maxDelay: Duration(seconds: 30),
    useJitter: true,     // Adds randomness to avoid thundering herd
    retryableStatusCodes: {
      408, // Request Timeout
      429, // Too Many Requests
      500, // Internal Server Error
      502, // Bad Gateway
      503, // Service Unavailable
      504, // Gateway Timeout
    },
    retryOnConnectionErrors: true,
  ),
);

Custom Retry Logic #

final client = Http2Client(
  retryConfig: RetryConfig(
    maxRetries: 2,
    retryOnError: (error) {
      // Only retry on specific conditions
      if (error is TimeoutException) return true;
      if (error is HttpStatusException && error.statusCode == 429) return true;
      return false;
    },
  ),
);

Interceptors #

// Logging interceptor (safe redaction built-in)
final client = Http2Client(
  interceptors: [
    LoggingInterceptor(const LoggingConfig(
      logRequest: true,
      logResponse: true,
      logRequestBody: false,  // Off by default for security
      logResponseBody: false, // Off by default for security
      logErrors: true,
    )),
  ],
);

// Custom interceptor
class AuthInterceptor extends Interceptor {
  final String token;

  AuthInterceptor(this.token);

  @override
  void onRequest(Http2Request request, RequestHandler handler) {
    request.headers['Authorization'] = 'Bearer $token';
    handler.next(request);
  }

  @override
  void onResponse(Http2Response response, ResponseHandler handler) {
    print('Response: ${response.statusCode}');
    handler.next(response);
  }

  @override
  void onError(Http2Exception error, ErrorHandler handler) {
    print('Error: $error');
    handler.next(error);
  }
}

final client = Http2Client(
  interceptors: [
    AuthInterceptor('your-token'),
    LoggingInterceptor(),
  ],
);

Certificate Pinning #

final client = Http2Client(
  enableCertificatePinning: true,
  pinnedCertificates: {
    'api.example.com': [
      '-----BEGIN CERTIFICATE-----\nMIIF...',
    ],
  },
);

Note: Certificate pinning is only supported on native platforms (Android, iOS, macOS, Linux, Windows). Not available on Web.

Error Handling #

try {
  final response = await client.get('https://api.example.com/data');
} on HttpStatusException catch (e) {
  print('HTTP error: ${e.statusCode} - ${e.message}');
  print('Response body: ${e.response?.bodyString}');
} on TimeoutException catch (e) {
  print('${e.timeoutType} timeout exceeded');
} on ConnectionException catch (e) {
  print('Connection failed to ${e.host}: ${e.message}');
} on CancelledException catch (e) {
  print('Request was cancelled');
} on CertificatePinningException catch (e) {
  print('Certificate pin mismatch for ${e.hostname}');
} on Http2Exception catch (e) {
  print('Other HTTP/2 error: ${e.message}');
}

Progress Tracking #

final response = await client.get(
  'https://api.example.com/large-file',
  onReceiveProgress: (received, total) {
    final progress = total > 0 ? (received / total * 100).toStringAsFixed(1) : '?';
    print('Downloaded $received / $total bytes ($progress%)');
  },
);

Architecture #

lib/
├── dart_http2_flutter_sse.dart      # Barrel export
├── src/
│   ├── client.dart                  # Http2Client public API
│   ├── models/
│   │   ├── http2_request.dart       # Request model with body serialization
│   │   ├── http2_response.dart      # Response model with JSON decoding
│   │   └── http2_exceptions.dart    # Typed exception hierarchy
│   ├── interceptors/
│   │   ├── interceptor.dart         # Interceptor interface + chain
│   │   ├── logging_interceptor.dart # Request/response logging with redaction
│   │   └── retry_interceptor.dart   # Retry with exponential backoff
│   ├── transport/
│   │   ├── transport.dart           # Abstract transport interface
│   │   ├── http2_transport.dart     # HTTP/2 transport (native)
│   │   ├── http1_fallback_transport.dart # HTTP/1.1 fallback
│   │   └── web_transport_stub.dart  # Web platform transport
│   └── utils/
│       ├── cancellation_token.dart  # Cancellation token system
│       ├── timeout_manager.dart     # Timeout enforcement
│       └── connection_pool.dart     # Connection pooling

Comparison: HTTP/2 vs HTTP/1.1 #

Feature HTTP/2 (this package) HTTP/1.1 (http package)
Multiplexing ✅ Multiple streams over single connection ❌ One request at a time per connection
Header compression ✅ HPACK compression ❌ Plain text headers
Server push ✅ Supported ❌ Not supported
Binary protocol ✅ Binary framing ❌ Text-based
Connection reuse ✅ Connection pooling built-in ⚠️ Limited (keep-alive)
Stream prioritization ✅ Supported ❌ Not supported
TLS requirement ⚠️ Most implementations require TLS ✅ Works over plain HTTP
Latency ✅ Lower (fewer round trips) ❌ Higher (head-of-line blocking)
Web support ⚠️ Limited (browser negotiation) ✅ Full support
Certificate pinning ✅ Supported (native) ❌ Not available

Comparison with Dio #

Feature dart_http2_flutter_sse Dio
HTTP/2 focus ✅ First-class HTTP/2 support ⚠️ HTTP/1.1 with h2 via adapter
Package size ✅ Minimal dependencies ❌ Heavier (crypto, etc.)
Interceptors ✅ Full interceptor chain ✅ Full interceptor chain
Retry ✅ Built-in with exponential backoff ⚠️ Via add-on package
Cancellation ✅ Cancellation tokens ✅ CancelToken
Certificate pinning ✅ Built-in ⚠️ Via custom adapter
Streaming ✅ Response streaming ✅ Via ResponseType.stream
Web support ✅ HTTP/1.1 fallback ✅ Full support
pub.flutter-io.cn popularity New ✅ Very popular

Running Tests #

# Run unit tests
flutter test

# Run integration tests (requires network)
flutter test --dart-define=RUN_INTEGRATION_TESTS=true integration_test/

Limitations #

  • Web platform: HTTP/2 is not directly controllable from Dart on the web. The browser may negotiate HTTP/2 with the server, but this package falls back to HTTP/1.1 semantics. Certificate pinning and streaming uploads are not available on Web.
  • ALPN negotiation: On native platforms, ALPN (Application-Layer Protocol Negotiation) is handled by the underlying TLS library. If the server does not support HTTP/2, the connection falls back to HTTP/1.1.
  • Server push: While HTTP/2 supports server push, this client does not currently expose a push-specific API. Pushed resources are handled transparently.
  • Flow control: HTTP/2 stream flow control is handled by the underlying implementation and is not directly configurable through this package.

Contributing #

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

License #

This project is licensed under the MIT License - see the LICENSE file for details.

1
likes
130
points
27
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A production-ready HTTP/2 client package for Dart and Flutter with interceptors, retry, timeouts, cancellation, streaming, and connection pooling.

Repository (GitHub)
View/report issues
Contributing

Topics

#http #http2 #networking #client #flutter

License

MIT (license)

Dependencies

flutter, http, http2, http_parser, meta

More

Packages that depend on dart_http2_flutter_sse