moozzyk / SignalR-Client-Swift

Swift SignalR Client for Asp.Net Core SignalR server
MIT License
356 stars 136 forks source link

Not getting status code in connectionDidFailToOpen delegate method #296

Closed omgore closed 7 months ago

omgore commented 7 months ago

PROBLEM: Actual error code returned by server is not passed on to the delegate transportDidClose() method with skipNegotiation flag. We've used the SignalR-Client-Swift in our project and we have enabled the skipNegotiation flag. Whenever server sends the any error code (E.g. 401 error code) we are not receiving that through delegate method. It only gives the error from Error object. Due to that we are unable to take action based on error code.

EXPECTED BEHAVIOUR: The delegate method transportDidClose() should send the error wrapped using SignalRError.webError(statusCode: statusCode) instead of error from Error object.

LOGS:

2024-03-11T09:46:57.210Z debug: urlSession didCompleteWithError invoked
2024-03-11T09:46:57.210Z debug: Marking transport as closed. 
2024-03-11T09:46:57.210Z info: Error starting webSocket. Error: Error Domain=NSURLErrorDomain Code=-1011 "There was a bad response from the server." UserInfo={NSErrorFailingURLStringKey='', NSErrorFailingURLKey='', _NSURLErrorWebSocketHandshakeFailureReasonKey=0, _NSURLErrorRelatedURLSessionTaskErrorKey=(     "LocalWebSocketTask <71E64DAF-5DC8-4801-B98A-09211B039E89>.<1>" ), _NSURLErrorFailingURLSessionTaskErrorKey=LocalWebSocketTask <71E64DAF-5DC8-4801-B98A-09211B039E89>.<1>, NSLocalizedDescription=There was a bad response from the server.}, `HttpStatusCode: 401`, WebSocket closeCode: 0 
2024-03-11T09:46:57.210Z info: Transport closed 
MicrosoftTeams-image

EXPECTED CODE CHANGE: Line 115: delegate?.transportDidClose(SignalRError.webError(statusCode: statusCode))

Let me know If is there any alternate way to handle this when skipNegotiation flag is enabled.

moozzyk commented 7 months ago

Thanks for the detailed report. Can you elaborate on this part:

EXPECTED BEHAVIOUR: The delegate method transportDidClose() should send the error wrapped using SignalRError.webError(statusCode: statusCode) instead of error from Error object.

I am curious why your expectation is that the error should be wrapped as SignalRError.webError?

omgore commented 7 months ago

Thank you for quick response.

We have used skipNegotiation flag for websocket connection, it is calling urlSession(didcompleteWithError). Whenever there is error from server, we are not getting error code in delegate method connectionDidFailToOpen.

We can see error code 401 is coming, but we are not getting it outside. But if we wrap this in SignalRError.webError then we are able to get this in delegate method connectionDidFailToOpen.

Let us know if there is any other way to get error code in delegate method.

moozzyk commented 7 months ago

When the transport closes due to an error, it forwards the original error: https://github.com/moozzyk/SignalR-Client-Swift/blob/a028e01acc5ef32c0f4933c4f56d645d64b36500/Sources/SignalRClient/WebsocketsTransport.swift#L115

You should be able to use it to retrieve the statusCode if you need it, similarly to how the code logging the error does it. The interface does not promise that the error you'll get will be a SignalRError. In fact, it is impossible to implement this for WebSockets due to the protocol specification. When a webSocket connection is being initialized, it uses the HTTP protocol, and if an error occurs at this stage, you can get an HTTP status code. However, once the HTTP request is upgraded to WebSockets (statusCode 101), the errors will be WebSockets codes as described in: https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code. The code does nothing special to distinguish between these two cases and returns the original error.

In practical terms, I am not sure why knowing the code is useful - the issue is on the server side, and the client cannot do much to fix it.

omgore commented 7 months ago

We cast the error as NSError and then using that -1011 code. But not getting 401 code that is coming.

moozzyk commented 7 months ago

I see. -1011 is NSURLErrorBadServerResponse which is documented here (along with other related errors): https://developer.apple.com/documentation/foundation/1508628-url_loading_system_error_codes/nsurlerrorbadserverresponse

This is the error the URLSession returns.

I am still curious how would you use the statusCode if you had it.

omgore commented 7 months ago

Actually when we received status code as 401- Unauthorized, which means web socket connection with server has failed to open because user is not authenticated. It can be because of session token expiry.

So once we receive 401 status code, we just re authenticate the user and send new session token to server.

moozzyk commented 7 months ago

I see. A possible workaround could be to always refresh the token before starting the connection.

moozzyk commented 7 months ago

I merged a change today, which will return SignalRError.webError if the statusCode is available: 3d46db30157c3b0ef0ca57f25acc7726938e6450