shelf
v1.4.2A model for web server middleware that encourages composition and easy reuse.
Package archive: https://pubdev.letsnova.ru/api/archives/shelf/1.4.2.tar.gz
dart pub add shelfReadme
Web Server Middleware for Dart
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.
See the
Dart HTTP server documentation
for more information. You may also want to look at
package:shelf_router and
package:shelf_static as examples of
packages that build on and extend package:shelf.
Example
See example/example.dart
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
void main() async {
var handler =
const Pipeline().addMiddleware(logRequests()).addHandler(_echoRequest);
var server = await shelf_io.serve(handler, 'localhost', 8080);
// Enable content compression
server.autoCompress = true;
print('Serving at http://${server.address.host}:${server.port}');
}
Response _echoRequest(Request request) =>
Response.ok('Request for "${request.url}"');
Handlers and Middleware
A Handler is any function that handles a Request and returns a 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 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.pathSegments.first;
var handler = _handlers[component];
if (handler == null) return Response.notFound(null);
// Create a new request just like this one but with whatever URL comes after
// [component] instead.
return handler(request.change(path: component));
Adapters
An adapter is any code that creates Request objects, passes them to a
handler, and deals with the resulting 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].
void catchTopLevelErrors(
void Function() callback,
void Function(Object error, StackTrace stackTrace) onError,
) {
if (Zone.current.inSameErrorZone(Zone.root)) {
return runZonedGuarded(callback, 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 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 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() 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
Changelog
1.4.2
Headers: added thefromEntriesconstructor.- Require
http_parser: ^4.1.0 - Require Dart
^3.4.0.
1.4.1
- Added package topics to the pubspec file.
1.4.0
- Add Response.unauthorized() constructor
- Add
poweredByHeaderargument toserve,serveRequests, andhandleRequest. - Require Dart >= 2.17
1.3.2
shelf_io.dart- Started setting
X-Powered-Byresponse header toDart with package:shelf. - Stopped setting
Serverheader (with valuedart:io with Shelf).
- Started setting
1.3.1
- Update the pubspec
repositoryfield.
1.3.0
- Add Response.badRequest() constructor
- Deprecate
ServerHandler. - Require Dart >= 2.16
1.2.0
- Added
MiddlewareExtensionswhich providesaddMiddlewareandaddHandlertoMiddlewareinstances. Provides easier access to the composition capabilities offered byPipeline.
1.1.4
- Documentation improvements.
1.1.3
- Automatically remove
content-lengthheader from aResponse.notModified. Restores some of the safety around malformed requests that was removed in1.1.2where we started allowingcontent-lengthfor some responses without bodies. - Documentation cleanup.
1.1.2
- Allow an explicit non-zero content-length header if the body is empty. (Needed to correctly support HEAD requests).
1.1.1
- Avoid wrapping response bodies that already contained
List<int>orStream<List<int>>in aCastList/CastStreamto improve performance.
1.1.0
- Change
Request.hijackreturn type fromvoidtoNever. This may cause analysis hints in packages using this method, but is not actually breaking for apps.
1.0.0
- Stable null safety release.
1.0.0-nullsafety.0
- Update to support Dart null-safety.
- Updated the
changefunction onRequestandResponseto clear existing values incontextif the providedcontextvalue hasnullvalues.
0.7.9
- Allow a
handlerPathto lead up to a double//in a URI.
0.7.8
- Handle malformed URLs (400 instead of 500).
0.7.7
- Fix internal error in
Requestwhen.change()matches the full path.
0.7.6
- Supports multiple header values in
RequestandResponse:headersAllfield contains all the header valuesheadersfield contains the same values as previously (appending values with,)headersparameter in the constructor and in the.change()method accepts bothStringandList<String>values.
0.7.5
- Return the correct HTTP status code for badly formatted requests.
0.7.4+1
- Allow
stream_channelversion 2.x
0.7.4
- Allow passing
sharedparameter to underlyingHttpServer.bindcalls viashelf_io.serve. - Correctly pass
encodinginResponseconstructorsforbidden,notFound, andinternalServerError. - Update
README.mdto point to latest docs.
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
createMiddlewarearguments to useFutureOr.- 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 addedFutureOr. -
Improved formatting of the
logRequestsdefault logger.
0.7.1
- The
shelf_ioserver now adds ashelf.io.connection_infofield toRequest.context, which provides access to the underlyingHttpConnectionInfoobject.
0.7.0
-
Give a return type to the
Handlertypedef. This may cause static warnings where there previously were none, but all handlers should have already been returning aResponseorFuture<Response>. -
Remove
HijackCallbackandOnHijackCallbacktypedefs. -
Breaking: Change type of
onHijackin theRequestconstructor to take an argument ofStreamChannel.
0.6.8
- Add a
securityContextparameter toself_io.serve().
0.6.7+2
-
Go back to auto-generating a
Content-Lengthheader when the length is known ahead-of-time and the user hasn't explicitly specified aTransfer-Encoding: chunked. -
Clarify adapter requirements around transfer codings.
-
Make
shelf_ioconsistent with the clarified adapter requirements. In particular, it removes theTransfer-Encodingheader from chunked requests, and it doesn't apply additional chunking to responses that already declareTransfer-Encoding: chunked.
0.6.7+1
- Never auto-generate a
Content-Lengthheader.
0.6.7
-
Add
Request.isEmptyandResponse.isEmptygetters which indicate whether a message has an empty body. -
Don't automatically generate
Content-Lengthheaders on messages where they may not be allowed. -
User-specified
Content-Lengthheaders 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-Lengthheaders when the body length is known ahead of time. -
Work around sdk#27660 by manually setting
HttpResponse.chunkedTransferEncodingtofalsefor requests known to have no content.
0.6.5+3
- Improve the documentation of
logRequests().
0.6.5+2
- Support
http_parser3.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 singleStreamChannelargument rather than separateStreamandStreamSinkarguments. The old callback signature is still supported, but should be considered deprecated. -
The
HijackCallbackandOnHijackCallbacktypedefs are deprecated.
0.6.4+3
- Support
http_parser2.0.0.
0.6.4+2
- Fix a bug where the
Content-Typeheader didn't interact properly with theencodingparameter fornew Request()andnew Response()if it wasn't lowercase.
0.6.4+1
- When the
shelf_ioadapter detects an error, print the request context as well as the error itself.
0.6.4
-
Add a
Serverinterface representing an adapter that knows its own URL. -
Add a
ServerHandlerclass that exposes aServerbacked by aHandler. -
Add an
IOServerclass that implementsServerin terms ofdart:io'sHttpServer.
0.6.3+1
- Cleaned up handling of certain
Mapinstances and related dependencies.
0.6.3
- Messages returned by
Request.change()andResponse.change()are marked read whenever the original message is read, and vice-versa. This means that it's possible to read a message on whichchange()has been called and to callchange()on a message more than once, as long asread()is called on only one of those messages.
0.6.2+1
- Support
http_parser1.0.0.
0.6.2
- Added a
bodynamed argument tochangemethod onRequestandResponse.
0.6.1+3
-
Updated minimum SDK to
1.9.0. -
Allow an empty
urlparameter to be passed in tonew Request(). This fits the stated semantics of the class, and should not have been forbidden.
0.6.1+2
logRequestsoutputs a better message a request has a query string.
0.6.1+1
- Don't throw a bogus exception for
nullresponses.
0.6.1
-
shelf_ionow takes a"shelf.io.buffer_output"Response.contextparameter that controlsHttpResponse.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
Requestto support thebodymodel fromResponse.
0.5.6
-
Fixed
createMiddlewareto only catch errors iferrorHandleris provided. -
Updated
handleRequestinshelf_ioto more gracefully handle errors when parsingHttpRequest.
0.5.5+1
- Updated
Request.changeto include the originalonHijackcallback if one exists.
0.5.5
-
Added default body text for
Response.forbiddenandResponse.notFoundif null is provided. -
Clarified documentation on a number of
Responseconstructors. -
Updated
READMElinks to point to latest docs.
0.5.4+3
- Widen the version constraint on the
collectionpackage.
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_ioadapter now sends theDateHTTP header by default. -
Fixed logic for setting Server header in
shelf_io.
0.5.3
- Add new named parameters to
Request.change:scriptNameandurl.
0.5.2
-
Add a
Cascadehelper that runs handlers in sequence until one returns a response that's neither a 404 nor a 405. -
Add a
Request.changemethod that copies a request with new header values. -
Add a
Request.hijackmethod 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
contextmap toRequestandResponsefor passing data among handlers and middleware.
0.5.0+1
- Allow
scheduled_testdevelopment dependency up to v0.12.0
0.5.0
- Renamed
StacktoPipeline.
0.4.0
-
Access to headers for
RequestandResponseis now case-insensitive. -
The constructor for
Requesthas been simplified. -
Requestnow exposesurlwhich replacespathInfo,queryString, andpathSegments.
0.3.0+9
-
Removed old testing infrastructure.
-
Updated documentation address.
0.3.0+8
- Added a dependency on the
http_parserpackage.
0.3.0+7
- Removed unused dependency on the
mimepackage.
0.3.0+6
- Added a dependency on the
string_scannerpackage.
0.3.0+5
- Updated
pubspecdetails for move to Dart SDK.
0.3.0 2014-03-25
Response- NEW!
int get contentLength - NEW!
DateTime get expires - NEW!
DateTime get lastModified
- NEW!
Request- BREAKING
contentLengthis now read fromheaders. The constructor argument has been removed. - NEW! supports an optional
Stream<List<int>> bodyconstructor argument. - NEW!
Stream<List<int>> read()andFuture<String> readAsString([Encoding encoding]) - NEW!
DateTime get ifModifiedSince - NEW!
String get mimeType - NEW!
Encoding get encoding
- BREAKING
0.2.0 2014-03-06
- BREAKING Removed
Shelfprefix from all classes. - BREAKING
Responsehas drastically different constructors. - NEW!
Responsenow accepts a body of eitherStringorStream<List<int>>. - NEW!
Responsenow exposesencodingandmimeType.
0.1.0 2014-03-02
- First reviewed release
