1
1
namespace FSharp.Data.GraphQL.Server.AspNetCore
2
2
3
3
open System
4
+ open System.Buffers
4
5
open System.Collections .Generic
6
+ open System.Diagnostics
7
+ open System.Linq
5
8
open System.Net .WebSockets
6
9
open System.Text .Json
7
10
open System.Text .Json .Serialization
@@ -11,6 +14,8 @@ open Microsoft.AspNetCore.Http
11
14
open Microsoft.Extensions .Hosting
12
15
open Microsoft.Extensions .Logging
13
16
open Microsoft.Extensions .Options
17
+
18
+ open Collections.Pooled
14
19
open FsToolkit.ErrorHandling
15
20
16
21
open FSharp.Data .GraphQL
@@ -47,9 +52,9 @@ type GraphQLWebSocketMiddleware<'Root>
47
52
static let invalidJsonInClientMessageError =
48
53
Result.Error <| InvalidMessage ( 4400 , " Invalid json in client message" )
49
54
50
- let deserializeClientMessage ( serializerOptions : JsonSerializerOptions ) ( msg : string ) = taskResult {
55
+ let deserializeClientMessage ( serializerOptions : JsonSerializerOptions ) ( msg : IReadOnlyPooledList < byte > ) = taskResult {
51
56
try
52
- return JsonSerializer.Deserialize< ClientMessage> ( msg, serializerOptions)
57
+ return JsonSerializer.Deserialize< ClientMessage> ( msg.Span , serializerOptions)
53
58
with
54
59
| : ? InvalidWebsocketMessageException as ex ->
55
60
logger.LogError( ex, " Invalid websocket message:\n {payload}" , msg)
@@ -75,31 +80,35 @@ type GraphQLWebSocketMiddleware<'Root>
75
80
&& not ( theSocket.State = WebSocketState.Closed)
76
81
77
82
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
103
112
}
104
113
105
114
let sendMessageViaSocket ( jsonSerializerOptions ) ( socket : WebSocket ) ( message : ServerMessage ) : Task = task {
0 commit comments