pkg/shelf - send Date header in shelf_io

R=nweiz@google.com

Review URL: https://codereview.chromium.org//300023003

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/shelf@36709 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a58fe9..0588275 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.5.4
+
+* The `shelf_io` adapter now sends the `Date` HTTP header by default.
+
 ## 0.5.3
 
 * Add new named parameters to `Request.change`: `scriptName` and `url`.
diff --git a/README.md b/README.md
index 7366ba8..98123a9 100644
--- a/README.md
+++ b/README.md
@@ -98,6 +98,10 @@
 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.
+
 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
diff --git a/lib/shelf_io.dart b/lib/shelf_io.dart
index d8d4d9c..6582e5a 100644
--- a/lib/shelf_io.dart
+++ b/lib/shelf_io.dart
@@ -121,6 +121,11 @@
     var value = httpResponse.headers.value(HttpHeaders.SERVER);
     httpResponse.headers.set(HttpHeaders.SERVER, '$value with Shelf');
   }
+
+  if (!response.headers.containsKey(HttpHeaders.DATE)) {
+    httpResponse.headers.date = new DateTime.now().toUtc();
+  }
+
   return httpResponse.addStream(response.read())
       .then((_) => httpResponse.close());
 }
diff --git a/pubspec.yaml b/pubspec.yaml
index f4e338f..548fe38 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: shelf
-version: 0.5.3
+version: 0.5.4-dev
 author: Dart Team <misc@dartlang.org>
 description: Web Server Middleware for Dart
 homepage: http://www.dartlang.org
diff --git a/test/shelf_io_test.dart b/test/shelf_io_test.dart
index 5205658..c7d9f7a 100644
--- a/test/shelf_io_test.dart
+++ b/test/shelf_io_test.dart
@@ -9,6 +9,7 @@
 import 'dart:io';
 
 import 'package:http/http.dart' as http;
+import 'package:http_parser/http_parser.dart' as parser;
 import 'package:scheduled_test/scheduled_test.dart';
 import 'package:shelf/shelf.dart';
 import 'package:shelf/shelf_io.dart' as shelf_io;
@@ -249,6 +250,40 @@
       });
     });
   });
+
+  group('date header', () {
+    test('is sent by default', () {
+      _scheduleServer(syncHandler);
+
+      // Update beforeRequest to be one second earlier. HTTP dates only have
+      // second-level granularity and the request will likely take less than a
+      // second.
+      var beforeRequest = new DateTime.now().subtract(new Duration(seconds: 1));
+
+      return _scheduleGet().then((response) {
+        expect(response.headers, contains('date'));
+        var responseDate = parser.parseHttpDate(response.headers['date']);
+
+        expect(responseDate.isAfter(beforeRequest), isTrue);
+        expect(responseDate.isBefore(new DateTime.now()), isTrue);
+      });
+    });
+
+    test('defers to header in response', () {
+      var date = new DateTime.utc(1981, 6, 5);
+      _scheduleServer((request) {
+        return new Response.ok('test', headers: {
+          HttpHeaders.DATE: parser.formatHttpDate(date)
+        });
+      });
+
+      return _scheduleGet().then((response) {
+        expect(response.headers, contains('date'));
+        var responseDate = parser.parseHttpDate(response.headers['date']);
+        expect(responseDate, date);
+      });
+    });
+  });
 }
 
 int _serverPort;