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