quadrant_server 2.5.0
quadrant_server: ^2.5.0 copied to clipboard
A Dart-first, batteries-included HTTP server framework built on dart:io with zero external dependencies.
2.5.0 #
Security & Hardening #
Response.redirect()validation — Replacedassertwith runtimeArgumentErrorvalidation so invalid status codes throw in release mode.bodyParser()error propagation — Narrowed body parser try-catch toon FormatExceptionso low-level socket and connection errors are not swallowed. AddedmaxBodyBytesguard to defend against body-size DoS (returns413 Payload Too Large).cors()preflight caching — AddedmaxAgeparameter (defaults to 86,400s) emittingAccess-Control-Max-Ageheader on OPTIONS preflights to prevent repeated OPTIONS round-trips.docs.dartCDN asset integrity — Added Subresource Integrity (SRI) hashes (integrity&crossorigin) to Swagger UI CDN scripts and stylesheets.- Static files path traversal security — Path resolution in
staticFiles()now normalizes paths with URI semantics to prevent directory traversal escapes (..). docsLocalOnlyoption wiring — Fixed constructor parameter passthrough inQuadrantServersodocsLocalOnly: falseis honored when intentionally deployed behind a proxy.
Added #
helmet()middleware — Injects 8 essential HTTP security headers (X-Content-Type-Options,X-Frame-Options,Referrer-Policy, etc.).requestId()middleware — Generates and forwardsX-Request-ID(UUID v4) for distributed request tracing, exposed inreq.context['requestId'].rateLimit()middleware — Token-bucket sliding-window rate limiting per client IP or custom key withX-RateLimit-*andRetry-Afterheaders.bearerAuth()middleware — Extracts bearer token, validates against async callback, and injects principal intoreq.context['user'].ipFilter()middleware — Allow-list or deny-list client IP addresses supporting exact match and IPv4/IPv6 CIDR ranges.cookieParser()middleware & Response cookie helpers — ParsesCookieheader intoreq.context['cookies']; addedResponse.withCookie()andResponse.clearCookie()withSet-Cookiegeneration.formParser()middleware — Decodesapplication/x-www-form-urlencodedpayloads intoreq.bodywith configurable byte limits.multipartParser()middleware — Handles multipart file uploads and form fields, exposing typedUploadedFileinstances inreq.context['files'].compress()middleware — Response gzip compression usingdart:ioGZipCodecwithAccept-Encodingnegotiation.etag()middleware — Generates weak ETags and handlesIf-None-Matchconditional requests returning304 Not Modified.SseContext— Push-based Server-Sent Events (SSE) compatible with browserEventSourceand automatic keep-alive comment pings.StreamResponse— Response subclass for streaming raw bytes directly to client sockets without memory buffering.- Request validation DSL —
BodySchemaandField(string,int_,double_,bool_,list) withvalidate()middleware returning422 Unprocessable Entity. listenSecure()— First-class HTTPS/TLS binding withcertificateChainandprivateKeyfiles.- Health check endpoint — Optional built-in
/healthreporting uptime, request counts, and RSS memory usage. - WebSocket heartbeats — Configurable
heartbeatduration onWebSocketRoutefor automatic ping/pong keepalive. - Graceful in-flight request draining —
app.close()drains active requests before terminating.
Fixed #
- WebSocket upgrade error handling — Upgrades failing in
onErrornow return an HTTP error response to the client rather than leaving the connection hanging. - WebSocket middleware chain sentinel — Replaced dummy 200 response with
Response(204)sentinel. - Connection close resilience — Guarded
_writeResponseagainst writing to already-closed HTTP sockets. - Banner ASCII padding — Handled high route counts safely with
max(0, ...).
2.0.0 #
Added #
QuadrantRouter— prefix-mounted route grouping; organise large apps without repeating path prefixes. Supports router-level middlewares applied to all routes in the group.WebSocketGroup— built-in broadcast/room utility.add(),remove(),broadcast(),broadcastJson(),closeAll()with lazy cleanup of stale connections.WebSocketContext.isOpen/isClosed— check connection state before sending to avoid throwing on a closed socket.WebSocketContext.sendJson()— convenience JSON-encode-and-send shortcut.- Wildcard route segments —
Route.get(path: '/files/*', ...)captures the full remaining path asreq.params['*']. Response.redirect()— 301/302/307/308 redirect withlocationheader.Response.text()— plain-text response withcontent-type: text/plain.Response.html()— HTML response withcontent-type: text/html.Response.conflict()— 409 Conflict.Response.unprocessableEntity()— 422 Unprocessable Entity.- Typed query helpers on
Request—queryString(),queryInt(),queryDouble(),queryBool()with default value support. Request.bodyAsMap/bodyAsList— safe typed casts for the parsed body.app.close()— graceful shutdown that waits for in-flight requests.docsLocalOnlyparameter — control whether/quadrant_docsis loopback-only (default:true).logger()output sink — injectable log destination:logger(output: myLogger.info).cors()named parameters —methodsandallowedHeadersnow configurable.- Startup banner — printed when
docs: trueshowing host, route counts, and docs URL. - Unit test suite (
test/quadrant_server_test.dart) covering router, response factories, QuadrantRouter, and middleware. analysis_options.yamlwithlints/recommendedand strict type-checking.
Fixed #
RequestHolderhash collision — replacedhashCode-basedMapkey withExpando<Object>keyed on theHttpRequestobject itself. Eliminates body cross-contamination under concurrent load.bodyParsersilently dropped JSON array bodies —req.bodyis nowdynamicand accepts top-levelListpayloads.- Plain-string responses lacked
Content-Type— all string response bodies now carrycontent-type: text/plain; charset=utf-8. - WebSocket upgrade errors silently swallowed — upgrade failures now delegate to
onErroror print a warning. /quadrant_docsaccessible from external IPs — loopback guard is now active by default (docsLocalOnly: true).internalServerErrorleaked raw exception messages — default error handler now returns a generic safe message; users opt in to detail viaonError.
Improved #
cors()now setsVary: Originwhenorigin != '*'for correct CDN/proxy caching behaviour.WebSocketContext.send()is now a no-op when the socket is closed instead of throwing.
1.2.0 #
Added #
- WebSocket support — declarative
WebSocketRoutewith four named callbacks (onStart,onMessage,onClose,onError) WebSocketContext— per-connection context withsend(),close(), and access to the original upgrade request (params, query, headers)webSocketRoutesparameter onQuadrantServer— declare WS endpoints alongside HTTP routes- Middleware support for WebSocket — route-level middlewares run before upgrade; rejection prevents the upgrade entirely
- Path parameters on WS routes —
/ws/chat/:roomIdextracts params just like HTTP routes MatchedWebSocketsealed type — router distinguishes WS upgrades from HTTP requests at the type level- Docs: WebSocket section —
/quadrant_docsnow renders WS endpoints in a table below Swagger UI
Improved #
- Router —
match()acceptsisUpgradeRequestflag; WS upgrades never fall through to HTTP GET routes
1.1.1 #
Added #
/quadrant_docsendpoint — opt-in interactive API explorer powered by Swagger UI (CDN-loaded), enabled viadocs: true- 405 Method Not Allowed — router now distinguishes "path exists, wrong method" from "path not found", returns proper
Allowheader - HEAD request support — HEAD automatically falls back to GET handlers per HTTP spec
- Trailing slash normalization —
/users/and/usersnow match the same route - URI-decoded path params —
%20and other encoded characters decoded automatically Route.paramNamesgetter — extracts parameter names from path pattern for introspection
Improved #
- Router performance — routes grouped by HTTP method for faster lookup (O(n/m) vs O(n))
- Method normalization —
'get'and'GET'both resolve correctly
Security #
/quadrant_docslocked to loopback addresses only — external IPs always get 404- Docs route excluded from generated OpenAPI spec (no self-referencing)
1.0.0 #
- Initial release
- Core framework:
QuadrantServer,Request,Response,Route,Router - Middleware system with global and route-level support
- Built-in middlewares:
cors(),logger(),bodyParser() - Path parameter extraction (
:paramsegments) - Immutable response pattern with static constructors
- Zero external dependencies — built entirely on
dart:io