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

Side by Side Diff: runtime/bin/tls_socket_impl.dart

Issue 10704159: Start adding TLS (SSL) sockets to dart:io. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add proper event handling during handshake phase of connection. Created 8 years, 4 months 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5
6 int _logging = true;
Anders Johnsen 2012/07/27 15:58:49 This, and below, does not belong here.
Bill Hesse 2012/08/08 17:00:05 Done.
7
8 void log(x) {
9 if (_logging) {print(x.toString());}
10 }
11
12 class _TlsSocket implements TlsSocket {
13 static final int _BUFFER_SIZE = 2048;
14
15 // Status states
16 static final int NOT_CONNECTED = 200;
17 static final int HANDSHAKE = 201;
18 static final int CONNECTED = 202;
19 static final int CLOSED = 203;
20
21 // Buffer identifiers.
22 static final int kReadPlaintext = 0;
23 static final int kWritePlaintext = 1;
24 static final int kReadEncrypted = 2;
25 static final int kWriteEncrypted = 3;
26 static final int kNumBuffers = 4;
27
28 // Constructs a new secure client socket.
29 _TlsSocket(String host,
30 int port)
31 : _socket = new Socket(host, port),
32 _tlsFilter = new _TlsFilter() {
33 _socket.onConnect = _tlsConnectHandler;
34 _socket.onWrite = _tlsWriteHandler;
35 _socket.onData = _tlsDataHandler;
36 _socket.onClosed = _tlsCloseHandler;
37 _tlsFilter.init();
38 _tlsFilter.registerHandshakeCallbacks(_tlsHandshakeStartHandler,
39 _tlsHandshakeFinishHandler);
40 }
41
42 void set onConnect(void callback()) {
43 _socketConnectHandler = callback;
44 }
45
46 void set onWrite(void callback()) {
47 _socketWriteHandler = callback;
48 // Reset the one-shot onWrite handler.
49 _socket.onWrite = _tlsWriteHandler;
50 }
51
52 void set onData(void callback()) {
53 _socketDataHandler = callback;
54 }
55
56 void set onClosed(void callback()) {
57 _socketCloseHandler = callback;
58 }
59
60 void _tlsConnectHandler() {
61 _tlsFilter.connect();
62 _connectPending = true;
63 }
64
65 void _tlsWriteHandler() {
66 log(' _TlsSocket._tlsWriteHandler entered');
67 if (_status == HANDSHAKE) {
68 _writeEncryptedData();
69 _readEncryptedData();
70 _tlsFilter.connect();
71 // Only do this if we have more data to write.
72 if (_tlsFilter.buffers[kWriteEncrypted].length > 0) {
73 _socket.onWrite = _tlsWriteHandler;
74 }
75 } else if (_status == CONNECTED) {
76 if (_socketWriteHandler != null) {
77 _socketWriteHandler();
78 }
79 }
80 log(' _TlsSocket._tlsWriteHandler exited');
81 }
82
83 void _tlsDataHandler() {
84 log(' _TlsSocket._tlsDataHandler entered');
85 if (_status == HANDSHAKE) {
86 _readEncryptedData();
87 _writeEncryptedData();
88 _tlsFilter.connect();
89 _socket.onWrite = _tlsWriteHandler;
90 } else {
91 if (scheduledDataEvent != null) {
92 scheduledDataEvent.cancel();
93 scheduledDataEvent = null;
94 }
95 if (_socketDataHandler != null) {
96 _readEncryptedData();
97 _socketDataHandler();
98 }
99 }
100 log(' _TlsSocket._tlsDataHandler exited');
101 }
102
103 void _tlsCloseHandler() {
104 _socketClosed = true;
105 _status = CLOSED;
106 _socket.close();
107 if (_filterEmpty) {
108 _fireCloseEvent();
109 } else {
110 _fireCloseEventPending = true;
111 }
112 }
113
114 void _tlsHandshakeStartHandler() {
115 log(' _TlsSocket._tlsHandshakeStartHandler entered');
116 _status = HANDSHAKE;
117 _socket.onWrite = _tlsWriteHandler;
118 log(' _TlsSocket._tlsHandshakeStartHandler exited');
119 }
120
121 void _tlsHandshakeFinishHandler() {
122 log(' _TlsSocket._tlsHandshakeFinishHandler entered');
123 _status = CONNECTED;
124 if (_connectPending && _socketConnectHandler != null) {
125 _connectPending = false;
126 _socketConnectHandler();
127 }
128 log(' _TlsSocket._tlsHandshakeFinishHandler exited');
129 }
130
131 void _fireCloseEvent() {
132 _fireCloseEventPending = false;
133 _tlsFilter.destroy();
134 _tlsFilter = null;
135 if (scheduledDataEvent != null) {
136 scheduledDataEvent.cancel();
137 }
138 if (_socketCloseHandler != null) {
139 _socketCloseHandler();
140 }
141 }
142
143 void close([bool halfClose]) {
144 log(' _TlsSocket.close called');
145 _socket.close(halfClose);
146 // _readEncryptedData();
147 // _tlsFilter.destroy();
148 }
149
150 int readList(List<int> data, int offset, int bytes) {
151 _readEncryptedData();
152 if (offset < 0 || bytes < 0 || offset + bytes > data.length) {
153 throw new IllegalArgumentException(
154 "Invalid offset or bytes in TlsSocket.readList");
155 }
156 int bytes_read = 0;
157 var buffer = _tlsFilter.buffers[kReadPlaintext];
158 if (buffer.length == 0 && buffer.start != 0) {
159 throw "Unexpected buffer state in tls_socket readList";
160 }
161 if (buffer.length > 0) {
162 int to_read = Math.min(bytes, buffer.length);
163 data.setRange(offset, to_read, buffer.data, buffer.start);
164 buffer.start += to_read;
165 buffer.length -= to_read;
166 if (buffer.length == 0) {
167 buffer.start = 0;
168 }
169 bytes_read += to_read;
170 }
171 int new_bytes = _tlsFilter.processBuffer(kReadPlaintext);
172 if (new_bytes > 0) {
173 buffer.length += new_bytes;
174 }
175 if (bytes - bytes_read > 0 && buffer.length > 0) {
176 int to_read = Math.min(bytes - bytes_read, buffer.length);
177 data.setRange(offset + bytes_read, to_read, buffer.data, buffer.start);
178 buffer.start += to_read;
179 buffer.length -= to_read;
180 if (buffer.length == 0) {
181 buffer.start = 0;
182 }
183 bytes_read += to_read;
184 }
185
186 // If bytes_read is 0, then something is blocked or empty, and
187 // we are guaranteed an event when it becomes unblocked.
188 // Otherwise, give an event if there is data available, and
189 // there has been a read call since the last data event.
190 // This gives the invariant that:
191 // If there is data available, and there has been a read after the
192 // last data event (or no previous one fired), then we are guaranteed
193 // to get a data event.
194 _filterEmpty = (bytes_read == 0);
195 if (bytes_read > 0 && scheduledDataEvent == null) {
196 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler());
197 } else if (bytes_read == 0) {
198 if (_fireCloseEventPending) {
199 _fireCloseEvent();
200 } else if (scheduledDataEvent != null) {
201 scheduledDataEvent.cancel();
202 scheduledDataEvent = null;
203 }
204 }
205 print(' _fireCloseEventPending: $_fireCloseEventPending');
206 print(' _filterEmpty: $_filterEmpty');
207 print(' _socketClosed: $_socketClosed');
208 return bytes_read;
209 }
210
211
212 // Write the data to the socket, and flush it as much as possible
213 // without blocking. If not all the data is written, enable the
214 // onWrite event. If data is not all flushed, add handlers to all
215 // relevant events.
216
217 // Thus, the Dart code that writes the Dart buffer to the socket needs
218 // to be repeatedly called during the process of flushing, so that
219 // all the data is written out.
220
221 int writeList(List<int> data, int offset, int bytes) {
222 _writeEncryptedData(); // Tries to flush all post-filter stages.
223 var buffer = _tlsFilter.buffers[kWritePlaintext];
224 var free = _TlsExternalBuffer.kSize - buffer.start - buffer.length;
225 if (bytes > free) {
226 bytes = free;
227 }
228 if (bytes > 0) {
229 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset);
230 buffer.length += bytes;
231 }
232 int bytes_written = _tlsFilter.processBuffer(kWritePlaintext);
233 buffer.length -= bytes_written;
234 buffer.start += bytes_written;
235 if (buffer.length == 0) {
236 buffer.start = 0;
237 }
238 _readEncryptedData();
239 _writeEncryptedData();
240 print('start: ${buffer.start}');
241 print('length: ${buffer.length}');
242 return bytes;
243 }
244
245 void _readEncryptedData() {
246 log(' Entering _readEncryptedData');
247 // Read from the socket and write to the filter.
248 var buffer = _tlsFilter.buffers[kReadEncrypted];
249 while (true) {
250 if (buffer.length > 0) {
251 int bytes = _tlsFilter.processBuffer(kReadEncrypted);
252 if (bytes > 0) {
253 buffer.length -= bytes;
254 buffer.start += bytes;
255 if (buffer.length == 0) {
256 buffer.start = 0;
257 }
258 } else {
259 break;
260 }
261 } else if (!_socketClosed) {
262 var free = _TlsExternalBuffer.kSize - buffer.start - buffer.length;
263 int bytes =
264 _socket.readList(buffer.data, buffer.start + buffer.length, free);
265 log(' Read $bytes bytes from socket');
266 if (bytes <= 0) break;
267 buffer.length += bytes;
268 } else {
269 break; // Socket is closed and read buffer is empty.
270 }
271 }
272 log(' Exiting _readEncryptedData');
273 }
274
275 void _writeEncryptedData() {
276 log(' Entering _writeEncryptedData');
277 // Write from the filter to the socket.
278 var buffer = _tlsFilter.buffers[kWriteEncrypted];
279 while (true) {
280 if (buffer.length > 0) {
281 int bytes = _socket.writeList(buffer.data, buffer.start, buffer.length);
282 if (bytes <= 0) break;
283 buffer.start += bytes;
284 buffer.length -= bytes;
285 if (buffer.length == 0) {
286 buffer.start = 0;
287 }
288 } else {
289 if (buffer.start != 0 || buffer.length != 0) {
290 print("Unexpected state in _writeEncryptedData");
291 throw "Unexpected state in _writeEncryptedData";
292 }
293 int bytes = _tlsFilter.processBuffer(kWriteEncrypted);
294 if (bytes <= 0) break;
295 buffer.length += bytes;
296 }
297 log(' Exiting _writeEncryptedData');
298 }
299 }
300
301 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor.
302 Socket _socket;
303
304 var _status = NOT_CONNECTED;
305 bool _socketClosed = false;
306 bool _filterEmpty = false;
307 bool _connectPending = false;
308 bool _fireCloseEventPending = false;
309 Function _socketConnectHandler;
310 Function _socketWriteHandler;
311 Function _socketDataHandler;
312 Function _socketCloseHandler;
313 Timer scheduledDataEvent;
314
315 var _tlsFilter;
316 }
317
318 class _TlsExternalBuffer {
319 static final int kSize = 8 * 1024;
320 _TlsExternalBuffer() : start = 0, length = 0;
321 List data; // This will be a ExternalByteArray, backed by C allocated data.
322 int start;
323 int length;
324 }
325
326 /**
327 * _TlsFilter wraps a filter that encrypts and decrypts data travelling
328 * over a TLS encrypted socket. The filter also handles the handshaking
329 * and certificate verification.
330 *
331 * The filter exposes its input and output buffers as Dart objects that
332 * are backed by an external C array of bytes, so that both Dart code and
333 * native code can access the same data.
334 */
335 class _TlsFilter extends NativeFieldWrapperClass1 {
336 _TlsFilter() {
337 buffers = new List<_TlsExternalBuffer>(_TlsSocket.kNumBuffers);
338 for (int i = 0; i < _TlsSocket.kNumBuffers; ++i) {
339 buffers[i] = new _TlsExternalBuffer();
340 }
341 }
342
343 void init() native "TlsSocket_Init";
344
345 void connect() native "TlsSocket_Connect";
346
347 void registerHandshakeCallbacks(Function startHandshakeHandler,
348 Function finishHandshakeHandler)
349 native "TlsSocket_RegisterHandshakeCallbacks";
350 int processBuffer(int bufferIndex) native "TlsSocket_ProcessBuffer";
351 int readPlaintext() native "TlsSocket_ReadPlaintext";
352 int writePlaintext() native "TlsSocket_WritePlaintext";
353 int readEncrypted() native "TlsSocket_ReadEncrypted";
354 int writeEncrypted() native "TlsSocket_WriteEncrypted";
355 void destroy() native "TlsSocket_Destroy";
356
357 bool get shouldRetry() => true;
358
359 bool get shouldRead() => true;
360 bool get shouldWrite() => true;
361
362 List<_TlsExternalBuffer> buffers;
363 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698