Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(80)

Side by Side Diff: lib/shelf_io.dart

Issue 1417523005: Include the request context for shelf_io errors. (Closed) Base URL: git@github.com:dart-lang/shelf@master
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « CHANGELOG.md ('k') | pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 /// This adapter supports request hijacking; see [Request.hijack]. It also 10 /// This adapter supports request hijacking; see [Request.hijack]. It also
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
46 /// to [serveRequests]. 46 /// to [serveRequests].
47 /// 47 ///
48 /// Errors thrown by [handler] while serving a request will be printed to the 48 /// Errors thrown by [handler] while serving a request will be printed to the
49 /// console and cause a 500 response with no body. Errors thrown asynchronously 49 /// console and cause a 500 response with no body. Errors thrown asynchronously
50 /// by [handler] will be printed to the console or, if there's an active error 50 /// by [handler] will be printed to the console or, if there's an active error
51 /// zone, passed to that zone. 51 /// zone, passed to that zone.
52 void serveRequests(Stream<HttpRequest> requests, Handler handler) { 52 void serveRequests(Stream<HttpRequest> requests, Handler handler) {
53 catchTopLevelErrors(() { 53 catchTopLevelErrors(() {
54 requests.listen((request) => handleRequest(request, handler)); 54 requests.listen((request) => handleRequest(request, handler));
55 }, (error, stackTrace) { 55 }, (error, stackTrace) {
56 _logError('Asynchronous error\n$error', stackTrace); 56 _logTopLevelError('Asynchronous error\n$error', stackTrace);
57 }); 57 });
58 } 58 }
59 59
60 /// Uses [handler] to handle [request]. 60 /// Uses [handler] to handle [request].
61 /// 61 ///
62 /// Returns a [Future] which completes when the request has been handled. 62 /// Returns a [Future] which completes when the request has been handled.
63 Future handleRequest(HttpRequest request, Handler handler) { 63 Future handleRequest(HttpRequest request, Handler handler) async {
64 var shelfRequest; 64 var shelfRequest;
65 try { 65 try {
66 shelfRequest = _fromHttpRequest(request); 66 shelfRequest = _fromHttpRequest(request);
67 } catch (error, stackTrace) { 67 } catch (error, stackTrace) {
68 var response = _logError('Error parsing request.\n$error', stackTrace); 68 var response = _logTopLevelError(
69 return _writeResponse(response, request.response); 69 'Error parsing request.\n$error', stackTrace);
70 await _writeResponse(response, request.response);
71 return;
70 } 72 }
71 73
72 // TODO(nweiz): abstract out hijack handling to make it easier to implement an 74 // TODO(nweiz): abstract out hijack handling to make it easier to implement an
73 // adapter. 75 // adapter.
74 return new Future.sync(() => handler(shelfRequest)) 76 var response;
75 .catchError((error, stackTrace) { 77 try {
76 if (error is HijackException) { 78 response = await handler(shelfRequest);
77 // A HijackException should bypass the response-writing logic entirely. 79 } on HijackException catch (error, stackTrace) {
78 if (!shelfRequest.canHijack) throw error; 80 // A HijackException should bypass the response-writing logic entirely.
81 if (!shelfRequest.canHijack) return;
79 82
80 // If the request wasn't hijacked, we shouldn't be seeing this exception. 83 // If the request wasn't hijacked, we shouldn't be seeing this exception.
81 return _logError( 84 response = _logError(
82 "Caught HijackException, but the request wasn't hijacked.", 85 shelfRequest,
83 stackTrace); 86 "Caught HijackException, but the request wasn't hijacked.",
84 } 87 stackTrace);
88 } catch (error, stackTrace) {
89 response = _logError(
90 shelfRequest, 'Error thrown by handler.\n$error', stackTrace);
91 }
85 92
86 return _logError('Error thrown by handler.\n$error', stackTrace); 93 if (response == null) {
87 }).then((response) { 94 await _writeResponse(
88 if (response == null) { 95 _logError(shelfRequest, 'null response from handler.'),
89 return _writeResponse( 96 request.response);
90 _logError('null response from handler.'), request.response); 97 return;
91 } else if (shelfRequest.canHijack) { 98 } else if (shelfRequest.canHijack) {
92 return _writeResponse(response, request.response); 99 await _writeResponse(response, request.response);
93 } 100 return;
101 }
94 102
95 var message = new StringBuffer() 103 var message = new StringBuffer()
96 ..writeln("Got a response for hijacked request " 104 ..writeln("Got a response for hijacked request "
97 "${shelfRequest.method} ${shelfRequest.requestedUri}:") 105 "${shelfRequest.method} ${shelfRequest.requestedUri}:")
98 ..writeln(response.statusCode); 106 ..writeln(response.statusCode);
99 response.headers 107 response.headers
100 .forEach((key, value) => message.writeln("${key}: ${value}")); 108 .forEach((key, value) => message.writeln("${key}: ${value}"));
101 throw new Exception(message.toString().trim()); 109 throw new Exception(message.toString().trim());
102 }).catchError((error, stackTrace) {
103 // Ignore HijackExceptions.
104 if (error is! HijackException) throw error;
105 });
106 } 110 }
107 111
108 /// Creates a new [Request] from the provided [HttpRequest]. 112 /// Creates a new [Request] from the provided [HttpRequest].
109 Request _fromHttpRequest(HttpRequest request) { 113 Request _fromHttpRequest(HttpRequest request) {
110 var headers = {}; 114 var headers = {};
111 request.headers.forEach((k, v) { 115 request.headers.forEach((k, v) {
112 // Multiple header values are joined with commas. 116 // Multiple header values are joined with commas.
113 // See http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21#page-22 117 // See http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21#page-22
114 headers[k] = v.join(','); 118 headers[k] = v.join(',');
115 }); 119 });
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 httpResponse.headers.date = new DateTime.now().toUtc(); 151 httpResponse.headers.date = new DateTime.now().toUtc();
148 } 152 }
149 153
150 return httpResponse 154 return httpResponse
151 .addStream(response.read()) 155 .addStream(response.read())
152 .then((_) => httpResponse.close()); 156 .then((_) => httpResponse.close());
153 } 157 }
154 158
155 // TODO(kevmoo) A developer mode is needed to include error info in response 159 // TODO(kevmoo) A developer mode is needed to include error info in response
156 // TODO(kevmoo) Make error output plugable. stderr, logging, etc 160 // TODO(kevmoo) Make error output plugable. stderr, logging, etc
157 Response _logError(String message, [StackTrace stackTrace]) { 161 Response _logError(Request request, String message, [StackTrace stackTrace]) {
162 // Add information about the request itself.
163 var buffer = new StringBuffer();
164 buffer.write("${request.method} ${request.requestedUri.path}");
165 if (request.requestedUri.query.isNotEmpty) {
166 buffer.write("?${request.requestedUri.query}");
167 }
168 buffer.writeln();
169 buffer.write(message);
170
171 return _logTopLevelError(buffer.toString(), stackTrace);
172 }
173
174 Response _logTopLevelError(String message, [StackTrace stackTrace]) {
158 var chain = new Chain.current(); 175 var chain = new Chain.current();
159 if (stackTrace != null) { 176 if (stackTrace != null) {
160 chain = new Chain.forTrace(stackTrace); 177 chain = new Chain.forTrace(stackTrace);
161 } 178 }
162 chain = chain 179 chain = chain
163 .foldFrames((frame) => frame.isCore || frame.package == 'shelf').terse; 180 .foldFrames((frame) => frame.isCore || frame.package == 'shelf').terse;
164 181
165 stderr.writeln('ERROR - ${new DateTime.now()}'); 182 stderr.writeln('ERROR - ${new DateTime.now()}');
166 stderr.writeln(message); 183 stderr.writeln(message);
167 stderr.writeln(chain); 184 stderr.writeln(chain);
168 return new Response.internalServerError(); 185 return new Response.internalServerError();
169 } 186 }
OLDNEW
« no previous file with comments | « CHANGELOG.md ('k') | pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698