go_http 0.2.3
go_http: ^0.2.3 copied to clipboard
Reliable HTTP client for Dart — built for control, cancellation and consistency.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.2.3 - 2026-07-13 #
Added #
- Rich Response API:
response.numBytesDownloaded— body byte count after decompression;response.nextRequest— computed redirect request (whenfollowRedirects: false);response.links— parsedLink:header as aMap<String, Map<String, String?>>keyed byrel;response.bytes— body asStream<List<int>>;response.httpVersion— protocol version string;response.reasonPhrase— alias forstatusMessage;response.defaultEncoding— function override for charset detection (e.g. chardet integration). - Limits pool config:
Limitsclass withmaxConnections(100),maxKeepaliveConnections(20),keepaliveExpiry(5s),copyWith,toString. Wired intoClientConfig.limitsandIoTransport(maxConnectionsPerHost,idleTimeout). - Cookies RFC 6265 jar:
PersistentCookieclass with domain/path/secure/httponly/expires metadata. FullSet-Cookieheader parser supportingDomain,Path,Secure,HttpOnly,Max-Age,Expires. RFC 6265 §5.1.3 domain-matching, §5.1.4 path-matching, §5.2.6 Secure-only enforcement, expiry with auto-removal. - StatusCode enum expansion: Added
resetContent(205),multipleChoices(300). - Obfuscated
toString:Request.toString()— one-line with method + URI, body truncated to 100 chars or shown asN bytes/<stream>(no sensitive data leak).Response.toString()— one-line"200 OK GET /path"usingStatusCode.phrase, no headers or body. - Incremental text/line decoder:
textStreamDecoder()transformsStream<List<int>>→Stream<String>with correct chunk-boundary handling for multi-byte encodings.lineStreamDecoder()splits a string stream by newlines. Both areStreamTransformer-based. - Streaming request bodies:
Stream<List<int>>accepted asRequest.bodyand piped viaHttpClientRequest.addStream(). Transports that don't support streaming will throw an appropriate error. - Top-level convenience API (
lib/src/api.dart):get(),post(),put(),delete(),patch(),head(),options()— standalone functions backed by a cached defaultGoHttpClient. Importgo_httpand callget('https://...')directly without creating a client. - Upload progress (
onSendProgress):ProgressCallbackon every request method.IoTransportcounts bytes as they are written — streaming bodies are wrapped with a counting transform (Stream.map), non-streaming bodies report 100% at once. Wired throughTransport.send(),MockTransport, andFakeTransport. Retry-Afterheader respect:GoHttpClient._sendWithRetryoverrides exponential backoff on429 Too Many Requests/503 Service Unavailablewhen the server sends aRetry-Afterheader (seconds or HTTP-date). Capped at 60s to prevent runaway waits.Request.extensions:Map<String, Object?>onRequestfor transport-specific metadata without interface changes. Preserved throughcopyWith.peekLengthhelper (lib/src/body_encoding.dart): returns byte length for common body types without materialising the entire body —null→0,String→UTF-8 bytes,List<int>→list length,Stream→null.poolTimeoutinLimits: new field (default 10s) for the maximum time to wait for a connection from the pool. Wired intocopyWith,toString, andfromJson.- Per-host certificate pinning (
lib/src/pinning.dart):PinnedCertificatesconfig class —Map<String, List<String>>mapping hostnames to base64-encoded SHA-256 fingerprints. Wired intoClientConfigandIoTransport._buildClientviabadCertificateCallback. - Makefile targets:
fix(dart fix —dry-run),fix-all(apply fixes + format),test(dart test),check-all(format → analyze → test). HappyEyeballDialer(lib/src/dialer.dart): RFC 8305 dual-stack TCP dialer — resolves both IPv6 and IPv4 addresses, starts IPv6 immediately with a 300ms head start before racing IPv4, first successful connection wins and losers are destroyed. Self-contained implementation usingInternetAddress.lookupdirectly.
Changed #
- WASM-ready:
web_transport.dartmigrated fromdart:htmltopackage:web+dart:js_interop. Unblocks compilation to WASM for Flutter Web.RequestandHeadersnaming conflicts resolved viahidein the import. MemoryCookieStorerewritten: RFC 6265 Set-Cookie parsing, per-cookie metadata, domain/path/secure filtering, cookie expiry, automatic dedup on re-set, default-path inference.getCookies()returns all matching cookies sorted by path specificity.
Tests #
- Added comprehensive test suite: Limits (8 tests), RFC 6265 cookies (16 tests), Request/Response toString (7 tests), text/line stream decoders (5 tests), streaming request body, expanded StatusCode enum.
- Added tests for
peekLength(5 tests),Request.extensions(2 tests), certificate pinning (PinnedCertificatesequality/hash),Retry-Afterparsing,nextRequest/elapsed/numBytesDownloadedon Response, andLimits.poolTimeout. - Added
HappyEyeballDialerintegration tests (3 tests): IPv4 fallback via localhost, DNS resolution failure, connection refused.
0.2.2 - 2026-07-13 #
Added #
- Per-request delay:
RequestOptions.delaypauses before sending the request (rate-limiting / polite crawling). - Body byte limits:
RequestOptions.maxBytesToRead/maxBytesToSavethrowMaxBytesReadErrorwhen the decoded response body exceeds the configured threshold. - Drain-and-close for keep-alive:
IoTransportdrains remaining response bytes before throwing read errors, returning the connection to the pool. RateLimiter— token-bucket rate limiter with.take()→Future<void>(configurable tokens/sec and burst).ResizeableSemaphore— adaptive semaphore with runtimeresize(), FIFO fair ordering, non-negativeavailable.HostCircuitBreaker— per-host circuit breaker (closed/open/half-open), configurable failure threshold and cooldown,CircuitOpenErrorfor fail-fast.RateLimitPolicy.global()/.perHost()— policy wrapper overRateLimiterfor shared or per-host rate limiting.ResponseEnrichment,ResponseTiming,TlsInfo— enrichment data model attached toResponse.enrichment.RequestTrace— per-request phase timestamps (wroteRequest, gotFirstResponseByte, responseDone) populated byIoTransport.RetryPolicy.scanning()/.single()— retry presets for probing vs critical workloads.- Idempotency extended:
PUTandDELETEare now retried by default (matchinghttpx/retryablehttp). - Gzip-fallback:
IoTransportreturns raw body instead of crashing when a server sendsContent-Encoding: gzipon an uncompressed body. - Auto-scheme fallback:
IoTransport.tryHttpOnHttpsError(defaultfalse) retries a failed HTTPS connection once with HTTP. - Graceful two-step shutdown:
GoHttpClient.shutdown()(soft — stops new requests, waits for in-flight) anddispose()(hard — closes transport immediately).ClientShutdownErrorthrown on new requests after shutdown. Dialerabstract class +SocketDialer— pluggable socket-level dialer interface (integration withIoTransportpending transport rewrite).- IP-override:
RequestOptions.dialAddress— connect to a specific IP:port while keeping the original hostname in theHostheader. - Dialed-IP exposure:
Response.remoteAddressandResponseEnrichment.remoteAddress— IP address of the remote server that handled the request. TlsInfoenrichment:subject,issuer,fingerprintSha1,validFrom,validTofields; populated byIoTransportfromHttpClientResponse.certificate.GoHttpClient.validate()— returnsList<ValidationError>with config issues (timeout <= 0, redirects < 0, etc.). Non-throwing, suitable for UI/config check.AggregateError— multierr equivalent collecting multiple errors into one (for close-multiple-resources patterns).ValidationErrorclass — field + message pair for config validation results.- Two-level config:
ClientConfig(transport/timeout/TLS/proxy/cookies) +ExecutorConfig(interceptors/metrics/hooks/enrichers), bothvalidate(), both optional and backward-compatible inGoHttpClientconstructor. ProgressReporter— per-batch progress with RPS, percentage, elapsed, and ETA (ProgressReporter.summary).ResponseEnricherSPI —abstract class ResponseEnricherregistered viaExecutorConfig.enrichers, called per response in enrichment pipeline. Extra data lands inResponseEnrichment.extra.ResponseFilterinterface +StatusCodeFilter,RegexFilter,Match(OR/AND short-circuit) — ready for inclusion/exclusion in batch pipelines.BearerAuth— statelessBearerAuth('token')strategy forAuthInterceptor. LegacytokenProvider/tokenRefreshermarked@Deprecated.stderrLog— log helper writing todart:iostderr (fallback toprinton Web).LoggingInterceptornow defaults tostderrLog, keeping stdout clean for JSONL/result output.ClientConfig.fromJson()+mergeEnv()— load config from JSON map, override fromGO_HTTP_*env vars.PortSpec.parse('http:8080,https:443')— nmap-style scheme→port mapping.Headers.parse('Content-Type: application/json')factory — one or moreKey: Valuelines.ClientConfig.copyWith()— produce a modified copy preserving other fields.- Two-phase file writing —
ResultSink.jsonl()/.csv()write to a temp file (O_EXCL+ incrementing suffix) and atomically rename onclose(), preventing parallel-writer corruption. ClientConfig.minTlsVersion/maxTlsVersion— reserved config surface for TLS version constraints (forward-compat, SDK pending).ResultCallbacktypedef +List<ResultCallback>? onResultsparam onBatchExecutor.run()— chain multiple callbacks alongside legacyonResult.
0.2.1 - 2026-07-13 #
Added #
- Public
Headerscollection: case-insensitive, multi-value (Set-Cookie,Vary,Link),getAll/multiItems, sensitive-header masking intoString. UseClientDefaultsentinel for three-statetimeout/followRedirects(useClientDefault→ client default,null→ disable, value → override).buildRequest()/send()/request()split —buildRequestis the merge boundary;senddispatches a preparedRequestwithout re-merging (escape hatch:client.send(client.buildRequest(req))).baseUrloption onGoHttpClient(relative request URIs resolve against it).- Structured exception hierarchy:
HttpError→RequestError→TransportError(TimeoutError/NetworkError/ProtocolError/ProxyError/UnsupportedProtocol) → typed leaves (ConnectTimeoutError,ReadTimeoutError,WriteTimeoutError,ConnectError, …);HttpStatusErrordeliberately does not extendRequestErrorsocatch (RequestError)never swallows a 4xx/5xx. - Event hooks:
EventHookswith multicastrequest/responsecallback lists, hot-swappable at runtime viaGoHttpClient.eventHooks. Url(immutable wrapper overUri: lowercased scheme/host, default-port collapsed tonull,copyWith, RFC 3986join, password-maskedtoString) andQueryParams(immutable multi-value query:add/set/remove/mergereturn new instances,getList, JSON-style bool).RequestOptions.queryParametersis now aQueryParams.Multipartencoder (zero-dependencymultipart/form-data):MultipartFile/MultipartField,render()/stream()/encodedLength, random 16-byte boundary, content-type guessing from extension, HTML5 attribute escaping.GoHttpClientauto-serializes aMultipartrequest body and setsContent-Type.- Proxy support:
Proxy(http/https/socks5/direct, parse + maskedtoString),URLPattern(wildcard*host,all://scheme,specificity),ProxyMounts(most-specific route wins,null= explicit direct), andbuildSecurityContexthonoring explicitverify/SSL_CERT_FILEenv.GoHttpClienttakesproxyMounts,trustEnv,verify; native transport wiresfindProxy,badCertificateCallback, and a per-clientSecurityContext. Result<T>(never-throw batch outcome:Result.ok/Result.fail) and pluggableResultSinkSPI withjsonl/csvfactories; CSV output sanitizes formula-injection cells (=,+,-,@).- Auth SPI:
Authstrategy (BasicAuth,FunctionAuth) applied on the request path, plusDigestAuth(RFC 2617/7616, MD5/SHA-256, qop, cnonce, nonce-count) driven by the interceptor's 401WWW-Authenticatechallenge via the existing retry path.AuthInterceptornow takes anauthstrategy and still supports the legacy BearertokenProvider/tokenRefresherflow. - Body encoding:
encodeRequestdispatch ladder —json→application/json,Map→application/x-www-form-urlencoded, rawString/bytes/Multipartpass through.post/put/patch/deleteroute theirdata/jsonthrough it. - Content decoder registry:
ContentDecoderSPI +contentDecodersmap (gzip/deflate always, brotli/zstd optional viaregisterBrotli/registerZstd),decodeContentEncodingfor stacked encodings, deflate-ambiguity fix (zlib → raw fallback).IoTransportnow always setsautoUncompress=falseand decodes manually. Defaultaccept-encodingnarrowed togzip.
Changed #
- Updated
README.md— complete rewrite reflecting all current APIs (proxy, auth, decoders, batch, multipart, structured timeouts, headers, mock transport, error hierarchy, etc.) - Deleted
example/main.dart(duplicate ofsimple_get.dart); addedexample/post_json.dartdemonstratingjson:parameter - Updated
example/download_progress.dartto useonProgresscallback - Updated
Makefile: replacedexample-maintarget withexample-post-json
0.2.0 - 2026-07-12 #
Fixed #
- Timeouts now actually work:
connectTimeout/sendTimeout/receiveTimeoutare enforced per phase on native (dart:io) and as an overall request timeout on web. Exceeding a timeout throwsTimeoutError(previously these options were silently ignored). - Web binary responses:
WebTransportnow readsresponseType = 'arraybuffer', so binary data (images, gzipped bodies, files) is no longer corrupted. - Multi-value
Set-Cookie: cookies withExpires/multiple headers are parsed correctly (no longer split on the comma insideExpires). TheDomainattribute is honored for subdomain matching. - Error interceptors now run exactly once per attempt (previously twice for HTTP errors).
- Auth refresh retry:
AuthInterceptornow retries the request once after refreshing the token on401(previously refreshed but never retried). - Dead redirect code removed;
followRedirects/maxRedirectsare now wired into the IO transport. - Header keys normalized to lowercase in both transports for case-insensitive lookups.
IoTransportno longer mutates a caller-suppliedHttpClient.
Added #
RetrySignalcontrol-flow type returned by interceptors to request a bounded retry.Decoder<T>support (decoder:parameter) on all request methods for real type-safe decoding.ProgressCallback(onReceiveProgress:) on all request methods, with byte progress on native and web.- Convenience methods:
put,delete,patch,head,options. - Injectable
Loggersink forLoggingInterceptorandConsoleMetricsSink. maxAuthRetriesoption onGoHttpClient.queryParametersfromRequestOptionsare now applied to the request URI.
Changed #
- Retry backoff switched to stateless exponential backoff with equal jitter (AWS-recommended; safe to share a single policy across concurrent requests). Previously claimed "decorrelated jitter" but implemented a different, stateful algorithm.
CancellationTokenevent delivery is now synchronous (instant listener notification).
Tests #
- Added comprehensive test suite: retry policy, cookie store, cancellation, decoders, and client flow (retries, auth refresh, cancellation, error-interceptor cardinality, decoders, query params, convenience methods) via a fake transport.
0.1.1 - 2025-11-13 #
Added #
- Basic example (
example/main.dart) for pub.dev package validation - Makefile with development commands for code analysis, formatting, running examples, and version management
0.1.0 - 2025-01-13 #
Added #
- Basic HTTP client with GET and POST methods
- Request cancellation support via
CancellationToken - Retry policy with decorrelated jitter backoff algorithm
- Interceptors for request/response/error handling
- Built-in
LoggingInterceptorandAuthInterceptor - Cookie store with
MemoryCookieStoreimplementation - Metrics collection with
ConsoleMetricsSink - Cross-platform support (Dart CLI, Flutter, Web)
- IO transport for native platforms
- Web transport for browser platforms
- Redirect policy support
- Type-safe response handling with generics
- Comprehensive error types (
NetworkError,HttpResponseError,TimeoutError,CancellationError)
Security #
- Retries only for idempotent HTTP methods (GET, HEAD, OPTIONS)
- Sensitive headers are masked in logs
- TLS verification enabled by default