11namespace FSharp.Data.GraphQL.Server.AspNetCore
22
33open System
4+ open System.Buffers
45open System.Collections .Generic
6+ open System.Diagnostics
7+ open System.Linq
58open System.Net .WebSockets
69open System.Text .Json
710open System.Text .Json .Serialization
@@ -11,6 +14,8 @@ open Microsoft.AspNetCore.Http
1114open Microsoft.Extensions .Hosting
1215open Microsoft.Extensions .Logging
1316open Microsoft.Extensions .Options
17+
18+ open Collections.Pooled
1419open FsToolkit.ErrorHandling
1520
1621open FSharp.Data .GraphQL
@@ -47,9 +52,9 @@ type GraphQLWebSocketMiddleware<'Root>
4752 static let invalidJsonInClientMessageError =
4853 Result.Error <| InvalidMessage ( 4400 , " Invalid json in client message" )
4954
50- let deserializeClientMessage ( serializerOptions : JsonSerializerOptions ) ( msg : string ) = taskResult {
55+ let deserializeClientMessage ( serializerOptions : JsonSerializerOptions ) ( msg : IReadOnlyPooledList < byte > ) = taskResult {
5156 try
52- return JsonSerializer.Deserialize< ClientMessage> ( msg, serializerOptions)
57+ return JsonSerializer.Deserialize< ClientMessage> ( msg.Span , serializerOptions)
5358 with
5459 | : ? InvalidWebsocketMessageException as ex ->
5560 logger.LogError( ex, " Invalid websocket message:\n {payload}" , msg)
@@ -75,31 +80,35 @@ type GraphQLWebSocketMiddleware<'Root>
7580 && not ( theSocket.State = WebSocketState.Closed)
7681
7782 let receiveMessageViaSocket ( cancellationToken : CancellationToken ) ( serializerOptions : JsonSerializerOptions ) ( socket : WebSocket ) = taskResult {
78- let buffer = Array.zeroCreate 4096
79- let completeMessage = new List< byte> ()
80- let mutable segmentResponse : WebSocketReceiveResult = null
81- while ( not cancellationToken.IsCancellationRequested)
82- && socket |> isSocketOpen
83- && (( segmentResponse = null )
84- || ( not segmentResponse.EndOfMessage)) do
85- try
86- let! r = socket.ReceiveAsync ( new ArraySegment< byte> ( buffer), cancellationToken)
87- segmentResponse <- r
88- completeMessage.AddRange ( new ArraySegment< byte> ( buffer, 0 , r.Count))
89- with : ? OperationCanceledException ->
90- ()
91-
92- // TODO: Allocate string only if a debugger is attached
93- let message =
94- completeMessage
95- |> Seq.filter ( fun x -> x > 0 uy)
96- |> Array.ofSeq
97- |> System.Text.Encoding.UTF8.GetString
98- if String.IsNullOrWhiteSpace message then
99- return ValueNone
100- else
101- let! result = message |> deserializeClientMessage serializerOptions
102- return ValueSome result
83+ let buffer = ArrayPool.Shared.Rent options.ReadBufferSize
84+ try
85+ let completeMessage = new PooledList< byte> ()
86+ let mutable segmentResponse : WebSocketReceiveResult = null
87+ while ( not cancellationToken.IsCancellationRequested)
88+ && socket |> isSocketOpen
89+ && (( segmentResponse = null )
90+ || ( not segmentResponse.EndOfMessage)) do
91+ try
92+ let! r = socket.ReceiveAsync ( new ArraySegment< byte> ( buffer), cancellationToken)
93+ segmentResponse <- r
94+ completeMessage.AddRange ( new ArraySegment< byte> ( buffer, 0 , r.Count))
95+ with : ? OperationCanceledException ->
96+ ()
97+
98+ if Debugger.IsAttached then
99+ let message =
100+ completeMessage
101+ |> Seq.filter ( fun x -> x > 0 uy)
102+ |> Array.ofSeq
103+ |> System.Text.Encoding.UTF8.GetString
104+ logger.LogInformation ( " -> Request: {request}" , message)
105+ if completeMessage.All( fun b -> b = 0 uy) then
106+ return ValueNone
107+ else
108+ let! result = deserializeClientMessage serializerOptions completeMessage
109+ return ValueSome result
110+ finally
111+ ArrayPool.Shared.Return buffer
103112 }
104113
105114 let sendMessageViaSocket ( jsonSerializerOptions ) ( socket : WebSocket ) ( message : ServerMessage ) : Task = task {
0 commit comments