Skip to content

Commit c8693a1

Browse files
committed
Merge 'datanav/master' into 'staltz/master'
2 parents aa94219 + 5072101 commit c8693a1

File tree

14 files changed

+381
-49
lines changed

14 files changed

+381
-49
lines changed

README.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,13 @@ react-native link react-native-tcp
4040

4141
### package.json
4242

43-
_only if you want to write require('net') in your javascript_
43+
_only if you want to write require('net') or require('tls') in your javascript_
4444

4545
```json
4646
{
47-
"browser": {
48-
"net": "react-native-tcp"
47+
"react-native": {
48+
"net": "react-native-tcp",
49+
"tls": "react-native-tcp/tls"
4950
}
5051
}
5152
```
@@ -56,8 +57,10 @@ _see/run [index.ios.js/index.android.js](examples/rctsockets) for a complete exa
5657

5758
```js
5859
var net = require('net');
59-
// OR, if not shimming via package.json "browser" field:
60+
var net = require('tls');
61+
// OR, if not shimming via package.json "react-native" field:
6062
// var net = require('react-native-tcp')
63+
// var tls = require('react-native-tcp/tls')
6164

6265
var server = net.createServer(function(socket) {
6366
socket.write('excellent!');
@@ -74,8 +77,25 @@ client.on('data', function(data) {
7477
});
7578
```
7679

80+
### TLS support
81+
82+
TLS is only supported in the client interface. To use TLS, use the `tls.connect()`
83+
syntax and not `socket = new tls.Socket()` syntax.
84+
85+
```
86+
const socket = tls.connect({port: 50002, host:'electrum.villocq.com', rejectUnauthorized: false}, () => {
87+
socket.write('{ "id": 5, "method": "blockchain.estimatefee", "params": [2] }\n')
88+
console.log('Connected')
89+
})
90+
91+
socket.on('data', (data) => {
92+
console.log('data:' + data.toString('ascii'))
93+
})
94+
```
95+
7796
### TODO
7897

98+
host name verification on tls upgrade not implemented on android
7999
add select tests from node's tests for net
80100

81101
PR's welcome!

TcpServer.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ function TcpServer(connectionListener: (socket: Socket) => void) {
5454
util.inherits(TcpServer, EventEmitter);
5555

5656
TcpServer.prototype._debug = function() {
57-
if (__DEV__) {
58-
var args = [].slice.call(arguments);
59-
console.log.apply(console, args);
60-
}
57+
// if (__DEV__) {
58+
// var args = [].slice.call(arguments);
59+
// console.log.apply(console, args);
60+
// }
6161
};
6262

6363
// TODO : determine how to properly overload this with flow

TcpSocket.js

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ function TcpSocket(options: ?{ id: ?number }) {
3434
if (!(this instanceof TcpSocket)) {
3535
return new TcpSocket(options);
3636
}
37+
this.useSsl = false
3738

3839
if (options && options.id) {
3940
// e.g. incoming server connections
@@ -60,17 +61,20 @@ function TcpSocket(options: ?{ id: ?number }) {
6061

6162
this._state = STATE.DISCONNECTED;
6263

64+
// cache all client.send calls to this array if currently upgrading
65+
this._upgradeCache = []
66+
6367
this.read(0);
6468
}
6569

6670
util.inherits(TcpSocket, stream.Duplex);
6771

6872
TcpSocket.prototype._debug = function() {
69-
if (__DEV__) {
70-
var args = [].slice.call(arguments);
71-
args.unshift('socket-' + this._id);
72-
console.log.apply(console, args);
73-
}
73+
// if (__DEV__) {
74+
// var args = [].slice.call(arguments);
75+
// args.unshift('socket-' + this._id);
76+
// console.log.apply(console, args);
77+
// }
7478
};
7579

7680
// TODO : determine how to properly overload this with flow
@@ -120,10 +124,15 @@ TcpSocket.prototype.connect = function(options, callback) : TcpSocket {
120124
}
121125

122126
this._state = STATE.CONNECTING;
123-
this._debug('connecting, host:', host, 'port:', port);
124127

125128
this._destroyed = false;
126-
Sockets.connect(this._id, host, Number(port), options);
129+
if (this.useSsl) {
130+
this._debug('connecting TLS, host:', host, 'port:', port);
131+
Sockets.connectTls(this._id, host, Number(port), options);
132+
} else {
133+
this._debug('connecting, host:', host, 'port:', port);
134+
Sockets.connect(this._id, host, Number(port), options);
135+
}
127136

128137
return this;
129138
};
@@ -278,6 +287,12 @@ TcpSocket.prototype._registerEvents = function(): void {
278287
return;
279288
}
280289
this._onError(ev.error);
290+
}),
291+
this._eventEmitter.addListener('secureConnect', ev => {
292+
if (this._id !== ev.id) {
293+
return;
294+
}
295+
this._onSecureConnect();
281296
})
282297
];
283298
};
@@ -340,10 +355,15 @@ TcpSocket.prototype._onClose = function(hadError: boolean): void {
340355
TcpSocket.prototype._onError = function(error: string): void {
341356
this._debug('received', 'error');
342357

343-
this.emit('error', normalizeError(error));
358+
this.emit('onerror', normalizeError(error));
344359
this.destroy();
345360
};
346361

362+
TcpSocket.prototype._onSecureConnect = function(error: string): void {
363+
this._debug('received', 'secureConnect');
364+
this.emit('secureConnect');
365+
};
366+
347367
TcpSocket.prototype.write = function(chunk, encoding, cb) {
348368
if (typeof chunk !== 'string' && !(Buffer.isBuffer(chunk))) {
349369
throw new TypeError(
@@ -356,13 +376,15 @@ TcpSocket.prototype.write = function(chunk, encoding, cb) {
356376
TcpSocket.prototype._write = function(buffer: any, encoding: ?String, callback: ?(err: ?Error) => void) : boolean {
357377
var self = this;
358378

379+
callback = callback || noop;
380+
359381
if (this._state === STATE.DISCONNECTED) {
360-
throw new Error('Socket is not connected.');
382+
return callback()
383+
// return callback(new Error('Socket is not connected.'));
361384
} else if (this._state === STATE.CONNECTING) {
362385
// we're ok, GCDAsyncSocket handles queueing internally
363386
}
364387

365-
callback = callback || noop;
366388
var str;
367389
if (typeof buffer === 'string') {
368390
self._debug('socket.WRITE(): encoding as base64');
@@ -374,6 +396,12 @@ TcpSocket.prototype._write = function(buffer: any, encoding: ?String, callback:
374396
'Invalid data, chunk must be a string or buffer, not ' + typeof buffer);
375397
}
376398

399+
if (this._upgrading) {
400+
self._debug('tls in progress, write added to queue');
401+
this._upgradeCache.push({ str, callback })
402+
return false;
403+
}
404+
377405
Sockets.write(this._id, str, function(err) {
378406
if (self._timeout) {
379407
self._activeTimer(self._timeout.msecs);
@@ -437,6 +465,44 @@ TcpSocket.prototype._normalizeConnectArgs = function(args) {
437465
return typeof cb === 'function' ? [options, cb] : [options];
438466
};
439467

468+
TcpSocket.prototype._enableSsl = function() {
469+
this.useSsl = true
470+
}
471+
472+
TcpSocket.prototype._upgradeToSecure = function(callback) {
473+
// TODO : if we stored the original requested hostname somewhere, then we could do host name verification
474+
var host = null;
475+
var port = this._address.port;
476+
this._debug('upgrading to TLS, host:', host, 'port:', port);
477+
this._upgrading = true;
478+
Sockets.upgradeToSecure(this._id, host, port, () => {
479+
// emit all cached requests
480+
setTimeout(() => {
481+
while (this._upgradeCache.length) {
482+
const cacheElement = this._upgradeCache.shift()
483+
this._debug('flushing tls cache queue', cacheElement);
484+
const self = this;
485+
Sockets.write(this._id, cacheElement.str, function (err) {
486+
if (self._timeout) {
487+
self._activeTimer(self._timeout.msecs);
488+
}
489+
490+
err = normalizeError(err);
491+
if (err) {
492+
self._debug('write failed', err);
493+
return cacheElement.callback(err);
494+
}
495+
496+
cacheElement.callback();
497+
});
498+
}
499+
this._upgrading = false;
500+
callback();
501+
});
502+
});
503+
return this;
504+
}
505+
440506
// unimplemented net.Socket apis
441507
TcpSocket.prototype.ref =
442508
TcpSocket.prototype.unref =

android/src/main/java/com/peel/react/TcpSocketListener.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public interface TcpSocketListener {
1010
void onConnection(Integer serverId, Integer clientId, InetSocketAddress socketAddress);
1111

1212
// client and server
13+
void onSecureConnect(Integer id);
1314
void onConnect(Integer id, InetSocketAddress socketAddress);
1415
void onData(Integer id, byte[] data);
1516
void onClose(Integer id, String error);

android/src/main/java/com/peel/react/TcpSocketManager.java

Lines changed: 71 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,8 @@
33
import android.support.annotation.Nullable;
44
import android.util.SparseArray;
55

6-
import com.koushikdutta.async.AsyncNetworkSocket;
7-
import com.koushikdutta.async.AsyncServer;
8-
import com.koushikdutta.async.AsyncServerSocket;
9-
import com.koushikdutta.async.AsyncSocket;
10-
import com.koushikdutta.async.ByteBufferList;
11-
import com.koushikdutta.async.DataEmitter;
12-
import com.koushikdutta.async.Util;
6+
import com.facebook.react.bridge.Callback;
7+
import com.koushikdutta.async.*;
138
import com.koushikdutta.async.callback.CompletedCallback;
149
import com.koushikdutta.async.callback.ConnectCallback;
1510
import com.koushikdutta.async.callback.DataCallback;
@@ -119,7 +114,7 @@ public void onCompleted(Exception ex) {
119114
});
120115
}
121116

122-
public void connect(final Integer cId, final @Nullable String host, final Integer port) throws UnknownHostException, IOException {
117+
public void connect(final Integer cId, final @Nullable String host, final Integer port, final boolean useTls) throws UnknownHostException, IOException {
123118
// resolve the address
124119
final InetSocketAddress socketAddress;
125120
if (host != null) {
@@ -131,21 +126,78 @@ public void connect(final Integer cId, final @Nullable String host, final Intege
131126
mServer.connectSocket(socketAddress, new ConnectCallback() {
132127
@Override
133128
public void onConnectCompleted(Exception ex, AsyncSocket socket) {
134-
TcpSocketListener listener = mListener.get();
135-
if (ex == null) {
136-
mClients.put(cId, socket);
137-
setSocketCallbacks(cId, socket);
138-
139-
if (listener != null) {
140-
listener.onConnect(cId, socketAddress);
141-
}
142-
} else if (listener != null) {
143-
listener.onError(cId, ex.getMessage());
129+
if (useTls) {
130+
AsyncSSLSocketWrapper.handshake(socket,
131+
socketAddress.getHostName(),
132+
socketAddress.getPort(),
133+
AsyncSSLSocketWrapper.getDefaultSSLContext().createSSLEngine(),
134+
null,
135+
null,
136+
true,
137+
new AsyncSSLSocketWrapper.HandshakeCallback() {
138+
@Override
139+
public void onHandshakeCompleted(Exception e, AsyncSSLSocket socket) {
140+
onConnectionCompleted(e, socket, cId, socketAddress);
141+
}
142+
});
143+
} else {
144+
onConnectionCompleted(ex, socket, cId, socketAddress);
144145
}
145146
}
146147
});
147148
}
148149

150+
private void onConnectionCompleted(Exception ex, AsyncSocket socket, Integer cId, InetSocketAddress socketAddress) {
151+
TcpSocketListener listener = mListener.get();
152+
mClients.put(cId, socket);
153+
if (ex == null) {
154+
setSocketCallbacks(cId, socket);
155+
156+
if (listener != null) {
157+
listener.onConnect(cId, socketAddress);
158+
}
159+
} else if (listener != null) {
160+
listener.onError(cId, "unable to open socket: " + ex);
161+
close(cId);
162+
} else {
163+
close(cId);
164+
}
165+
}
166+
167+
public void upgradeToSecure(final Integer cId, String host, Integer port, final Callback callback) {
168+
Object existingSocket = mClients.get(cId);
169+
if (existingSocket != null && existingSocket instanceof AsyncSocket) {
170+
AsyncSSLSocketWrapper.handshake((AsyncSocket) existingSocket,
171+
host,
172+
port,
173+
AsyncSSLSocketWrapper.getDefaultSSLContext().createSSLEngine(),
174+
null,
175+
null,
176+
true,
177+
new AsyncSSLSocketWrapper.HandshakeCallback() {
178+
@Override
179+
public void onHandshakeCompleted(Exception ex, AsyncSSLSocket upgradedSocket) {
180+
TcpSocketListener listener = mListener.get();
181+
mClients.put(cId, upgradedSocket);
182+
if (ex == null) {
183+
setSocketCallbacks(cId, upgradedSocket);
184+
if (listener != null) {
185+
listener.onSecureConnect(cId);
186+
}
187+
if (callback != null) {
188+
callback.invoke();
189+
}
190+
} else if (listener != null) {
191+
listener.onError(cId, "unable to upgrade socket to tls: " + ex);
192+
close(cId);
193+
} else {
194+
close(cId);
195+
}
196+
}
197+
});
198+
}
199+
}
200+
149201
public void write(final Integer cId, final byte[] data) {
150202
Object socket = mClients.get(cId);
151203
if (socket != null && socket instanceof AsyncSocket) {
@@ -176,3 +228,4 @@ public void closeAllSockets() {
176228
mClients.clear();
177229
}
178230
}
231+

0 commit comments

Comments
 (0)