* Do not forcibly log out user if user retrieval fails with a server error code
This behaviour caused users to get forcibly logged out of the game
during yesterday's redis outage.
From one case where logs were provided
(https://discord.com/channels/188630481301012481/1097318920991559880/1480201862610423933):
- User had repeated timeouts on API requests; consequently, API went
into failing state
- On one of the login retries `/api/v2/me` returned a 500 with no error
details (`{"error":"null}` JSON response) which resulted in
an instant logout as per
https://github.com/ppy/osu/blob/7263551aa868911a7d9148cf2cb16f9e0325f531/osu.Game/Online/API/APIAccess.cs#L323-L324
This PR intends to only forcibly log the user out if the returned error
code indicates a client error. If it is a server error, the login is
preserved and a normal retry loop proceeds.
This can be tested with a local web instance via following steps:
1. Start `osu-web` and a client instance connected to it.
2. Log in on the client instance.
3. Kill (`^C`) `osu-web`.
4. Trigger a few requests in the client and wait for enough of them to
fail for the API to change to `Failing` state.
5. Apply
```diff
diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php
index db34639abf2..392a844882a 100644
--- a/app/Http/Controllers/UsersController.php
+++ b/app/Http/Controllers/UsersController.php
@@ -581,6 +581,8 @@ class UsersController extends Controller
*/
public function me($mode = null)
{
+ abort(500);
+
$user = \Auth::user();
$currentMode = $mode ?? $user->playmode;
```
6. Start `osu-web` again.
7. On master this will log the user out forcibly. On this PR, the user
will remain in `Failing` state.
8. Undo patch from step (5) (restarting web is not required).
9. On this PR, the client will be logged back in.
* Update framework
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
Closes https://github.com/ppy/osu/issues/26824... I think?
Can be reproduced via something like
diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs
index 485274f349..e6e93ab4c7 100644
--- a/osu.Game/Online/API/OAuth.cs
+++ b/osu.Game/Online/API/OAuth.cs
@@ -151,6 +151,11 @@ internal string RequestAccessToken()
{
if (!ensureAccessToken()) return null;
+ for (int i = 0; i < 10000; ++i)
+ {
+ _ = Token.Value.AccessToken;
+ }
+
return Token.Value.AccessToken;
}
The cause is `SecondFactorAuthForm` calling `Logout()`, which calls
`OAuth.Clear()`, _while_ the `APIAccess` connect loop is checking if
`authentication.HasValidAccessToken` is true, which happens to
internally check `Token.Value.AccessToken`, which the clearing of
tokens can brutally interrupt.
This time for `SocketException`s. I seem to recall looking at this and
deciding there was a reason to not catch socket exceptions, but on
revisiting it seems sane to do so.
This covers a fail case like reported:
```
2023-10-06 03:24:17 [verbose]: Request to https://lazer.ppy.sh/oauth/token failed with System.Net.Http.HttpRequestException: No such host is known. (lazer.ppy.sh:443)
2023-10-06 03:24:17 [verbose]: ---> System.Net.Sockets.SocketException (11001): No such host is known.
2023-10-06 03:24:17 [verbose]: at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
```
Closes https://github.com/ppy/osu/issues/24890 (again).
This is important when using dynamic compiling to rapidly iterate. Until we actually split projects out into pieces (like the abstract ruleset project we have talked about) there is no advantage to using internal in the osu! game code.