shelf 0.7.5

  • Readme
  • Changelog
  • Example
  • Installing
  • 95

Web Server Middleware for Dart #

Build Status Coverage Status

Introduction #

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.

Example #

See example/example.dart

import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;

void main() {
  var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())

  io.serve(handler, 'localhost', 8080).then((server) {
    print('Serving at http://${}:${server.port}');

shelf.Response _echoRequest(shelf.Request request) {
  return new shelf.Response.ok('Request for "${request.url}"');

Handlers and Middleware #

A handler is any function that handles a shelf.Request and returns a shelf.Response. It can either handle the request itself--for example, a static file server that looks up the requested URI on the filesystem--or it can do some processing and forward it to another handler--for example, a logger that prints information about requests and responses to the command line.

The latter kind of handler is called "middleware", since it sits in the middle of the server stack. Middleware can be thought of as a function that takes a handler and wraps it in another handler to provide additional functionality. A Shelf application is usually composed of many layers of middleware with one or more handlers at the very center; the shelf.Pipeline class makes this sort of application easy to construct.

Some middleware can also take multiple handlers and call one or more of them for each request. For example, a routing middleware might choose which handler to call based on the request's URI or HTTP method, while a cascading middleware might call each one in sequence until one returns a successful response.

Middleware that routes requests between handlers should be sure to update each request's handlerPath and url. This allows inner handlers to know where they are in the application so they can do their own routing correctly. This can be easily accomplished using Request.change():

// In an imaginary routing middleware...
var component = request.url.pathComponents.first;
var handler = _handlers[component];
if (handler == null) return new Response.notFound(null);

// Create a new request just like this one but with whatever URL comes after
// [component] instead.
return handler(request.change(script: component));

Adapters #

An adapter is any code that creates shelf.Request objects, passes them to a handler, and deals with the resulting shelf.Response. For the most part, adapters forward requests from and responses to an underlying HTTP server; shelf_io.serve is this sort of adapter. An adapter might also synthesize HTTP requests within the browser using window.location and window.history, or it might pipe requests directly from an HTTP client to a Shelf handler.

API Requirements #

An adapter must handle all errors from the handler, including the handler returning a null response. It should print each error to the console if possible, then act as though the handler returned a 500 response. The adapter may include body data for the 500 response, but this body data must not include information about the error that occurred. This ensures that unexpected errors don't result in exposing internal information in production by default; if the user wants to return detailed error descriptions, they should explicitly include middleware to do so.

An adapter should ensure that asynchronous errors thrown by the handler don't cause the application to crash, even if they aren't reported by the future chain. Specifically, these errors shouldn't be passed to the root zone's error handler; however, if the adapter is run within another error zone, it should allow these errors to be passed to that zone. The following function can be used to capture only errors that would otherwise be top-leveled:

/// Run [callback] and capture any errors that would otherwise be top-leveled.
/// If [this] is called in a non-root error zone, it will just run [callback]
/// and return the result. Otherwise, it will capture any errors using
/// [runZoned] and pass them to [onError].
catchTopLevelErrors(callback(), void onError(error, StackTrace stackTrace)) {
  if (Zone.current.inSameErrorZone(Zone.ROOT)) {
    return runZoned(callback, onError: onError);
  } else {
    return callback();

An adapter that knows its own URL should provide an implementation of the Server interface.

Request Requirements #

When implementing an adapter, some rules must be followed. The adapter must not pass the url or handlerPath parameters to new shelf.Request; it should only pass requestedUri. If it passes the context parameter, all keys must begin with the adapter's package name followed by a period. If multiple headers with the same name are received, the adapter must collapse them into a single header separated by commas as per RFC 2616 section 4.2.

If the underlying request uses a chunked transfer coding, the adapter must decode the body before passing it to new shelf.Request and should remove the Transfer-Encoding header. This ensures that message bodies are chunked if and only if the headers declare that they are.

Response Requirements #

An adapter must not add or modify any entity headers for a response.

If none of the following conditions are true, the adapter must apply chunked transfer coding to a response's body and set its Transfer-Encoding header to chunked:

  • The status code is less than 200, or equal to 204 or 304.
  • A Content-Length header is provided.
  • The Content-Type header indicates the MIME type multipart/byteranges.
  • The Transfer-Encoding header is set to anything other than identity.

Adapters may find the [addChunkedEncoding()][addChunkedEncoding] middleware useful for implementing this behavior, if the underlying server doesn't implement it manually.

When responding to a HEAD request, the adapter must not emit an entity body. Otherwise, it shouldn't modify the entity body in any way.

An adapter should include information about itself in the Server header of the response by default. If the handler returns a response with the Server header set, that must take precedence over the adapter's default header.

An adapter should include the Date header with the time the handler returns a response. If the handler returns a response with the Date header set, that must take precedence.

Inspiration #

0.7.5 #

  • Return the correct HTTP status code for badly formatted requests.

0.7.4+1 #

  • Allow stream_channel version 2.x

0.7.4 #

  • Allow passing shared parameter to underlying HttpServer.bind calls via shelf_io.serve.
  • Correctly pass encoding in Response constructors forbidden, notFound, and internalServerError.
  • Update to point to latest docs on

0.7.3+3 #

  • Set max SDK version to <3.0.0, and adjust other dependencies.

0.7.3+2 #

  • Fix constant evaluation analyzer error in shelf_unmodifiable_map.dart.

  • Update usage of HTTP constants from the Dart SDK. Now require 2.0.0-dev.61.

0.7.3+1 #

  • Updated SDK version to 2.0.0-dev.55.0.

0.7.3 #

  • Fix some Dart 2 runtime errors.

0.7.2 #

  • Update createMiddleware arguments to use FutureOr.

    • Note: this change is not breaking for the runtime behavior, but it might cause new errors during static analysis due the the type changes.
  • Updated minimum Dart SDK to 1.24.0, which added FutureOr.

  • Improved formatting of the logRequests default logger.

0.7.1 #

  • The shelf_io server now adds a field to Request.context, which provides access to the underlying HttpConnectionInfo object.

0.7.0 #

  • Give a return type to the Handler typedef. This may cause static warnings where there previously were none, but all handlers should have already been returning a Response or Future<Response>.

  • Remove HijackCallback and OnHijackCallback typedefs.

  • Breaking: Change type of onHijack in the Request constructor to take an argument of StreamChannel.

0.6.8 #

  • Add a securityContext parameter to self_io.serve().

0.6.7+2 #

  • Go back to auto-generating a Content-Length header when the length is known ahead-of-time and the user hasn't explicitly specified a Transfer-Encoding: chunked.

  • Clarify adapter requirements around transfer codings.

  • Make shelf_io consistent with the clarified adapter requirements. In particular, it removes the Transfer-Encoding header from chunked requests, and it doesn't apply additional chunking to responses that already declare Transfer-Encoding: chunked.

0.6.7+1 #

  • Never auto-generate a Content-Length header.

0.6.7 #

  • Add Request.isEmpty and Response.isEmpty getters which indicate whether a message has an empty body.

  • Don't automatically generate Content-Length headers on messages where they may not be allowed.

  • User-specified Content-Length headers now always take precedence over automatically-generated headers.

0.6.6 #

  • Allow List<int>s to be passed as request or response bodies.

  • Requests and responses now automatically set Content-Length headers when the body length is known ahead of time.

  • Work around sdk#27660 by manually setting HttpResponse.chunkedTransferEncoding to false for requests known to have no content.

0.6.5+3 #

  • Improve the documentation of logRequests().

0.6.5+2 #

  • Support http_parser 3.0.0.

0.6.5+1 #

  • Fix all strong-mode warnings and errors.

0.6.5 #

  • Request.hijack() now takes a callback that accepts a single StreamChannel argument rather than separate Stream and StreamSink arguments. The old callback signature is still supported, but should be considered deprecated.

  • The HijackCallback and OnHijackCallback typedefs are deprecated.

0.6.4+3 #

  • Support http_parser 2.0.0.

0.6.4+2 #

  • Fix a bug where the Content-Type header didn't interact properly with the encoding parameter for new Request() and new Response() if it wasn't lowercase.

0.6.4+1 #

  • When the shelf_io adapter detects an error, print the request context as well as the error itself.

0.6.4 #

  • Add a Server interface representing an adapter that knows its own URL.

  • Add a ServerHandler class that exposes a Server backed by a Handler.

  • Add an IOServer class that implements Server in terms of dart:io's HttpServer.

0.6.3+1 #

  • Cleaned up handling of certain Map instances and related dependencies.

0.6.3 #

  • Messages returned by Request.change() and Response.change() are marked read whenever the original message is read, and vice-versa. This means that it's possible to read a message on which change() has been called and to call change() on a message more than once, as long as read() is called on only one of those messages.

0.6.2+1 #

  • Support http_parser 1.0.0.

0.6.2 #

  • Added a body named argument to change method on Request and Response.

0.6.1+3 #

  • Updated minimum SDK to 1.9.0.

  • Allow an empty url parameter to be passed in to new Request(). This fits the stated semantics of the class, and should not have been forbidden.

0.6.1+2 #

  • logRequests outputs a better message a request has a query string.

0.6.1+1 #

  • Don't throw a bogus exception for null responses.

0.6.1 #

  • shelf_io now takes a "" Response.context parameter that controls HttpResponse.bufferOutput.

  • Fixed spelling errors in README and code comments.

0.6.0 #

Breaking change: The semantics of Request.scriptName and Request.url have been overhauled, and the former has been renamed to Request.handlerPath. handlerPath is now the root-relative URL path to the current handler, while url's path is the relative path from the current handler to the requested. The new semantics are easier to describe and to understand.

Practically speaking, the main difference is that the / at the beginning of url's path has been moved to the end of handlerPath. This makes url's path easier to parse using the path package.

Request.change's handling of handlerPath and url has also changed. Instead of taking both parameters separately and requiring that the user manually maintain all the associated guarantees, it now takes a single path parameter. This parameter is the relative path from the current handlerPath to the next one, and sets both handlerPath and url on the new Request accordingly.

0.5.7 #

  • Updated Request to support the body model from Response.

0.5.6 #

  • Fixed createMiddleware to only catch errors if errorHandler is provided.

  • Updated handleRequest in shelf_io to more gracefully handle errors when parsing HttpRequest.

0.5.5+1 #

  • Updated Request.change to include the original onHijack callback if one exists.

0.5.5 #

  • Added default body text for Response.forbidden and Response.notFound if null is provided.

  • Clarified documentation on a number of Response constructors.

  • Updated README links to point to latest docs on

0.5.4+3 #

  • Widen the version constraint on the collection package.

0.5.4+2 #

  • Updated headers map to use a more efficient case-insensitive backing store.

0.5.4+1 #

  • Widen the version constraint for stack_trace.

0.5.4 #

  • The shelf_io adapter now sends the Date HTTP header by default.

  • Fixed logic for setting Server header in shelf_io.

0.5.3 #

  • Add new named parameters to Request.change: scriptName and url.

0.5.2 #

  • Add a Cascade helper that runs handlers in sequence until one returns a response that's neither a 404 nor a 405.

  • Add a Request.change method that copies a request with new header values.

  • Add a Request.hijack method that allows handlers to gain access to the underlying HTTP socket.

0.5.1+1 #

  • Capture all asynchronous errors thrown by handlers if they would otherwise be top-leveled.

  • Add more detail to the README about handlers, middleware, and the rules for implementing an adapter.

0.5.1 #

  • Add a context map to Request and Response for passing data among handlers and middleware.

0.5.0+1 #

  • Allow scheduled_test development dependency up to v0.12.0

0.5.0 #

  • Renamed Stack to Pipeline.

0.4.0 #

  • Access to headers for Request and Response is now case-insensitive.

  • The constructor for Request has been simplified.

  • Request now exposes url which replaces pathInfo, queryString, and pathSegments.

0.3.0+9 #

  • Removed old testing infrastructure.

  • Updated documentation address.

0.3.0+8 #

  • Added a dependency on the http_parser package.

0.3.0+7 #

  • Removed unused dependency on the mime package.

0.3.0+6 #

  • Added a dependency on the string_scanner package.

0.3.0+5 #

  • Updated pubspec details for move to Dart SDK.

0.3.0 2014-03-25 #

  • Response
    • NEW! int get contentLength
    • NEW! DateTime get expires
    • NEW! DateTime get lastModified
  • Request
    • BREAKING contentLength is now read from headers. The constructor argument has been removed.
    • NEW! supports an optional Stream<List<int>> body constructor argument.
    • NEW! Stream<List<int>> read() and Future<String> readAsString([Encoding encoding])
    • NEW! DateTime get ifModifiedSince
    • NEW! String get mimeType
    • NEW! Encoding get encoding

0.2.0 2014-03-06 #

  • BREAKING Removed Shelf prefix from all classes.
  • BREAKING Response has drastically different constructors.
  • NEW! Response now accepts a body of either String or Stream<List<int>>.
  • NEW! Response now exposes encoding and mimeType.

0.1.0 2014-03-02 #

  • First reviewed release


// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;

main() async {
  var handler = const shelf.Pipeline()

  var server = await io.serve(handler, 'localhost', 8080);

  // Enable content compression
  server.autoCompress = true;

  print('Serving at http://${}:${server.port}');

shelf.Response _echoRequest(shelf.Request request) {
  return shelf.Response.ok('Request for "${request.url}"');

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:

  shelf: ^0.7.5

2. Install it

You can install packages from the command line:

with pub:

$ pub get

with Flutter:

$ flutter pub get

Alternatively, your editor might support pub get or flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:

import 'package:shelf/shelf.dart';
Describes how popular the package is relative to other packages. [more]
Code health derived from static analysis. [more]
Reflects how tidy and up-to-date the package is. [more]
Weighted score of the above. [more]
Learn more about scoring.

We analyzed this package on Jun 5, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.2
  • pana: 0.13.8-dev

Health suggestions

Fix lib/src/request.dart. (-0.50 points)

Analysis of lib/src/request.dart reported 1 hint:

line 171 col 7: DO use curly braces for all flow control structures.

Maintenance suggestions

Package is getting outdated. (-17.81 points)

The package was last published 61 weeks ago.


Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0 <3.0.0
async ^2.0.7 2.4.1
collection ^1.5.0 1.14.12
http_parser ^3.1.0 3.1.4
path ^1.0.0 1.7.0
stack_trace ^1.0.0 1.9.3
stream_channel >=1.0.0 <3.0.0 2.0.0
Transitive dependencies
charcode 1.1.3
meta 1.1.8
source_span 1.7.0
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.1.6
Dev dependencies
http ^0.12.0
pedantic ^1.3.0
test ^1.3.4