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

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: Address comment Created 8 years 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 _socketWriteHandler != null &&
199 _socketWriteHandler(); 232 _tlsFilter.buffers[WRITE_PLAINTEXT].free > 0) {
200 } 233 // We must be able to set onWrite from the onWrite callback.
234 var handler = _socketWriteHandler;
235 // Reset the one-shot handler.
236 _socketWriteHandler = null;
237 handler();
201 } 238 }
202 } 239 }
203 240
204 void _tlsDataHandler() { 241 void _tlsDataHandler() {
205 if (_status == HANDSHAKE) { 242 if (_status == HANDSHAKE) {
206 _tlsHandshake(); 243 _tlsHandshake();
207 } else { 244 } else {
208 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure. 245 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure.
209 _readEncryptedData(); 246 _readEncryptedData();
210 var buffer = _tlsFilter.buffers[READ_PLAINTEXT]; 247 if (!_filterReadEmpty) {
211 if (_filterEmpty) { 248 // Call the onData event.
212 if (_fireCloseEventPending) {
213 _fireCloseEvent();
214 }
215 } else { // Filter is not empty.
216 if (scheduledDataEvent != null) { 249 if (scheduledDataEvent != null) {
217 scheduledDataEvent.cancel(); 250 scheduledDataEvent.cancel();
218 scheduledDataEvent = null; 251 scheduledDataEvent = null;
219 } 252 }
220 if (_socketDataHandler != null) { 253 if (_socketDataHandler != null) {
221 _socketDataHandler(); 254 _socketDataHandler();
222 } 255 }
223 } 256 }
224 } 257 }
225 } 258 }
226 259
227 void _tlsCloseHandler() { 260 void _tlsCloseHandler() {
228 _socketClosed = true; 261 _socketClosedRead = true;
229 _status = CLOSED; 262 if (_filterReadEmpty) {
230 _socket.close(); 263 _closedRead = true;
231 if (_filterEmpty) {
232 _fireCloseEvent(); 264 _fireCloseEvent();
233 } else { 265 if (_socketClosedWrite) {
234 _fireCloseEventPending = true; 266 _tlsFilter.destroy();
267 _tlsFilter = null;
268 _status = CLOSED;
269 }
235 } 270 }
236 } 271 }
237 272
238 void _tlsHandshake() { 273 void _tlsHandshake() {
239 _readEncryptedData(); 274 _readEncryptedData();
240 _tlsFilter.handshake(); 275 _tlsFilter.handshake();
241 _writeEncryptedData(); 276 _writeEncryptedData();
242 if (_tlsFilter.buffers[WRITE_ENCRYPTED].length > 0) { 277 if (_tlsFilter.buffers[WRITE_ENCRYPTED].length > 0) {
243 _socket.onWrite = _tlsWriteHandler; 278 _socket.onWrite = _tlsWriteHandler;
244 } 279 }
245 } 280 }
246 281
247 void _tlsHandshakeCompleteHandler() { 282 void _tlsHandshakeCompleteHandler() {
248 _status = CONNECTED; 283 _status = CONNECTED;
249 if (_connectPending && _socketConnectHandler != null) { 284 if (_connectPending && _socketConnectHandler != null) {
250 _connectPending = false; 285 _connectPending = false;
251 _socketConnectHandler(); 286 _socketConnectHandler();
252 } 287 }
253 } 288 }
254 289
255 void _fireCloseEvent() { 290 void _fireCloseEvent() {
256 _fireCloseEventPending = false;
257 _tlsFilter.destroy();
258 _tlsFilter = null;
259 if (scheduledDataEvent != null) { 291 if (scheduledDataEvent != null) {
260 scheduledDataEvent.cancel(); 292 scheduledDataEvent.cancel();
261 } 293 }
262 if (_socketCloseHandler != null) { 294 if (_socketCloseHandler != null) {
263 _socketCloseHandler(); 295 _socketCloseHandler();
264 } 296 }
265 } 297 }
266 298
267 void _readEncryptedData() { 299 void _readEncryptedData() {
268 // Read from the socket, and push it through the filter as far as 300 // Read from the socket, and push it through the filter as far as
269 // possible. 301 // possible.
270 var encrypted = _tlsFilter.buffers[READ_ENCRYPTED]; 302 var encrypted = _tlsFilter.buffers[READ_ENCRYPTED];
271 var plaintext = _tlsFilter.buffers[READ_PLAINTEXT]; 303 var plaintext = _tlsFilter.buffers[READ_PLAINTEXT];
272 bool progress = true; 304 bool progress = true;
273 while (progress) { 305 while (progress) {
274 progress = false; 306 progress = false;
275 // Do not try to read plaintext from the filter while handshaking. 307 // Do not try to read plaintext from the filter while handshaking.
276 if ((_status == CONNECTED || _status == CLOSED) && plaintext.free > 0) { 308 if ((_status == CONNECTED) && plaintext.free > 0) {
277 int bytes = _tlsFilter.processBuffer(READ_PLAINTEXT); 309 int bytes = _tlsFilter.processBuffer(READ_PLAINTEXT);
278 if (bytes > 0) { 310 if (bytes > 0) {
279 plaintext.length += bytes; 311 plaintext.length += bytes;
280 progress = true; 312 progress = true;
281 } 313 }
282 } 314 }
283 if (encrypted.length > 0) { 315 if (encrypted.length > 0) {
284 int bytes = _tlsFilter.processBuffer(READ_ENCRYPTED); 316 int bytes = _tlsFilter.processBuffer(READ_ENCRYPTED);
285 if (bytes > 0) { 317 if (bytes > 0) {
286 encrypted.advanceStart(bytes); 318 encrypted.advanceStart(bytes);
287 progress = true; 319 progress = true;
288 } 320 }
289 } 321 }
290 if (!_socketClosed) { 322 if (!_socketClosedRead) {
291 int bytes = _socket.readList(encrypted.data, 323 int bytes = _socket.readList(encrypted.data,
292 encrypted.start + encrypted.length, 324 encrypted.start + encrypted.length,
293 encrypted.free); 325 encrypted.free);
294 if (bytes > 0) { 326 if (bytes > 0) {
295 encrypted.length += bytes; 327 encrypted.length += bytes;
296 progress = true; 328 progress = true;
297 } 329 }
298 } 330 }
299 } 331 }
300 // TODO(whesse): This can be incorrect if there is a partial 332 // If there is any data in any stages of the filter, there should
301 // encrypted block stuck in the tlsFilter, and no other data. 333 // be data in the plaintext buffer after this process.
302 // Fix this - we do need to know when the filter is empty. 334 // TODO(whesse): Verify that this is true, and there can be no
303 _filterEmpty = (plaintext.length == 0); 335 // partial encrypted block stuck in the tlsFilter.
336 _filterReadEmpty = (plaintext.length == 0);
304 } 337 }
305 338
306 void _writeEncryptedData() { 339 void _writeEncryptedData() {
307 // Write from the filter to the socket. 340 if (_socketClosedWrite) return;
308 var buffer = _tlsFilter.buffers[WRITE_ENCRYPTED]; 341 var encrypted = _tlsFilter.buffers[WRITE_ENCRYPTED];
342 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT];
309 while (true) { 343 while (true) {
310 if (buffer.length > 0) { 344 if (encrypted.length > 0) {
311 int bytes = _socket.writeList(buffer.data, buffer.start, buffer.length); 345 // Write from the filter to the socket.
346 int bytes = _socket.writeList(encrypted.data,
347 encrypted.start,
348 encrypted.length);
312 if (bytes == 0) { 349 if (bytes == 0) {
313 // The socket has blocked while we have data to write. 350 // The socket has blocked while we have data to write.
314 // We must be notified when it becomes unblocked. 351 // We must be notified when it becomes unblocked.
315 _socket.onWrite = _tlsWriteHandler; 352 _socket.onWrite = _tlsWriteHandler;
353 _filterWriteEmpty = false;
316 break; 354 break;
317 } 355 }
318 buffer.advanceStart(bytes); 356 encrypted.advanceStart(bytes);
319 } else { 357 } else {
320 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT]; 358 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT];
321 if (plaintext.length > 0) { 359 if (plaintext.length > 0) {
322 int plaintext_bytes = _tlsFilter.processBuffer(WRITE_PLAINTEXT); 360 int plaintext_bytes = _tlsFilter.processBuffer(WRITE_PLAINTEXT);
323 plaintext.advanceStart(plaintext_bytes); 361 plaintext.advanceStart(plaintext_bytes);
324 } 362 }
325 int bytes = _tlsFilter.processBuffer(WRITE_ENCRYPTED); 363 int bytes = _tlsFilter.processBuffer(WRITE_ENCRYPTED);
326 if (bytes <= 0) break; 364 if (bytes <= 0) {
327 buffer.length += bytes; 365 // We know the WRITE_ENCRYPTED buffer is empty, and the
366 // filter wrote zero bytes to it, so the filter must be empty.
367 // Also, the WRITE_PLAINTEXT buffer must have been empty, or
368 // it would have written to the filter.
369 // TODO(whesse): Verify that the filter works this way.
370 _filterWriteEmpty = true;
371 break;
372 }
373 encrypted.length += bytes;
328 } 374 }
329 } 375 }
330 } 376 }
331 377
332 /* After a read, the onData handler is enabled to fire again. 378 /* 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. 379 * We may also have a close event waiting for the TlsFilter to empty.
334 */ 380 */
335 void _setHandlersAfterRead() { 381 void _setHandlersAfterRead() {
336 // If the filter is empty, then we are guaranteed an event when it 382 // If the filter is empty, then we are guaranteed an event when it
337 // becomes unblocked. 383 // becomes unblocked. Cancel any _tlsDataHandler call.
338 // Otherwise, schedule a _tlsDataHandler call since there may data 384 // Otherwise, schedule a _tlsDataHandler call since there may data
339 // available, and this read call enables the data event. 385 // available, and this read call enables the data event.
340 if (!_filterEmpty && scheduledDataEvent == null) { 386 if (_filterReadEmpty) {
341 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler()); 387 if (scheduledDataEvent != null) {
342 } else if (_filterEmpty && scheduledDataEvent != null) {
343 scheduledDataEvent.cancel(); 388 scheduledDataEvent.cancel();
344 scheduledDataEvent = null; 389 scheduledDataEvent = null;
390 }
391 } else if (scheduledDataEvent == null) {
392 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler());
345 } 393 }
346 if (_filterEmpty && _fireCloseEventPending) { 394
347 _fireCloseEvent(); 395 if (_socketClosedRead) { // An onClose event is pending.
396 // _closedRead is false, since we are in a read or readList call.
397 if (!_filterReadEmpty) {
398 // _filterReadEmpty may be out of date since read and readList empty
399 // the plaintext buffer after calling _readEncryptedData.
400 // TODO(whesse): Fix this as part of fixing read and readList.
401 _readEncryptedData();
402 }
403 if (_filterReadEmpty) {
404 // This can't be an else clause: the value of _filterReadEmpty changes.
405 // This must be asynchronous, because we are in a read or readList call.
406 new Timer(0, (_) => _fireCloseEvent());
407 }
348 } 408 }
349 } 409 }
350 410
351 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor. 411 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor.
352 Socket _socket; 412 Socket _socket;
353 String _host; 413 String _host;
354 int _port; 414 int _port;
355 bool _is_server; 415 bool _is_server;
356 String _certificateName; 416 String _certificateName;
357 417
358 var _status = NOT_CONNECTED; 418 var _status = NOT_CONNECTED;
359 bool _socketClosed = false; 419 bool _socketClosedRead = false; // The network socket is closed for reading.
360 bool _filterEmpty = false; 420 bool _socketClosedWrite = false; // The network socket is closed for writing.
421 bool _closedRead = false; // The secure socket has fired an onClosed event.
422 bool _closedWrite = false; // The secure socket has been closed for writing.
423 bool _filterReadEmpty = true; // There is no buffered data to read.
424 bool _filterWriteEmpty = true; // There is no buffered data to be written.
361 bool _connectPending = false; 425 bool _connectPending = false;
362 bool _fireCloseEventPending = false;
363 Function _socketConnectHandler; 426 Function _socketConnectHandler;
364 Function _socketWriteHandler; 427 Function _socketWriteHandler;
365 Function _socketDataHandler; 428 Function _socketDataHandler;
366 Function _socketCloseHandler; 429 Function _socketCloseHandler;
367 Timer scheduledDataEvent; 430 Timer scheduledDataEvent;
368 431
369 _TlsFilter _tlsFilter; 432 _TlsFilter _tlsFilter;
370 } 433 }
371 434
372 435
(...skipping 26 matching lines...) Expand all
399 bool is_server, 462 bool is_server,
400 String certificateName); 463 String certificateName);
401 void destroy(); 464 void destroy();
402 void handshake(); 465 void handshake();
403 void init(); 466 void init();
404 int processBuffer(int bufferIndex); 467 int processBuffer(int bufferIndex);
405 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); 468 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler);
406 469
407 List<_TlsExternalBuffer> get buffers; 470 List<_TlsExternalBuffer> get buffers;
408 } 471 }
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