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.
3 files changed
tree: 24090335fb3d6ac1566952b8fec8b4ea2f310eeb
  1. .github/
  2. pkgs/
  3. tool/
  4. vendor/
  5. .gitignore
  6. .gitmodules
  7. analysis_options.yaml
  8. CONTRIBUTING.md
  9. LICENSE
  10. mono_repo.yaml
  11. README.md
README.md

Build Status

About Shelf

Shelf makes it easy to create and compose web servers and parts of web servers. How?

  • Expose a small set of simple types.
  • Map server logic into a simple function: a single argument for the request, the response is the return value.
  • Trivially mix and match synchronous and asynchronous processing.
  • Flexibility to return a simple string or a byte stream with the same model.

It was inspired by Connect for NodeJS and Rack for Ruby.

See the package:shelf readme for more information.

Packages

PackageDescriptionIssuesVersion
shelfA model for web server middleware that encourages composition and easy reuse.issuespub package
shelf_packages_handlerA shelf handler for serving a packages/ directory.issuespub package
shelf_proxyA shelf handler for proxying HTTP requests to another server.issuespub package
shelf_routerA convenient request router for the shelf web-framework, with support for URL-parameters, nested routers and routers generated from source annotations.issuespub package
shelf_router_generatorA package:build-compatible builder for generating request routers for the shelf web-framework based on source annotations.issuespub package
shelf_staticStatic file server support for the shelf package and ecosystem.issuespub package
shelf_test_handlerA Shelf handler that makes it easy to test HTTP interactions.issuespub package
shelf_web_socketA shelf handler that wires up a listener for every connection.issuespub package
_shelf_complianceHTTP/1.1 compliance and hardening tests.issues(not published)

Publishing automation

For information about our publishing automation and release process, see https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.