Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: sdk/lib/io/tls_socket.dart

Issue 11417116: Fix secure socket tests to close sockets after writing. Fix handling of close events in TlsSocket … (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Improve comments and code. Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/standalone/io/tls_server_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /** 5 /**
6 * TlsSocket provides a secure (SSL or TLS) client connection to a server. 6 * TlsSocket provides a secure (SSL or TLS) client connection to a server.
7 * The certificate provided by the server is checked 7 * The certificate provided by the server is checked
8 * using the certificate database provided in setCertificateDatabase. 8 * using the certificate database provided in setCertificateDatabase.
9 */ 9 */
10 abstract class TlsSocket implements Socket { 10 abstract class TlsSocket implements Socket {
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 OutputStream get outputStream { 114 OutputStream get outputStream {
115 // TODO(6701): Implement stream interfaces on TlsSocket. 115 // TODO(6701): Implement stream interfaces on TlsSocket.
116 throw new UnimplementedError("TlsSocket.inputStream not implemented yet"); 116 throw new UnimplementedError("TlsSocket.inputStream not implemented yet");
117 } 117 }
118 118
119 int available() { 119 int available() {
120 throw new UnimplementedError("TlsSocket.available not implemented yet"); 120 throw new UnimplementedError("TlsSocket.available not implemented yet");
121 } 121 }
122 122
123 void close([bool halfClose]) { 123 void close([bool halfClose]) {
124 _socket.close(halfClose); 124 if (halfClose) {
125 _closedWrite = true;
126 _writeEncryptedData();
127 if (_filterWriteEmpty) {
128 _socket.close(true);
129 _socketClosedWrite = true;
130 }
131 } else {
132 _closedWrite = true;
133 _closedRead = true;
134 _socket.close(false);
135 _socketClosedWrite = true;
136 _socketClosedRead = true;
137 _tlsFilter.destroy();
138 _tlsFilter = null;
139 if (scheduledDataEvent != null) {
140 scheduledDataEvent.cancel();
141 }
142 _status = CLOSED;
143 }
125 } 144 }
126 145
127 List<int> read([int len]) { 146 List<int> read([int len]) {
147 if (_closedRead) {
148 throw new SocketException("Reading from a closed socket");
149 }
128 var buffer = _tlsFilter.buffers[READ_PLAINTEXT]; 150 var buffer = _tlsFilter.buffers[READ_PLAINTEXT];
129 _readEncryptedData(); 151 _readEncryptedData();
130 int toRead = buffer.length; 152 int toRead = buffer.length;
131 if (len != null) { 153 if (len != null) {
132 if (len is! int || len < 0) { 154 if (len is! int || len < 0) {
133 throw new ArgumentError( 155 throw new ArgumentError(
134 "Invalid len parameter in TlsSocket.read (len: $len)"); 156 "Invalid len parameter in TlsSocket.read (len: $len)");
135 } 157 }
136 if (len < toRead) { 158 if (len < toRead) {
137 toRead = len; 159 toRead = len;
138 } 160 }
139 } 161 }
140 List<int> result = buffer.data.getRange(buffer.start, toRead); 162 List<int> result = buffer.data.getRange(buffer.start, toRead);
141 buffer.advanceStart(toRead); 163 buffer.advanceStart(toRead);
142 _setHandlersAfterRead(); 164 _setHandlersAfterRead();
143 return result; 165 return result;
144 } 166 }
145 167
146 int readList(List<int> data, int offset, int bytes) { 168 int readList(List<int> data, int offset, int bytes) {
169 if (_closedRead) {
170 throw new SocketException("Reading from a closed socket");
171 }
147 if (offset < 0 || bytes < 0 || offset + bytes > data.length) { 172 if (offset < 0 || bytes < 0 || offset + bytes > data.length) {
148 throw new ArgumentError( 173 throw new ArgumentError(
149 "Invalid offset or bytes in TlsSocket.readList"); 174 "Invalid offset or bytes in TlsSocket.readList");
150 } 175 }
151 176
152 int bytesRead = 0; 177 int bytesRead = 0;
153 var buffer = _tlsFilter.buffers[READ_PLAINTEXT]; 178 var buffer = _tlsFilter.buffers[READ_PLAINTEXT];
154 // TODO(whesse): Currently this fails if the if is turned into a while loop. 179 // TODO(whesse): Currently this fails if the if is turned into a while loop.
155 // Fix it so that it can loop and read more than one buffer's worth of data. 180 // Fix it so that it can loop and read more than one buffer's worth of data.
156 if (bytes > bytesRead) { 181 if (bytes > bytesRead) {
157 _readEncryptedData(); 182 _readEncryptedData();
158 if (buffer.length > 0) { 183 if (buffer.length > 0) {
159 int toRead = min(bytes - bytesRead, buffer.length); 184 int toRead = min(bytes - bytesRead, buffer.length);
160 data.setRange(offset, toRead, buffer.data, buffer.start); 185 data.setRange(offset, toRead, buffer.data, buffer.start);
161 buffer.advanceStart(toRead); 186 buffer.advanceStart(toRead);
162 bytesRead += toRead; 187 bytesRead += toRead;
163 offset += toRead; 188 offset += toRead;
164 } 189 }
165 } 190 }
166 191
167 _setHandlersAfterRead(); 192 _setHandlersAfterRead();
168 return bytesRead; 193 return bytesRead;
169 } 194 }
170 195
171 // Write the data to the socket, and flush it as much as possible 196 // Write the data to the socket, and flush it as much as possible
172 // until it would block. If the write would block, _writeEncryptedData sets 197 // until it would block. If the write would block, _writeEncryptedData sets
173 // up handlers to flush the pipeline when possible. 198 // up handlers to flush the pipeline when possible.
174 int writeList(List<int> data, int offset, int bytes) { 199 int writeList(List<int> data, int offset, int bytes) {
200 if (_closedWrite) {
201 throw new SocketException("Writing to a closed socket");
202 }
175 var buffer = _tlsFilter.buffers[WRITE_PLAINTEXT]; 203 var buffer = _tlsFilter.buffers[WRITE_PLAINTEXT];
176 if (bytes > buffer.free) { 204 if (bytes > buffer.free) {
177 bytes = buffer.free; 205 bytes = buffer.free;
178 } 206 }
179 if (bytes > 0) { 207 if (bytes > 0) {
180 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset); 208 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset);
181 buffer.length += bytes; 209 buffer.length += bytes;
182 } 210 }
183 _writeEncryptedData(); // Tries to flush all pipeline stages. 211 _writeEncryptedData(); // Tries to flush all pipeline stages.
184 return bytes; 212 return bytes;
185 } 213 }
186 214
187 void _tlsConnectHandler() { 215 void _tlsConnectHandler() {
188 _connectPending = true; 216 _connectPending = true;
189 _tlsFilter.connect(_host, _port, _is_server, _certificateName); 217 _tlsFilter.connect(_host, _port, _is_server, _certificateName);
190 _status = HANDSHAKE; 218 _status = HANDSHAKE;
191 _tlsHandshake(); 219 _tlsHandshake();
192 } 220 }
193 221
194 void _tlsWriteHandler() { 222 void _tlsWriteHandler() {
223 _writeEncryptedData();
224 if (_filterWriteEmpty && _closedWrite && !_socketClosedWrite) {
225 _socket.close(true);
226 _sockedClosedWrite = true;
227 }
195 if (_status == HANDSHAKE) { 228 if (_status == HANDSHAKE) {
196 _tlsHandshake(); 229 _tlsHandshake();
197 } else if (_status == CONNECTED) { 230 } else if (_status == CONNECTED) {
198 if (_socketWriteHandler != null) { 231 if (_socketWriteHandler != null) {
199 _socketWriteHandler(); 232 _socketWriteHandler();
233 _socketWriteHandler = null;
Mads Ager (google) 2012/11/21 16:06:31 I don't understand this change. Why would you want
Bill Hesse 2012/11/21 16:25:25 The onWrite handler is a one-shot handler, that is
Mads Ager (google) 2012/11/22 10:04:16 Thanks, that's right. I keep forgetting that the w
200 } 234 }
201 } 235 }
202 } 236 }
203 237
204 void _tlsDataHandler() { 238 void _tlsDataHandler() {
205 if (_status == HANDSHAKE) { 239 if (_status == HANDSHAKE) {
206 _tlsHandshake(); 240 _tlsHandshake();
207 } else { 241 } else {
208 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure. 242 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure.
209 _readEncryptedData(); 243 _readEncryptedData();
210 var buffer = _tlsFilter.buffers[READ_PLAINTEXT]; 244 if (!_filterReadEmpty) {
211 if (_filterEmpty) { 245 // Call the onData event.
212 if (_fireCloseEventPending) {
213 _fireCloseEvent();
214 }
215 } else { // Filter is not empty.
216 if (scheduledDataEvent != null) { 246 if (scheduledDataEvent != null) {
217 scheduledDataEvent.cancel(); 247 scheduledDataEvent.cancel();
218 scheduledDataEvent = null; 248 scheduledDataEvent = null;
219 } 249 }
220 if (_socketDataHandler != null) { 250 if (_socketDataHandler != null) {
221 _socketDataHandler(); 251 _socketDataHandler();
222 } 252 }
223 } 253 }
224 } 254 }
225 } 255 }
226 256
227 void _tlsCloseHandler() { 257 void _tlsCloseHandler() {
228 _socketClosed = true; 258 _socketClosedRead = true;
229 _status = CLOSED; 259 if (_filterReadEmpty) {
230 _socket.close(); 260 _closedRead = true;
231 if (_filterEmpty) {
232 _fireCloseEvent(); 261 _fireCloseEvent();
233 } else { 262 if (_socketClosedWrite) {
234 _fireCloseEventPending = true; 263 _tlsFilter.destroy();
264 _tlsFilter = null;
265 _status = CLOSED;
266 }
235 } 267 }
236 } 268 }
237 269
238 void _tlsHandshake() { 270 void _tlsHandshake() {
239 _readEncryptedData(); 271 _readEncryptedData();
240 _tlsFilter.handshake(); 272 _tlsFilter.handshake();
241 _writeEncryptedData(); 273 _writeEncryptedData();
242 if (_tlsFilter.buffers[WRITE_ENCRYPTED].length > 0) { 274 if (_tlsFilter.buffers[WRITE_ENCRYPTED].length > 0) {
243 _socket.onWrite = _tlsWriteHandler; 275 _socket.onWrite = _tlsWriteHandler;
244 } 276 }
245 } 277 }
246 278
247 void _tlsHandshakeCompleteHandler() { 279 void _tlsHandshakeCompleteHandler() {
248 _status = CONNECTED; 280 _status = CONNECTED;
249 if (_connectPending && _socketConnectHandler != null) { 281 if (_connectPending && _socketConnectHandler != null) {
250 _connectPending = false; 282 _connectPending = false;
251 _socketConnectHandler(); 283 _socketConnectHandler();
252 } 284 }
253 } 285 }
254 286
255 void _fireCloseEvent() { 287 void _fireCloseEvent() {
256 _fireCloseEventPending = false;
257 _tlsFilter.destroy();
258 _tlsFilter = null;
259 if (scheduledDataEvent != null) { 288 if (scheduledDataEvent != null) {
260 scheduledDataEvent.cancel(); 289 scheduledDataEvent.cancel();
261 } 290 }
262 if (_socketCloseHandler != null) { 291 if (_socketCloseHandler != null) {
263 _socketCloseHandler(); 292 _socketCloseHandler();
264 } 293 }
265 } 294 }
266 295
267 void _readEncryptedData() { 296 void _readEncryptedData() {
268 // Read from the socket, and push it through the filter as far as 297 // Read from the socket, and push it through the filter as far as
269 // possible. 298 // possible.
270 var encrypted = _tlsFilter.buffers[READ_ENCRYPTED]; 299 var encrypted = _tlsFilter.buffers[READ_ENCRYPTED];
271 var plaintext = _tlsFilter.buffers[READ_PLAINTEXT]; 300 var plaintext = _tlsFilter.buffers[READ_PLAINTEXT];
272 bool progress = true; 301 bool progress = true;
273 while (progress) { 302 while (progress) {
274 progress = false; 303 progress = false;
275 // Do not try to read plaintext from the filter while handshaking. 304 // Do not try to read plaintext from the filter while handshaking.
276 if ((_status == CONNECTED || _status == CLOSED) && plaintext.free > 0) { 305 if ((_status == CONNECTED) && plaintext.free > 0) {
277 int bytes = _tlsFilter.processBuffer(READ_PLAINTEXT); 306 int bytes = _tlsFilter.processBuffer(READ_PLAINTEXT);
278 if (bytes > 0) { 307 if (bytes > 0) {
279 plaintext.length += bytes; 308 plaintext.length += bytes;
280 progress = true; 309 progress = true;
281 } 310 }
282 } 311 }
283 if (encrypted.length > 0) { 312 if (encrypted.length > 0) {
284 int bytes = _tlsFilter.processBuffer(READ_ENCRYPTED); 313 int bytes = _tlsFilter.processBuffer(READ_ENCRYPTED);
285 if (bytes > 0) { 314 if (bytes > 0) {
286 encrypted.advanceStart(bytes); 315 encrypted.advanceStart(bytes);
287 progress = true; 316 progress = true;
288 } 317 }
289 } 318 }
290 if (!_socketClosed) { 319 if (!_socketClosedRead) {
291 int bytes = _socket.readList(encrypted.data, 320 int bytes = _socket.readList(encrypted.data,
292 encrypted.start + encrypted.length, 321 encrypted.start + encrypted.length,
293 encrypted.free); 322 encrypted.free);
294 if (bytes > 0) { 323 if (bytes > 0) {
295 encrypted.length += bytes; 324 encrypted.length += bytes;
296 progress = true; 325 progress = true;
297 } 326 }
298 } 327 }
299 } 328 }
300 // TODO(whesse): This can be incorrect if there is a partial 329 // If there is any data in any stages of the filter, there should
301 // encrypted block stuck in the tlsFilter, and no other data. 330 // be data in the plaintext buffer after this process.
302 // Fix this - we do need to know when the filter is empty. 331 // TODO(whesse): Verify that this is true, and there can be no
303 _filterEmpty = (plaintext.length == 0); 332 // partial encrypted block stuck in the tlsFilter.
333 _filterReadEmpty = (plaintext.length == 0);
304 } 334 }
305 335
306 void _writeEncryptedData() { 336 void _writeEncryptedData() {
307 // Write from the filter to the socket. 337 if (_socketClosedWrite) return;
308 var buffer = _tlsFilter.buffers[WRITE_ENCRYPTED]; 338 var encrypted = _tlsFilter.buffers[WRITE_ENCRYPTED];
339 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT];
309 while (true) { 340 while (true) {
310 if (buffer.length > 0) { 341 if (encrypted.length > 0) {
311 int bytes = _socket.writeList(buffer.data, buffer.start, buffer.length); 342 // Write from the filter to the socket.
343 int bytes = _socket.writeList(encrypted.data,
344 encrypted.start,
345 encrypted.length);
312 if (bytes == 0) { 346 if (bytes == 0) {
313 // The socket has blocked while we have data to write. 347 // The socket has blocked while we have data to write.
314 // We must be notified when it becomes unblocked. 348 // We must be notified when it becomes unblocked.
315 _socket.onWrite = _tlsWriteHandler; 349 _socket.onWrite = _tlsWriteHandler;
350 _filterWriteEmpty = false;
316 break; 351 break;
317 } 352 }
318 buffer.advanceStart(bytes); 353 encrypted.advanceStart(bytes);
319 } else { 354 } else {
320 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT]; 355 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT];
321 if (plaintext.length > 0) { 356 if (plaintext.length > 0) {
322 int plaintext_bytes = _tlsFilter.processBuffer(WRITE_PLAINTEXT); 357 int plaintext_bytes = _tlsFilter.processBuffer(WRITE_PLAINTEXT);
323 plaintext.advanceStart(plaintext_bytes); 358 plaintext.advanceStart(plaintext_bytes);
324 } 359 }
325 int bytes = _tlsFilter.processBuffer(WRITE_ENCRYPTED); 360 int bytes = _tlsFilter.processBuffer(WRITE_ENCRYPTED);
326 if (bytes <= 0) break; 361 if (bytes <= 0) {
327 buffer.length += bytes; 362 // We know the WRITE_ENCRYPTED buffer is empty, and the
363 // filter wrote zero bytes to it, so the filter must be empty.
364 // Also, the WRITE_PLAINTEXT buffer must have been empty, or
365 // it would have written to the filter.
366 // TODO(whesse): Verify that the filter works this way.
367 _filterWriteEmpty = true;
368 break;
369 }
370 encrypted.length += bytes;
328 } 371 }
329 } 372 }
330 } 373 }
331 374
332 /* After a read, the onData handler is enabled to fire again. 375 /* After a read, the onData handler is enabled to fire again.
333 * We may also have a close event waiting for the TlsFilter to empty. 376 * We may also have a close event waiting for the TlsFilter to empty.
334 */ 377 */
335 void _setHandlersAfterRead() { 378 void _setHandlersAfterRead() {
336 // If the filter is empty, then we are guaranteed an event when it 379 // If the filter is empty, then we are guaranteed an event when it
337 // becomes unblocked. 380 // becomes unblocked. Cancel any _tlsDataHandler call.
338 // Otherwise, schedule a _tlsDataHandler call since there may data 381 // Otherwise, schedule a _tlsDataHandler call since there may data
339 // available, and this read call enables the data event. 382 // available, and this read call enables the data event.
340 if (!_filterEmpty && scheduledDataEvent == null) { 383 if (_filterReadEmpty) {
341 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler()); 384 if (scheduledDataEvent != null) {
342 } else if (_filterEmpty && scheduledDataEvent != null) {
343 scheduledDataEvent.cancel(); 385 scheduledDataEvent.cancel();
344 scheduledDataEvent = null; 386 scheduledDataEvent = null;
387 }
388 } else if (scheduledDataEvent == null) {
389 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler());
345 } 390 }
346 if (_filterEmpty && _fireCloseEventPending) { 391
347 _fireCloseEvent(); 392 if (_socketClosedRead) { // An onClose event is pending.
393 // _closedRead is false, since we are in a read or readList call.
394 if (!_filterReadEmpty) {
395 // _filterReadEmpty may be out of date since read and readList empty
396 // the plaintext buffer after calling _readEncryptedData.
397 // TODO(whesse): Fix this as part of fixing read and readList.
Mads Ager (google) 2012/11/21 16:06:31 What does _readEncryptedData() do in that case? Is
Bill Hesse 2012/11/21 16:25:25 Yes, _readEncryptedData is harmless - it advances
398 _readEncryptedData();
399 }
400 if (_filterReadEmpty) {
401 // This can't be an else clause: the value of _filterReadEmpty changes.
402 // This must be asynchronous, because we are in a read or readList call.
403 new Timer(0, (_) => _fireCloseEvent());
404 }
348 } 405 }
349 } 406 }
350 407
351 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor. 408 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor.
352 Socket _socket; 409 Socket _socket;
353 String _host; 410 String _host;
354 int _port; 411 int _port;
355 bool _is_server; 412 bool _is_server;
356 String _certificateName; 413 String _certificateName;
357 414
358 var _status = NOT_CONNECTED; 415 var _status = NOT_CONNECTED;
359 bool _socketClosed = false; 416 bool _socketClosedRead = false; // The network socket is closed for reading.
360 bool _filterEmpty = false; 417 bool _socketClosedWrite = false; // The network socket is closed for writing.
418 bool _closedRead = false; // The secure socket has fired an onClosed event.
419 bool _closedWrite = false; // The secure socket has been closed for writing.
420 bool _filterReadEmpty = null; // There is no buffered data to read.
Mads Ager (google) 2012/11/21 16:06:31 Please initialize to booleans. It would make sense
421 bool _filterWriteEmpty = null; // There is no buffered data to be written.
361 bool _connectPending = false; 422 bool _connectPending = false;
362 bool _fireCloseEventPending = false;
363 Function _socketConnectHandler; 423 Function _socketConnectHandler;
364 Function _socketWriteHandler; 424 Function _socketWriteHandler;
365 Function _socketDataHandler; 425 Function _socketDataHandler;
366 Function _socketCloseHandler; 426 Function _socketCloseHandler;
367 Timer scheduledDataEvent; 427 Timer scheduledDataEvent;
368 428
369 _TlsFilter _tlsFilter; 429 _TlsFilter _tlsFilter;
370 } 430 }
371 431
372 432
(...skipping 26 matching lines...) Expand all
399 bool is_server, 459 bool is_server,
400 String certificateName); 460 String certificateName);
401 void destroy(); 461 void destroy();
402 void handshake(); 462 void handshake();
403 void init(); 463 void init();
404 int processBuffer(int bufferIndex); 464 int processBuffer(int bufferIndex);
405 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); 465 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler);
406 466
407 List<_TlsExternalBuffer> get buffers; 467 List<_TlsExternalBuffer> get buffers;
408 } 468 }
OLDNEW
« no previous file with comments | « no previous file | tests/standalone/io/tls_server_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698