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); | |
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 } | |
129 if (response is! Future) { | |
turnidge
2015/08/06 18:12:54
I would prefer to force the user to always return
Cutch
2015/08/06 23:20:18
Done.
| |
130 response = new Future.value(response); | |
131 } | |
132 response.catchError((e, st) { | |
133 var errorDetails = (st == null) ? '$e' : '$e\n$st'; | |
134 return new ServiceExtensionResponse.error( | |
135 ServiceExtensionResponse.kExtensionError, | |
136 errorDetails); | |
137 }).then((response) { | |
138 if (response == null) { | |
139 response = new ServiceExtensionResponse.error( | |
140 ServiceExtensionResponse.kExtensionError, | |
141 "Extension handler returned null"); | |
142 } | |
143 _postResponse(replyPort, id, response); | |
144 }); | |
145 // Push an event on the event loop so that we invoke the scheduled microtasks. | |
146 Timer.run(() {}); | |
147 return true; | |
148 } | |
149 | |
150 _postResponse(SendPort replyPort, | |
151 Object id, | |
152 ServiceExtensionResponse response) { | |
153 assert(replyPort != null); | |
154 if (id == null) { | |
155 // No id -> no response. | |
156 // TODO(johnmccutchan): This code and the code in service.cc leave the | |
157 // service isolate with an open port. Consider posting 'null' to indicate | |
158 // that no response is coming. | |
159 return; | |
160 } | |
161 assert(id != null); | |
162 StringBuffer sb = new StringBuffer(); | |
163 sb.write('{"jsonrpc":"2.0",'); | |
164 if (response._isError()) { | |
165 sb.write('"error":'); | |
166 } else { | |
167 sb.write('"result":'); | |
168 } | |
169 sb.write('${response._toString()},'); | |
170 if (id is String) { | |
171 sb.write('"id":"$id"}'); | |
172 } else { | |
173 sb.write('"id":$id}'); | |
174 } | |
175 replyPort.send(sb.toString()); | |
176 } | |
OLD | NEW |