Chromium Code Reviews| 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 |