Add a poweredByHeader argument to server (#272)
Fixes #270
Allow passing `null` to omit the header.
diff --git a/pkgs/shelf/CHANGELOG.md b/pkgs/shelf/CHANGELOG.md
index 357fc6f..a69299d 100644
--- a/pkgs/shelf/CHANGELOG.md
+++ b/pkgs/shelf/CHANGELOG.md
@@ -1,6 +1,8 @@
## 1.4.0-dev
* Add Response.unauthorized() constructor
+* Add `poweredByHeader` argument to `serve`, `serveRequests`, and
+ `handleRequest`.
## 1.3.2
diff --git a/pkgs/shelf/lib/shelf_io.dart b/pkgs/shelf/lib/shelf_io.dart
index 3387b74..062ade7 100644
--- a/pkgs/shelf/lib/shelf_io.dart
+++ b/pkgs/shelf/lib/shelf_io.dart
@@ -39,6 +39,14 @@
///
/// See the documentation for [HttpServer.bind] and [HttpServer.bindSecure]
/// for more details on [address], [port], [backlog], and [shared].
+///
+/// {@template shelf_io_header_defaults}
+/// Every response will get a "date" header and an "X-Powered-By" header.
+/// If the either header is present in the `Response`, it will not be
+/// overwritten.
+/// Pass [poweredByHeader] to set the default content for "X-Powered-By",
+/// pass `null` to omit this header.
+/// {@endtemplate}
Future<HttpServer> serve(
Handler handler,
Object address,
@@ -46,6 +54,7 @@
SecurityContext? securityContext,
int? backlog,
bool shared = false,
+ String? poweredByHeader = 'Dart with package:shelf',
}) async {
backlog ??= 0;
var server = await (securityContext == null
@@ -57,7 +66,7 @@
backlog: backlog,
shared: shared,
));
- serveRequests(server, handler);
+ serveRequests(server, handler, poweredByHeader: poweredByHeader);
return server;
}
@@ -70,9 +79,16 @@
/// console and cause a 500 response with no body. Errors thrown asynchronously
/// by [handler] will be printed to the console or, if there's an active error
/// zone, passed to that zone.
-void serveRequests(Stream<HttpRequest> requests, Handler handler) {
+///
+/// {@macro shelf_io_header_defaults}
+void serveRequests(
+ Stream<HttpRequest> requests,
+ Handler handler, {
+ String? poweredByHeader = 'Dart with package:shelf',
+}) {
catchTopLevelErrors(() {
- requests.listen((request) => handleRequest(request, handler));
+ requests.listen((request) =>
+ handleRequest(request, handler, poweredByHeader: poweredByHeader));
}, (error, stackTrace) {
_logTopLevelError('Asynchronous error\n$error', stackTrace);
});
@@ -81,7 +97,13 @@
/// Uses [handler] to handle [request].
///
/// Returns a [Future] which completes when the request has been handled.
-Future<void> handleRequest(HttpRequest request, Handler handler) async {
+///
+/// {@macro shelf_io_header_defaults}
+Future<void> handleRequest(
+ HttpRequest request,
+ Handler handler, {
+ String? poweredByHeader = 'Dart with package:shelf',
+}) async {
Request shelfRequest;
try {
shelfRequest = _fromHttpRequest(request);
@@ -94,17 +116,17 @@
body: 'Bad Request',
headers: {HttpHeaders.contentTypeHeader: 'text/plain'},
);
- await _writeResponse(response, request.response);
+ await _writeResponse(response, request.response, poweredByHeader);
} else {
_logTopLevelError('Error parsing request.\n$error', stackTrace);
final response = Response.internalServerError();
- await _writeResponse(response, request.response);
+ await _writeResponse(response, request.response, poweredByHeader);
}
return;
} catch (error, stackTrace) {
_logTopLevelError('Error parsing request.\n$error', stackTrace);
final response = Response.internalServerError();
- await _writeResponse(response, request.response);
+ await _writeResponse(response, request.response, poweredByHeader);
return;
}
@@ -136,11 +158,12 @@
await _writeResponse(
_logError(
shelfRequest, 'null response from handler.', StackTrace.current),
- request.response);
+ request.response,
+ poweredByHeader);
return;
}
if (shelfRequest.canHijack) {
- await _writeResponse(response, request.response);
+ await _writeResponse(response, request.response, poweredByHeader);
return;
}
@@ -179,7 +202,8 @@
);
}
-Future<void> _writeResponse(Response response, HttpResponse httpResponse) {
+Future<void> _writeResponse(
+ Response response, HttpResponse httpResponse, String? poweredByHeader) {
if (response.context.containsKey('shelf.io.buffer_output')) {
httpResponse.bufferOutput =
response.context['shelf.io.buffer_output'] as bool;
@@ -217,9 +241,9 @@
httpResponse.headers.set(HttpHeaders.transferEncodingHeader, 'chunked');
}
- if (!response.headers.containsKey(_xPoweredByResponseHeader)) {
- httpResponse.headers
- .set(_xPoweredByResponseHeader, 'Dart with package:shelf');
+ if (poweredByHeader != null &&
+ !response.headers.containsKey(_xPoweredByResponseHeader)) {
+ httpResponse.headers.set(_xPoweredByResponseHeader, poweredByHeader);
}
if (!response.headers.containsKey(HttpHeaders.dateHeader)) {
diff --git a/pkgs/shelf/test/shelf_io_test.dart b/pkgs/shelf/test/shelf_io_test.dart
index 0887585..4175f54 100644
--- a/pkgs/shelf/test/shelf_io_test.dart
+++ b/pkgs/shelf/test/shelf_io_test.dart
@@ -365,7 +365,7 @@
);
});
- test('defers to header in response', () async {
+ test('defers to header in response when default', () async {
await _scheduleServer((request) {
return Response.ok('test', headers: {poweredBy: 'myServer'});
});
@@ -373,6 +373,49 @@
var response = await _get();
expect(response.headers, containsPair(poweredBy, 'myServer'));
});
+
+ test('can be set at the server level', () async {
+ _server = await shelf_io.serve(
+ syncHandler,
+ 'localhost',
+ 0,
+ poweredByHeader: 'ourServer',
+ );
+ var response = await _get();
+ expect(
+ response.headers,
+ containsPair(poweredBy, 'ourServer'),
+ );
+ });
+
+ test('defers to header in response when set at the server level', () async {
+ _server = await shelf_io.serve(
+ (request) {
+ return Response.ok('test', headers: {poweredBy: 'myServer'});
+ },
+ 'localhost',
+ 0,
+ poweredByHeader: 'ourServer',
+ );
+
+ var response = await _get();
+ expect(response.headers, containsPair(poweredBy, 'myServer'));
+ });
+
+ test('is omitted when set to null', () async {
+ _server = await shelf_io.serve(
+ syncHandler,
+ 'localhost',
+ 0,
+ poweredByHeader: null,
+ );
+
+ var response = await _get();
+ expect(
+ response.headers,
+ isNot(contains(poweredBy)),
+ );
+ });
});
group('chunked coding', () {