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 library channel.byte_stream; | 5 library channel.byte_stream; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:convert'; | 8 import 'dart:convert'; |
| 9 import 'dart:io'; | 9 import 'dart:io'; |
| 10 | 10 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 54 return responseStream.firstWhere((Response response) => response.id == id); | 54 return responseStream.firstWhere((Response response) => response.id == id); |
| 55 } | 55 } |
| 56 } | 56 } |
| 57 | 57 |
| 58 /** | 58 /** |
| 59 * Instances of the class [ByteStreamServerChannel] implement a | 59 * Instances of the class [ByteStreamServerChannel] implement a |
| 60 * [ServerCommunicationChannel] that uses a stream and a sink (typically, | 60 * [ServerCommunicationChannel] that uses a stream and a sink (typically, |
| 61 * standard input and standard output) to communicate with clients. | 61 * standard input and standard output) to communicate with clients. |
| 62 */ | 62 */ |
| 63 class ByteStreamServerChannel implements ServerCommunicationChannel { | 63 class ByteStreamServerChannel implements ServerCommunicationChannel { |
| 64 /** | 64 final Stream _input; |
| 65 * Value of [_outputState] indicating that there is no outstanding data in | |
| 66 * [_pendingOutput], and that the most recent flush of [_output] has | |
| 67 * completed. | |
| 68 */ | |
| 69 static const int _STATE_IDLE = 0; | |
| 70 | |
| 71 /** | |
| 72 * Value of [_outputState] indicating that there is outstanding data in | |
| 73 * [_pendingOutput], and that the most recent flush of [_output] has | |
| 74 * completed; therefore a microtask has been scheduled to send the data. | |
| 75 */ | |
| 76 static const int _STATE_MICROTASK_PENDING = 1; | |
| 77 | |
| 78 /** | |
| 79 * Value of [_outputState] indicating that data has been sent to the | |
| 80 * [_output] stream and flushed, but the flush has not completed, so we must | |
| 81 * wait for it to complete before sending more data. There may or may not be | |
| 82 * outstanding data in [_pendingOutput]. | |
| 83 */ | |
| 84 static const int _STATE_FLUSH_PENDING = 2; | |
| 85 | |
| 86 final Stream input; | |
| 87 | 65 |
| 88 final IOSink _output; | 66 final IOSink _output; |
| 89 | 67 |
| 90 /** | 68 /** |
| 91 * The instrumentation service that is to be used by this analysis server. | 69 * The instrumentation service that is to be used by this analysis server. |
| 92 */ | 70 */ |
| 93 final InstrumentationService instrumentationService; | 71 final InstrumentationService _instrumentationService; |
| 94 | 72 |
| 95 /** | 73 /** |
| 96 * Completer that will be signalled when the input stream is closed. | 74 * Completer that will be signalled when the input stream is closed. |
| 97 */ | 75 */ |
| 98 final Completer _closed = new Completer(); | 76 final Completer _closed = new Completer(); |
| 99 | 77 |
| 100 /** | 78 /** |
| 101 * State of the output stream (see constants above). | |
| 102 */ | |
| 103 int _outputState = _STATE_IDLE; | |
| 104 | |
| 105 /** | |
| 106 * List of strings that need to be sent to [_output] at the next available | |
| 107 * opportunity. | |
| 108 */ | |
| 109 List<String> _pendingOutput = <String>[]; | |
| 110 | |
| 111 /** | |
| 112 * True if [close] has been called. | 79 * True if [close] has been called. |
| 113 */ | 80 */ |
| 114 bool _closeRequested = false; | 81 bool _closeRequested = false; |
| 115 | 82 |
| 116 ByteStreamServerChannel(this.input, this._output, | 83 ByteStreamServerChannel(this._input, this._output, |
| 117 this.instrumentationService); | 84 this._instrumentationService); |
| 118 | 85 |
| 119 /** | 86 /** |
| 120 * Future that will be completed when the input stream is closed. | 87 * Future that will be completed when the input stream is closed. |
| 121 */ | 88 */ |
| 122 Future get closed { | 89 Future get closed { |
| 123 return _closed.future; | 90 return _closed.future; |
| 124 } | 91 } |
| 125 | 92 |
| 126 @override | 93 @override |
| 127 void close() { | 94 void close() { |
| 128 if (!_closeRequested) { | 95 if (!_closeRequested) { |
| 129 _closeRequested = true; | 96 _closeRequested = true; |
| 130 if (_outputState == _STATE_IDLE) { | 97 assert(!_closed.isCompleted); |
| 131 assert(!_closed.isCompleted); | 98 _closed.complete(); |
| 132 _closed.complete(); | |
| 133 } else { | |
| 134 // Nothing to do. [_flushCompleted] will call _closed.complete() after | |
| 135 // the flush completes. | |
| 136 } | |
| 137 } | 99 } |
| 138 } | 100 } |
| 139 | 101 |
| 140 @override | 102 @override |
| 141 void listen(void onRequest(Request request), {Function onError, void | 103 void listen(void onRequest(Request request), {Function onError, void |
| 142 onDone()}) { | 104 onDone()}) { |
| 143 input.transform( | 105 _input.transform( |
| 144 (new Utf8Codec()).decoder).transform( | 106 (new Utf8Codec()).decoder).transform( |
| 145 new LineSplitter()).listen( | 107 new LineSplitter()).listen( |
| 146 (String data) => _readRequest(data, onRequest), | 108 (String data) => _readRequest(data, onRequest), |
| 147 onError: onError, | 109 onError: onError, |
| 148 onDone: () { | 110 onDone: () { |
| 149 close(); | 111 close(); |
| 150 onDone(); | 112 onDone(); |
| 151 }); | 113 }); |
| 152 } | 114 } |
| 153 | 115 |
| 154 @override | 116 @override |
| 155 void sendNotification(Notification notification) { | 117 void sendNotification(Notification notification) { |
| 156 // Don't send any further notifications after the communication channel is | 118 // Don't send any further notifications after the communication channel is |
| 157 // closed. | 119 // closed. |
| 158 if (_closeRequested) { | 120 if (_closeRequested) { |
| 159 return; | 121 return; |
| 160 } | 122 } |
| 161 ServerCommunicationChannel.ToJson.start(); | 123 ServerCommunicationChannel.ToJson.start(); |
| 162 String jsonEncoding = JSON.encode(notification.toJson()); | 124 String jsonEncoding = JSON.encode(notification.toJson()); |
| 163 ServerCommunicationChannel.ToJson.stop(); | 125 ServerCommunicationChannel.ToJson.stop(); |
| 164 _outputLine(jsonEncoding); | 126 _outputLine(jsonEncoding); |
| 165 instrumentationService.logNotification(jsonEncoding); | 127 _instrumentationService.logNotification(jsonEncoding); |
| 166 } | 128 } |
| 167 | 129 |
| 168 @override | 130 @override |
| 169 void sendResponse(Response response) { | 131 void sendResponse(Response response) { |
| 170 // Don't send any further responses after the communication channel is | 132 // Don't send any further responses after the communication channel is |
| 171 // closed. | 133 // closed. |
| 172 if (_closeRequested) { | 134 if (_closeRequested) { |
| 173 return; | 135 return; |
| 174 } | 136 } |
| 175 ServerCommunicationChannel.ToJson.start(); | 137 ServerCommunicationChannel.ToJson.start(); |
| 176 String jsonEncoding = JSON.encode(response.toJson()); | 138 String jsonEncoding = JSON.encode(response.toJson()); |
| 177 ServerCommunicationChannel.ToJson.stop(); | 139 ServerCommunicationChannel.ToJson.stop(); |
| 178 _outputLine(jsonEncoding); | 140 _outputLine(jsonEncoding); |
| 179 instrumentationService.logResponse(jsonEncoding); | 141 _instrumentationService.logResponse(jsonEncoding); |
| 180 } | |
| 181 | |
| 182 /** | |
| 183 * Callback invoked after a flush of [_output] completes. Closes the stream | |
| 184 * if necessary. Otherwise schedules additional pending output. | |
| 185 */ | |
| 186 void _flushCompleted(_) { | |
| 187 assert(_outputState == _STATE_FLUSH_PENDING); | |
| 188 if (_pendingOutput.isNotEmpty) { | |
| 189 _output.write(_pendingOutput.join()); | |
| 190 _output.flush().then(_flushCompleted); | |
| 191 _pendingOutput.clear(); | |
| 192 // Since we've done another flush, stay in _STATE_FLUSH_PENDING. | |
| 193 } else { | |
| 194 _outputState = _STATE_IDLE; | |
| 195 if (_closeRequested) { | |
| 196 assert(!_closed.isCompleted); | |
| 197 _closed.complete(); | |
| 198 } | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 /** | |
| 203 * Microtask that writes pending output to the output stream and flushes it. | |
| 204 */ | |
| 205 void _microtask() { | |
| 206 assert(_outputState == _STATE_MICROTASK_PENDING); | |
| 207 _output.write(_pendingOutput.join()); | |
| 208 _output.flush().then(_flushCompleted); | |
| 209 _pendingOutput.clear(); | |
| 210 _outputState = _STATE_FLUSH_PENDING; | |
| 211 } | 142 } |
| 212 | 143 |
| 213 /** | 144 /** |
| 214 * Send the string [s] to [_output] followed by a newline. | 145 * Send the string [s] to [_output] followed by a newline. |
| 215 */ | 146 */ |
| 216 void _outputLine(String s) { | 147 void _outputLine(String s) { |
| 217 _pendingOutput.add(s); | 148 stdout.writeln(s); |
|
Paul Berry
2015/01/23 21:26:00
Did you mean to do _output.writeln()?
| |
| 218 _pendingOutput.add('\n'); | |
| 219 if (_outputState == _STATE_IDLE) { | |
| 220 // Don't send the output just yet; schedule a microtask to do it, so that | |
| 221 // if caller decides to output additional lines, they will get sent in | |
| 222 // the same call to _output.write(). | |
| 223 new Future.microtask(_microtask); | |
| 224 _outputState = _STATE_MICROTASK_PENDING; | |
| 225 } | |
| 226 } | 149 } |
| 227 | 150 |
| 228 /** | 151 /** |
| 229 * Read a request from the given [data] and use the given function to handle | 152 * Read a request from the given [data] and use the given function to handle |
| 230 * the request. | 153 * the request. |
| 231 */ | 154 */ |
| 232 void _readRequest(Object data, void onRequest(Request request)) { | 155 void _readRequest(Object data, void onRequest(Request request)) { |
| 233 // Ignore any further requests after the communication channel is closed. | 156 // Ignore any further requests after the communication channel is closed. |
| 234 if (_closed.isCompleted) { | 157 if (_closed.isCompleted) { |
| 235 return; | 158 return; |
| 236 } | 159 } |
| 237 instrumentationService.logRequest(data); | 160 _instrumentationService.logRequest(data); |
| 238 // Parse the string as a JSON descriptor and process the resulting | 161 // Parse the string as a JSON descriptor and process the resulting |
| 239 // structure as a request. | 162 // structure as a request. |
| 240 ServerCommunicationChannel.FromJson.start(); | 163 ServerCommunicationChannel.FromJson.start(); |
| 241 Request request = new Request.fromString(data); | 164 Request request = new Request.fromString(data); |
| 242 ServerCommunicationChannel.FromJson.stop(); | 165 ServerCommunicationChannel.FromJson.stop(); |
| 243 if (request == null) { | 166 if (request == null) { |
| 244 sendResponse(new Response.invalidRequestFormat()); | 167 sendResponse(new Response.invalidRequestFormat()); |
| 245 return; | 168 return; |
| 246 } | 169 } |
| 247 onRequest(request); | 170 onRequest(request); |
| 248 } | 171 } |
| 249 } | 172 } |
| OLD | NEW |