rhttp

repository·main·Indexed 42 days ago

https://github.com/tienisto/rhttp

A high-performance HTTP client for Flutter developers using a Rust backend (via the reqwest crate). It provides support for HTTP/2, HTTP/3, and connection pooling, offering improved performance over the standard dart:io client. Features include RhttpCompatibleClient for http package compatibility, advanced TLS settings with certificate pinning, proxy configuration, custom DNS resolution, and an interceptor system for request and response modification.

Tokens
19.5K
Snippets
70
Records
86
Agent score
51%

What's inside rhttp

  1. Implement Interceptors to modify requests and responses

    main

    Interceptors allow you to observe, modify, or handle errors for every request and response. You can implement the Interceptor class or use the simpler SimpleInterceptor.

    Interceptor Methods

    • beforeRequest: Modify the HttpRequest before it is sent.
    • afterResponse: Process the HttpResponse after it is received.
    • onError: Handle exceptions occurring during the request lifecycle.

    Termination Methods

    When returning an InterceptorResult, use:

    • Interceptor.next(): Continue to the next interceptor.
    • Interceptor.stop(): Halt the interceptor chain.
    • Interceptor.resolve(): Resolve the request immediately with a provided response.
    • throw RhttpException: Throw an exception (preserves stack trace).
    class TestInterceptor extends Interceptor {
      @override
      Future<InterceptorResult<HttpRequest>> beforeRequest(
        HttpRequest request,
      ) async {
        return Interceptor.next(request.addHeader(
          name: HttpHeaderName.accept,
          value: 'application/json',
        ));
      }
    
      @override
      Future<InterceptorResult<HttpResponse>> afterResponse(
        HttpResponse response,
      ) async {
        return Interceptor.next();
      }
    
      @override
      Future<InterceptorResult<RhttpException>> onError(
        RhttpException exception,
      ) async {
        return Interceptor.next();
      }
    }
  2. Customize iOS launch screen assets

    main

    To change the launch screen image for the iOS version of your application, you can either replace the image files directly in the ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory or use Xcode for a more visual approach.

    Using Xcode:

    1. Open your Flutter project's iOS workspace using open ios/Runner.xcworkspace.
    2. In the Xcode Project Navigator, navigate to Runner/Assets.xcassets.
    3. Drag and drop your desired images into the asset catalog to replace the existing ones.
    open ios/Runner.xcworkspace
  3. Use rhttp_example as a Flutter starting point

    main
    The rhttp_example project serves as a demonstration of how to implement and use the rhttp plugin within a Flutter application. It is designed to be used as a template or reference for developers integrating rhttp into their own Flutter projects.
  4. Optimize performance with connection reuse

    main

    To improve performance and avoid the overhead of creating new connections for every request, create a single RhttpClient instance and reuse it for multiple requests. This allows the client to reuse existing connections to the same servers.

    When you are finished with the client, call dispose() to release resources.

    final client = await RhttpClient.create();
    
    await client.get('https://example.com');
    
    // When finished
    client.dispose();
  5. Initialize rhttp

    main

    Before making any HTTP requests, you must initialize the rhttp engine in your application's entry point (usually main()).

    import 'package:rhttp/rhttp.dart';
    
    void main() async {
      await Rhttp.init(); // Required initialization
      runApp(MyApp());
    }
  6. Generate Dart code after modifying the Rust code

    main

    If you modify the Rust source code in rhttp, you must regenerate the Dart bindings to ensure the two layers remain in sync. This process requires the flutter_rust_bridge_codegen tool.

    1. Install the codegen tool

    Install the generator via cargo:

    cargo install flutter_rust_bridge_codegen

    2. Run the generation command

    You must set the RUSTFLAGS environment variable to include --cfg reqwest_unstable during the generation process to ensure compatibility with the underlying reqwest configuration used by the project.

    ### Unix
    RUSTFLAGS='--cfg reqwest_unstable' flutter_rust_bridge_codegen generate
    
    ### Windows (Powershell)
    $env:RUSTFLAGS='--cfg reqwest_unstable'; flutter_rust_bridge_codegen generate
  7. Make basic HTTP requests with rhttp

    main

    You can perform simple HTTP requests using the static methods on the Rhttp class. The most common pattern is using Rhttp.get() or Rhttp.post() and specifying the expected response type (e.g., getText()).

    import 'package:rhttp/rhttp.dart';
    
    void main() async {
      await Rhttp.init();
      
      // Make a GET request and expect a text response
      HttpTextResponse response = await Rhttp.get('https://example.com');
      
      int statusCode = response.statusCode;
      String body = response.body;
    }
  8. Install rhttp

    main

    To use rhttp in your Flutter project, follow these steps:

    1. Install Rust: Ensure you have Rust 1.80.0 or later installed via rustup.
    2. Android Setup: Install the Android Command-line tools and ensure you have the latest NDK installed.
    3. Add Dependency: Add rhttp to your pubspec.yaml file.

    Note: The APK size will increase by approximately 2 MB on arm64, or 6 MB if compiled for all architectures (x64, arm32, arm64). Web is not currently supported.

    dependencies:
      rhttp: <version>
  9. Run the rhttp benchmark

    main

    To run the performance benchmarks for rhttp, you must first start the Node.js mock server and then run the Flutter benchmark application in release mode.

    1. Start the server: Navigate to the nodejs directory and run the server script.
    2. Run the benchmark: Navigate to the benchmark directory and execute the Flutter app using the --release flag to ensure accurate performance measurements.
    # Start server
    cd nodejs
    node server.js
    
    # Start benchmark
    cd benchmark
    flutter run --release
  10. Use Rhttp with the Dio package

    main

    You can integrate rhttp into your dio workflows by using the dio_compatibility_layer package. This involves creating an RhttpCompatibleClient and assigning it to the dio.httpClientAdapter using a ConversionLayerAdapter.

    Future<Dio> createDioClient() async {
      final dio = Dio();
      final compatibleClient = await RhttpCompatibleClient.create(); // or createSync()
      dio.httpClientAdapter = ConversionLayerAdapter(compatibleClient);
      return dio;
    }
  11. Handle HTTP responses with HttpResponse and its subtypes

    main

    The HttpResponse class is a sealed base class representing an HTTP response. Depending on the response body type, you will interact with one of three specific subtypes:

    1. HttpTextResponse: Used when the response body is a string. It includes a bodyToJson getter to conveniently parse the body as JSON using jsonDecode.
    2. HttpBytesResponse: Used when the response body is raw binary data (Uint8List).
    3. HttpStreamResponse: Used for streaming responses where the body is provided as a Stream<Uint8List>.

    All response types provide access to common metadata:

    • remoteIp: The remote IP address of the server (may be null if HTTP/3 is used).
    • request: The HttpRequest that triggered this response.
    • version: An HttpVersion enum indicating the protocol used.
    • statusCode: The integer HTTP status code.
    • headers: A List<(String, String)> of response headers.
    • headerMap: A Map<String, String> view of the headers.
    • headerMapList: A Map<String, List<String>> view of the headers, respecting multiple values for the same key.
    // Example of handling different response types
    void handleResponse(HttpResponse response) {
      if (response is HttpTextResponse) {
        print('Text body: ${response.body}');
        final json = response.bodyToJson;
        print('JSON data: $json');
      } else if (response is HttpBytesResponse) {
        print('Bytes length: ${response.body.length}');
      } else if (response is HttpStreamResponse) {
        response.body.listen((chunk) => print('Received chunk of size ${chunk.length}'));
      }
    
      print('Status: ${response.statusCode}');
      print('Headers: ${response.headerMap}');
    }
  12. Control automatic error throwing on status codes

    main

    By default, rhttp throws an RhttpStatusCodeException if a response has a 4xx or 5xx status code. You can disable this behavior by setting throwOnStatusCode: false in ClientSettings.

    await Rhttp.get(
      'https://example.com',
      settings: const ClientSettings(
        throwOnStatusCode: false,
      ),
    );