OpenNative / open-native

Open Native brings cross-platform communities together to help them collaborate and strengthen each other through development diversity.
https://open-native.org/
MIT License
454 stars 8 forks source link

react-native-tcp-socket error NativeModules.TcpSockets #9

Closed vallemar closed 1 year ago

vallemar commented 1 year ago

When I try to use the react-native-tcp-socket plugin I get an exception when making the connection:

System.err: An uncaught Exception occurred on "main" thread.
  System.err: Calling js method onClick failed
  System.err: TypeError: Cannot read property 'connect' of undefined
  System.err:
  System.err: StackTrace:
  System.err: connect(file: app\webpack:\app-ns-rn\node_modules\react-native-tcp-socket\src\Socket.js:168:16)
  System.err:   at createConnection(file: app\webpack:\app-ns-rn\node_modules\react-native-tcp-socket\src\index.js:50:21)

It seems that TcpSockets is undefined in this line: https://github.com/Rapsssito/react-native-tcp-socket/blob/master/src/Socket.js#L167 and this import comes from here https://github.com/Rapsssito/react-native-tcp-socket/blob/master/src/Socket.js#L6 and from react-native

ammarahm-ed commented 1 year ago

Here are steps to get it working on android:

  1. Install the module
  2. Add the following line/patch it with patch-package react-native-tcp-socket+6.0.2.patch.

    
    diff --git a/node_modules/react-native-tcp-socket/android/src/main/java/com/asterinet/react/tcpsocket/SSLCertificateHelper.java b/node_modules/react-native-tcp-socket/android/src/main/java/com/asterinet/react/tcpsocket/SSLCertificateHelper.java
    index 20bc9f8..32b80fe 100644
    --- a/node_modules/react-native-tcp-socket/android/src/main/java/com/asterinet/react/tcpsocket/SSLCertificateHelper.java
    +++ b/node_modules/react-native-tcp-socket/android/src/main/java/com/asterinet/react/tcpsocket/SSLCertificateHelper.java
    @@ -84,6 +84,7 @@ final class SSLCertificateHelper {
     }
    
     private static InputStream getRawResourceStream(@NonNull final Context context, @NonNull final String resourceUri) throws IOException {
    +        if (resourceUri.startsWith("file:///android_asset")) return context.getAssets().open(resourceUri.replace("file:///android_asset/",""));
         final int resId = getResourceId(context, resourceUri);
         if (resId == 0)
             return URI.create(resourceUri).toURL().openStream(); // From metro on development

4. Webpack config. Alias the following as below:
```js
    config.resolve.alias.set('net', 'react-native-tcp-socket');
    config.resolve.alias.set('tls', 'react-native-tcp-socket');
    config.resolve.alias.set('fs', 'react-native-tcp-socket');

    config.module
      .rule('rnmodules')
      .include.add(/node_modules(.*[/\\])+react-native-auth0/)
      .add(/node_modules(.*[/\\])+react-native-tcp-socket/)
      .end()
      .use('babel-loader')
      .before('ts-loader')
      .loader('babel-loader')
      .options({
        babelrc: false,
        presets: ['module:metro-react-native-babel-preset'],
      });
  1. Place server certs in AppResources/Android/src/main/assets/tls

Example server/client connection:

const tls = require('tls');
const net = require('net');
// @ts-ignore
const fs = require('fs');

// @ts-ignore
const runningNode = typeof process !== 'undefined' && process.release?.name === 'node';
console.log(runningNode);
if (!runningNode) {
  // @ts-ignore
  tls.Server = tls.TLSServer;
  fs.readFileSync = () => {};
}

// Our patch above allows us to use `file:///android_asset/` urls to load certs & p12 files.
// For ios, place the cert files in AppResources/iOS folder & the path to the certificate files will be just `server-cert.pem`
const ca = !runningNode ? { uri: 'file:///android_asset/tls/server-cert.pem' } : fs.readFileSync('tls/server-cert.pem');
const serverKey = !runningNode ? { uri: 'file:///android_asset/tls/server-key.pem' } : fs.readFileSync('tls/server-key.pem');
// @ts-ignore
const keystore = !runningNode ? { uri: 'file:///android_asset/tls/server-keystore.p12' } : undefined;

const server = new tls.Server();
const clientSocket = new net.Socket();
const client = new tls.TLSSocket(clientSocket, { ca });

function init() {
  server.setSecureContext({
    // @ts-ignore
    key: serverKey,
    cert: ca,
    keystore: keystore,
  });

  server.on('secureConnection', (socket) => {
    socket.on('data', () => {
      socket.write('Echo server\r\n');
    });
  });

  client.on('data', () => {
    client.destroy(); // kill client after server's response
  });
  console.log('start listener');
  server.listen({ port: 0, host: '127.0.0.1', reuseAddress: true }, () => {
    console.log('listener opened');
    const port = server.address()?.port;
    if (!port) throw new Error('Server port not found');

    clientSocket.on('data', (data) => {
      console.log('AUX socket data ' + data);
    });

    clientSocket
      .on('error', (error) => {
        console.log('AUX socket error ' + error);
      })
      .on('close', (error) => {
        console.log('AUX socket closed ' + (error ? error : ''));
      })
      .on('connect', () => {
        console.log('AUX socket connected');
      })

    clientSocket.connect(
      {
        port: port,
        host: '127.0.0.1',
        localAddress: '127.0.0.1',
        reuseAddress: true,
        // localPort: 20000,
        // interface: "wifi",
      },
      () => {
        console.log('Writing message from client');
        client.write('Hello, server! Love, Client.');
      }
    );
  });
}

export { init, server, client };

The only difference here is that we don't use require(...) as that won't work in NativeScript hence we replace it with an object having a uri to the android asset. Here's the link to the server certs I am using https://github.com/Rapsssito/react-native-tcp-socket/tree/master/examples/tcpsockets/tls.

Here's the example code running the client/server connection:

import { init, server, client } from './echo.js';

// Inside your function etc..
server.on('secureConnection', (socket) => {
      this.updateChatter('SSL Client connected to server on ' + JSON.stringify(socket.address()));

      socket.on('data', (data) => {
        this.updateChatter('SSL Server client received: ' + (data.length < 500 ? data : data.length + ' bytes'));
      });

      socket.on('error', (error) => {
        this.updateChatter('SSL Server client error ' + error);
      });

      socket.on('close', (error) => {
        this.updateChatter('SSL Server client closed ' + (error ? error : ''));
      });
    });

    server.on('connection', (socket) => {
      this.updateChatter('Client connected to server on ' + JSON.stringify(socket.address()));

      socket.on('data', (data) => {
        this.updateChatter('Server client received: ' + (data.length < 500 ? data : data.length + ' bytes'));
      });

      socket.on('error', (error) => {
        this.updateChatter('Server client error ' + error);
      });

      socket.on('close', (error) => {
        this.updateChatter('Server client closed ' + (error ? error : ''));
      });
    });

    client.on('secureConnect', () => {
      console.log('Opened SSL client on ' + JSON.stringify(client.address()));
    });

    server.on('error', (error) => {
      this.updateChatter('Server error ' + error);
    });

    server.on('close', () => {
      this.updateChatter('Server closed');
    });

    client.on('connect', () => {
      this.updateChatter('Opened client on ' + JSON.stringify(client.address()));
    });

    client.on('drain', () => {
      this.updateChatter('Client drained');
    });

    client.on('data', (data) => {
      this.updateChatter('Client received: ' + (data.length < 500 ? data : data.length + ' bytes'));
    });

    client.on('error', (error) => {
      this.updateChatter('Client error ' + error);
    });

    client.on('close', (error) => {
      this.updateChatter('Client closed ' + (error ? error : ''));
    });

    init();

This will work in alpha.8 which I will release soon.

For ios, place the cert files in AppResources/iOS folder & the path to the certificate files will be just server-cert.pem

vallemar commented 1 year ago

Awasome