birdofpreyru / react-native-static-server

Embedded HTTP server for React Native
https://dr.pogodin.studio/docs/react-native-static-server
Other
142 stars 22 forks source link

Needs improvements for network interface selection for non-local mode #21

Open jole141 opened 1 year ago

jole141 commented 1 year ago

IOS server starts up when I'm on Wi-Fi and works, but when I'm on mobile data I can't start the server and I get a message that the server cannot be started and the URL is undefined.

birdofpreyru commented 1 year ago

Do you create it with nonLocal option, do you speicify port explicitly? My first naive guess would be that there is something wrong with getLocalIpAddress() (used when nonLocal is opted) or getOpenPort() (used when no port specified explicitly) implementation. These both were implemented for iOS by @rafalzawadzki, and I have not really looked how they work, but I guess they lookup for target IP and port going through different network interfaces currently available on the device, thus probably the presence of mobile data connection confuses them.

Hey @rafalzawadzki, any chance you can have a look into it?

rafalzawadzki commented 1 year ago

hi @birdofpreyru, you're correct: with one of my recent PRs I disabled fetching IP address for all interfaces other than wifi: https://github.com/birdofpreyru/react-native-static-server/commit/4a27a0e7e6c5a2f967763928dfcf4c18f03bdeee. This is because starting a server in most mobile networks does not work. It's rare for providers to assign static and externally accessible IP addresses.

I am not very good with networking etc so I may be mistaken, lmk if you see use cases or simply a way to start such a server correctly? What we could also do is return IP address from native code normally, but also have another return value that lets the consumer know which interface that IP came from.

aostrun commented 1 year ago

Would it be possible to run the server on the hotspot interface? As far as I know both Android and iOS have a network interface for the hotspot network.

birdofpreyru commented 1 year ago

Hmm... yeah, I don't know much about network interfaces, and consequences of launching the server on different of them.

Technically, the Lighttp server will try to bind to any IP we tell him, and then it is the question to the OS and app permissions, whether it will be allowed to bind to the IP successfully. I guess, we can / should extend the library interface in this regard, say we provide a new function which queries the array of all network interfaces available on the device / the interfaces of selected kind; and we provide a new option of server constructor to start on any user-specified IP address. And then we'll leave it to each developer to figure out which network interface is appropriate for specific OS / use case.

aostrun commented 1 year ago

@birdofpreyru that sounds like an ideal solution. Furthermore, allowing users to specify more than one interface would be awesome as a server should be able to bind to multiple interfaces. Also, setting this parameter to 0.0.0.0 should bind the server to all available network interfaces.

The quick fix for the issue, before implementing the ideal solution described above, could be to bind the server to all available network interfaces by default, like setting it to 0.0.0.0. I'm not sure if the app permissions on iOS/Android would stand in the way of doing this.

aostrun commented 1 year ago

Hey @birdofpreyru, any comments on my last post? Are there any updates regarding this?

birdofpreyru commented 1 year ago

Hey @aostrun , no, I don't have any update. Resolving this issue is currently not too high on my priorities list, thus it probably will have to wait some time.

jole141 commented 1 year ago

@birdofpreyru Hi, I managed to start a server on mobile data on iOS. Here is my code snippet:

const handleHotspotTurnOn = async () => {
    ...
    const PORT = 8080;
    const server = new StaticServer({
      fileDir: path!,
      nonLocal: true,
      port: PORT,
    });
    setServerInstance(server);
    server._hostname = '0.0.0.0';
    server._signalBarrier = undefined;
    const ip = await NetworkInfo.getIPV4Address();
    server
      .start()
      .then(() => {
        const endpointUrl = `http://${ip}:${PORT}/${filename}.xml`;
        console.log(`Serving at URL ${endpointUrl}`);
        setUrl(endpointUrl);
      })
      .catch((error: any) => {
        console.log('error', error);
      });
  };

So, the temporary fix is to add these two lines before starting the server:

server._hostname = '0.0.0.0';
server._signalBarrier = undefined;

I also added the Network library to the project to be able to get the device's IP address.