relic_headers 2.0.0-rc.5
relic_headers: ^2.0.0-rc.5 copied to clipboard
HTTP header names and stores shared by Relic adapters and other packages.
2.0.0-rc.5 #
- feat:
relic_nativetakes client certificates, encrypted and PKCS#1 private keys andv6Only, selects http/1.1 through ALPN, and an IPv6 listener takes IPv4 clients. The core packages are unchanged and released in step
2.0.0-rc.4 #
- fix:
relic_nativeruns on Windows. The core packages are unchanged and released in step
2.0.0-rc.3 #
- feat!: Replace the request stream with an exchange-based
Adapter- BREAKING:
Adapteris an interface withlisteners,capabilitiesandstart(sink). It hands overAdapterExchangeobjects withrespond,hijack,upgradeWebSocketandabort.Adapter.requests,Adapter.portandAdapterRequestare gone. The port is onlistenersand onRelicServer.port. The toolkit for writing an adapter is inpackage:relic_core/adapter.dart - BREAKING:
IOAdapter.port,requests,respond,hijack,connectandIOAdapterRequestare gone - BREAKING:
HttpResponseExtension.applyHeaders(headers, body)isapplyFraming(headers, framing) - BREAKING:
HijackCallbackreceives aStreamChannel<Uint8List> - BREAKING:
hijackthrowsHijackRefusedwhen the request body is chunked and not read to its end - BREAKING:
Request.protocolVersionisRequest.protocol, anHttpProtocol - BREAKING:
RelicWebSocketimplementers providedone, andtryCloseaccepts 1001 - BREAKING:
RelicServer.close()waits for the exchanges in flight and sends 1001 to every WebSocket.close(grace:)forces the close after a deadline.close(force: true)ends a graceful close that is still waiting.RelicApp.closetakes the sameforceandgrace - The adapter conformance suite in
test_utilsruns against any adapter
- BREAKING:
- feat!: Rebase
HeadersonHeaderStore- BREAKING:
HeadersandMutableHeadersare no longerMaps. Read withheaders[name],headers(accessor),getortryGet. Write withHeaders.build,transform, andMutableHeaders.set,add,removeandassign. TheHeaders.x[headers]form, theHeaderextension type andHeadersBaseare gone - BREAKING:
Headers.fromMapthrowsFormatExceptionfor a name that is not a token or a value with CR, LF or NUL - BREAKING:
HeaderAccessortakes aHeaderName.ReadOnlyAccessorhas an abstractdecode, and an accessor built from a function extendsFunctionAccessor - BREAKING:
Request.targetholds the path and query as aRequestTarget. An adapter answers 400 to a malformed one - New package
relic_headers, re-exported by relic_core:HeaderName,HeaderStore,MutableHeaderStore,MapHeaderStoreandByteHeaderStore.HeaderName.customtakes a lowercase token - The
Headers.xHeaderstring constants are deprecated in favour ofHeaderName.x
- BREAKING:
- feat!: The
Bodyframes every response- BREAKING:
Content-Length,Content-Typeand the chunked coding come from theBody. Setting them on the response headers has no effect BodyType.fromContentTypeand theHeaders.contentTypeaccessor are public- A HEAD request gets the same
Responseas GET. The adapter sends its length and type and no body
- BREAKING:
- fix!: A HEAD request to a path without a HEAD route runs its GET route (RFC 9110 9.1)
- BREAKING:
Router.lookup(Method.head, ...)returns the GET route of such a path, andMethodMiss.allowedlists HEAD beside GET. A GET handler that must not run on HEAD checksrequest.method - BREAKING:
RouterMatchhas no public constructor, andLookupResulttakesT extends Object - A
HijackorWebSocketUpgradeanswering such a HEAD gets 405.RouterMatch.headFallbackmarks the match andRouterMatch.registeredlists the path's own methods
- BREAKING:
- perf!: A sync response reaches the adapter without a Future, and a
ContextPropertytakes its slot once- BREAKING: The routing handler and
respondWithreturn a sync handler'sResponseas is. A handler that throws before its firstawaitthrows to the caller - BREAKING: A
ContextPropertyholds its slot for the life of the isolate. Create each one once, as a global
- BREAKING: The routing handler and
- perf: Typed headers decode from the wire bytes.
HeaderCodec.bytesandHeaderCodec.fieldstake a parser over bytes.HeaderCodec.singleand the text constructors stay - feat: New package
relic_native, a Relic adapter on a Zig HTTP server - feat:
FramedWebSocketframes RFC 6455 over anyStreamChannel<Uint8List>. The server upgrades through the adapter whencapabilities.webSocketis set and frames overhijackotherwise. With neither it answers 501 - feat: The
Bodyconstructor is public and takesbytes,streamoropen. A body built withopenopens only when read.read(maxLength:)rejects a known oversize body without reading it, and a rejected upload gets its answer instead of a reset - feat:
Body.bytes,Body.readAll(),Body.consumeandhttpDate() - feat:
httpDate,logMessageandlogRequestsread the time throughpackage:clock, sowithClockcontrols them - fix: A response head carries
Connection: closewhen the connection closes after it (RFC 9112 9.6) - fix: A 206 range response defaults its Content-Type to application/octet-stream
- fix: A
WebSocketUpgradeanswers a request other than GET with 405 - fix:
RelicApp.run(backtrack: false)androuteWith(backtrack: false)look routes up without backtracking - fix:
Request.forwardTokeeps thebacktrackanduseHostWhenRoutingoptions of the original
2.0.0-rc.2 #
- feat!: Add HTML form and multipart parsing (#380) - closes #379
- BREAKING: The minimum Dart SDK is 3.10.0. The Dart 3.9.0 compiler crashes in the FFI transform (dart-lang/sdk#61321)
- BREAKING:
BodyTypehas no const constructor. DropconstfromBodyType(...)calls - BREAKING: Extended
Content-Dispositionparameters such asfilename*are RFC 8187 ext-values and always encode as UTF-8.ContentDispositionParameterhas noencoding, and itslanguageis aLanguageTag. Parsing throwsFormatExceptionfor a charset other than UTF-8 and for a malformed ext-value - Add
req.urlEncodedForm(),req.multipartForm()andreq.formData(), which picks the parser from the request Content-Type - Add
req.multipart()to stream parts without aggregating them. Each part is aMultipartFieldPart,MultipartFilePartorMultipartOtherPart - Read fields through accessors such as
IntFormField('age')withform.fields.get,tryGetandgetAll, and files throughFormFile. A missing field throwsMissingFormFieldException, a value that does not decode throwsInvalidFormFieldException FormLimitscaps body size, part, field and file counts, and field and file sizes. Going over one throwsFormLimitExceededException, whoselimitnames theFormLimit- Uploads go to
MemoryUploadStorageby default.TempUploadStoragein relic_io writes each upload to its own directory. On POSIX systems the directory has mode 0700. On Windows it inherits the permissions of its parent, and the default parent, the user's temp directory, is private to that user. CallMultipartFormData.dispose()to delete them - Upload filenames are reduced to their basename, with control and bidi formatting characters removed.
.,..and an empty filename give a nullfilename.filename*is ignored, per RFC 7578 - The server answers an uncaught
FormExceptionwith itsstatusCode, which is 400, 413 or 415. When parsing fails it also sendsConnection: close, as the rest of the body may be unread BodyTypecarries Content-Type parameters such asboundaryinparameters.Body.fromDataandBody.fromDataStreamtake aparametersargument, and the dart:io adapter carries them through- Add a typed
ContentTypeHeader, read withheaders.contentType. Set Content-Type through the body
2.0.0-rc.1 #
- refactor!: Drop
NormalizedPathinterning (#375) - fixes #118, #342, closes #344- BREAKING:
NormalizedPath.internedis removed. Construction no longer caches, so there is no per-isolate cache to size, and no input that can thrash it - Add
Router.lookupUri(method, url), the entry point for request routing: it derives the path withNormalizedPath.fromUriand looks it up withlookupPath - Routing recovers most of the lost throughput by pre-computing repeated work and by undoing parameter bindings in place while backtracking, instead of copying the parameter map at every level
- BREAKING:
- fix!: Address security audit findings (#369)
- BREAKING: Cross-origin WebSocket upgrades are rejected with 403 by default; pass
WebSocketUpgrade(..., allowAnyOrigin: true)to accept them. Only the host is compared, since a TLS-terminating proxy changes scheme and port - BREAKING: Header names and values are validated on assignment. CR, LF, NUL, and invalid names now throw where every header write funnels, rather than reaching the wire
- BREAKING: Malformed auth headers and non-token MIME type parts are rejected where they were previously tolerated
- The request path is split into segments before percent-decoding, so an encoded separator (
%2F) can no longer introduce a path boundary that no upstream proxy saw;NormalizedPath.fromUrinames the safe conversion - The path is normalized before the routing host is prepended, so a path resolving upwards can no longer pop the host and select another virtual host
- Auth headers are parsed with the linear header scanner: one pass with no quadratic backtracking on a long value, and text between auth-params is rejected instead of skipped
Content-Dispositionparameter values andAuthenticationHeaderchallenge parts are escaped and validated, so a quote in a filename or realm can no longer end the value and graft on another parameter or challenge- The token grammar is enforced on MIME type parts, which
io.ContentTypetook separately and thus bypassed the CR/LF check applied to ordinary header values - Multi-range responses are streamed lazily and the range count is capped, so a short request can no longer ask for many times the file size
- Multipart boundaries are drawn from a cryptographic generator instead of ~20 bits from
Random - The adapter dispatch is awaited, so a response that fails to write yields a 500 instead of leaving the connection open and the client waiting
- Detached (hijacked and upgraded) connections are tracked by the adapter: counted by
connectionsInfo(), drained and sent a 1001 close on graceful shutdown, and destroyed onclose(force: true) use()on a catch-all route now applies to the prefix itself, so/apino longer bypasses the middleware that/api/keysruns- Add
Token68for the RFC 9110token68form, used when validating a single-tokenWWW-Authenticatechallenge - docs: The static file handler never filtered hidden files; the documentation no longer claims it does. Every file in the served directory is served, dot files included
- BREAKING: Cross-origin WebSocket upgrades are rejected with 403 by default; pass
2.0.0-beta.1 #
- feat: Add
CacheControlHeader.parseStrictfor validating own values (#370)- Throws a
FormatExceptionfor an unrecognized directive or a malformed delta-seconds value instead of ignoring it - Intended for values under the sender's own control, such as server configuration;
parseremains lenient per RFC 9111 5.2
- Throws a
2.0.0-beta.0 #
Overhaul of typed HTTP headers for RFC compliance, landing in concert with serverpod 4.0. Parsing of received (request) headers is lenient (except security-sensitive ones), while construction and serialization are always strict. Many typed headers changed shape and/or semantics, so this is a breaking release.
- refactor!: Cookie and Set-Cookie headers (#361)
- BREAKING:
SetCookienow represents a single cookie, andSetCookieHeaderis aList<SetCookie>collection (oneSet-Cookieline per cookie, RFC 6265 4.1). The previous single-cookieSetCookieHeader(name:, value:, ...)API is replaced bySetCookie(...); augment a response withmh.setCookie = (mh.setCookie ?? const SetCookieHeader.empty()).add(cookie) - The request
Cookieheader is parsed leniently: a malformed cookie is skipped and the header is rejected only when no cookie in it is usable - Add
CookieHeader.getCookies(name)returning every cookie with that name in order; byte-identical duplicates are preserved (not collapsed) Set-Cookiedecode is strict (it is produced by the server): a malformed attribute rejects the cookieDomainis normalized per RFC 6265 5.2.3 — the full leading-dot run is stripped and the host is lower-cased; an all-dotsDomainis rejectedMax-Ageis parsed as a strict decimal integer per RFC 6265 5.2.2; non-decimal values such as0xFFare rejected- Nameless
=valuesegments are dropped when parsing the requestCookieheader
- BREAKING:
- feat!: RFC compliant typed headers (#360) — fixes #113, #142
- Many typed headers changed their Dart interface and/or their parse/serialize semantics
- Parsing is lenient on receive; construction and serialization are strict
1.2.0 #
- feat: Make
NormalizedPathinterning cache configurable (#343)- Introduce abstract
Cache<K, V>interface - Add
NoCacheimplementation for high-cardinality workloads LruCachenow implementsCacheNormalizedPath.internedcan be swapped to anyCacheimplementation
- Introduce abstract
- feat: Add
staticChangeCountand VM service extension toDeveloperTools(#341) - feat: Add
DeveloperToolsclass toRelicApp(#340)
1.1.0 #
1.0.0 #
Relic is now considered stable and production-ready! 🎉
0.15.0 #
- feat: Add virtual host routing support (#322)
- Add
useHostWhenRoutingparameter torouteWith()middleware - Add
useHostWhenRoutingparameter toRelicApp - When enabled, routing uses
{host}{path}for route matching
- Add
- feat: Add
paths,toString(), andtoDot()to PathTrie for debugging (#320) - feat: Add
forceparameter to serverclose()method (#317) - refactor: Restructure into multi-package workspace with relic_core, relic_io, and relic packages
0.14.0 #
- feat!: Add optional consume flag to attach (#309)
- BREAKING:
attachnow throwsArgumentErrorwhen called with a tail path (/**), unlessconsume: trueand the attached trie/router has a single value at the root (isSingle) - BREAKING:
groupnow throwsArgumentErrorwhen called with a tail path - Add
consumeflag toattachthat resets the attached trie's root after attachment - Add
isSingleproperty to check if a trie/router has only a single root value/route - Allow
attach(..., consume: true)at tail paths (/**) when the attached trie/routerisSingle - Update
injectAtto useattach(..., consume: true), still enabling injection at tail paths for simpleHandlerObjects (those adding a single handler at the root ('/'))
- BREAKING:
0.11.0 #
- feat!: Add typed accessor for query and path parameters (#288)
- feat: Introduce ConnectionInfo (#273)
- feat: Add IPAddress class (independent of dart:io) (#272)
- refactor!: Simplify ContextProperty interface
- refactor!: Disallow url rewriting in Request.copyWith (#289)
- refactor!: Use named ctors on all header classes (#283)
- refactor!: Avoid repeating ctor name on parameter (#282)
- fix!: Remove stale arguments from Request and Response class (#274)
- fix: Don't overwrite Content-Length when set as header (#291)
0.10.0 #
- refactor!: The Great Simplification (#264)
- Merged
RequestContextintoRequestby moving the token property HandledContextrenamed toResult- Merge
ResponseContextintoResponse(now implementsResult) - Removed
respond(),hijack(), andconnect()methods - handlers can construct and returnResults directly - Updated handler signatures:
Handler = FutureOr<Result> Function(Request req) - Consolidated context classes into single file using part files
- Updated all examples and tests
- Significant simplification of the class hierarchy by eliminating the:
Context,RequestContext,RespondableContext,HijackableContext,ConnectableContext, andResponseContextinterfaces- Renamed
ConnectionContexttoWebSocketUpgrade(now extendsResult) - Renamed
HijackedContexttoHijack(now extendsResult)
- Merged
0.9.2 #
0.9.1 #
- feat: Add async RelicServer.connectionsInfo() method (#255)
- Adds a
connectionsInfo()method toRelicServerthat returns the current number of active, closing, and idle connections. - A new
ConnectionsInfotypedef is introduced as a record type withactive,closing, andidlefields.
- Adds a
0.9.0 #
- refactor!: Context renaming (#251)
Renames core context types for improved clarity and consistency:
NewContext→RequestContextConnectContext→ConnectionContextHijackContext→HijackedContext- Base class
RequestContextrenamed toContext
- chore!: Upgrade sdk to ^3.7.0 (#239)
- feat: Introduce MultiIsolateRelicServer (#216)
Implements multi-isolate support for
RelicServerto enable concurrent request handling across multiple CPU cores. AddsnoOfIsolatesoptional named parameter toRelicServerconstructor, andRelicApp.run. - fix: Mask sensitive credentials in authorization header toString() methods (#238)
0.8.0 #
- feat!: RelicApp now supports hot-reload (#226)
- feat: Introduce RelicApp (#212)
- feat: Introduce MiddlewareObject (#211)
- feat: Introduce HandlerObject (#210)
- feat(router): Add fallback property for unmatched routes (#196)
- feat(router): Add asHandler extension to use Router
- feat: Adds support for cache busting (#192)
- feat: Support mime-type inference (#206)
- feat: Add NewContext.withRequest convenience method (#198)
- fix: Build platform correct path when serving files. (#207)
0.7.0 #
0.6.0 #
- feat!: Custom CacheControl header per file (#167)
- feat!: Distinguish between path and method miss in router lookups (#179)
- feat!: Remove automatic X-Powered-By header (#169)
- feat!: Remove unused strict flag (#181)
- feat(router): Router.group (#157)
- feat: Add operator== and hashCode on all header classes (#155)
- fix: HEAD request should match GET except for body, including Content-Length (#180)
- fix: Host header is not a Uri. Introduce an RFC3986 compliant HostHeader class (#142)
- fix: Rip out Router.staticCache (#172)
- fix: Set content length on body, not headers (#161)
0.5.0 #
0.4.1 #
0.4.0 #
- fix: Missing convenience getters and setter for Headers.xForwardedFor (#108)
- refactor!: Get rid of old context map on Message (Request/Response) (#105)
- feat: X-Forwarded-For typed header (#107)
- feat: Add ContextProperty class (wraps Expando) (#94)
- feat: Typed forwarded header (#101)
- build(deps): Bump cli_tools from 0.5.1 to 0.6.0 (#100)
- refactor!: Replace DuplexStreamChannel with RelicWebSocket (#91)
- test: Add headers constants test and rename file (#89)
- build(deps): Bump lints from 5.1.1 to 6.0.0 (#92)
- fix: Fix request/response header sets (#90)
- docs: Update router docs (#88)
- docs: Update router entry comment (#87)
- feat: WebSocket support (#84)
- feat: Store benchmark results with git notes (#79)
- feat: The routeWith middleware builder function is now generic. (#78)
- feat(Handler)!: Signature changed (#76)
- feat: Add Router.isEmpty getter (#75)
- docs: Add CONTRIBUTING.md and CODE_OF_CONDUCT.md (#69)
- feat: PathTrie wildcard and tail matching support (#70)
- feat: Router middleware (#68)
- feat!: Router now supports verb directly (#65)
- docs: Add badges for codecov, etc. (#67)
- feat: Add addOrUpdate, update, and remove to PathTrie (#63)
- feat: Support Router.attach (#62)
- feat: Allow a trie to be attached as a subtrie to another (#61)
- feat: Router class (#52)
- refactor!: Decouple from dart:io and avoid using exceptions for control-flow (#48)
- chore: Add serverpod lints (#46)
- feat!: Replace HeaderDecode with HeaderCodec to allow customization on encoding as well (#43)
- fix: Ensure cache is updated immediately (#42)
- feat!: Support typed access to custom headers (#38)
- ci: Hoist continue-on-error to job (#37)
- ci: Add test coverage (#36)
- fix!: RelicServer cannot reliably know the Uri to use to hit it (#32)
- chore: Automate publishing to pub.dev when semver tag is created (#31)
- chore: Add pull request title validation. (#30)
- refactor!: Get rid of RelicAddress. (#29)
0.3.0 #
- feat: Implements lazy loading when parsing headers to avoid unnecessary validation.
- feat: Makes address strongly typed and adds
RelicAddresstype. - fix: Resolves issue with
Content-Lengthheader conflicting withTransfer-Encoding: chunked.
0.2.0 #
- First tech preview.
0.1.0 #
- Initial version.