Resolve shelf.io.connection_info lazily (#542) `shelf_io` resolves the `shelf.io.connection_info` context value lazily instead of while constructing every `Request`. `dart:io` does not cache `HttpRequest.connectionInfo`. Each call constructs a fresh `_HttpConnectionInfo` by reading `remoteAddress` and `remotePort` off the socket (`_HttpConnectionInfo.create` in `sdk/lib/_http/http_impl.dart`), so building it eagerly spent two `getpeername` calls on every request — including the majority that never read the value. #37, which added the field, asked for it for request logging, which most handlers do not do per request. ## Effect 200 requests, a fresh connection each (`Connection: close`), counted with `strace -f -e trace=getpeername,getsockname`. The handler either ignores the context value or reads `remotePort` from it. | build | getpeername | per request | getsockname | | :---------------- | ----------: | ----------: | ----------: | | `master`, ignored | 402 | **2.000** | 7 total | | `master`, read | 402 | **2.000** | 7 total | | this PR, ignored | **0** | **0.000** | 7 total | | this PR, read | 400 | **2.000** | 7 total | A handler that ignores the value pays nothing; one that reads it pays exactly what it paid before. On a small-response workload where `shelf_io` issues ~14.8 syscalls/request, this removes ~13% of them for handlers that do not use the value. `getsockname` is 7 calls total in every arm — fixed startup cost, not per-request. `socket.port` is served from cache and never reaches the kernel. ## Implementation The context value is a private `_LazyHttpConnectionInfo implements HttpConnectionInfo` holding the `HttpRequest`. It resolves all three fields together on first access and caches the result. `HttpConnectionInfo` is an interface with exactly three getters (`remoteAddress`, `remotePort`, `localPort`), and the SDK's implementation is private, so no caller can be depending on a concrete type. If the connection is already gone, `HttpRequest.connectionInfo` is `null` and the `!` throws. That error now surfaces when a handler reads a field rather than while the adapter builds the `Request`. ## Tests Existing coverage passes unchanged, including the `isA<HttpConnectionInfo>()` assertion. One test added, covering `remotePort` (not previously asserted), stability across repeated reads, and survival through `Request.change()` — since middleware copies `context`, that is where a lazy value would break. `dart analyze` clean, `dart format` clean, 232 tests pass.
Shelf makes it easy to create and compose web servers and parts of web servers. How?
It was inspired by Connect for NodeJS and Rack for Ruby.
See the package:shelf readme for more information.
| Package | Description | Issues | Version |
|---|---|---|---|
| shelf | A model for web server middleware that encourages composition and easy reuse. | ||
| shelf_packages_handler | A shelf handler for serving a packages/ directory. | ||
| shelf_proxy | A shelf handler for proxying HTTP requests to another server. | ||
| shelf_router | A convenient request router for the shelf web-framework, with support for URL-parameters, nested routers and routers generated from source annotations. | ||
| shelf_router_generator | A package:build-compatible builder for generating request routers for the shelf web-framework based on source annotations. | ||
| shelf_static | Static file server support for the shelf package and ecosystem. | ||
| shelf_test_handler | A Shelf handler that makes it easy to test HTTP interactions. | ||
| shelf_web_socket | A shelf handler that wires up a listener for every connection. | ||
| _shelf_compliance | HTTP/1.1 compliance and hardening tests. | (not published) |
For information about our publishing automation and release process, see https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.