Skip to content

collection of Dart and Flutter libraries for HTTP request handling

License

Notifications You must be signed in to change notification settings

ralph-bergmann/HttpTools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

50 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

HttpTools

A comprehensive collection of Dart and Flutter packages for HTTP request handling, providing intercepting, logging, and caching capabilities to enhance your network operations.

πŸ“¦ Packages Overview

Package Version Description
http_client_interceptor pub package Core HTTP interceptor framework
http_client_logger pub package Comprehensive HTTP request/response logging
http_client_cache pub package HTTP caching with disk/memory storage

πŸš€ Quick Start

Installation

Add any or all packages to your pubspec.yaml:

dependencies:
  http_client_interceptor: ^1.0.1
  http_client_logger: ^1.1.2
  http_client_cache: ^1.0.3

Basic Usage - All Features Combined

import 'dart:async';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:http_client_interceptor/http_client_interceptor.dart';
import 'package:http_client_logger/http_client_logger.dart';
import 'package:http_client_cache/http_client_cache.dart';
import 'package:logging/logging.dart' hide Level;

Future<void> main() async {
  // Enable logging to see what's happening
  Logger.root.onRecord.listen((record) {
    print('[${record.level.name}] ${record.message}');
  });

  // Initialize cache
  final cache = HttpCache();
  await cache.initInMemory(); // or initLocal(Directory) for persistent cache

  // Configure HTTP client with all interceptors
  await http.runWithClient(
    () async {
      final client = http.Client();
      
      // Make requests - they'll be logged and cached automatically
      final response1 = await client.get(
        Uri.parse('https://jsonplaceholder.typicode.com/posts/1')
      );
      print('First request: ${response1.statusCode}');
      
      // Second identical request will be served from cache
      final response2 = await client.get(
        Uri.parse('https://jsonplaceholder.typicode.com/posts/1')
      );
      print('Second request: ${response2.statusCode}');
      
      client.close();
    },
    () => HttpClientProxy(
      interceptors: [
        HttpLogger(level: Level.headers), // Log requests/responses
        cache, // Cache responses
      ],
    ),
  );
}

πŸ“‹ Detailed Package Documentation

http_client_interceptor

Core HTTP interceptor framework - Foundation for all other packages

Key Features:

  • βœ… Intercept HTTP requests and responses
  • βœ… Modify headers, body, and URLs
  • βœ… Handle errors and retries
  • βœ… Chain multiple interceptors
  • βœ… Compatible with all popular HTTP packages

Simple Custom Interceptor:

import 'package:http_client_interceptor/http_client_interceptor.dart';

class AuthInterceptor extends HttpInterceptor {
  final String apiKey;
  AuthInterceptor(this.apiKey);

  @override
  FutureOr<OnRequest> onRequest(BaseRequest request) {
    // Add API key to all requests
    request.headers['Authorization'] = 'Bearer $apiKey';
    return OnRequest.next(request);
  }

  @override
  FutureOr<OnResponse> onResponse(StreamedResponse response) {
    print('Response: ${response.statusCode} for ${response.request?.url}');
    return OnResponse.next(response);
  }

  @override
  FutureOr<OnError> onError(BaseRequest request, Object error, StackTrace? stackTrace) {
    print('Request failed: ${request.url} - Error: $error');
    return OnError.next(request, error, stackTrace);
  }
}

http_client_logger

Comprehensive HTTP logging - See exactly what your app is sending and receiving

Key Features:

  • βœ… Unique Request IDs - Track concurrent requests easily
  • βœ… Multiple Log Levels - basic, headers, body
  • βœ… Smart Binary Detection - Clean logs without garbage data
  • βœ… Production Ready - Configurable for development vs production
  • βœ… Human Readable - Clean, structured log format

Log Output Examples:

[a1b2c3d4] --> GET https://api.example.com/users/123
[a1b2c3d4]     authorization: Bearer ***
[a1b2c3d4]     content-type: application/json
[a1b2c3d4] --> END GET
[a1b2c3d4] <-- 200 OK (145ms)
[a1b2c3d4]     content-type: application/json
[a1b2c3d4]     {"id": 123, "name": "John Doe"}
[a1b2c3d4] <-- END

Usage:

import 'package:http_client_logger/http_client_logger.dart';
import 'package:logging/logging.dart' hide Level;

// Set up logging
Logger.root.onRecord.listen((record) => print(record.message));

// Configure logger
HttpClientProxy(
  interceptors: [
    HttpLogger(
      level: Level.body, // basic | headers | body
      logBodyContentTypes: {'application/json'}, // Only log JSON as text
    ),
  ],
)

http_client_cache

HTTP caching with disk/memory storage - Improve performance and reduce network usage

Key Features:

  • βœ… RFC 7234 Compliant - Standard HTTP caching behavior
  • βœ… Memory & Disk Storage - Choose what fits your needs
  • βœ… Cache-Control Support - Respects server cache directives
  • βœ… Stale-While-Revalidate - Serve stale content while updating
  • βœ… Stale-If-Error - Fallback to cache when network fails
  • βœ… Automatic Cleanup - LRU eviction and size limits
  • βœ… Private Content Filtering - Secure handling of sensitive data

Cache Behavior Examples:

Cache miss for https://api.example.com/posts/1      # First request
Cache hit for https://api.example.com/posts/1       # Served from cache
Cache entry expired for https://api.example.com/posts/1  # Needs refresh
Serving stale content due to network error           # Stale-if-error fallback

Usage:

import 'package:http_client_cache/http_client_cache.dart';

Future<void> setupCache() async {
  final cache = HttpCache();
  
  // Option 1: Memory cache (faster, doesn't persist)
  await cache.initInMemory(maxCacheSize: 50 * 1024 * 1024); // 50MB
  
  // Option 2: Disk cache (persists between app restarts)
  // final cacheDir = Directory('cache');
  // await cache.initLocal(cacheDir, maxCacheSize: 100 * 1024 * 1024);

  return HttpClientProxy(interceptors: [cache]);
}

πŸ”§ Advanced Usage Patterns

Conditional Interceptors

HttpClientProxy(
  interceptors: [
    if (kDebugMode) HttpLogger(level: Level.body), // Only log in debug
    AuthInterceptor(apiKey),
    cache,
  ],
)

Custom Cache Control

class CacheControlInterceptor extends HttpInterceptor {
  @override
  FutureOr<OnResponse> onResponse(StreamedResponse response) {
    final headers = Map<String, String>.from(response.headers);
    
    // Cache API responses with resilience features
    if (response.request?.url.path.startsWith('/api/') ?? false) {
      headers['cache-control'] = 
        'max-age=300, stale-while-revalidate=60, stale-if-error=3600';
      // Cache for 5 min, serve stale for 1 min while revalidating,
      // serve stale for 1 hour if network fails
    }
    
    return OnResponse.next(response.copyWith(headers: headers));
  }
}

πŸ”— Framework Compatibility

Works seamlessly with popular HTTP packages:

  • βœ… http - A composable, multi-platform, Future-based API for HTTP requests.
  • βœ… chopper - An http client generator using source_gen, inspired by Retrofit
  • βœ… retrofit - An dio client generator using source_gen and inspired by Chopper and Retrofit
  • βœ… dio - A powerful HTTP networking package

Note: dio and retrofit (which uses dio) require the dio_compatibility_layer package to work with the standard http package that these interceptors depend on.

dependencies:
  http: ^1.2.0
  dio_compatibility_layer: ^3.0.0  # Required for dio/retrofit compatibility

πŸ“Š Performance Considerations

Development vs Production:

HttpClientProxy(
  interceptors: [
    // Production: Only basic logging
    if (kReleaseMode) HttpLogger(level: Level.basic),
    
    // Development: Full logging with bodies  
    if (kDebugMode) HttpLogger(level: Level.body),
    
    // Always cache for better performance
    cache,
  ],
)

Memory Management:

  • Cache automatically manages size with LRU eviction
  • Use cache.clearCache() when memory is low
  • Use cache.deletePrivateContent() when user logs out

πŸ› Debugging Tips

Enable verbose logging:

Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
  print('[${record.time}] ${record.level.name}: ${record.message}');
  if (record.error != null) print('Error: ${record.error}');
  if (record.stackTrace != null) print('Stack: ${record.stackTrace}');
});

Check cache status:

// Look for Cache-Status headers in responses
final cacheStatus = response.headers['cache-status'];
print('Cache status: $cacheStatus');

🀝 Contributing

Found a bug or want to contribute? Check our GitHub repository for:

πŸ“„ License

This project is licensed under the MIT License - see individual package licenses for details.


Need help? Check out the individual package documentation or open an issue on GitHub!

About

collection of Dart and Flutter libraries for HTTP request handling

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages