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 /// |
| 10 /// The `dart:io` adapter supports request hijacking; see [Request.hijack]. |
9 library shelf.io; | 11 library shelf.io; |
10 | 12 |
11 import 'dart:async'; | 13 import 'dart:async'; |
12 import 'dart:io'; | 14 import 'dart:io'; |
13 | 15 |
14 import 'package:stack_trace/stack_trace.dart'; | 16 import 'package:stack_trace/stack_trace.dart'; |
15 | 17 |
16 import 'shelf.dart'; | 18 import 'shelf.dart'; |
17 import 'src/util.dart'; | 19 import 'src/util.dart'; |
18 | 20 |
(...skipping 27 matching lines...) Expand all Loading... |
46 _logError('Asynchronous error\n$error', stackTrace); | 48 _logError('Asynchronous error\n$error', stackTrace); |
47 }); | 49 }); |
48 } | 50 } |
49 | 51 |
50 /// Uses [handler] to handle [request]. | 52 /// Uses [handler] to handle [request]. |
51 /// | 53 /// |
52 /// Returns a [Future] which completes when the request has been handled. | 54 /// Returns a [Future] which completes when the request has been handled. |
53 Future handleRequest(HttpRequest request, Handler handler) { | 55 Future handleRequest(HttpRequest request, Handler handler) { |
54 var shelfRequest = _fromHttpRequest(request); | 56 var shelfRequest = _fromHttpRequest(request); |
55 | 57 |
| 58 // TODO(nweiz): abstract out hijack handling to make it easier to implement an |
| 59 // adapter. |
56 return syncFuture(() => handler(shelfRequest)) | 60 return syncFuture(() => handler(shelfRequest)) |
57 .catchError((error, stackTrace) { | 61 .catchError((error, stackTrace) { |
58 return _logError('Error thrown by handler\n$error', stackTrace); | 62 if (error is HijackException) { |
| 63 // A HijackException should bypass the response-writing logic entirely. |
| 64 if (!shelfRequest.canHijack) throw error; |
| 65 |
| 66 // If the request wasn't hijacked, we shouldn't be seeing this exception. |
| 67 return _logError( |
| 68 "Caught HijackException, but the request wasn't hijacked.", |
| 69 stackTrace); |
| 70 } |
| 71 |
| 72 return _logError('Error thrown by handler.\n$error', stackTrace); |
59 }).then((response) { | 73 }).then((response) { |
60 if (response == null) { | 74 if (response == null) { |
61 response = _logError('null response from handler'); | 75 response = _logError('null response from handler.'); |
| 76 } else if (!shelfRequest.canHijack) { |
| 77 var message = new StringBuffer() |
| 78 ..writeln("Got a response for hijacked request " |
| 79 "${shelfRequest.method} ${shelfRequest.requestedUri}:") |
| 80 ..writeln(response.statusCode); |
| 81 response.headers.forEach((key, value) => |
| 82 message.writeln("${key}: ${value}")); |
| 83 throw new Exception(message.toString().trim()); |
62 } | 84 } |
63 | 85 |
64 return _writeResponse(response, request.response); | 86 return _writeResponse(response, request.response); |
| 87 }).catchError((error, stackTrace) { |
| 88 // Ignore HijackExceptions. |
| 89 if (error is! HijackException) throw error; |
65 }); | 90 }); |
66 } | 91 } |
67 | 92 |
68 /// Creates a new [Request] from the provided [HttpRequest]. | 93 /// Creates a new [Request] from the provided [HttpRequest]. |
69 Request _fromHttpRequest(HttpRequest request) { | 94 Request _fromHttpRequest(HttpRequest request) { |
70 var headers = {}; | 95 var headers = {}; |
71 request.headers.forEach((k, v) { | 96 request.headers.forEach((k, v) { |
72 // Multiple header values are joined with commas. | 97 // Multiple header values are joined with commas. |
73 // See http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21#page-22 | 98 // See http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21#page-22 |
74 headers[k] = v.join(','); | 99 headers[k] = v.join(','); |
75 }); | 100 }); |
76 | 101 |
| 102 onHijack(callback) { |
| 103 return request.response.detachSocket(writeHeaders: false) |
| 104 .then((socket) => callback(socket, socket)); |
| 105 } |
| 106 |
77 return new Request(request.method, request.requestedUri, | 107 return new Request(request.method, request.requestedUri, |
78 protocolVersion: request.protocolVersion, headers: headers, | 108 protocolVersion: request.protocolVersion, headers: headers, |
79 body: request); | 109 body: request, onHijack: onHijack); |
80 } | 110 } |
81 | 111 |
82 Future _writeResponse(Response response, HttpResponse httpResponse) { | 112 Future _writeResponse(Response response, HttpResponse httpResponse) { |
83 httpResponse.statusCode = response.statusCode; | 113 httpResponse.statusCode = response.statusCode; |
84 | 114 |
85 response.headers.forEach((header, value) { | 115 response.headers.forEach((header, value) { |
86 if (value == null) return; | 116 if (value == null) return; |
87 httpResponse.headers.set(header, value); | 117 httpResponse.headers.set(header, value); |
88 }); | 118 }); |
89 | 119 |
(...skipping 14 matching lines...) Expand all Loading... |
104 } | 134 } |
105 chain = chain | 135 chain = chain |
106 .foldFrames((frame) => frame.isCore || frame.package == 'shelf') | 136 .foldFrames((frame) => frame.isCore || frame.package == 'shelf') |
107 .terse; | 137 .terse; |
108 | 138 |
109 stderr.writeln('ERROR - ${new DateTime.now()}'); | 139 stderr.writeln('ERROR - ${new DateTime.now()}'); |
110 stderr.writeln(message); | 140 stderr.writeln(message); |
111 stderr.writeln(chain); | 141 stderr.writeln(chain); |
112 return new Response.internalServerError(); | 142 return new Response.internalServerError(); |
113 } | 143 } |
OLD | NEW |