OLD | NEW |
---|---|
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// A Shelf adapter for handling [HttpRequest] objects from `dart:io`. | 5 /// A Shelf adapter for handling [HttpRequest] objects from `dart:io`. |
6 /// | 6 /// |
7 /// One can provide an instance of [HttpServer] as the `requests` parameter in | 7 /// One can provide an instance of [HttpServer] as the `requests` parameter in |
8 /// [serveRequests]. | 8 /// [serveRequests]. |
9 /// | 9 /// |
10 /// The `dart:io` adapter supports request hijacking; see [Request.hijack]. | 10 /// The `dart:io` adapter supports request hijacking; see [Request.hijack]. |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
45 catchTopLevelErrors(() { | 45 catchTopLevelErrors(() { |
46 requests.listen((request) => handleRequest(request, handler)); | 46 requests.listen((request) => handleRequest(request, handler)); |
47 }, (error, stackTrace) { | 47 }, (error, stackTrace) { |
48 _logError('Asynchronous error\n$error', stackTrace); | 48 _logError('Asynchronous error\n$error', stackTrace); |
49 }); | 49 }); |
50 } | 50 } |
51 | 51 |
52 /// Uses [handler] to handle [request]. | 52 /// Uses [handler] to handle [request]. |
53 /// | 53 /// |
54 /// Returns a [Future] which completes when the request has been handled. | 54 /// Returns a [Future] which completes when the request has been handled. |
55 Future handleRequest(HttpRequest request, Handler handler) { | 55 /// |
56 /// If [includeDateHeader] is `true`, the `Date` header is included with a value | |
57 /// of `new DateTime.now` at the time the response is written. The default is | |
58 /// `true`. | |
nweiz
2014/05/27 19:52:44
Why is this configurable? Any configuration that's
kevmoo
2014/05/27 20:50:58
DONE
I guess we can always add this later if dema
| |
59 Future handleRequest(HttpRequest request, Handler handler, | |
60 {bool includeDateHeader: true}) { | |
56 var shelfRequest = _fromHttpRequest(request); | 61 var shelfRequest = _fromHttpRequest(request); |
57 | 62 |
58 // TODO(nweiz): abstract out hijack handling to make it easier to implement an | 63 // TODO(nweiz): abstract out hijack handling to make it easier to implement an |
59 // adapter. | 64 // adapter. |
60 return syncFuture(() => handler(shelfRequest)) | 65 return syncFuture(() => handler(shelfRequest)) |
61 .catchError((error, stackTrace) { | 66 .catchError((error, stackTrace) { |
62 if (error is HijackException) { | 67 if (error is HijackException) { |
63 // A HijackException should bypass the response-writing logic entirely. | 68 // A HijackException should bypass the response-writing logic entirely. |
64 if (!shelfRequest.canHijack) throw error; | 69 if (!shelfRequest.canHijack) throw error; |
65 | 70 |
(...skipping 10 matching lines...) Expand all Loading... | |
76 } else if (!shelfRequest.canHijack) { | 81 } else if (!shelfRequest.canHijack) { |
77 var message = new StringBuffer() | 82 var message = new StringBuffer() |
78 ..writeln("Got a response for hijacked request " | 83 ..writeln("Got a response for hijacked request " |
79 "${shelfRequest.method} ${shelfRequest.requestedUri}:") | 84 "${shelfRequest.method} ${shelfRequest.requestedUri}:") |
80 ..writeln(response.statusCode); | 85 ..writeln(response.statusCode); |
81 response.headers.forEach((key, value) => | 86 response.headers.forEach((key, value) => |
82 message.writeln("${key}: ${value}")); | 87 message.writeln("${key}: ${value}")); |
83 throw new Exception(message.toString().trim()); | 88 throw new Exception(message.toString().trim()); |
84 } | 89 } |
85 | 90 |
86 return _writeResponse(response, request.response); | 91 return _writeResponse(response, request.response, includeDateHeader); |
87 }).catchError((error, stackTrace) { | 92 }).catchError((error, stackTrace) { |
88 // Ignore HijackExceptions. | 93 // Ignore HijackExceptions. |
89 if (error is! HijackException) throw error; | 94 if (error is! HijackException) throw error; |
90 }); | 95 }); |
91 } | 96 } |
92 | 97 |
93 /// Creates a new [Request] from the provided [HttpRequest]. | 98 /// Creates a new [Request] from the provided [HttpRequest]. |
94 Request _fromHttpRequest(HttpRequest request) { | 99 Request _fromHttpRequest(HttpRequest request) { |
95 var headers = {}; | 100 var headers = {}; |
96 request.headers.forEach((k, v) { | 101 request.headers.forEach((k, v) { |
97 // Multiple header values are joined with commas. | 102 // Multiple header values are joined with commas. |
98 // See http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21#page-22 | 103 // See http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21#page-22 |
99 headers[k] = v.join(','); | 104 headers[k] = v.join(','); |
100 }); | 105 }); |
101 | 106 |
102 onHijack(callback) { | 107 onHijack(callback) { |
103 return request.response.detachSocket(writeHeaders: false) | 108 return request.response.detachSocket(writeHeaders: false) |
104 .then((socket) => callback(socket, socket)); | 109 .then((socket) => callback(socket, socket)); |
105 } | 110 } |
106 | 111 |
107 return new Request(request.method, request.requestedUri, | 112 return new Request(request.method, request.requestedUri, |
108 protocolVersion: request.protocolVersion, headers: headers, | 113 protocolVersion: request.protocolVersion, headers: headers, |
109 body: request, onHijack: onHijack); | 114 body: request, onHijack: onHijack); |
110 } | 115 } |
111 | 116 |
112 Future _writeResponse(Response response, HttpResponse httpResponse) { | 117 Future _writeResponse(Response response, HttpResponse httpResponse, |
118 bool includeDateHeader) { | |
113 httpResponse.statusCode = response.statusCode; | 119 httpResponse.statusCode = response.statusCode; |
114 | 120 |
115 response.headers.forEach((header, value) { | 121 response.headers.forEach((header, value) { |
116 if (value == null) return; | 122 if (value == null) return; |
117 httpResponse.headers.set(header, value); | 123 httpResponse.headers.set(header, value); |
118 }); | 124 }); |
119 | 125 |
120 if (response.headers[HttpHeaders.SERVER] == null) { | 126 if (response.headers[HttpHeaders.SERVER] == null) { |
121 var value = httpResponse.headers.value(HttpHeaders.SERVER); | 127 var value = httpResponse.headers.value(HttpHeaders.SERVER); |
122 httpResponse.headers.set(HttpHeaders.SERVER, '$value with Shelf'); | 128 httpResponse.headers.set(HttpHeaders.SERVER, '$value with Shelf'); |
123 } | 129 } |
130 | |
131 if (includeDateHeader && !response.headers.containsKey(HttpHeaders.DATE)) { | |
132 httpResponse.headers.date = new DateTime.now().toUtc(); | |
133 } | |
134 | |
124 return httpResponse.addStream(response.read()) | 135 return httpResponse.addStream(response.read()) |
125 .then((_) => httpResponse.close()); | 136 .then((_) => httpResponse.close()); |
126 } | 137 } |
127 | 138 |
128 // TODO(kevmoo) A developer mode is needed to include error info in response | 139 // TODO(kevmoo) A developer mode is needed to include error info in response |
129 // TODO(kevmoo) Make error output plugable. stderr, logging, etc | 140 // TODO(kevmoo) Make error output plugable. stderr, logging, etc |
130 Response _logError(String message, [StackTrace stackTrace]) { | 141 Response _logError(String message, [StackTrace stackTrace]) { |
131 var chain = new Chain.current(); | 142 var chain = new Chain.current(); |
132 if (stackTrace != null) { | 143 if (stackTrace != null) { |
133 chain = new Chain.forTrace(stackTrace); | 144 chain = new Chain.forTrace(stackTrace); |
134 } | 145 } |
135 chain = chain | 146 chain = chain |
136 .foldFrames((frame) => frame.isCore || frame.package == 'shelf') | 147 .foldFrames((frame) => frame.isCore || frame.package == 'shelf') |
137 .terse; | 148 .terse; |
138 | 149 |
139 stderr.writeln('ERROR - ${new DateTime.now()}'); | 150 stderr.writeln('ERROR - ${new DateTime.now()}'); |
140 stderr.writeln(message); | 151 stderr.writeln(message); |
141 stderr.writeln(chain); | 152 stderr.writeln(chain); |
142 return new Response.internalServerError(); | 153 return new Response.internalServerError(); |
143 } | 154 } |
OLD | NEW |