WebSocket Issues

Hey All,
Currently I’m having some connectivity problems I’m trying to get to the bottom of.
I’ve built a web app in PSU, that will serve around 500 internal users every quarter.
These users are global, I initially went down the route of azure app proxy (on another thread) until I realized it doesnt fully support web sockets.
I then turned to using F5 and I’ve configured an origin pool and load balancer with whitelisting to internal clients only.

It’s been working really well in testing, and for the majority of users I’ve trialled it with, they’ve also given great feedback about the PSU app that’s been built.
However I’d say around 4 out of 20 users had issues.

The content of the page users visit is for access reviews, it’s a New-UDTable with a list of potentially hundreds of rows (server side w/pagination).
They can select up to 100 rows at a time and hit a button to ‘review selected’ (in the table toolbar) which uses a Get-UDElement to pull the selected rows and then show a udmodal with more controls and a submit button.

However it’s at this point, the action of Get-UDElement seems to stall and close the websocket.

Here’s what the console looks like with errors:

However after the reconnect, the component doesnt seem to continue the code after the get-udelement, which is to show a udmodal - it never pops up.

Is there anything that I should be looking at from a troubleshooting point of view?

Details about my environment:
PSU installed as a service on windows v4.2.21
Outbound connectivity runs through squid proxy (we’ve put local proxy bypasses in no_proxy env vars due to grpc issues previously)
The web app is exposed to our global corp users via F5, with an origin pool and loadbalancer config, whitelisted to corp devices only - this is when we have issues.
Internal testing has been performed on an AVD to the on-prem VM hosting PSU and has been completed without issue, it is super responsive. Issues only seem rife when testing externally via F5.

Another thing I would mention here as well, incase anyone comes across doing the same. I had to disable HTTP2 on both load balancer and origin pool for websockets to work, I didnt realise but websockets only really works with HTTP1.1 from what I understand.

Seeing quite a few error logs on the PSU server:

2024-06-18 08:50:18.077 +01:00 [ERR] Exception calling "SendWebSocketMessageWithResult" with "3" argument(s): "Status(StatusCode="Unknown", Detail="Exception was thrown by handler.", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1718697018.043000000","description":"Error received from peer ipv4:127.0.0.1:53379","file":"..\..\..\src\core\lib\surface\call.cc","file_line":953,"grpc_message":"Exception was thrown by handler.","grpc_status":2}")"
2024-06-18 08:50:18.083 +01:00 [ERR] Cannot bind argument to parameter 'InputObject' because it is null.
2024-06-18 08:50:18.089 +01:00 [ERR] An error occurred: Exception calling "SendWebSocketMessageWithResult" with "3" argument(s): "Status(StatusCode="Unknown", Detail="Exception was thrown by handler.", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1718697018.043000000","description":"Error received from peer ipv4:127.0.0.1:53379","file":"..\..\..\src\core\lib\surface\call.cc","file_line":953,"grpc_message":"Exception was thrown by handler.","grpc_status":2}")"
Endpoint: AccessReviewPortal_ReviewSelectedButton_InComplete
Session: b52fd408-9e08-4aa6-8206-397077d2390c, User: -----@----.com

at Get-UDElement, C:\Program Files (x86)\Universal\Modules\Universal\UniversalDashboard.MaterialUI.psm1: line 16023
at AccessReviewPortal_ReviewSelectedButton_InComplete: line 5
at AccessReviewPortal_ReviewSelectedButton_InComplete: line 1
at AccessReviewPortal.ps1 : line 664

at Get-UDElement, C:\Program Files (x86)\Universal\Modules\Universal\UniversalDashboard.MaterialUI.psm1: line 16023
at AccessReviewPortal_ReviewSelectedButton_InComplete: line 5
at AccessReviewPortal_ReviewSelectedButton_InComplete: line 1
at AccessReviewPortal.ps1 : line 664

at Get-UDElement, C:\Program Files (x86)\Universal\Modules\Universal\UniversalDashboard.MaterialUI.psm1: line 16023
at AccessReviewPortal_ReviewSelectedButton_InComplete: line 5
at AccessReviewPortal_ReviewSelectedButton_InComplete: line 1
at AccessReviewPortal.ps1 : line 664

at Get-UDElement, C:\Program Files (x86)\Universal\Modules\Universal\UniversalDashboard.MaterialUI.psm1: line 16023
at AccessReviewPortal_ReviewSelectedButton_InComplete: line 5
at AccessReviewPortal_ReviewSelectedButton_InComplete: line 1
at AccessReviewPortal.ps1 : line 664

2024-06-18 08:50:18.091 +01:00 [ERR] An error occurred: Cannot bind argument to parameter 'InputObject' because it is null.
Endpoint: AccessReviewPortal_ReviewSelectedButton_InComplete
Session: b52fd408-9e08-4aa6-8206-397077d2390c, User: -----@-----.com
 

and

iversal\universal\src\Universal.Server\Utilities\HttpContextExtensions.cs:line 23
   at PowerShellProTools.UniversalDashboard.Controllers.JavaScriptController.Index(String asset) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Controllers\JavaScriptController.cs:line 59
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at PowerShellUniversal.FeatureMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\FeatureMiddleware.cs:line 43
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at PowerShellUniversal.DisallowedModeMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\ModeMiddleware.cs:line 47
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at PowerShellUniversal.CspMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\CspMiddleware.cs:line 21
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Universal.Server.Middleware.RoutingMiddleware.Invoke(HttpContext httpContext, IPolicyEvaluator policyEvaluator) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\RoutingMiddleware.cs:line 197
   at PowerShellUniversal.PSUMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\PowerShellMiddleware.cs:line 44
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Universal.Server.Middleware.SwaggerAuthenticationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\SwaggerAuthMiddleware.cs:line 53
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at AspNetCoreRateLimit.RateLimitMiddleware`1.Invoke(HttpContext context) in C:\actions-runner\_work\universal\universal\src\AspNetCoreRateLimit\Middleware\RateLimitMiddleware.cs:line 110
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.<Invoke>g__Awaited|8_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)
2024-06-18 10:44:53.953 +01:00 [ERR] An unhandled exception has occurred while executing the request.
System.Exception: dashboardid header not found.
   at Universal.Server.Utilities.HttpContextExtensions.GetDashboardId(HttpContext context) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Utilities\HttpContextExtensions.cs:line 23
   at PowerShellProTools.UniversalDashboard.Controllers.JavaScriptController.Index(String asset) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Controllers\JavaScriptController.cs:line 59
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at PowerShellUniversal.FeatureMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\FeatureMiddleware.cs:line 43
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at PowerShellUniversal.DisallowedModeMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\ModeMiddleware.cs:line 47
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at PowerShellUniversal.CspMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\CspMiddleware.cs:line 21
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Universal.Server.Middleware.RoutingMiddleware.Invoke(HttpContext httpContext, IPolicyEvaluator policyEvaluator) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\RoutingMiddleware.cs:line 197
   at PowerShellUniversal.PSUMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\PowerShellMiddleware.cs:line 44
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Universal.Server.Middleware.SwaggerAuthenticationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Middleware\SwaggerAuthMiddleware.cs:line 53
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at AspNetCoreRateLimit.RateLimitMiddleware`1.Invoke(HttpContext context) in C:\actions-runner\_work\universal\universal\src\AspNetCoreRateLimit\Middleware\RateLimitMiddleware.cs:line 110
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.<Invoke>g__Awaited|8_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)
2024-06-18 10:45:59.410 +01:00 [ERR] An unhandled exception has occurred while executing the request.
System.Exception: dashboardid header not found. 

Lastly

2024-06-17 09:30:44.387 +01:00 [ERR] Error when dispatching 'OnConnectedAsync' on hub.
Grpc.Core.RpcException: Status(StatusCode="Unknown", Detail="Exception was thrown by handler.", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1718613044.386000000","description":"Error received from peer ipv6:[::1]:53380","file":"..\..\..\src\core\lib\surface\call.cc","file_line":953,"grpc_message":"Exception was thrown by handler.","grpc_status":2}")
 ---> Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1718613044.386000000","description":"Error received from peer ipv6:[::1]:53380","file":"..\..\..\src\core\lib\surface\call.cc","file_line":953,"grpc_message":"Exception was thrown by handler.","grpc_status":2}
   --- End of inner exception stack trace ---
   at ProtoBuf.Grpc.Internal.Reshape.UnaryTaskAsyncImpl[TRequest,TResponse](AsyncUnaryCall`1 call, MetadataContext metadata, CancellationToken cancellationToken) in /_/src/protobuf-net.Grpc/Internal/Reshape.cs:line 300
   at Universal.Server.Services.DashboardProxy.SetSessionId(DashboardSetSessionId request) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Services\Dashboard\DashboardProxy.cs:line 416
   at Universal.Server.Services.DashboardProxy.SetSessionId(String connectionId, String sessionId, String pageId) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Services\Dashboard\DashboardProxy.cs:line 597
   at UniversalDashboard.DashboardHub.OnConnectedAsync() in C:\actions-runner\_work\universal\universal\src\Universal.Server\Hubs\DashboardHub.cs:line 84
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.RunHubAsync(HubConnectionContext connection)
2024-06-17 10:11:53.834 +01:00 [ERR] Error when dispatching 'OnConnectedAsync' on hub.
Grpc.Core.RpcException: Status(StatusCode="Unknown", Detail="Exception was thrown by handler.", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1718615513.833000000","description":"Error received from peer ipv6:[::1]:53380","file":"..\..\..\src\core\lib\surface\call.cc","file_line":953,"grpc_message":"Exception was thrown by handler.","grpc_status":2}")
 ---> Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1718615513.833000000","description":"Error received from peer ipv6:[::1]:53380","file":"..\..\..\src\core\lib\surface\call.cc","file_line":953,"grpc_message":"Exception was thrown by handler.","grpc_status":2}
   --- End of inner exception stack trace ---
   at ProtoBuf.Grpc.Internal.Reshape.UnaryTaskAsyncImpl[TRequest,TResponse](AsyncUnaryCall`1 call, MetadataContext metadata, CancellationToken cancellationToken) in /_/src/protobuf-net.Grpc/Internal/Reshape.cs:line 300
   at Universal.Server.Services.DashboardProxy.SetSessionId(DashboardSetSessionId request) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Services\Dashboard\DashboardProxy.cs:line 416
   at Universal.Server.Services.DashboardProxy.SetSessionId(String connectionId, String sessionId, String pageId) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Services\Dashboard\DashboardProxy.cs:line 597
   at UniversalDashboard.DashboardHub.OnConnectedAsync() in C:\actions-runner\_work\universal\universal\src\Universal.Server\Hubs\DashboardHub.cs:line 84
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.RunHubAsync(HubConnectionContext connection)
2024-06-17 10:40:33.643 +01:00 [ERR] Error when dispatching 'OnConnectedAsync' on hub.
Grpc.Core.RpcException: Status(StatusCode="Unknown", Detail="Exception was thrown by handler.", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1718617233.642000000","description":"Error received from peer ipv6:[::1]:53380","file":"..\..\..\src\core\lib\surface\call.cc","file_line":953,"grpc_message":"Exception was thrown by handler.","grpc_status":2}")
 ---> Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1718617233.642000000","description":"Error received from peer ipv6:[::1]:53380","file":"..\..\..\src\core\lib\surface\call.cc","file_line":953,"grpc_message":"Exception was thrown by handler.","grpc_status":2}
   --- End of inner exception stack trace ---
   at ProtoBuf.Grpc.Internal.Reshape.UnaryTaskAsyncImpl[TRequest,TResponse](AsyncUnaryCall`1 call, MetadataContext metadata, CancellationToken cancellationToken) in /_/src/protobuf-net.Grpc/Internal/Reshape.cs:line 300
   at Universal.Server.Services.DashboardProxy.SetSessionId(DashboardSetSessionId request) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Services\Dashboard\DashboardProxy.cs:line 416
   at Universal.Server.Services.DashboardProxy.SetSessionId(String connectionId, String sessionId, String pageId) in C:\actions-runner\_work\universal\universal\src\Universal.Server\Services\Dashboard\DashboardProxy.cs:line 597
   at UniversalDashboard.DashboardHub.OnConnectedAsync() in C:\actions-runner\_work\universal\universal\src\Universal.Server\Hubs\DashboardHub.cs:line 84
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.RunHubAsync(HubConnectionContext connection)

I just did some more testing with my team, had 5 people run through the testing scenario which is… change pagination on the UDtable to 100, select all rows, hit ‘Review Selected’ (a udbutton in the table toolbar) which runs get-udelement on the table, and then shows a popup (Modal)
It seemed completely sporadic on who got the popup and who didnt (for those that didnt in some cases they got errors in the background for components not loading properly). We all refreshed and tried again, and most times it worked.

This leads me to believe my speculation around connection speed/latency is not relevant and the issue is intermittent/sporadic.

It also seems the issue only occurs when using the review selected button and trying to get multiple items from the udtable. if I just put a button on each row - thats working fine, although it’s not feasible from a UI perspective for users with 100’s of rows to review.

Futher to this, I don’t seem to have these issues when testing internally and not via F5.

I logged a support ticket with F5 and their response has been:

I have looked over the logs and see that for all the websocket 101 upgrade requests your origin is closing the connection for these requests.

Are you able to check the origin to see why it may be closing the connection for these requests? As would you also be able to check the timeout that you have configured on the origin? Unfortunately, I cannot test this origin direct since it’s internal to your network.

Quick update:
Looks like there’s two issues at play, the websockets closing when using get-udelement and also sometimes components do not load and do not show any errors, they just dont appear on page, this is caused by 'ERR_CONTENT_LENGTH_MISMATCH. Which adam has suggested might be an issue with proxy buffer settings - I’ll attempt to test this tomorrow.
In the meantime i switched from lastest PS7 version on my server (not using integrated as per best practice article) and put my app on the integrated env and the performance is night and day, it’s super responsive and I’m now unable to replicate the issues with websockets closing just by refreshing and retrying - so I’ll keep monitoring it for now.

As an update to this, Adam suggested switching to the integrated environment, after doing so everything has been working great.
It seems that non-integrated introduces a slightly different method of connection and gRPC issues with the server proxy are wrecking havoc, while integrated bypasses all that and works without issue. I don’t have a definitive fix only a workaround by changing environments, but now know where to look and know that it’s proxy related.