Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Realtime: Unable to build WebSocket connection through Tor proxy #925

Closed
nns52k opened this issue May 13, 2024 · 1 comment
Closed

Realtime: Unable to build WebSocket connection through Tor proxy #925

nns52k opened this issue May 13, 2024 · 1 comment
Labels
bug Something isn't working realtime This issue or pull request is related to realtime

Comments

@nns52k
Copy link
Contributor

nns52k commented May 13, 2024

Describe the bug

Unable to create a WebSocket connection to access Realtime on an .onion server.

To Reproduce

  1. Have a working Tor serving Supabase.
  2. Run this Dart script:
import 'dart:io';
import 'package:http/io_client.dart';
import 'package:supabase/supabase.dart';
import 'package:socks5_proxy/socks_client.dart';

void main(List<String> arguments) async {
  HttpClient httpClient = await connectTorProxy(9050);
  final supabase = SupabaseClient(
    'http://your_onion_address.onion',
    'your_anon_key',
    httpClient: IOClient(httpClient),
  );
  await listenToATable(supabase);
}
Future<HttpClient> connectTorProxy(int portNumber) async {
  final httpClient = HttpClient();
  SocksTCPClient.assignToHttpClient(httpClient, [
    ProxySettings(InternetAddress.loopbackIPv4, portNumber),
  ]);
  return httpClient;
}
Future<void> listenToATable(SupabaseClient supabase) async {
  final SupabaseQueryBuilder queryBuilder = supabase.from('countries'); // Assume we have SQL table `countries`.
  final SupabaseStreamFilterBuilder streamFilterBuilder = queryBuilder.stream(primaryKey: ['id']);
  streamFilterBuilder.listen((List<Map<String, dynamic>> resultSet) {
    print(supabase.realtime.isConnected);
    printResultSet(resultSet);
  });
}
void printResultSet(List<Map<String, dynamic>> resultSet, {bool hasRowSeparator = true}) {
  for (Map<String, dynamic> row in resultSet) {
    row.forEach((String columnName, dynamic columnValue) {
      print('$columnName: $columnValue');
    });
    if (hasRowSeparator) print('----------');
  }
}

Expected behavior

Whenever the SQL table countries in the server is changed, our Dart script shall receive an update, but because of the bug, it doesn't. There is no WebSocket connection between our client app and the .onion server providing Supabase.

Version (please complete the following information):
On Linux/macOS

├── supabase 2.1.2
│   ├── functions_client 2.1.0
│   ├── gotrue 2.6.1
│   ├── postgrest 2.1.1
│   ├── realtime_client 2.0.4
│   ├── storage_client 2.0.1

Additional context

According to Tor Project: FAQ | Tor onion services: How do I access onion services?, the client shan't lookup .onion address. Please note that the top-level domain is .onion. It will doom to fail to lookup .onion address, because .onion is only recognizable by Tor Network. The client shall simply pass the .onion address to Tor proxy without looking up the .onion address in advance.

When running the above code example that reproduces the bug, it will throw this exception:

SocketException (SocketException: Failed host lookup: 'your_onion_address.onion' (OS Error: Name or service not known, errno = -2))

The callstack of the DNS lookup that causes the exception is as follows:

_NativeSocket.staggeredLookup.<anonymous closure>.lookupAddresses (~/.local/packages/flutter/bin/cache/dart-sdk/lib/_internal/vm/bin/socket_patch.dart:633)
_NativeSocket.staggeredLookup.<anonymous closure> (~/.local/packages/flutter/bin/cache/dart-sdk/lib/_internal/vm/bin/socket_patch.dart:655)
_runGuarded (~/.local/packages/flutter/bin/cache/dart-sdk/lib/async/stream_controller.dart:823)
_StreamController._subscribe.<anonymous closure> (~/.local/packages/flutter/bin/cache/dart-sdk/lib/async/stream_controller.dart:702)
_BufferingStreamSubscription._guardCallback (~/.local/packages/flutter/bin/cache/dart-sdk/lib/async/stream_impl.dart:415)
_StreamController._subscribe (~/.local/packages/flutter/bin/cache/dart-sdk/lib/async/stream_controller.dart:701)
_ControllerStream._createSubscription (~/.local/packages/flutter/bin/cache/dart-sdk/lib/async/stream_controller.dart:836)
_StreamImpl.listen (~/.local/packages/flutter/bin/cache/dart-sdk/lib/async/stream_impl.dart:471)
_NativeSocket.tryConnectToResolvedAddresses (~/.local/packages/flutter/bin/cache/dart-sdk/lib/_internal/vm/bin/socket_patch.dart:966)
_NativeSocket.startConnect.<anonymous closure> (~/.local/packages/flutter/bin/cache/dart-sdk/lib/_internal/vm/bin/socket_patch.dart:740)
<asynchronous gap> (Unknown Source:0)
_RawSocket.startConnect.<anonymous closure> (~/.local/packages/flutter/bin/cache/dart-sdk/lib/_internal/vm/bin/socket_patch.dart:1915)
<asynchronous gap> (Unknown Source:0)
Socket._startConnect.<anonymous closure> (~/.local/packages/flutter/bin/cache/dart-sdk/lib/_internal/vm/bin/socket_patch.dart:2140)
<asynchronous gap> (Unknown Source:0)
_ConnectionTarget.connect.<anonymous closure> (~/.local/packages/flutter/bin/cache/dart-sdk/lib/_http/http_impl.dart:2490)
<asynchronous gap> (Unknown Source:0)
_HttpClient._openUrl.<anonymous closure> (~/.local/packages/flutter/bin/cache/dart-sdk/lib/_http/http_impl.dart:2787)
<asynchronous gap> (Unknown Source:0)
_WebSocketImpl.connect.<anonymous closure> (~/.local/packages/flutter/bin/cache/dart-sdk/lib/_http/websocket_impl.dart:1021)
<asynchronous gap> (Unknown Source:0)
_WebSocketImpl.connect.<anonymous closure> (~/.local/packages/flutter/bin/cache/dart-sdk/lib/_http/websocket_impl.dart:1048)
<asynchronous gap> (Unknown Source:0)
new IOWebSocketChannel.connect.<anonymous closure> (~/.pub-cache/hosted/pub.dev/web_socket_channel-2.4.5/lib/io.dart:90)
<asynchronous gap> (Unknown Source:0)
@nns52k nns52k added the bug Something isn't working label May 13, 2024
@dshukertjr dshukertjr added the realtime This issue or pull request is related to realtime label May 13, 2024
@nns52k
Copy link
Contributor Author

nns52k commented May 16, 2024

There is no way to pass a HttpClient to WebSocketChannel like what we can do to SupabaseClient, but defining HttpOverrides.createHttpClient and running all Supabase code in a HttpOverrides.runZoned is a workaround.

@nns52k nns52k closed this as completed May 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working realtime This issue or pull request is related to realtime
Projects
None yet
Development

No branches or pull requests

2 participants