Shelf makes it easy to create and compose web servers and parts of web servers. How?
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())
.addHandler(_echoRequest);
io.serve(handler, 'localhost', 8080).then((server) {
print('Serving at http://${server.address.host}:${server.port}');
});
}
shelf.Response _echoRequest(shelf.Request request) {
return new shelf.Response.ok('Request for "${request.url}"');
}
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));
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.
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.
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.
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
:
multipart/byteranges
.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.
Update createMiddleware
arguments to use FutureOr
.
Updated minimum Dart SDK to 1.24.0
, which added FutureOr
.
Improved formatting of the logRequests
default logger.
shelf_io
server now adds a shelf.io.connection_info
field to
Request.context
, which provides access to the underlying
HttpConnectionInfo
object.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
.
securityContext
parameter to self_io.serve()
.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
.
Content-Length
header.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.
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.
logRequests()
.http_parser
3.0.0.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.
http_parser
2.0.0.Content-Type
header didn't interact properly with the
encoding
parameter for new Request()
and new Response()
if it wasn't
lowercase.shelf_io
adapter detects an error, print the request context as
well as the error itself.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
.
Map
instances and related dependencies.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.http_parser
1.0.0.body
named argument to change
method on Request
and Response
.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.
logRequests
outputs a better message a request has a query string.null
responses.shelf_io
now takes a "shelf.io.buffer_output"
Response.context
parameter
that controls HttpResponse.bufferOutput
.
Fixed spelling errors in README and code comments.
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.
Request
to support the body
model from Response
.Fixed createMiddleware
to only catch errors if errorHandler
is provided.
Updated handleRequest
in shelf_io
to more gracefully handle errors when
parsing HttpRequest
.
Request.change
to include the original onHijack
callback if one
exists.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 www.dartdocs.org
.
collection
package.stack_trace
.The shelf_io
adapter now sends the Date
HTTP header by default.
Fixed logic for setting Server header in shelf_io
.
Request.change
: scriptName
and url
.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.
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.
context
map to Request
and Response
for passing data among
handlers and middleware.scheduled_test
development dependency up to v0.12.0Stack
to Pipeline
.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
.
Removed old testing infrastructure.
Updated documentation address.
http_parser
package.mime
package.string_scanner
package.pubspec
details for move to Dart SDK.Response
int get contentLength
DateTime get expires
DateTime get lastModified
Request
contentLength
is now read from headers
. The constructor
argument has been removed.Stream<List<int>> body
constructor argument.Stream<List<int>> read()
and
Future<String> readAsString([Encoding encoding])
DateTime get ifModifiedSince
String get mimeType
Encoding get encoding
Shelf
prefix from all classes.Response
has drastically different constructors.Response
now accepts a body of either String
or
Stream<List<int>>
.Response
now exposes encoding
and mimeType
.example/example.dart
// 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()
.addMiddleware(shelf.logRequests())
.addHandler(_echoRequest);
var server = await io.serve(handler, 'localhost', 8080);
// Enable content compression
server.autoCompress = true;
print('Serving at http://${server.address.host}:${server.port}');
}
shelf.Response _echoRequest(shelf.Request request) {
return new shelf.Response.ok('Request for "${request.url}"');
}
Add this to your package's pubspec.yaml file:
dependencies:
shelf: ^0.7.3+1
You can install packages from the command line:
with pub:
$ pub get
Alternatively, your editor might support pub get
.
Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:shelf/shelf.dart';
Version | Uploaded | Documentation | Archive |
---|---|---|---|
0.7.4 | Dec 17, 2018 |
|
|
0.7.3+3 | Jul 16, 2018 |
|
|
0.7.3+2 | Jul 10, 2018 |
|
|
0.7.3+1 | May 22, 2018 |
|
|
0.7.3 | May 2, 2018 |
|
|
0.7.2 | Jan 4, 2018 |
|
|
0.7.1 | Oct 26, 2017 |
|
|
0.7.0 | Aug 29, 2017 |
|
|
0.6.8 | Jun 23, 2017 |
|
|
0.6.7+2 | Dec 6, 2016 |
|
|
Popularity:
Describes how popular the package is relative to other packages.
[more]
|
96
|
Health:
Code health derived from static analysis.
[more]
|
--
|
Maintenance:
Reflects how tidy and up-to-date the package is.
[more]
|
--
|
Overall:
Weighted score of the above.
[more]
|
48
|
The package version is not analyzed, because it does not support Dart 2. Until this is resolved, the package will receive a health and maintenance score of 0.
Support Dart 2 in pubspec.yaml
.
The SDK constraint in pubspec.yaml
doesn't allow the Dart 2.0.0 release. For information about upgrading it to be Dart 2 compatible, please see https://www.dartlang.org/dart-2#migration.
Make sure dartdoc
successfully runs on your package's source files. (-10 points)
Dependencies were not resolved.
Package | Constraint | Resolved | Available |
---|---|---|---|
Direct dependencies | |||
Dart SDK | >=2.0.0-dev.55.0 <2.0.0 |