Skip to content

Commit ca731ab

Browse files
authored
Add a poweredByHeader argument to server (#272)
Fixes #270 Allow passing `null` to omit the header.
1 parent 6f80ea1 commit ca731ab

File tree

3 files changed

+83
-14
lines changed

3 files changed

+83
-14
lines changed

pkgs/shelf/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
## 1.4.0-dev
22

33
* Add Response.unauthorized() constructor
4+
* Add `poweredByHeader` argument to `serve`, `serveRequests`, and
5+
`handleRequest`.
46

57
## 1.3.2
68

pkgs/shelf/lib/shelf_io.dart

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,22 @@ export 'src/io_server.dart' show IOServer;
3939
///
4040
/// See the documentation for [HttpServer.bind] and [HttpServer.bindSecure]
4141
/// for more details on [address], [port], [backlog], and [shared].
42+
///
43+
/// {@template shelf_io_header_defaults}
44+
/// Every response will get a "date" header and an "X-Powered-By" header.
45+
/// If the either header is present in the `Response`, it will not be
46+
/// overwritten.
47+
/// Pass [poweredByHeader] to set the default content for "X-Powered-By",
48+
/// pass `null` to omit this header.
49+
/// {@endtemplate}
4250
Future<HttpServer> serve(
4351
Handler handler,
4452
Object address,
4553
int port, {
4654
SecurityContext? securityContext,
4755
int? backlog,
4856
bool shared = false,
57+
String? poweredByHeader = 'Dart with package:shelf',
4958
}) async {
5059
backlog ??= 0;
5160
var server = await (securityContext == null
@@ -57,7 +66,7 @@ Future<HttpServer> serve(
5766
backlog: backlog,
5867
shared: shared,
5968
));
60-
serveRequests(server, handler);
69+
serveRequests(server, handler, poweredByHeader: poweredByHeader);
6170
return server;
6271
}
6372

@@ -70,9 +79,16 @@ Future<HttpServer> serve(
7079
/// console and cause a 500 response with no body. Errors thrown asynchronously
7180
/// by [handler] will be printed to the console or, if there's an active error
7281
/// zone, passed to that zone.
73-
void serveRequests(Stream<HttpRequest> requests, Handler handler) {
82+
///
83+
/// {@macro shelf_io_header_defaults}
84+
void serveRequests(
85+
Stream<HttpRequest> requests,
86+
Handler handler, {
87+
String? poweredByHeader = 'Dart with package:shelf',
88+
}) {
7489
catchTopLevelErrors(() {
75-
requests.listen((request) => handleRequest(request, handler));
90+
requests.listen((request) =>
91+
handleRequest(request, handler, poweredByHeader: poweredByHeader));
7692
}, (error, stackTrace) {
7793
_logTopLevelError('Asynchronous error\n$error', stackTrace);
7894
});
@@ -81,7 +97,13 @@ void serveRequests(Stream<HttpRequest> requests, Handler handler) {
8197
/// Uses [handler] to handle [request].
8298
///
8399
/// Returns a [Future] which completes when the request has been handled.
84-
Future<void> handleRequest(HttpRequest request, Handler handler) async {
100+
///
101+
/// {@macro shelf_io_header_defaults}
102+
Future<void> handleRequest(
103+
HttpRequest request,
104+
Handler handler, {
105+
String? poweredByHeader = 'Dart with package:shelf',
106+
}) async {
85107
Request shelfRequest;
86108
try {
87109
shelfRequest = _fromHttpRequest(request);
@@ -94,17 +116,17 @@ Future<void> handleRequest(HttpRequest request, Handler handler) async {
94116
body: 'Bad Request',
95117
headers: {HttpHeaders.contentTypeHeader: 'text/plain'},
96118
);
97-
await _writeResponse(response, request.response);
119+
await _writeResponse(response, request.response, poweredByHeader);
98120
} else {
99121
_logTopLevelError('Error parsing request.\n$error', stackTrace);
100122
final response = Response.internalServerError();
101-
await _writeResponse(response, request.response);
123+
await _writeResponse(response, request.response, poweredByHeader);
102124
}
103125
return;
104126
} catch (error, stackTrace) {
105127
_logTopLevelError('Error parsing request.\n$error', stackTrace);
106128
final response = Response.internalServerError();
107-
await _writeResponse(response, request.response);
129+
await _writeResponse(response, request.response, poweredByHeader);
108130
return;
109131
}
110132

@@ -136,11 +158,12 @@ Future<void> handleRequest(HttpRequest request, Handler handler) async {
136158
await _writeResponse(
137159
_logError(
138160
shelfRequest, 'null response from handler.', StackTrace.current),
139-
request.response);
161+
request.response,
162+
poweredByHeader);
140163
return;
141164
}
142165
if (shelfRequest.canHijack) {
143-
await _writeResponse(response, request.response);
166+
await _writeResponse(response, request.response, poweredByHeader);
144167
return;
145168
}
146169

@@ -179,7 +202,8 @@ Request _fromHttpRequest(HttpRequest request) {
179202
);
180203
}
181204

182-
Future<void> _writeResponse(Response response, HttpResponse httpResponse) {
205+
Future<void> _writeResponse(
206+
Response response, HttpResponse httpResponse, String? poweredByHeader) {
183207
if (response.context.containsKey('shelf.io.buffer_output')) {
184208
httpResponse.bufferOutput =
185209
response.context['shelf.io.buffer_output'] as bool;
@@ -217,9 +241,9 @@ Future<void> _writeResponse(Response response, HttpResponse httpResponse) {
217241
httpResponse.headers.set(HttpHeaders.transferEncodingHeader, 'chunked');
218242
}
219243

220-
if (!response.headers.containsKey(_xPoweredByResponseHeader)) {
221-
httpResponse.headers
222-
.set(_xPoweredByResponseHeader, 'Dart with package:shelf');
244+
if (poweredByHeader != null &&
245+
!response.headers.containsKey(_xPoweredByResponseHeader)) {
246+
httpResponse.headers.set(_xPoweredByResponseHeader, poweredByHeader);
223247
}
224248

225249
if (!response.headers.containsKey(HttpHeaders.dateHeader)) {

pkgs/shelf/test/shelf_io_test.dart

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,14 +365,57 @@ void main() {
365365
);
366366
});
367367

368-
test('defers to header in response', () async {
368+
test('defers to header in response when default', () async {
369369
await _scheduleServer((request) {
370370
return Response.ok('test', headers: {poweredBy: 'myServer'});
371371
});
372372

373373
var response = await _get();
374374
expect(response.headers, containsPair(poweredBy, 'myServer'));
375375
});
376+
377+
test('can be set at the server level', () async {
378+
_server = await shelf_io.serve(
379+
syncHandler,
380+
'localhost',
381+
0,
382+
poweredByHeader: 'ourServer',
383+
);
384+
var response = await _get();
385+
expect(
386+
response.headers,
387+
containsPair(poweredBy, 'ourServer'),
388+
);
389+
});
390+
391+
test('defers to header in response when set at the server level', () async {
392+
_server = await shelf_io.serve(
393+
(request) {
394+
return Response.ok('test', headers: {poweredBy: 'myServer'});
395+
},
396+
'localhost',
397+
0,
398+
poweredByHeader: 'ourServer',
399+
);
400+
401+
var response = await _get();
402+
expect(response.headers, containsPair(poweredBy, 'myServer'));
403+
});
404+
405+
test('is omitted when set to null', () async {
406+
_server = await shelf_io.serve(
407+
syncHandler,
408+
'localhost',
409+
0,
410+
poweredByHeader: null,
411+
);
412+
413+
var response = await _get();
414+
expect(
415+
response.headers,
416+
isNot(contains(poweredBy)),
417+
);
418+
});
376419
});
377420

378421
group('chunked coding', () {

0 commit comments

Comments
 (0)