Skip to content

Commit 85a9d8c

Browse files
committed
Excluded GraphQLWebSocketMiddleware from exception stack trace if request is not a Web Socket
1 parent 5b448ee commit 85a9d8c

File tree

3 files changed

+42
-25
lines changed

3 files changed

+42
-25
lines changed

RELEASE_NOTES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@
232232
* Migrated all solutions to SLNX format
233233
* Various performance optimizations and bug fixes
234234

235-
### Unreleased
235+
### 3.1.0 - Unreleased
236236

237237
* Added `TimeOnly` GraphQL type
238+
* Excluded `GraphQLWebSocketMiddleware` from exception stack trace if request not a Web Socket

src/FSharp.Data.GraphQL.Server.AspNetCore/GraphQLWebsocketMiddleware.fs

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ open FSharp.Data.GraphQL.Shared.WebSockets
2525

2626
type GraphQLWebSocketMiddleware<'Root>
2727
(
28-
next : RequestDelegate,
28+
next : RequestDelegate, // must be kept for middleware signature compatibility
2929
applicationLifetime : IHostApplicationLifetime,
3030
serviceProvider : IServiceProvider,
3131
logger : ILogger<GraphQLWebSocketMiddleware<'Root>>,
@@ -35,7 +35,6 @@ type GraphQLWebSocketMiddleware<'Root>
3535
let options = options.Value
3636
let serializerOptions = options.SerializerOptions
3737
let pingHandler = options.WebsocketOptions.CustomPingHandler
38-
let endpointUrl = PathString options.WebsocketOptions.EndpointUrl
3938
let connectionInitTimeout = options.WebsocketOptions.ConnectionInitTimeout
4039

4140
let serializeServerMessage (jsonSerializerOptions : JsonSerializerOptions) (serverMessage : ServerMessage) = task {
@@ -346,25 +345,29 @@ type GraphQLWebSocketMiddleware<'Root>
346345
return Result.Error <| "{nameof ConnectionInit} timeout"
347346
}
348347

349-
member _.InvokeAsync (ctx : HttpContext) = task {
350-
if not (ctx.Request.Path = endpointUrl) then
351-
do! next.Invoke (ctx)
352-
else if ctx.WebSockets.IsWebSocketRequest then
353-
use! socket = ctx.WebSockets.AcceptWebSocketAsync ("graphql-transport-ws")
354-
let! connectionInitResult = socket |> waitForConnectionInitAndRespondToClient
355-
match connectionInitResult with
356-
| Result.Error errMsg -> logger.LogWarning errMsg
357-
| Ok _ ->
358-
let longRunningCancellationToken =
359-
(CancellationTokenSource
360-
.CreateLinkedTokenSource(ctx.RequestAborted, applicationLifetime.ApplicationStopping)
361-
.Token)
362-
longRunningCancellationToken.Register (fun _ -> (socket |> tryToGracefullyCloseSocketWithDefaultBehavior).Wait ())
363-
|> ignore
364-
try
365-
do! socket |> handleMessages longRunningCancellationToken ctx
366-
with ex ->
367-
logger.LogError (ex, "Cannot handle WebSocket message.")
348+
member _.InvokeAsync (ctx : HttpContext) : Task =
349+
if ctx.WebSockets.IsWebSocketRequest then
350+
task {
351+
use! socket = ctx.WebSockets.AcceptWebSocketAsync ("graphql-transport-ws")
352+
let! connectionInitResult = socket |> waitForConnectionInitAndRespondToClient
353+
match connectionInitResult with
354+
| Result.Error errMsg -> logger.LogWarning errMsg
355+
| Ok _ ->
356+
let longRunningCancellationToken =
357+
(CancellationTokenSource
358+
.CreateLinkedTokenSource(ctx.RequestAborted, applicationLifetime.ApplicationStopping)
359+
.Token)
360+
longRunningCancellationToken.Register (fun _ -> (socket |> tryToGracefullyCloseSocketWithDefaultBehavior).Wait ())
361+
|> ignore
362+
try
363+
do! socket |> handleMessages longRunningCancellationToken ctx
364+
with ex ->
365+
logger.LogError (ex, "Cannot handle WebSocket message.")
366+
}
368367
else
369-
do! next.Invoke (ctx)
370-
}
368+
TypedResults.Problem (
369+
title = "WebSocket connection expected.",
370+
detail = $"'{options.WebsocketOptions.EndpointUrl}' endpoint only accepts WebSocket connections.",
371+
statusCode = StatusCodes.Status400BadRequest
372+
) :> IResult
373+
|> _.ExecuteAsync(ctx)

src/FSharp.Data.GraphQL.Server.AspNetCore/ServiceCollectionExtensions.fs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,5 +329,18 @@ module ApplicationBuilderExtensions =
329329

330330
type IApplicationBuilder with
331331

332+
/// <summary>
333+
/// Registers the GraphQL WebSocket middleware to handle WebSocket connections at the configured endpoint.
334+
/// The middleware will only be applied to requests matching the endpoint path configured in <see cref="GraphQLOptions" />.
335+
/// </summary>
332336
[<Extension; CompiledName "UseWebSocketsForGraphQL">]
333-
member builder.UseWebSocketsForGraphQL<'Root> () = builder.UseMiddleware<GraphQLWebSocketMiddleware<'Root>> ()
337+
member builder.UseWebSocketsForGraphQL<'Root> () =
338+
339+
let options = builder.ApplicationServices.GetRequiredService<IOptions<GraphQLOptions<'Root>>>()
340+
let endpointPath = PathString options.Value.WebsocketOptions.EndpointUrl
341+
342+
builder.UseWhen(
343+
(fun ctx -> ctx.Request.Path = endpointPath),
344+
fun appBuilder ->
345+
appBuilder.UseMiddleware<GraphQLWebSocketMiddleware<'Root>>() |> ignore
346+
)

0 commit comments

Comments
 (0)