OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of dart.io; | 5 part of dart.io; |
6 | 6 |
7 /** | 7 /** |
8 * SecureSocket provides a secure (SSL or TLS) client connection to a server. | 8 * A high-level class for communicating securely over a TCP socket, using |
9 * The certificate provided by the server is checked | 9 * TLS and SSL. The [SecureSocket] exposes both a [Stream] and an |
10 * using the certificate database (optionally) provided in initialize(). | 10 * [IOSink] interface, making it ideal for using together with |
| 11 * other [Stream]s. |
11 */ | 12 */ |
12 abstract class SecureSocket implements Socket { | 13 abstract class SecureSocket implements Socket { |
| 14 external factory SecureSocket._(RawSecureSocket rawSocket); |
| 15 |
13 /** | 16 /** |
14 * Constructs a new secure client socket and connect it to the given | 17 * Constructs a new secure client socket and connect it to the given |
15 * host on the given port. The returned socket is not yet connected | 18 * [host] on port [port]. The returned Future will complete with a |
16 * but ready for registration of callbacks. If sendClientCertificate is | 19 * [SecureSocket] that is connected and ready for subscription. |
17 * set to true, the socket will send a client certificate if one is | 20 * |
18 * requested by the server. If clientCertificate is the nickname of | 21 * If [sendClientCertificate] is set to true, the socket will send a client |
19 * a certificate in the certificate database, that certificate will be sent. | 22 * certificate if one is requested by the server. |
20 * If clientCertificate is null, which is the usual use case, an | 23 * |
| 24 * If [certificateName] is the nickname of a certificate in the certificate |
| 25 * database, that certificate will be sent. |
| 26 * |
| 27 * If [certificateName] is null, which is the usual use case, an |
21 * appropriate certificate will be searched for in the database and | 28 * appropriate certificate will be searched for in the database and |
22 * sent automatically, based on what the server says it will accept. | 29 * sent automatically, based on what the server says it will accept. |
| 30 * |
| 31 * [onBadCertificate] is an optional handler for unverifiable certificates. |
| 32 * The handler receives the [X509Certificate], and can inspect it and |
| 33 * decide (or let the user decide) whether to accept |
| 34 * the connection or not. The handler should return true |
| 35 * to continue the [SecureSocket] connection. |
23 */ | 36 */ |
24 factory SecureSocket(String host, | 37 static Future<SecureSocket> connect( |
25 int port, | 38 String host, |
26 {bool sendClientCertificate: false, | 39 int port, |
27 String certificateName}) { | 40 {bool sendClientCertificate: false, |
28 return new _SecureSocket(host, | 41 String certificateName, |
29 port, | 42 bool onBadCertificate(X509Certificate certificate)}) { |
30 certificateName, | 43 return RawSecureSocket.connect(host, |
31 is_server: false, | 44 port, |
32 sendClientCertificate: sendClientCertificate); | 45 sendClientCertificate: sendClientCertificate, |
| 46 certificateName: certificateName, |
| 47 onBadCertificate: onBadCertificate) |
| 48 .then((rawSocket) => new SecureSocket._(rawSocket)); |
33 } | 49 } |
34 | 50 |
35 /** | 51 /** |
36 * Install a handler for unverifiable certificates. The handler can inspect | 52 * Get the peer certificate for a connected SecureSocket. If this |
37 * the certificate, and decide (or let the user decide) whether to accept | 53 * SecureSocket is the server end of a secure socket connection, |
38 * the connection or not. The callback should return true | 54 * [peerCertificate] will return the client certificate, or null, if no |
39 * to continue the SecureSocket connection. | 55 * client certificate was received. If it is the client end, |
40 */ | 56 * [peerCertificate] will return the server's certificate. |
41 void set onBadCertificate(bool callback(X509Certificate certificate)); | |
42 | |
43 /** | |
44 * Get the peerCertificate for a connected secure socket. For a server | |
45 * socket, this will return the client certificate, or null, if no | |
46 * client certificate was received. For a client socket, this | |
47 * will return the server's certificate. | |
48 */ | 57 */ |
49 X509Certificate get peerCertificate; | 58 X509Certificate get peerCertificate; |
50 | 59 |
51 /** | 60 /** |
52 * Initializes the NSS library. If [initialize] is not called, the library | 61 * Initializes the NSS library. If [initialize] is not called, the library |
53 * is automatically initialized as if [initialize] were called with no | 62 * is automatically initialized as if [initialize] were called with no |
54 * arguments. | 63 * arguments. |
55 * | 64 * |
56 * The optional argument [database] is the path to a certificate database | 65 * The optional argument [database] is the path to a certificate database |
57 * containing root certificates for verifying certificate paths on | 66 * containing root certificates for verifying certificate paths on |
58 * client connections, and server certificates to provide on server | 67 * client connections, and server certificates to provide on server |
59 * connections. The argument [password] should be used when creating | 68 * connections. The argument [password] should be used when creating |
60 * secure server sockets, to allow the private key of the server | 69 * secure server sockets, to allow the private key of the server |
61 * certificate to be fetched. If [useBuiltinRoots] is true (the default), | 70 * certificate to be fetched. If [useBuiltinRoots] is true (the default), |
(...skipping 19 matching lines...) Expand all Loading... |
81 * front of the absolute path of the database directory, or setting the | 90 * front of the absolute path of the database directory, or setting the |
82 * environment variable [[NSS_DEFAULT_DB_TYPE]] to "sql". | 91 * environment variable [[NSS_DEFAULT_DB_TYPE]] to "sql". |
83 */ | 92 */ |
84 external static void initialize({String database, | 93 external static void initialize({String database, |
85 String password, | 94 String password, |
86 bool useBuiltinRoots: true}); | 95 bool useBuiltinRoots: true}); |
87 } | 96 } |
88 | 97 |
89 | 98 |
90 /** | 99 /** |
| 100 * RawSecureSocket provides a secure (SSL or TLS) network connection. |
| 101 * Client connections to a server are provided by calling |
| 102 * RawSecureSocket.connect. A secure server, created with |
| 103 * RawSecureServerSocket, also returns RawSecureSocket objects representing |
| 104 * the server end of a secure connection. |
| 105 * The certificate provided by the server is checked |
| 106 * using the certificate database provided in SecureSocket.initialize, and/or |
| 107 * the default built-in root certificates. |
| 108 */ |
| 109 abstract class RawSecureSocket implements RawSocket { |
| 110 /** |
| 111 * Constructs a new secure client socket and connect it to the given |
| 112 * host on the given port. The returned Future is completed with the |
| 113 * RawSecureSocket when it is connected and ready for subscription. |
| 114 * |
| 115 * The certificate provided by the server is checked using the certificate |
| 116 * database provided in [SecureSocket.initialize], and/or the default built-in |
| 117 * root certificates. If [sendClientCertificate] is |
| 118 * set to true, the socket will send a client certificate if one is |
| 119 * requested by the server. If [certificateName] is the nickname of |
| 120 * a certificate in the certificate database, that certificate will be sent. |
| 121 * If [certificateName] is null, which is the usual use case, an |
| 122 * appropriate certificate will be searched for in the database and |
| 123 * sent automatically, based on what the server says it will accept. |
| 124 * |
| 125 * [onBadCertificate] is an optional handler for unverifiable certificates. |
| 126 * The handler receives the [X509Certificate], and can inspect it and |
| 127 * decide (or let the user decide) whether to accept |
| 128 * the connection or not. The handler should return true |
| 129 * to continue the [RawSecureSocket] connection. |
| 130 */ |
| 131 static Future<RawSecureSocket> connect( |
| 132 String host, |
| 133 int port, |
| 134 {bool sendClientCertificate: false, |
| 135 String certificateName, |
| 136 bool onBadCertificate(X509Certificate certificate)}) { |
| 137 return _RawSecureSocket.connect( |
| 138 host, |
| 139 port, |
| 140 certificateName, |
| 141 is_server: false, |
| 142 sendClientCertificate: sendClientCertificate, |
| 143 onBadCertificate: onBadCertificate); |
| 144 } |
| 145 |
| 146 /** |
| 147 * Get the peer certificate for a connected RawSecureSocket. If this |
| 148 * RawSecureSocket is the server end of a secure socket connection, |
| 149 * [peerCertificate] will return the client certificate, or null, if no |
| 150 * client certificate was received. If it is the client end, |
| 151 * [peerCertificate] will return the server's certificate. |
| 152 */ |
| 153 X509Certificate get peerCertificate; |
| 154 } |
| 155 |
| 156 |
| 157 /** |
91 * X509Certificate represents an SSL certificate, with accessors to | 158 * X509Certificate represents an SSL certificate, with accessors to |
92 * get the fields of the certificate. | 159 * get the fields of the certificate. |
93 */ | 160 */ |
94 class X509Certificate { | 161 class X509Certificate { |
95 X509Certificate(this.subject, | 162 X509Certificate(this.subject, |
96 this.issuer, | 163 this.issuer, |
97 this.startValidity, | 164 this.startValidity, |
98 this.endValidity); | 165 this.endValidity); |
99 final String subject; | 166 final String subject; |
100 final String issuer; | 167 final String issuer; |
101 final DateTime startValidity; | 168 final DateTime startValidity; |
102 final DateTime endValidity; | 169 final DateTime endValidity; |
103 } | 170 } |
104 | 171 |
105 | 172 |
106 class _SecureSocket implements SecureSocket { | 173 class _RawSecureSocket extends Stream<RawSocketEvent> |
| 174 implements RawSecureSocket { |
107 // Status states | 175 // Status states |
108 static final int NOT_CONNECTED = 200; | 176 static final int NOT_CONNECTED = 200; |
109 static final int HANDSHAKE = 201; | 177 static final int HANDSHAKE = 201; |
110 static final int CONNECTED = 202; | 178 static final int CONNECTED = 202; |
111 static final int CLOSED = 203; | 179 static final int CLOSED = 203; |
112 | 180 |
113 // Buffer identifiers. | 181 // Buffer identifiers. |
114 // These must agree with those in the native C++ implementation. | 182 // These must agree with those in the native C++ implementation. |
115 static final int READ_PLAINTEXT = 0; | 183 static final int READ_PLAINTEXT = 0; |
116 static final int WRITE_PLAINTEXT = 1; | 184 static final int WRITE_PLAINTEXT = 1; |
117 static final int READ_ENCRYPTED = 2; | 185 static final int READ_ENCRYPTED = 2; |
118 static final int WRITE_ENCRYPTED = 3; | 186 static final int WRITE_ENCRYPTED = 3; |
119 static final int NUM_BUFFERS = 4; | 187 static final int NUM_BUFFERS = 4; |
120 | 188 |
121 _SecureSocket(String this.host, | 189 RawSocket _socket; |
122 int requestedPort, | 190 final Completer<_RawSecureSocket> _handshakeComplete = |
123 String this.certificateName, | 191 new Completer<_RawSecureSocket>(); |
124 {bool this.is_server, | 192 StreamController<RawSocketEvent> _controller; |
125 Socket this.socket, | 193 Stream<RawSocketEvent> _stream; |
126 bool this.requestClientCertificate: false, | 194 StreamSubscription<RawSocketEvent> _socketSubscription; |
127 bool this.requireClientCertificate: false, | 195 final String host; |
128 bool this.sendClientCertificate: false}) | 196 final bool is_server; |
129 : secureFilter = new _SecureFilter() { | 197 final String certificateName; |
130 // Throw an ArgumentError if any field is invalid. | 198 final bool requestClientCertificate; |
| 199 final bool requireClientCertificate; |
| 200 final bool sendClientCertificate; |
| 201 final Function onBadCertificate; |
| 202 |
| 203 var _status = NOT_CONNECTED; |
| 204 bool _writeEventsEnabled = true; |
| 205 bool _readEventsEnabled = true; |
| 206 bool _socketClosedRead = false; // The network socket is closed for reading. |
| 207 bool _socketClosedWrite = false; // The network socket is closed for writing. |
| 208 bool _closedRead = false; // The secure socket has fired an onClosed event. |
| 209 bool _closedWrite = false; // The secure socket has been closed for writing. |
| 210 bool _filterReadEmpty = true; // There is no buffered data to read. |
| 211 bool _filterWriteEmpty = true; // There is no buffered data to be written. |
| 212 bool _connectPending = false; |
| 213 _SecureFilter _secureFilter = new _SecureFilter(); |
| 214 |
| 215 static Future<_RawSecureSocket> connect( |
| 216 String host, |
| 217 int requestedPort, |
| 218 String certificateName, |
| 219 {bool is_server, |
| 220 RawSocket socket, |
| 221 bool requestClientCertificate: false, |
| 222 bool requireClientCertificate: false, |
| 223 bool sendClientCertificate: false, |
| 224 bool onBadCertificate(X509Certificate certificate)}){ |
| 225 return new _RawSecureSocket(host, |
| 226 requestedPort, |
| 227 certificateName, |
| 228 is_server, |
| 229 socket, |
| 230 requestClientCertificate, |
| 231 requireClientCertificate, |
| 232 sendClientCertificate, |
| 233 onBadCertificate) |
| 234 ._handshakeComplete.future; |
| 235 } |
| 236 |
| 237 _RawSecureSocket( |
| 238 String this.host, |
| 239 int requestedPort, |
| 240 String this.certificateName, |
| 241 bool this.is_server, |
| 242 RawSocket socket, |
| 243 bool this.requestClientCertificate, |
| 244 bool this.requireClientCertificate, |
| 245 bool this.sendClientCertificate, |
| 246 bool this.onBadCertificate(X509Certificate certificate)) { |
| 247 _controller = new StreamController<RawSocketEvent>( |
| 248 onPauseStateChange: _onPauseStateChange, |
| 249 onSubscriptionStateChange: _onSubscriptionStateChange); |
| 250 _stream = _controller.stream; |
| 251 // Throw an ArgumentError if any field is invalid. After this, all |
| 252 // errors will be reported through the future or the stream. |
131 _verifyFields(); | 253 _verifyFields(); |
| 254 _secureFilter.init(); |
| 255 _secureFilter.registerHandshakeCompleteCallback( |
| 256 _secureHandshakeCompleteHandler); |
| 257 if (onBadCertificate != null) { |
| 258 _secureFilter.registerBadCertificateCallback(onBadCertificate); |
| 259 } |
| 260 var futureSocket; |
132 if (socket == null) { | 261 if (socket == null) { |
133 socket = new Socket(host, requestedPort); | 262 futureSocket = RawSocket.connect(host, requestedPort); |
134 } | 263 } else { |
135 socket.onConnect = _secureConnectHandler; | 264 futureSocket = new Future.immediate(socket); |
136 socket.onData = _secureDataHandler; | 265 } |
137 socket.onClosed = _secureCloseHandler; | 266 futureSocket.then((rawSocket) { |
138 socket.onError = _secureErrorHandler; | 267 rawSocket.writeEventsEnabled = false; |
139 secureFilter.init(); | 268 _socket = rawSocket; |
140 secureFilter.registerHandshakeCompleteCallback( | 269 _socketSubscription = _socket.listen(_eventDispatcher, |
141 _secureHandshakeCompleteHandler); | 270 onError: _errorHandler, |
| 271 onDone: _doneHandler); |
| 272 _connectPending = true; |
| 273 _secureFilter.connect(host, |
| 274 port, |
| 275 is_server, |
| 276 certificateName, |
| 277 requestClientCertificate || |
| 278 requireClientCertificate, |
| 279 requireClientCertificate, |
| 280 sendClientCertificate); |
| 281 _status = HANDSHAKE; |
| 282 _secureHandshake(); |
| 283 }) |
| 284 .catchError((error) { |
| 285 _handshakeComplete.completeError(error); |
| 286 close(); |
| 287 }); |
| 288 } |
| 289 |
| 290 StreamSubscription listen(void onData(RawSocketEvent data), |
| 291 {void onError(AsyncError error), |
| 292 void onDone(), |
| 293 bool unsubscribeOnError}) { |
| 294 if (_writeEventsEnabled) { |
| 295 _writeEventsEnabled = false; |
| 296 _controller.add(RawSocketEvent.WRITE); |
| 297 } |
| 298 return _stream.listen(onData, |
| 299 onError: onError, |
| 300 onDone: onDone, |
| 301 unsubscribeOnError: unsubscribeOnError); |
142 } | 302 } |
143 | 303 |
144 void _verifyFields() { | 304 void _verifyFields() { |
145 if (host is! String) throw new ArgumentError( | |
146 "SecureSocket constructor: host is not a String"); | |
147 assert(is_server is bool); | 305 assert(is_server is bool); |
148 assert(socket == null || socket is Socket); | 306 assert(_socket == null || _socket is RawSocket); |
| 307 if (host is! String) { |
| 308 throw new ArgumentError( |
| 309 "RawSecureSocket constructor: host is not a String"); |
| 310 } |
149 if (certificateName != null && certificateName is! String) { | 311 if (certificateName != null && certificateName is! String) { |
150 throw new ArgumentError( | 312 throw new ArgumentError("certificateName is not null or a String"); |
151 "SecureSocket constructor: certificateName is not null or a String"); | |
152 } | 313 } |
153 if (certificateName == null && is_server) { | 314 if (certificateName == null && is_server) { |
154 throw new ArgumentError( | 315 throw new ArgumentError("certificateName is null on a server"); |
155 "SecureSocket constructor: certificateName is null on a server"); | |
156 } | 316 } |
157 if (requestClientCertificate is! bool) { | 317 if (requestClientCertificate is! bool) { |
158 throw new ArgumentError( | 318 throw new ArgumentError("requestClientCertificate is not a bool"); |
159 "SecureSocket constructor: requestClientCertificate is not a bool"); | |
160 } | 319 } |
161 if (requireClientCertificate is! bool) { | 320 if (requireClientCertificate is! bool) { |
162 throw new ArgumentError( | 321 throw new ArgumentError("requireClientCertificate is not a bool"); |
163 "SecureSocket constructor: requireClientCertificate is not a bool"); | |
164 } | 322 } |
165 if (sendClientCertificate is! bool) { | 323 if (sendClientCertificate is! bool) { |
166 throw new ArgumentError( | 324 throw new ArgumentError("sendClientCertificate is not a bool"); |
167 "SecureSocket constructor: sendClientCertificate is not a bool"); | 325 } |
168 } | 326 if (onBadCertificate != null && onBadCertificate is! Function) { |
169 } | 327 throw new ArgumentError("onBadCertificate is not null or a Function"); |
170 | 328 } |
171 int get port => socket.port; | 329 } |
172 | 330 |
173 String get remoteHost => socket.remoteHost; | 331 int get port => _socket.port; |
174 | 332 |
175 int get remotePort => socket.remotePort; | 333 String get remoteHost => _socket.remoteHost; |
176 | 334 |
177 void set onClosed(void callback()) { | 335 int get remotePort => _socket.remotePort; |
178 if (_inputStream != null && callback != null) { | |
179 throw new StreamException( | |
180 "Cannot set close handler when input stream is used"); | |
181 } | |
182 _onClosed = callback; | |
183 } | |
184 | |
185 void set _onClosed(void callback()) { | |
186 _socketCloseHandler = callback; | |
187 } | |
188 | |
189 void set onConnect(void callback()) { | |
190 if (_status == CONNECTED || _status == CLOSED) { | |
191 throw new StreamException( | |
192 "Cannot set connect handler when already connected"); | |
193 } | |
194 _onConnect = callback; | |
195 } | |
196 | |
197 void set _onConnect(void callback()) { | |
198 _socketConnectHandler = callback; | |
199 } | |
200 | |
201 void set onData(void callback()) { | |
202 if (_inputStream != null && callback != null) { | |
203 throw new StreamException( | |
204 "Cannot set data handler when input stream is used"); | |
205 } | |
206 _onData = callback; | |
207 } | |
208 | |
209 void set _onData(void callback()) { | |
210 _socketDataHandler = callback; | |
211 } | |
212 | |
213 void set onError(void callback(e)) { | |
214 _socketErrorHandler = callback; | |
215 } | |
216 | |
217 void set onWrite(void callback()) { | |
218 if (_outputStream != null && callback != null) { | |
219 throw new StreamException( | |
220 "Cannot set write handler when output stream is used"); | |
221 } | |
222 _onWrite = callback; | |
223 } | |
224 | |
225 void set _onWrite(void callback()) { | |
226 _socketWriteHandler = callback; | |
227 // Reset the one-shot onWrite handler. | |
228 socket.onWrite = _secureWriteHandler; | |
229 } | |
230 | |
231 void set onBadCertificate(bool callback(X509Certificate certificate)) { | |
232 if (callback is! Function && callback != null) { | |
233 throw new SocketIOException( | |
234 "Callback provided to onBadCertificate is not a function or null"); | |
235 } | |
236 secureFilter.registerBadCertificateCallback(callback); | |
237 } | |
238 | |
239 InputStream get inputStream { | |
240 if (_inputStream == null) { | |
241 if (_socketDataHandler != null || _socketCloseHandler != null) { | |
242 throw new StreamException( | |
243 "Cannot get input stream when socket handlers are used"); | |
244 } | |
245 _inputStream = new _SocketInputStream(this); | |
246 } | |
247 return _inputStream; | |
248 } | |
249 | |
250 OutputStream get outputStream { | |
251 if (_outputStream == null) { | |
252 if (_socketWriteHandler != null) { | |
253 throw new StreamException( | |
254 "Cannot get output stream when socket write handler is used"); | |
255 } | |
256 _outputStream = new _SocketOutputStream(this); | |
257 } | |
258 return _outputStream; | |
259 } | |
260 | 336 |
261 int available() { | 337 int available() { |
262 throw new UnimplementedError("SecureSocket.available not implemented yet"); | 338 if (_status != CONNECTED) return 0; |
263 } | 339 _readEncryptedData(); |
264 | 340 return _secureFilter.buffers[READ_PLAINTEXT].length; |
265 void close([bool halfClose = false]) { | 341 } |
266 if (_status == CLOSED) return; | 342 |
267 if (halfClose) { | 343 void close() { |
| 344 _closedWrite = true; |
| 345 _closedRead = true; |
| 346 if (_socket != null) { |
| 347 _socket.close(); |
| 348 } |
| 349 _socketClosedWrite = true; |
| 350 _socketClosedRead = true; |
| 351 if (_secureFilter != null) { |
| 352 _secureFilter.destroy(); |
| 353 _secureFilter = null; |
| 354 } |
| 355 if (_socketSubscription != null) { |
| 356 _socketSubscription.cancel(); |
| 357 } |
| 358 _controller.close(); |
| 359 _status = CLOSED; |
| 360 } |
| 361 |
| 362 void shutdown(SocketDirection direction) { |
| 363 if (direction == SocketDirection.BOTH) { |
| 364 close(); |
| 365 } else if (direction == SocketDirection.SEND) { |
268 _closedWrite = true; | 366 _closedWrite = true; |
269 _writeEncryptedData(); | 367 _writeEncryptedData(); |
270 if (_filterWriteEmpty) { | 368 if (_filterWriteEmpty) { |
271 socket.close(true); | 369 _socket.shutdown(SocketDirection.SEND); |
272 _socketClosedWrite = true; | 370 _socketClosedWrite = true; |
273 if (_closedRead) { | 371 if (_closedRead) { |
274 close(false); | 372 close(); |
275 } | 373 } |
276 } | 374 } |
| 375 } else if (direction == SocketDirection.RECEIVE) { |
| 376 _closedRead = true; |
| 377 _socketClosedRead = true; |
| 378 _socket.shutdown(SocketDirection.RECEIVE); |
| 379 if (_socketClosedWrite) { |
| 380 close(); |
| 381 } |
| 382 } |
| 383 } |
| 384 |
| 385 bool get writeEventsEnabled => _writeEventsEnabled; |
| 386 |
| 387 void set writeEventsEnabled(bool value) { |
| 388 if (value && |
| 389 _secureFilter != null && |
| 390 _secureFilter.buffers[WRITE_PLAINTEXT].free > 0) { |
| 391 new Timer(0, (_) => _controller.add(RawSocketEvent.WRITE)); |
277 } else { | 392 } else { |
278 _closedWrite = true; | 393 _writeEventsEnabled = value; |
279 _closedRead = true; | 394 } |
280 socket.close(false); | 395 } |
281 _socketClosedWrite = true; | 396 |
282 _socketClosedRead = true; | 397 bool get readEventsEnabled => _readEventsEnabled; |
283 secureFilter.destroy(); | 398 |
284 secureFilter = null; | 399 void set readEventsEnabled(bool value) { |
285 if (scheduledDataEvent != null) { | 400 _readEventsEnabled = value; |
286 scheduledDataEvent.cancel(); | 401 if (_socketClosedRead) { |
| 402 if (value) { |
| 403 // We have no underlying socket to set off read events. |
| 404 new Timer(0, (_) => _readHandler()); |
287 } | 405 } |
288 _status = CLOSED; | 406 } |
289 } | 407 } |
290 } | |
291 | |
292 void _closeWrite() => close(true); | |
293 | 408 |
294 List<int> read([int len]) { | 409 List<int> read([int len]) { |
295 if (_closedRead) { | 410 if (_closedRead) { |
296 throw new SocketIOException("Reading from a closed socket"); | 411 throw new SocketIOException("Reading from a closed socket"); |
297 } | 412 } |
298 if (_status != CONNECTED) { | 413 if (_status != CONNECTED) { |
299 return new List<int>(0); | 414 return new List<int>(0); |
300 } | 415 } |
301 var buffer = secureFilter.buffers[READ_PLAINTEXT]; | 416 var buffer = _secureFilter.buffers[READ_PLAINTEXT]; |
302 _readEncryptedData(); | 417 _readEncryptedData(); |
303 int toRead = buffer.length; | 418 int toRead = buffer.length; |
304 if (len != null) { | 419 if (len != null) { |
305 if (len is! int || len < 0) { | 420 if (len is! int || len < 0) { |
306 throw new ArgumentError( | 421 throw new ArgumentError( |
307 "Invalid len parameter in SecureSocket.read (len: $len)"); | 422 "Invalid len parameter in SecureSocket.read (len: $len)"); |
308 } | 423 } |
309 if (len < toRead) { | 424 if (len < toRead) { |
310 toRead = len; | 425 toRead = len; |
311 } | 426 } |
312 } | 427 } |
313 List<int> result = (toRead == 0) ? null : | 428 List<int> result = (toRead == 0) ? null : |
314 buffer.data.getRange(buffer.start, toRead); | 429 buffer.data.getRange(buffer.start, toRead); |
315 buffer.advanceStart(toRead); | 430 buffer.advanceStart(toRead); |
316 _setHandlersAfterRead(); | |
317 return result; | |
318 } | |
319 | 431 |
320 int readList(List<int> data, int offset, int bytes) { | 432 // Set up a read event if the filter still has data. |
321 if (_closedRead) { | 433 if (!_filterReadEmpty) { |
322 throw new SocketIOException("Reading from a closed socket"); | 434 new Timer(0, (_) => _readHandler()); |
323 } | |
324 if (offset < 0 || bytes < 0 || offset + bytes > data.length) { | |
325 throw new ArgumentError( | |
326 "Invalid offset or bytes in SecureSocket.readList"); | |
327 } | |
328 if (_status != CONNECTED && _status != CLOSED) { | |
329 return 0; | |
330 } | 435 } |
331 | 436 |
332 int bytesRead = 0; | 437 if (_socketClosedRead) { // An onClose event is pending. |
333 var buffer = secureFilter.buffers[READ_PLAINTEXT]; | 438 // _closedRead is false, since we are in a read call. |
334 // TODO(whesse): Currently this fails if the if is turned into a while loop. | 439 if (!_filterReadEmpty) { |
335 // Fix it so that it can loop and read more than one buffer's worth of data. | 440 // _filterReadEmpty may be out of date since read empties |
336 if (bytes > bytesRead) { | 441 // the plaintext buffer after calling _readEncryptedData. |
337 _readEncryptedData(); | 442 // TODO(whesse): Fix this as part of fixing read. |
338 if (buffer.length > 0) { | 443 _readEncryptedData(); |
339 int toRead = min(bytes - bytesRead, buffer.length); | 444 } |
340 data.setRange(offset, toRead, buffer.data, buffer.start); | 445 if (_filterReadEmpty) { |
341 buffer.advanceStart(toRead); | 446 // This can't be an else clause: the value of _filterReadEmpty changes. |
342 bytesRead += toRead; | 447 // This must be asynchronous, because we are in a read call. |
343 offset += toRead; | 448 new Timer(0, (_) => _closeHandler()); |
344 } | 449 } |
345 } | 450 } |
346 | 451 |
347 _setHandlersAfterRead(); | 452 return result; |
348 return bytesRead; | |
349 } | 453 } |
350 | 454 |
351 // Write the data to the socket, and flush it as much as possible | 455 // Write the data to the socket, and flush it as much as possible |
352 // until it would block. If the write would block, _writeEncryptedData sets | 456 // until it would block. If the write would block, _writeEncryptedData sets |
353 // up handlers to flush the pipeline when possible. | 457 // up handlers to flush the pipeline when possible. |
354 int writeList(List<int> data, int offset, int bytes) { | 458 int write(List<int> data, [int offset, int bytes]) { |
355 if (_closedWrite) { | 459 if (_closedWrite) { |
356 throw new SocketIOException("Writing to a closed socket"); | 460 _controller.signalError(new AsyncError(new SocketIOException( |
| 461 "Writing to a closed socket"))); |
| 462 return 0; |
357 } | 463 } |
358 if (_status != CONNECTED) return 0; | 464 if (_status != CONNECTED) return 0; |
359 var buffer = secureFilter.buffers[WRITE_PLAINTEXT]; | 465 var buffer = _secureFilter.buffers[WRITE_PLAINTEXT]; |
360 if (bytes > buffer.free) { | 466 if (bytes > buffer.free) { |
361 bytes = buffer.free; | 467 bytes = buffer.free; |
362 } | 468 } |
363 if (bytes > 0) { | 469 if (bytes > 0) { |
364 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset); | 470 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset); |
365 buffer.length += bytes; | 471 buffer.length += bytes; |
366 } | 472 } |
367 _writeEncryptedData(); // Tries to flush all pipeline stages. | 473 _writeEncryptedData(); // Tries to flush all pipeline stages. |
368 return bytes; | 474 return bytes; |
369 } | 475 } |
370 | 476 |
371 X509Certificate get peerCertificate => secureFilter.peerCertificate; | 477 X509Certificate get peerCertificate => _secureFilter.peerCertificate; |
372 | 478 |
373 void _secureConnectHandler() { | 479 void _writeHandler() { |
374 _connectPending = true; | 480 if (_status == CLOSED) return; |
375 secureFilter.connect(host, | |
376 port, | |
377 is_server, | |
378 certificateName, | |
379 requestClientCertificate || requireClientCertificate, | |
380 requireClientCertificate, | |
381 sendClientCertificate); | |
382 _status = HANDSHAKE; | |
383 _secureHandshake(); | |
384 } | |
385 | |
386 void _secureWriteHandler() { | |
387 _writeEncryptedData(); | 481 _writeEncryptedData(); |
388 if (_filterWriteEmpty && _closedWrite && !_socketClosedWrite) { | 482 if (_filterWriteEmpty && _closedWrite && !_socketClosedWrite) { |
389 close(true); | 483 // Close _socket for write, by calling shutdown(), to avoid cloning the |
| 484 // socket closing code in shutdown(). |
| 485 shutdown(SocketDirection.SEND); |
390 } | 486 } |
391 if (_status == HANDSHAKE) { | 487 if (_status == HANDSHAKE) { |
392 _secureHandshake(); | 488 try { |
| 489 _secureHandshake(); |
| 490 } catch (e) { _reportError(e, "RawSecureSocket error"); } |
393 } else if (_status == CONNECTED && | 491 } else if (_status == CONNECTED && |
394 _socketWriteHandler != null && | 492 _writeEventsEnabled && |
395 secureFilter.buffers[WRITE_PLAINTEXT].free > 0) { | 493 _secureFilter.buffers[WRITE_PLAINTEXT].free > 0) { |
396 // We must be able to set onWrite from the onWrite callback. | |
397 var handler = _socketWriteHandler; | |
398 // Reset the one-shot handler. | 494 // Reset the one-shot handler. |
399 _socketWriteHandler = null; | 495 _writeEventsEnabled = false; |
400 handler(); | 496 _controller.add(RawSocketEvent.WRITE); |
401 } | 497 } |
402 } | 498 } |
403 | 499 |
404 void _secureDataHandler() { | 500 void _eventDispatcher(RawSocketEvent event) { |
405 if (_status == HANDSHAKE) { | 501 if (event == RawSocketEvent.READ) { |
| 502 _readHandler(); |
| 503 } else if (event == RawSocketEvent.WRITE) { |
| 504 _writeHandler(); |
| 505 } else if (event == RawSocketEvent.READ_CLOSED) { |
| 506 _closeHandler(); |
| 507 } |
| 508 } |
| 509 |
| 510 void _readHandler() { |
| 511 if (_status == CLOSED) { |
| 512 return; |
| 513 } else if (_status == HANDSHAKE) { |
406 try { | 514 try { |
407 _secureHandshake(); | 515 _secureHandshake(); |
408 } catch (e) { _reportError(e, "SecureSocket error"); } | 516 if (_status != HANDSHAKE) _readHandler(); |
| 517 } catch (e) { _reportError(e, "RawSecureSocket error"); } |
409 } else { | 518 } else { |
| 519 if (_status != CONNECTED) { |
| 520 // Cannot happen. |
| 521 throw new SocketIOException("Internal SocketIO Error"); |
| 522 } |
410 try { | 523 try { |
411 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure. | |
412 _readEncryptedData(); | 524 _readEncryptedData(); |
413 } catch (e) { _reportError(e, "SecureSocket error"); } | 525 } catch (e) { _reportError(e, "RawSecureSocket error"); } |
414 if (!_filterReadEmpty) { | 526 if (!_filterReadEmpty) { |
415 // Call the onData event. | 527 if (_readEventsEnabled) { |
416 if (scheduledDataEvent != null) { | 528 _controller.add(RawSocketEvent.READ); |
417 scheduledDataEvent.cancel(); | 529 if (_socketClosedRead) { |
418 scheduledDataEvent = null; | 530 // Keep firing read events until we are paused or buffer is empty. |
419 } | 531 new Timer(0, (_) => _readHandler()); |
420 if (_socketDataHandler != null) { | 532 } |
421 _socketDataHandler(); | |
422 } | 533 } |
423 } else if (_socketClosedRead) { | 534 } else if (_socketClosedRead) { |
424 _secureCloseHandler(); | 535 _closeHandler(); |
425 } | 536 } |
426 } | 537 } |
427 } | 538 } |
428 | 539 |
429 void _secureErrorHandler(e) { | 540 void _doneHandler() { |
430 _reportError(e, 'Error on underlying Socket'); | 541 if (_filterReadEmpty) { |
| 542 close(); |
| 543 } |
| 544 } |
| 545 |
| 546 void _errorHandler(e) { |
| 547 _reportError(e, 'Error on underlying RawSocket'); |
431 } | 548 } |
432 | 549 |
433 void _reportError(error, String message) { | 550 void _reportError(error, String message) { |
434 // TODO(whesse): Call _reportError from all internal functions that throw. | 551 // TODO(whesse): Call _reportError from all internal functions that throw. |
435 var e; | 552 var e; |
436 if (error is SocketIOException) { | 553 if (error is AsyncError) { |
| 554 e = error; |
| 555 } else if (error is SocketIOException) { |
437 e = new SocketIOException('$message (${error.message})', error.osError); | 556 e = new SocketIOException('$message (${error.message})', error.osError); |
438 } else if (error is OSError) { | 557 } else if (error is OSError) { |
439 e = new SocketIOException(message, error); | 558 e = new SocketIOException(message, error); |
440 } else { | 559 } else { |
441 e = new SocketIOException('$message (${error.toString()})', null); | 560 e = new SocketIOException('$message (${error.toString()})', null); |
442 } | 561 } |
443 close(false); | 562 if (_connectPending) { |
444 bool reported = false; | 563 _handshakeComplete.completeError(e); |
445 if (_socketErrorHandler != null) { | 564 } else { |
446 reported = true; | 565 _controller.signalError(e); |
447 _socketErrorHandler(e); | |
448 } | 566 } |
449 if (_inputStream != null) { | 567 close(); |
450 reported = reported || _inputStream._onSocketError(e); | |
451 } | |
452 if (_outputStream != null) { | |
453 reported = reported || _outputStream._onSocketError(e); | |
454 } | |
455 if (!reported) throw e; | |
456 } | 568 } |
457 | 569 |
458 void _secureCloseHandler() { | 570 void _closeHandler() { |
459 if (_closedRead) return; | 571 if (_status == CONNECTED) { |
460 _socketClosedRead = true; | 572 if (_closedRead) return; |
461 if (_filterReadEmpty) { | 573 _socketClosedRead = true; |
462 _closedRead = true; | 574 if (_filterReadEmpty) { |
463 if (scheduledDataEvent != null) { | 575 _closedRead = true; |
464 scheduledDataEvent.cancel(); | 576 _controller.add(RawSocketEvent.READ_CLOSED); |
| 577 if (_socketClosedWrite) { |
| 578 close(); |
| 579 } |
465 } | 580 } |
466 if (_socketCloseHandler != null) { | 581 } else if (_status == HANDSHAKE) { |
467 _socketCloseHandler(); | 582 _reportError( |
468 } | 583 new SocketIOException('Connection terminated during handshake'), |
469 if (_socketClosedWrite) { | 584 'handshake error'); |
470 close(false); | |
471 } | |
472 } | 585 } |
473 } | 586 } |
474 | 587 |
475 void _secureHandshake() { | 588 void _secureHandshake() { |
476 _readEncryptedData(); | 589 _readEncryptedData(); |
477 secureFilter.handshake(); | 590 _secureFilter.handshake(); |
478 _writeEncryptedData(); | 591 _writeEncryptedData(); |
479 if (secureFilter.buffers[WRITE_ENCRYPTED].length > 0) { | |
480 socket.onWrite = _secureWriteHandler; | |
481 } | |
482 } | 592 } |
483 | 593 |
484 void _secureHandshakeCompleteHandler() { | 594 void _secureHandshakeCompleteHandler() { |
485 _status = CONNECTED; | 595 _status = CONNECTED; |
486 if (_connectPending && _socketConnectHandler != null) { | 596 if (_connectPending) { |
487 _connectPending = false; | 597 _connectPending = false; |
488 _socketConnectHandler(); | 598 // If we complete the future synchronously, user code will run here, |
489 } | 599 // and modify the state of the RawSecureSocket. For example, it |
490 if (_socketWriteHandler != null) { | 600 // could close the socket, and set _filter to null. |
491 socket.onWrite = _secureWriteHandler; | 601 new Timer(0, (_) => _handshakeComplete.complete(this)); |
492 } | 602 } |
493 } | 603 } |
494 | 604 |
495 // True if the underlying socket is closed, the filter has been emptied of | 605 void _onPauseStateChange() { |
496 // all data, and the close event has been fired. | 606 if (!_socketClosedRead || !_socketClosedWrite) { |
497 get _closed => _socketClosed; | 607 if (_controller.isPaused) { |
| 608 _socketSubscription.pause(); |
| 609 } else { |
| 610 _socketSubscription.resume(); |
| 611 } |
| 612 } |
| 613 } |
| 614 |
| 615 void _onSubscriptionStateChange() { |
| 616 if (_controller.hasSubscribers) { |
| 617 // TODO(ajohnsen): Do something here? |
| 618 } |
| 619 } |
498 | 620 |
499 void _readEncryptedData() { | 621 void _readEncryptedData() { |
500 // Read from the socket, and push it through the filter as far as | 622 // Read from the socket, and push it through the filter as far as |
501 // possible. | 623 // possible. |
502 var encrypted = secureFilter.buffers[READ_ENCRYPTED]; | 624 var encrypted = _secureFilter.buffers[READ_ENCRYPTED]; |
503 var plaintext = secureFilter.buffers[READ_PLAINTEXT]; | 625 var plaintext = _secureFilter.buffers[READ_PLAINTEXT]; |
504 bool progress = true; | 626 bool progress = true; |
505 while (progress) { | 627 while (progress) { |
506 progress = false; | 628 progress = false; |
507 // Do not try to read plaintext from the filter while handshaking. | 629 // Do not try to read plaintext from the filter while handshaking. |
508 if ((_status == CONNECTED) && plaintext.free > 0) { | 630 if ((_status == CONNECTED) && plaintext.free > 0) { |
509 int bytes = secureFilter.processBuffer(READ_PLAINTEXT); | 631 int bytes = _secureFilter.processBuffer(READ_PLAINTEXT); |
510 if (bytes > 0) { | 632 if (bytes > 0) { |
511 plaintext.length += bytes; | 633 plaintext.length += bytes; |
512 progress = true; | 634 progress = true; |
513 } | 635 } |
514 } | 636 } |
515 if (encrypted.length > 0) { | 637 if (encrypted.length > 0) { |
516 int bytes = secureFilter.processBuffer(READ_ENCRYPTED); | 638 int bytes = _secureFilter.processBuffer(READ_ENCRYPTED); |
517 if (bytes > 0) { | 639 if (bytes > 0) { |
518 encrypted.advanceStart(bytes); | 640 encrypted.advanceStart(bytes); |
519 progress = true; | 641 progress = true; |
520 } | 642 } |
521 } | 643 } |
522 if (!_socketClosedRead) { | 644 if (!_socketClosedRead && encrypted.free > 0) { |
523 int bytes = socket.readList(encrypted.data, | 645 List<int> data = _socket.read(encrypted.free); |
524 encrypted.start + encrypted.length, | 646 if (data != null) { |
525 encrypted.free); | 647 int bytes = data.length; |
526 if (bytes > 0) { | 648 encrypted.data.setRange(encrypted.start + encrypted.length, |
| 649 bytes, |
| 650 data); |
527 encrypted.length += bytes; | 651 encrypted.length += bytes; |
528 progress = true; | 652 progress = true; |
529 } | 653 } |
530 } | 654 } |
531 } | 655 } |
532 // If there is any data in any stages of the filter, there should | 656 // If there is any data in any stages of the filter, there should |
533 // be data in the plaintext buffer after this process. | 657 // be data in the plaintext buffer after this process. |
534 // TODO(whesse): Verify that this is true, and there can be no | 658 // TODO(whesse): Verify that this is true, and there can be no |
535 // partial encrypted block stuck in the secureFilter. | 659 // partial encrypted block stuck in the secureFilter. |
536 _filterReadEmpty = (plaintext.length == 0); | 660 _filterReadEmpty = (plaintext.length == 0); |
537 } | 661 } |
538 | 662 |
539 void _writeEncryptedData() { | 663 void _writeEncryptedData() { |
540 if (_socketClosedWrite) return; | 664 if (_socketClosedWrite) return; |
541 var encrypted = secureFilter.buffers[WRITE_ENCRYPTED]; | 665 var encrypted = _secureFilter.buffers[WRITE_ENCRYPTED]; |
542 var plaintext = secureFilter.buffers[WRITE_PLAINTEXT]; | 666 var plaintext = _secureFilter.buffers[WRITE_PLAINTEXT]; |
543 while (true) { | 667 while (true) { |
544 if (encrypted.length > 0) { | 668 if (encrypted.length > 0) { |
545 // Write from the filter to the socket. | 669 // Write from the filter to the socket. |
546 int bytes = socket.writeList(encrypted.data, | 670 int bytes = _socket.write(encrypted.data, |
547 encrypted.start, | 671 encrypted.start, |
548 encrypted.length); | 672 encrypted.length); |
549 if (bytes == 0) { | 673 encrypted.advanceStart(bytes); |
| 674 if (encrypted.start < encrypted.length) { |
550 // The socket has blocked while we have data to write. | 675 // The socket has blocked while we have data to write. |
551 // We must be notified when it becomes unblocked. | 676 // We must be notified when it becomes unblocked. |
552 socket.onWrite = _secureWriteHandler; | 677 _socket.writeEventsEnabled = true; |
553 _filterWriteEmpty = false; | 678 _filterWriteEmpty = false; |
554 break; | 679 break; |
555 } | 680 } |
556 encrypted.advanceStart(bytes); | |
557 } else { | 681 } else { |
558 var plaintext = secureFilter.buffers[WRITE_PLAINTEXT]; | 682 var plaintext = _secureFilter.buffers[WRITE_PLAINTEXT]; |
559 if (plaintext.length > 0) { | 683 if (plaintext.length > 0) { |
560 int plaintext_bytes = secureFilter.processBuffer(WRITE_PLAINTEXT); | 684 int plaintext_bytes = _secureFilter.processBuffer(WRITE_PLAINTEXT); |
561 plaintext.advanceStart(plaintext_bytes); | 685 plaintext.advanceStart(plaintext_bytes); |
562 } | 686 } |
563 int bytes = secureFilter.processBuffer(WRITE_ENCRYPTED); | 687 int bytes = _secureFilter.processBuffer(WRITE_ENCRYPTED); |
564 if (bytes <= 0) { | 688 if (bytes <= 0) { |
565 // We know the WRITE_ENCRYPTED buffer is empty, and the | 689 // We know the WRITE_ENCRYPTED buffer is empty, and the |
566 // filter wrote zero bytes to it, so the filter must be empty. | 690 // filter wrote zero bytes to it, so the filter must be empty. |
567 // Also, the WRITE_PLAINTEXT buffer must have been empty, or | 691 // Also, the WRITE_PLAINTEXT buffer must have been empty, or |
568 // it would have written to the filter. | 692 // it would have written to the filter. |
569 // TODO(whesse): Verify that the filter works this way. | 693 // TODO(whesse): Verify that the filter works this way. |
570 _filterWriteEmpty = true; | 694 _filterWriteEmpty = true; |
571 break; | 695 break; |
572 } | 696 } |
573 encrypted.length += bytes; | 697 encrypted.length += bytes; |
574 } | 698 } |
575 } | 699 } |
576 } | 700 } |
577 | |
578 /* After a read, the onData handler is enabled to fire again. | |
579 * We may also have a close event waiting for the SecureFilter to empty. | |
580 */ | |
581 void _setHandlersAfterRead() { | |
582 // If the filter is empty, then we are guaranteed an event when it | |
583 // becomes unblocked. Cancel any _secureDataHandler call. | |
584 // Otherwise, schedule a _secureDataHandler call since there may data | |
585 // available, and this read call enables the data event. | |
586 if (_filterReadEmpty) { | |
587 if (scheduledDataEvent != null) { | |
588 scheduledDataEvent.cancel(); | |
589 scheduledDataEvent = null; | |
590 } | |
591 } else if (scheduledDataEvent == null) { | |
592 scheduledDataEvent = Timer.run(_secureDataHandler); | |
593 } | |
594 | |
595 if (_socketClosedRead) { // An onClose event is pending. | |
596 // _closedRead is false, since we are in a read or readList call. | |
597 if (!_filterReadEmpty) { | |
598 // _filterReadEmpty may be out of date since read and readList empty | |
599 // the plaintext buffer after calling _readEncryptedData. | |
600 // TODO(whesse): Fix this as part of fixing read and readList. | |
601 _readEncryptedData(); | |
602 } | |
603 if (_filterReadEmpty) { | |
604 // This can't be an else clause: the value of _filterReadEmpty changes. | |
605 // This must be asynchronous, because we are in a read or readList call. | |
606 Timer.run(_secureCloseHandler); | |
607 } | |
608 } | |
609 } | |
610 | |
611 bool get _socketClosed => _closedRead; | |
612 | |
613 // _SecureSocket cannot extend _Socket and use _Socket's factory constructor. | |
614 Socket socket; | |
615 final String host; | |
616 final bool is_server; | |
617 final String certificateName; | |
618 final bool requestClientCertificate; | |
619 final bool requireClientCertificate; | |
620 final bool sendClientCertificate; | |
621 | |
622 var _status = NOT_CONNECTED; | |
623 bool _socketClosedRead = false; // The network socket is closed for reading. | |
624 bool _socketClosedWrite = false; // The network socket is closed for writing. | |
625 bool _closedRead = false; // The secure socket has fired an onClosed event. | |
626 bool _closedWrite = false; // The secure socket has been closed for writing. | |
627 bool _filterReadEmpty = true; // There is no buffered data to read. | |
628 bool _filterWriteEmpty = true; // There is no buffered data to be written. | |
629 _SocketInputStream _inputStream; | |
630 _SocketOutputStream _outputStream; | |
631 bool _connectPending = false; | |
632 Function _socketConnectHandler; | |
633 Function _socketWriteHandler; | |
634 Function _socketDataHandler; | |
635 Function _socketErrorHandler; | |
636 Function _socketCloseHandler; | |
637 Timer scheduledDataEvent; | |
638 | |
639 _SecureFilter secureFilter; | |
640 } | 701 } |
641 | 702 |
642 | 703 |
643 class _ExternalBuffer { | 704 class _ExternalBuffer { |
644 // Performance is improved if a full buffer of plaintext fits | 705 // Performance is improved if a full buffer of plaintext fits |
645 // in the encrypted buffer, when encrypted. | 706 // in the encrypted buffer, when encrypted. |
646 static final int SIZE = 8 * 1024; | 707 static final int SIZE = 8 * 1024; |
647 static final int ENCRYPTED_SIZE = 10 * 1024; | 708 static final int ENCRYPTED_SIZE = 10 * 1024; |
648 _ExternalBuffer() : start = 0, length = 0; | 709 _ExternalBuffer() : start = 0, length = 0; |
649 | 710 |
(...skipping 27 matching lines...) Expand all Loading... |
677 void destroy(); | 738 void destroy(); |
678 void handshake(); | 739 void handshake(); |
679 void init(); | 740 void init(); |
680 X509Certificate get peerCertificate; | 741 X509Certificate get peerCertificate; |
681 int processBuffer(int bufferIndex); | 742 int processBuffer(int bufferIndex); |
682 void registerBadCertificateCallback(Function callback); | 743 void registerBadCertificateCallback(Function callback); |
683 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); | 744 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); |
684 | 745 |
685 List<_ExternalBuffer> get buffers; | 746 List<_ExternalBuffer> get buffers; |
686 } | 747 } |
OLD | NEW |