OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 part of dart.developer; |
| 6 |
| 7 class ServiceExtensionResponse { |
| 8 final String _result; |
| 9 final int _errorCode; |
| 10 final String _errorDetail; |
| 11 |
| 12 ServiceExtensionResponse.result(this._result) |
| 13 : _errorCode = null, |
| 14 _errorDetail = null { |
| 15 if (_result is! String) { |
| 16 throw new ArgumentError.value(_result, "result", "Must be a String"); |
| 17 } |
| 18 } |
| 19 |
| 20 ServiceExtensionResponse.error(this._errorCode, this._errorDetail) |
| 21 : _result = null { |
| 22 _validateErrorCode(_errorCode); |
| 23 if (_errorDetail is! String) { |
| 24 throw new ArgumentError.value(_errorDetail, |
| 25 "errorDetail", |
| 26 "Must be a String"); |
| 27 } |
| 28 } |
| 29 |
| 30 /// Invalid method parameter(s) error code. |
| 31 static const kInvalidParams = -32602; |
| 32 /// Generic extension error code. |
| 33 static const kExtensionError = -32000; |
| 34 /// Maximum extension provided error code. |
| 35 static const kExtensionErrorMax = -32000; |
| 36 /// Minimum extension provided error code. |
| 37 static const kExtensionErrorMin = -32016; |
| 38 |
| 39 static String _errorCodeMessage(int errorCode) { |
| 40 _validateErrorCode(errorCode); |
| 41 if (errorCode == kInvalidParams) { |
| 42 return "Invalid params"; |
| 43 } |
| 44 return "Server error"; |
| 45 } |
| 46 |
| 47 static _validateErrorCode(int errorCode) { |
| 48 if (errorCode is! int) { |
| 49 throw new ArgumentError.value(errorCode, "errorCode", "Must be an int"); |
| 50 } |
| 51 if (errorCode == kInvalidParams) { |
| 52 return; |
| 53 } |
| 54 if ((errorCode >= kExtensionErrorMin) && |
| 55 (errorCode <= kExtensionErrorMax)) { |
| 56 return; |
| 57 } |
| 58 throw new ArgumentError.value(errorCode, "errorCode", "Out of range"); |
| 59 } |
| 60 |
| 61 bool _isError() => (_errorCode != null) && (_errorDetail != null); |
| 62 |
| 63 String _toString() { |
| 64 if (_result != null) { |
| 65 return _result; |
| 66 } else { |
| 67 assert(_errorCode != null); |
| 68 assert(_errorDetail != null); |
| 69 return JSON.encode({ |
| 70 'code': _errorCode, |
| 71 'message': _errorCodeMessage(_errorCode), |
| 72 'data': { |
| 73 'details': _errorDetail |
| 74 } |
| 75 }); |
| 76 } |
| 77 } |
| 78 } |
| 79 |
| 80 /// A service protocol extension handler. Registered with [registerExtension]. |
| 81 /// |
| 82 /// Must complete to a [ServiceExtensionResponse]. |
| 83 /// |
| 84 /// [method] - the method name. |
| 85 /// [parameters] - the parameters. |
| 86 typedef Future<ServiceExtensionResponse> |
| 87 ServiceExtensionHandler(String method, Map parameters); |
| 88 |
| 89 final _extensions = new Map<String, ServiceExtensionHandler>(); |
| 90 |
| 91 /// Register a [ServiceExtensionHandler] that will be invoked in this isolate |
| 92 /// for [method]. |
| 93 void registerExtension(String method, ServiceExtensionHandler handler) { |
| 94 if (_extensions[method] != null) { |
| 95 throw new ArgumentError('Extension already registered: $method'); |
| 96 } |
| 97 if (handler is! ServiceExtensionHandler) { |
| 98 throw new ArgumentError.value(handler, |
| 99 'handler', |
| 100 'Must be a ServiceExtensionHandler'); |
| 101 } |
| 102 _extensions[method] = handler; |
| 103 } |
| 104 |
| 105 bool _extensionExists(String method) { |
| 106 return _extensions[method] != null; |
| 107 } |
| 108 |
| 109 bool _invokeExtension(String method, |
| 110 List<String> parameterKeys, |
| 111 List<String> parameterValues, |
| 112 SendPort replyPort, |
| 113 Object id) { |
| 114 ServiceExtensionHandler handler = _extensions[method]; |
| 115 assert(handler != null); |
| 116 var parameters = {}; |
| 117 for (var i = 0; i < parameterKeys.length; i++) { |
| 118 parameters[parameterKeys[i]] = parameterValues[i]; |
| 119 } |
| 120 var response; |
| 121 try { |
| 122 response = handler(method, parameters); |
| 123 } catch (e, st) { |
| 124 var errorDetails = (st == null) ? '$e' : '$e\n$st'; |
| 125 response = new ServiceExtensionResponse.error( |
| 126 ServiceExtensionResponse.kExtensionError, |
| 127 errorDetails); |
| 128 _postResponse(replyPort, id, response); |
| 129 return true; |
| 130 } |
| 131 if (response is! Future) { |
| 132 response = new ServiceExtensionResponse.error( |
| 133 ServiceExtensionResponse.kExtensionError, |
| 134 "Extension handler must return a Future"); |
| 135 _postResponse(replyPort, id, response); |
| 136 return true; |
| 137 } |
| 138 response.catchError((e, st) { |
| 139 var errorDetails = (st == null) ? '$e' : '$e\n$st'; |
| 140 return new ServiceExtensionResponse.error( |
| 141 ServiceExtensionResponse.kExtensionError, |
| 142 errorDetails); |
| 143 }).then((response) { |
| 144 if (response == null) { |
| 145 response = new ServiceExtensionResponse.error( |
| 146 ServiceExtensionResponse.kExtensionError, |
| 147 "Extension handler returned null"); |
| 148 } |
| 149 _postResponse(replyPort, id, response); |
| 150 }); |
| 151 // Push an event on the event loop so that we invoke the scheduled microtasks. |
| 152 Timer.run(() {}); |
| 153 return true; |
| 154 } |
| 155 |
| 156 _postResponse(SendPort replyPort, |
| 157 Object id, |
| 158 ServiceExtensionResponse response) { |
| 159 assert(replyPort != null); |
| 160 if (id == null) { |
| 161 // No id -> no response. |
| 162 // TODO(johnmccutchan): This code and the code in service.cc leave the |
| 163 // service isolate with an open port. Consider posting 'null' to indicate |
| 164 // that no response is coming. |
| 165 return; |
| 166 } |
| 167 assert(id != null); |
| 168 StringBuffer sb = new StringBuffer(); |
| 169 sb.write('{"jsonrpc":"2.0",'); |
| 170 if (response._isError()) { |
| 171 sb.write('"error":'); |
| 172 } else { |
| 173 sb.write('"result":'); |
| 174 } |
| 175 sb.write('${response._toString()},'); |
| 176 if (id is String) { |
| 177 sb.write('"id":"$id"}'); |
| 178 } else { |
| 179 sb.write('"id":$id}'); |
| 180 } |
| 181 replyPort.send(sb.toString()); |
| 182 } |
OLD | NEW |