jamiewest / signalr_core

ASP.NET Core SignalR Dart Client
https://pub.dev/packages/signalr_core
MIT License
90 stars 58 forks source link

The underlying connection was closed before the hub handshake could complete. #95

Open nayefradwi opened 1 year ago

nayefradwi commented 1 year ago

I am facing an issue where I am unable to connect to signalr hub using this package but I am able to connect using a different (not flutter) signalR client.

At first I thought that it could be an issue with the certificate, but i have added

class BadCertificateHttpOverride extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    return super.createHttpClient(context)
      ..badCertificateCallback =
          (X509Certificate cert, String host, int port) => true;
  }
}

and it is being called, I am not sure what is the issue and I am struggling to find out please help. Here are the logs:

I/flutter (23866): FINER: 2022-10-27 22:07:35.314430: Starting HubConnection.
I/flutter (23866): FINER: 2022-10-27 22:07:37.919484: Starting connection with transfer format 'TransferFormat.Text'.
I/flutter (23866): FINER: 2022-10-27 22:07:41.187448: The HttpConnection connected successfully.
I/flutter (23866): FINER: 2022-10-27 22:07:41.187898: Sending handshake request.
I/flutter (23866): FINER: 2022-10-27 22:07:46.800255: HttpConnection.stopConnection(Unknown) called while in state ConnectionState.Connected.
I/flutter (23866): FINER: 2022-10-27 22:07:48.346132: HubConnection.connectionClosed(null) called while in state HubConnectionState.Connecting.
I/flutter (23866): FINER: 2022-10-27 22:07:49.159922: Hub handshake failed with error 'The underlying connection was closed before the hub handshake could complete.' during start(). Stopping HubConnection.
I/flutter (23866): FINER: 2022-10-27 22:07:49.161156: Call to HttpConnection.stop(Exception: The underlying connection was closed before the hub handshake could complete.) ignored because the connection is already in the disconnected state.
I/flutter (23866): FINER: 2022-10-27 22:07:49.162053: HubConnection failed to start successfully because of error 'The underlying connection was closed before the hub handshake could complete.'.

this is how I am building the connection:

  HubConnection buildConnection() {
    final transportProtLogger = Logger("SignalR - transport");
    final hubProtLogger = Logger("SignalR - hub");
    PrintUtil.info(text: "building hub connection");
    HubConnection connection = HubConnectionBuilder()
        .withUrl(
          AppConfig.signalrUrl,
          options: HttpConnectionOptions(
            logger: transportProtLogger,
            logMessageContent: !AppConfig.isProd,
            transport: HttpTransportType.WebSockets,
            accessTokenFactory: () async {
              String accessToken = GetIt.I<ShopperApiClient>().accessToken;
              return accessToken;
            },
          ),
        )
        .withAutomaticReconnect(retryDelays: delaysInMilliSeconds)
        .configureLogging(hubProtLogger)
        .withHubProtocol(JsonHubProtocol())
        .build();
    return connection;
  }

it works with long polling but not websockets

nayefradwi commented 1 year ago

After investigating this issue, It seems that the handshake request is being sent without the authorization header, which in the backend I am working with is required to establish the connection.

I tried to do something like this but still did not fix the issue

    MessageHeaders headers = MessageHeaders();
    headers.setHeaderValue(AuthInterceptor.authorizedHeader,
        "Bearer ${GetIt.I<ShopperApiClient>().accessToken}");
    HubConnection connection = HubConnectionBuilder()
        .withUrl(
          AppConfig.signalrUrl,
          options: HttpConnectionOptions(
            logger: transportProtLogger,
            logMessageContent: !AppConfig.isProd,
            headers: headers,
            skipNegotiation: true,
            requestTimeout: 120000,
            transport: HttpTransportType.WebSockets,
            accessTokenFactory: () async {
              String accessToken = GetIt.I<ShopperApiClient>().accessToken;
              return accessToken;
            },
          ),
        )
        .withAutomaticReconnect(retryDelays: delaysInMilliSeconds)
        .configureLogging(hubProtLogger)
        .withHubProtocol(JsonHubProtocol())
        .build();
    return connection;

I have noticed that the first request and the negotiation both have the access token in their headers, just the handshake request does not have it and this causes the error "The underlying connection was closed before the hub handshake could complete."