diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 69c8e2c..d13852f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,9 @@ +#### 0.2.0 - 2019-05-24 +* BREAKING: API Changes +* FEATURE: Doc Comments for all public functions + #### 0.1.2 - 2019-05-23 -* Update Nuget Description +* MINOR: Update Nuget Description #### 0.1.1 - 2019-05-23 -* Initial release +* FEATURE: Initial release diff --git a/docs/coverage/FSharp.Control.Websockets.TPL_AssemblyVersionInformation.htm b/docs/coverage/FSharp.Control.Websockets.TPL_AssemblyVersionInformation.htm index 0d1357b..d8e5c56 100644 --- a/docs/coverage/FSharp.Control.Websockets.TPL_AssemblyVersionInformation.htm +++ b/docs/coverage/FSharp.Control.Websockets.TPL_AssemblyVersionInformation.htm @@ -26,6 +26,6 @@

Summary

File(s)

No files found. This usually happens if a file isn't covered by a test or the class does not contain any sequence points (e.g. a class that only contains auto properties).

- + \ No newline at end of file diff --git a/docs/coverage/FSharp.Control.Websockets.TPL_Stream.htm b/docs/coverage/FSharp.Control.Websockets.TPL_Stream.htm index f40de3e..4b9f63a 100644 --- a/docs/coverage/FSharp.Control.Websockets.TPL_Stream.htm +++ b/docs/coverage/FSharp.Control.Websockets.TPL_Stream.htm @@ -20,7 +20,7 @@

Summary

Covered lines:4 Uncovered lines:2 Coverable lines:6 -Total lines:236 +Total lines:535 Line coverage:66.6% @@ -28,9 +28,9 @@

Metrics

- - - + + +
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
MemoryStream.UTF8toMemoryStream.Static(...)10000
MemoryStream.ToUTF8String.Static(...)1010001
MemoryStream.ToUTF8String(...)10000
MemoryStream.UTF8toMemoryStream.Static(...)10000
MemoryStream.ToUTF8String.Static(...)1010001
MemoryStream.ToUTF8String(...)10000

File(s)

@@ -46,243 +46,542 @@

 6module Stream =  7    open System  8    type System.IO.MemoryStream with9        static member UTF8toMemoryStream (text : string) = - 010            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)1112        static member ToUTF8String (stream : IO.MemoryStream) = - 413            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream - 414            stream.ToArray() - 415            |> Text.Encoding.UTF8.GetString - 416            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters1718        member stream.ToUTF8String () = - 019            stream |> System.IO.MemoryStream.ToUTF8String2021module WebSocket =22    open Stream23    open System24    open System.Net.WebSockets25    open FSharp.Control.Tasks.V22627    /// **Description**28    /// (16 * 1024) = 1638429    /// https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/WebSocketHelpers.cs,285b8b64a4da685130    /// **Output Type**31    ///   * `int`32    [<Literal>]33    let DefaultBufferSize  : int = 16384 // (16 * 1024)3435    let isWebsocketOpen (socket : #WebSocket) =36        socket.State = WebSocketState.Open3738    let close (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellationToken: CancellationToken) (w39        return! ws.CloseAsync(closeStatus, statusDescription, cancellationToken)40    }4142    let closeOutput (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellationToken: CancellationTok43        return! ws.CloseOutputAsync(closeStatus, statusDescription, cancellationToken)44    }4546    /// Sends a whole message to the websocket read from the given stream47    let sendMessage cancellationToken bufferSize messageType (readableStream : #IO.Stream) (socket : #WebSocket) = task 48        let buffer = Array.create (bufferSize) Byte.MinValue49        let mutable moreToRead = true50        while moreToRead && isWebsocketOpen socket do51            let! read = readableStream.ReadAsync(buffer, 0, buffer.Length)52            if read > 0 then53                do! socket.SendAsync(ArraySegment(buffer |> Array.take read), messageType, false, cancellationToken)54            else55                moreToRead <- false56                do! socket.SendAsync((ArraySegment(Array.empty)), messageType, true, cancellationToken)57        }5859    let sendMessageAsUTF8 cancellationToken text socket = task {60        use stream = IO.MemoryStream.UTF8toMemoryStream text61        do! sendMessage cancellationToken DefaultBufferSize WebSocketMessageType.Text stream socket62    }910        /// **Description**11        ///12        /// Turns a string into a UTF8 MemoryStream13        ///14        /// **Parameters**15        ///   * `text` - parameter of type `string`16        ///17        /// **Output Type**18        ///   * `IO.MemoryStream`19        ///20        /// **Exceptions**21        ///22        static member UTF8toMemoryStream (text : string) = + 023            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)2425        /// **Description**26        ///27        /// Turns a `MemoryStream` into a a UTF8 string28        ///29        /// **Parameters**30        ///   * `stream` - parameter of type `IO.MemoryStream`31        ///32        /// **Output Type**33        ///   * `string`34        ///35        /// **Exceptions**36        ///37        static member ToUTF8String (stream : IO.MemoryStream) = + 438            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream + 439            stream.ToArray() + 440            |> Text.Encoding.UTF8.GetString + 441            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters4243        /// **Description**44        ///45        /// Turns a `MemoryStream` into a a UTF8 string46        ///47        /// **Parameters**48        ///49        ///50        /// **Output Type**51        ///   * `string`52        ///53        /// **Exceptions**54        ///55        member stream.ToUTF8String () = + 056            stream |> System.IO.MemoryStream.ToUTF8String5758module WebSocket =59    open Stream60    open System61    open System.Net.WebSockets62    open FSharp.Control.Tasks.V2  6364    type ReceiveStreamResult =65        | Stream of IO.Stream66        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string676869    let receiveMessage cancellationToken bufferSize messageType (writeableStream : IO.Stream) (socket : WebSocket) = tas70        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)71        let mutable moreToRead = true72        let mutable mainResult = Unchecked.defaultof<ReceiveStreamResult>73        while moreToRead do74            let! result  = socket.ReceiveAsync(buffer, cancellationToken)75            match result with76            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived77                do! socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Close received", cancellationToken)78                moreToRead <- false79                mainResult <- StreamClosed(socket.CloseStatus.Value, socket.CloseStatusDescription)80            | result ->81                // printfn "result.MessageType -> %A" result.MessageType82                if result.MessageType <> messageType then83                    failwithf "Invalid message type received %A, expected %A" result.MessageType messageType84                do! writeableStream.WriteAsync(buffer.Array, buffer.Offset, result.Count)85                if result.EndOfMessage then86                    moreToRead <- false87                    mainResult <- Stream writeableStream88        return mainResult89    }90    type ReceiveUTF8Result =91        | String of string92        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string939495    let receiveMessageAsUTF8 cancellationToken socket = task {96        use stream =  new IO.MemoryStream()97        // if not(System.Diagnostics.Debugger.IsAttached) then98        //   printfn "Please attach a debugger, PID: %d" (System.Diagnostics.Process.GetCurrentProcess().Id)99        // while not(System.Diagnostics.Debugger.IsAttached) do100        //   System.Threading.Thread.Sleep(100)101        // System.Diagnostics.Debugger.Break()102        let! result = receiveMessage cancellationToken DefaultBufferSize WebSocketMessageType.Text stream socket103        match result with104        | ReceiveStreamResult.Stream s ->105            return stream |> IO.MemoryStream.ToUTF8String |> String106        | ReceiveStreamResult.StreamClosed(status, reason) ->107            return ReceiveUTF8Result.StreamClosed(status, reason)108    }64    /// **Description**65    ///66    /// Same as the `DefaultReceiveBufferSize` and `DefaultClientSendBufferSize` from the internal [WebSocketHelpers]( h67    ///68    /// Current value: 1638469    ///70    /// **Output Type**71    ///   * `int`72    [<Literal>]73    let DefaultBufferSize  : int = 16384 // (16 * 1024)7475    /// **Description**76    ///77    /// Determines if the websocket is open78    ///79    /// **Parameters**80    ///   * `socket` - parameter of type `WebSocket`81    ///82    /// **Output Type**83    ///   * `bool`84    ///85    /// **Exceptions**86    ///87    let isWebsocketOpen (socket : #WebSocket) =88        socket.State = WebSocketState.Open89909192    /// **Description**93    ///94    /// Receives data from the `System.Net.WebSockets.WebSocket` connection asynchronously.95    ///96    /// **Parameters**97    ///   * `websocket` - parameter of type `WebSocket`98    ///   * `buffer` - parameter of type `ArraySegment<byte>`- References the application buffer that is the storage loc99    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh100    ///101    /// **Output Type**102    ///   * `Tasks.Task<WebSocketReceiveResult>` - An instance of this class represents the result of performing a singl103    ///104    /// **Exceptions**105    ///106    let receive (websocket : WebSocket) (buffer : ArraySegment<byte>) (cancellationToken : CancellationToken) =107        websocket.ReceiveAsync(buffer, cancellationToken)108  109  110111112module ThreadSafeWebSocket =113    open System114    open System.Threading115    open System.Net.WebSockets116    open Stream117    open System.Threading.Tasks118    open System.Threading.Tasks.Dataflow119    open FSharp.Control.Tasks.V2120121    type MessageSendResult = Result<unit, ExceptionDispatchInfo>122    type MessageReceiveResult = Result<WebSocket.ReceiveStreamResult, ExceptionDispatchInfo>123    type SendMessages =124    | Send of  bufferSize : CancellationToken * int * WebSocketMessageType *  IO.Stream * TaskCompletionSource<MessageSe125    | Close of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<MessageSendResult>126    | CloseOutput of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<MessageSendResult>127    type ReceiveMessage = CancellationToken * int * WebSocketMessageType * IO.Stream  * TaskCompletionSource<MessageRece128129    type ThreadSafeWebSocket =130        { websocket : WebSocket131          sendChannel : BufferBlock<SendMessages>132          receiveChannel : BufferBlock<ReceiveMessage>133        }134        interface IDisposable with135            member x.Dispose() =136                x.websocket.Dispose()137138        member x.State =139            x.websocket.State140141        member x.CloseStatus =142            x.websocket.CloseStatus |> Option.ofNullable143144        member x.CloseStatusDescription =145            x.websocket.CloseStatusDescription146147148    let createFromWebSocket dataflowBlockOptions (webSocket : WebSocket) =149        let sendBuffer = BufferBlock<SendMessages>(dataflowBlockOptions)150        let receiveBuffer = BufferBlock<ReceiveMessage>(dataflowBlockOptions)151152        /// handle executing a task in a try/catch and wrapping up the callstack info for later153        let inline wrap (action: unit -> Task<_>) (reply: TaskCompletionSource<_>) = task {154            try155                let! result = action ()156                reply.SetResult (Ok result)157            with158            | ex ->159                let dispatch = ExceptionDispatchInfo.Capture ex160                reply.SetResult(Error dispatch)161        }162163        let sendLoop () = task {164            let mutable hasClosedBeenSent = false165166            while webSocket |> WebSocket.isWebsocketOpen && not hasClosedBeenSent do167                let! message = sendBuffer.ReceiveAsync()168                match message with169                | Send (cancellationToken, buffer, messageType, stream, replyChannel) ->170                    do! wrap (fun () -> WebSocket.sendMessage cancellationToken buffer messageType stream webSocket) rep171                | Close (cancellationToken, status, message, replyChannel) ->172                    hasClosedBeenSent <- true173                    do! wrap (fun ( ) -> WebSocket.close status message cancellationToken webSocket) replyChannel174                | CloseOutput (cancellationToken, status, message, replyChannel) ->175                    hasClosedBeenSent <- true176                    do! wrap (fun () -> WebSocket.closeOutput status message cancellationToken webSocket) replyChannel177        }178179        let receiveLoop () = task {180            while webSocket |> WebSocket.isWebsocketOpen do181                let! (cancellationToken, buffer, messageType, stream, replyChannel) = receiveBuffer.ReceiveAsync()182                do! wrap (fun () -> WebSocket.receiveMessage cancellationToken buffer messageType stream webSocket) repl183        }184185        Task.Run<unit>(Func<Task<unit>>(sendLoop)) |> ignore186        Task.Run<unit>(Func<Task<unit>>(receiveLoop)) |> ignore187188        {189            websocket = webSocket190            sendChannel = sendBuffer191            receiveChannel = receiveBuffer192        }193194    let sendMessage (wsts : ThreadSafeWebSocket) cancellationToken bufferSize messageType stream = task {195        let reply = new TaskCompletionSource<_>()196        let msg = Send(cancellationToken,bufferSize, messageType, stream, reply)197        let! accepted = wsts.sendChannel.SendAsync msg198        return! reply.Task199    }200201    let sendMessageAsUTF8(wsts : ThreadSafeWebSocket) cancellationToken (text : string) = task {202        use stream = IO.MemoryStream.UTF8toMemoryStream text203        return! sendMessage wsts cancellationToken WebSocket.DefaultBufferSize WebSocketMessageType.Text stream204    }205206    let receiveMessage (wsts : ThreadSafeWebSocket) cancellationToken bufferSize messageType stream = task {207        let reply = new TaskCompletionSource<_>()208        let msg = (cancellationToken, bufferSize, messageType, stream, reply)209        let! accepted = wsts.receiveChannel.SendAsync(msg)210        return! reply.Task211    }212213    let receiveMessageAsUTF8 (wsts : ThreadSafeWebSocket) cancellationToken = task {214        use stream = new IO.MemoryStream()215        let! response = receiveMessage wsts cancellationToken WebSocket.DefaultBufferSize WebSocketMessageType.Text stre216        match response with217        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece218        | Ok (WebSocket.ReceiveStreamResult.StreamClosed(status, reason)) -> return WebSocket.ReceiveUTF8Result.StreamCl219        | Error ex -> return Error ex220221    }222223    let close (wsts : ThreadSafeWebSocket) cancellationToken status message = task {224        let reply = new TaskCompletionSource<_>()225        let msg = Close(cancellationToken,status, message, reply)226        let! accepted = wsts.sendChannel.SendAsync msg227        return! reply.Task228    }229230    let closeOutput (wsts : ThreadSafeWebSocket) cancellationToken status message = task {231        let reply = new TaskCompletionSource<_>()232        let msg = CloseOutput(cancellationToken,status, message, reply)233        let! accepted = wsts.sendChannel.SendAsync msg234        return! reply.Task235    }236111    /// **Description**112    ///113    ///  Sends data over the `System.Net.WebSockets.WebSocket` connection asynchronously.114    ///115    /// **Parameters**116    ///   * `buffer` - parameter of type `ArraySegment<byte>` - The buffer to be sent over the connection.117    ///   * `messageType` - parameter of type `WebSocketMessageType`- Indicates whether the application is sending a bin118    ///   * `endOfMessage` - parameter of type `bool` - Indicates whether the data in `buffer` is the last part of a mes119    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh120    ///121    /// **Output Type**122    ///   * `Tasks.Task<unit>`123    ///124    /// **Exceptions**125    ///126    let send (websocket : WebSocket) (buffer : ArraySegment<byte>) (messageType : WebSocketMessageType) (endOfMessage : 127        return! websocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken)128    }129130131    /// **Description**132    ///133    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too134    ///135    /// **Parameters**136    ///   * `websocket` - parameter of type `WebSocket`137    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co138    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con139    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh140    ///141    /// **Output Type**142    ///   * `Tasks.Task<unit>`143    ///144    /// **Exceptions**145    ///146    let close (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellationToke147        return! websocket.CloseAsync(closeStatus, statusDescription, cancellationToken)148    }149150151    /// **Description**152    ///153    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke154    ///155    /// **Parameters**156    ///   * `websocket` - parameter of type `WebSocket`157    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co158    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect159    ///   * `cancellationToken` - parameter of type `CancellationToken`  - Propagates the notification that operations s160    ///161    /// **Output Type**162    ///   * `Tasks.Task<unit>`163    ///164    /// **Exceptions**165    ///166    let closeOutput (websocket : WebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellat167        return! websocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken)168    }169170    /// Sends a whole message to the websocket read from the given stream171172    /// **Description**173    ///174    /// Sends a whole message to the websocket read from the given stream175    ///176    /// **Parameters**177    ///   * `socket` - parameter of type `WebSocket`    ///   * `bufferSize` - parameter of type `int` - How many bytes 178    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b179    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh180    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect181    ///182    /// **Output Type**183    ///   * `Tasks.Task<unit>`184    ///185    /// **Exceptions**186    ///187    let sendMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (cancellationToken : Ca188        let buffer = Array.create (bufferSize) Byte.MinValue189        let mutable moreToRead = true190        while moreToRead && isWebsocketOpen socket do191            let! read = readableStream.ReadAsync(buffer, 0, buffer.Length)192            if read > 0 then193                do!  send socket (ArraySegment(buffer |> Array.take read)) messageType false cancellationToken194            else195                moreToRead <- false196                do! send socket (ArraySegment(Array.empty)) messageType true cancellationToken197        }198199200    /// **Description**201    ///202    /// Sends a string as UTF8 over a websocket connection.203    ///204    /// **Parameters**205    ///   * `socket` - parameter of type `WebSocket`206    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh207    ///   * `text` - parameter of type `string` - The string to send over the websocket.208    ///209    /// **Output Type**210    ///   * `Tasks.Task<unit>`211    ///212    /// **Exceptions**213    ///214    let sendMessageAsUTF8 (socket : WebSocket) (cancellationToken : CancellationToken) (text : string) = task {215        use stream = IO.MemoryStream.UTF8toMemoryStream text216        do! sendMessage socket DefaultBufferSize  WebSocketMessageType.Text cancellationToken stream217    }218219    /// One of the possible results from reading a whole message from a websocket.220    type ReceiveStreamResult =221        /// Reading from the websocket completed222        | Stream of IO.Stream223        /// The websocket was closed during reading224        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string225226227228    /// **Description**229    ///230    /// Reads an entire message from a websocket.231    ///232    /// **Parameters**233    ///   * `socket` - parameter of type `WebSocket`234    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u235    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is receiving a236    ///   * `writeableStream` - parameter of type237    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh238    ///   * `writeableStream` - parameter of type `IO.Stream` -  A writeable stream that data from the websocket is writ239    ///240    /// **Output Type**241    ///   * `Tasks.Task<ReceiveStreamResult>`  - One of the possible results from reading a whole message from a websock242    ///243    /// **Exceptions**244    ///245    let receiveMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (cancellationToken :246        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)247        let mutable moreToRead = true248        let mutable mainResult = Unchecked.defaultof<ReceiveStreamResult>249        while moreToRead do250            let! result  = receive socket buffer cancellationToken251            match result with252            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived253                do! socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Close received", cancellationToken)254                moreToRead <- false255                mainResult <- Closed(socket.CloseStatus.Value, socket.CloseStatusDescription)256            | result ->257                if result.MessageType <> messageType then258                    failwithf "Invalid message type received %A, expected %A" result.MessageType messageType259                do! writeableStream.WriteAsync(buffer.Array, buffer.Offset, result.Count)260                if result.EndOfMessage then261                    moreToRead <- false262                    mainResult <- Stream writeableStream263        return mainResult264    }265266    /// One of the possible results from reading a whole message from a websocket.267    type ReceiveUTF8Result =268        /// Reading from the websocket completed.269        | String of string270        /// The websocket was closed during reading.271        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string272273274275276    /// **Description**277    ///278    /// Reads an entire message from a websocket as a string.279    ///280    /// **Parameters**281    ///   * `socket` - parameter of type `WebSocket`282    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh283    ///284    /// **Output Type**285    ///   * `Tasks.Task<ReceiveUTF8Result>`286    ///287    /// **Exceptions**288    ///289    let receiveMessageAsUTF8 (socket : WebSocket) (cancellationToken : CancellationToken)  = task {290        use stream =  new IO.MemoryStream()291        let! result = receiveMessage socket  DefaultBufferSize WebSocketMessageType.Text cancellationToken stream292        match result with293        | ReceiveStreamResult.Stream s ->294            return stream |> IO.MemoryStream.ToUTF8String |> String295        | ReceiveStreamResult.Closed(status, reason) ->296            return ReceiveUTF8Result.Closed(status, reason)297    }298299300301module ThreadSafeWebSocket =302    open System303    open System.Threading304    open System.Net.WebSockets305    open Stream306    open System.Threading.Tasks307    open System.Threading.Tasks.Dataflow308    open FSharp.Control.Tasks.V2309310    type SendMessages =311    | Send of  bufferSize : CancellationToken * int * WebSocketMessageType *  IO.Stream * TaskCompletionSource<Result<un312    | Close of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<Result<unit, ExceptionDispatchIn313    | CloseOutput of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<Result<unit, ExceptionDisp314    type ReceiveMessage = CancellationToken * int * WebSocketMessageType * IO.Stream  * TaskCompletionSource<Result<WebS315316     /// The ThreadSafeWebSocket record allows applications to send and receive data after the WebSocket upgrade has com317     ///318     /// `There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can 319    type ThreadSafeWebSocket =320        { websocket : WebSocket321          sendChannel : BufferBlock<SendMessages>322          receiveChannel : BufferBlock<ReceiveMessage>323        }324        interface IDisposable with325            /// Used to clean up unmanaged resources for ASP.NET and self-hosted implementations.326            member x.Dispose() =327                x.websocket.Dispose()328        /// Returns the current state of the WebSocket connection.329        member x.State =330            x.websocket.State331        /// Indicates the reason why the remote endpoint initiated the close handshake.332        member x.CloseStatus =333            x.websocket.CloseStatus |> Option.ofNullable334        ///Allows the remote endpoint to describe the reason why the connection was closed.335        member x.CloseStatusDescription =336            x.websocket.CloseStatusDescription337338339340    /// **Description**341    ///342    /// Creates a `ThreadSafeWebSocket` from an existing `WebSocket`.343    ///344    /// **Parameters**345    ///   * `webSocket` - parameter of type `WebSocket`346    ///347    /// **Output Type**348    ///   * `ThreadSafeWebSocket`349    ///350    /// **Exceptions**351    ///352    let createFromWebSocket dataflowBlockOptions (webSocket : WebSocket) =353        let sendBuffer = BufferBlock<SendMessages>(dataflowBlockOptions)354        let receiveBuffer = BufferBlock<ReceiveMessage>(dataflowBlockOptions)355356        /// handle executing a task in a try/catch and wrapping up the callstack info for later357        let inline wrap (action: unit -> Task<_>) (reply: TaskCompletionSource<_>) = task {358            try359                let! result = action ()360                reply.SetResult (Ok result)361            with362            | ex ->363                let dispatch = ExceptionDispatchInfo.Capture ex364                reply.SetResult(Error dispatch)365        }366367        let sendLoop () = task {368            let mutable hasClosedBeenSent = false369370            while webSocket |> WebSocket.isWebsocketOpen && not hasClosedBeenSent do371                let! message = sendBuffer.ReceiveAsync()372                match message with373                | Send (cancellationToken, buffer, messageType, stream, replyChannel) ->374                    do! wrap (fun () -> WebSocket.sendMessage webSocket  buffer messageType cancellationToken stream ) r375                | Close (cancellationToken, status, message, replyChannel) ->376                    hasClosedBeenSent <- true377                    do! wrap (fun ( ) -> WebSocket.close webSocket status message cancellationToken ) replyChannel378                | CloseOutput (cancellationToken, status, message, replyChannel) ->379                    hasClosedBeenSent <- true380                    do! wrap (fun () -> WebSocket.closeOutput webSocket status message cancellationToken ) replyChannel381        }382383        let receiveLoop () = task {384            while webSocket |> WebSocket.isWebsocketOpen do385                let! (cancellationToken, buffer, messageType, stream, replyChannel) = receiveBuffer.ReceiveAsync()386                do! wrap (fun () -> WebSocket.receiveMessage webSocket buffer messageType cancellationToken stream ) rep387        }388389        Task.Run<unit>(Func<Task<unit>>(sendLoop)) |> ignore390        Task.Run<unit>(Func<Task<unit>>(receiveLoop)) |> ignore391392        {393            websocket = webSocket394            sendChannel = sendBuffer395            receiveChannel = receiveBuffer396        }397398399    /// **Description**400    ///401    /// Sends a whole message to the websocket read from the given stream.402    ///403    /// **Parameters**404    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`405    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u406    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b407    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh408    ///   * `readableStream` - parameter of type `IO.Stream`409    ///410    /// **Output Type**411    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`412    ///413    /// **Exceptions**414    ///415    let sendMessage (threadSafeWebSocket : ThreadSafeWebSocket)  (bufferSize : int) (messageType : WebSocketMessageType)416        let reply = new TaskCompletionSource<_>()417        let msg = Send(cancellationToken,bufferSize, messageType, readableStream, reply)418        let! accepted = threadSafeWebSocket.sendChannel.SendAsync msg419        return! reply.Task420    }421422423    /// **Description**424    ///425    /// Sends a string as UTF8 over a websocket connection.426    ///427    /// **Parameters**428    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`429    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh430    ///   * `text` - parameter of type `string` - The string to send over the websocket.431    ///432    /// **Output Type**433    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`434    ///435    /// **Exceptions**436    ///437    let sendMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) (cancellationToken : CancellationToken) (text : st438        use stream = IO.MemoryStream.UTF8toMemoryStream text439        return! sendMessage threadSafeWebSocket  WebSocket.DefaultBufferSize WebSocketMessageType.Text cancellationToken440    }441442443    /// **Description**444    ///445    /// Reads an entire message from a websocket as a string.446    ///447    /// **Parameters**448    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`449   ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to us450    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b451    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh452    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt453    ///454    /// **Output Type**455    ///   * `Task<Result<WebSocket.ReceiveStreamResult,ExceptionDispatchInfo>>`- One of the possible results from readin456    ///457    /// **Exceptions**458    ///459    let receiveMessage (threadSafeWebSocket : ThreadSafeWebSocket)  (bufferSize : int) (messageType : WebSocketMessageTy460        let reply = new TaskCompletionSource<_>()461        let msg = (cancellationToken, bufferSize, messageType, writeableStream, reply)462        let! accepted = threadSafeWebSocket.receiveChannel.SendAsync(msg)463        return! reply.Task464    }465466467    /// **Description**468    ///469    /// Reads an entire message as a string.470    ///471    /// **Parameters**472    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`473    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh474    ///475    /// **Output Type**476    ///   * `Task<Result<WebSocket.ReceiveUTF8Result,ExceptionDispatchInfo>>`477    ///478    /// **Exceptions**479    ///480    let receiveMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) (cancellationToken : CancellationToken) = task 481        use stream = new IO.MemoryStream()482        let! response = receiveMessage threadSafeWebSocket  WebSocket.DefaultBufferSize WebSocketMessageType.Text cancel483        match response with484        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece485        | Ok (WebSocket.ReceiveStreamResult.Closed(status, reason)) -> return WebSocket.ReceiveUTF8Result.Closed(status,486        | Error ex -> return Error ex487488    }489490491    /// **Description**492    ///493    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too494    ///495    /// **Parameters**496    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`497    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co498    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con499    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh500    ///501    /// **Output Type**502    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`503    ///504    /// **Exceptions**505    ///506    let close (threadSafeWebSocket : ThreadSafeWebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription : str507        let reply = new TaskCompletionSource<_>()508        let msg = Close(cancellationToken,closeStatus, statusDescription, reply)509        let! accepted = threadSafeWebSocket.sendChannel.SendAsync msg510        return! reply.Task511    }512513514    /// **Description**515    ///516    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too517    ///518    /// **Parameters**519    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`520///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket connec521    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con522    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh523    ///524    /// **Output Type**525    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`526    ///527    /// **Exceptions**528    ///529    let closeOutput (threadSafeWebSocket : ThreadSafeWebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription 530        let reply = new TaskCompletionSource<_>()531        let msg = CloseOutput(cancellationToken,closeStatus, statusDescription, reply)532        let! accepted = threadSafeWebSocket.sendChannel.SendAsync msg533        return! reply.Task534    }535 - +
diff --git a/docs/coverage/FSharp.Control.Websockets.TPL_ThreadSafeWebSocket.htm b/docs/coverage/FSharp.Control.Websockets.TPL_ThreadSafeWebSocket.htm index e8e36ca..d618575 100644 --- a/docs/coverage/FSharp.Control.Websockets.TPL_ThreadSafeWebSocket.htm +++ b/docs/coverage/FSharp.Control.Websockets.TPL_ThreadSafeWebSocket.htm @@ -20,7 +20,7 @@

Summary

Covered lines:50 Uncovered lines:5 Coverable lines:55 -Total lines:236 +Total lines:535 Line coverage:90.9% Branch coverage:100% @@ -29,58 +29,58 @@

Metrics

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
sendLoop@163(...)1010001
receiveLoop@179(...)1010001
createFromWebSocket(...)1010001
sendMessage(...)1010001
sendMessageAsUTF8(...)1010001
receiveMessage(...)1010001
receiveMessageAsUTF8(...)1010001
close(...)1010001
closeOutput(...)1010001
System-IDisposable-Dispose()1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)3010003
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke()1010001
Invoke()1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)3233.331005.67
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
sendLoop@367(...)1010001
receiveLoop@383(...)1010001
createFromWebSocket(...)1010001
sendMessage(...)1010001
sendMessageAsUTF8(...)1010001
receiveMessage(...)1010001
receiveMessageAsUTF8(...)1010001
close(...)1010001
closeOutput(...)1010001
System-IDisposable-Dispose()1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)3010003
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke()1010001
Invoke()1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)3233.331005.67
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001

File(s)

@@ -96,283 +96,582 @@

 6module Stream =  7    open System  8    type System.IO.MemoryStream with9        static member UTF8toMemoryStream (text : string) =10            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)1112        static member ToUTF8String (stream : IO.MemoryStream) =13            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream14            stream.ToArray()15            |> Text.Encoding.UTF8.GetString16            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters1718        member stream.ToUTF8String () =19            stream |> System.IO.MemoryStream.ToUTF8String2021module WebSocket =22    open Stream23    open System24    open System.Net.WebSockets25    open FSharp.Control.Tasks.V22627    /// **Description**28    /// (16 * 1024) = 1638429    /// https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/WebSocketHelpers.cs,285b8b64a4da685130    /// **Output Type**31    ///   * `int`32    [<Literal>]33    let DefaultBufferSize  : int = 16384 // (16 * 1024)3435    let isWebsocketOpen (socket : #WebSocket) =36        socket.State = WebSocketState.Open3738    let close (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellationToken: CancellationToken) (w39        return! ws.CloseAsync(closeStatus, statusDescription, cancellationToken)40    }4142    let closeOutput (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellationToken: CancellationTok43        return! ws.CloseOutputAsync(closeStatus, statusDescription, cancellationToken)44    }4546    /// Sends a whole message to the websocket read from the given stream47    let sendMessage cancellationToken bufferSize messageType (readableStream : #IO.Stream) (socket : #WebSocket) = task 48        let buffer = Array.create (bufferSize) Byte.MinValue49        let mutable moreToRead = true50        while moreToRead && isWebsocketOpen socket do51            let! read = readableStream.ReadAsync(buffer, 0, buffer.Length)52            if read > 0 then53                do! socket.SendAsync(ArraySegment(buffer |> Array.take read), messageType, false, cancellationToken)54            else55                moreToRead <- false56                do! socket.SendAsync((ArraySegment(Array.empty)), messageType, true, cancellationToken)57        }5859    let sendMessageAsUTF8 cancellationToken text socket = task {60        use stream = IO.MemoryStream.UTF8toMemoryStream text61        do! sendMessage cancellationToken DefaultBufferSize WebSocketMessageType.Text stream socket62    }910        /// **Description**11        ///12        /// Turns a string into a UTF8 MemoryStream13        ///14        /// **Parameters**15        ///   * `text` - parameter of type `string`16        ///17        /// **Output Type**18        ///   * `IO.MemoryStream`19        ///20        /// **Exceptions**21        ///22        static member UTF8toMemoryStream (text : string) =23            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)2425        /// **Description**26        ///27        /// Turns a `MemoryStream` into a a UTF8 string28        ///29        /// **Parameters**30        ///   * `stream` - parameter of type `IO.MemoryStream`31        ///32        /// **Output Type**33        ///   * `string`34        ///35        /// **Exceptions**36        ///37        static member ToUTF8String (stream : IO.MemoryStream) =38            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream39            stream.ToArray()40            |> Text.Encoding.UTF8.GetString41            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters4243        /// **Description**44        ///45        /// Turns a `MemoryStream` into a a UTF8 string46        ///47        /// **Parameters**48        ///49        ///50        /// **Output Type**51        ///   * `string`52        ///53        /// **Exceptions**54        ///55        member stream.ToUTF8String () =56            stream |> System.IO.MemoryStream.ToUTF8String5758module WebSocket =59    open Stream60    open System61    open System.Net.WebSockets62    open FSharp.Control.Tasks.V2  6364    type ReceiveStreamResult =65        | Stream of IO.Stream66        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string676869    let receiveMessage cancellationToken bufferSize messageType (writeableStream : IO.Stream) (socket : WebSocket) = tas70        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)71        let mutable moreToRead = true72        let mutable mainResult = Unchecked.defaultof<ReceiveStreamResult>73        while moreToRead do74            let! result  = socket.ReceiveAsync(buffer, cancellationToken)75            match result with76            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived77                do! socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Close received", cancellationToken)78                moreToRead <- false79                mainResult <- StreamClosed(socket.CloseStatus.Value, socket.CloseStatusDescription)80            | result ->81                // printfn "result.MessageType -> %A" result.MessageType82                if result.MessageType <> messageType then83                    failwithf "Invalid message type received %A, expected %A" result.MessageType messageType84                do! writeableStream.WriteAsync(buffer.Array, buffer.Offset, result.Count)85                if result.EndOfMessage then86                    moreToRead <- false87                    mainResult <- Stream writeableStream88        return mainResult89    }90    type ReceiveUTF8Result =91        | String of string92        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string939495    let receiveMessageAsUTF8 cancellationToken socket = task {96        use stream =  new IO.MemoryStream()97        // if not(System.Diagnostics.Debugger.IsAttached) then98        //   printfn "Please attach a debugger, PID: %d" (System.Diagnostics.Process.GetCurrentProcess().Id)99        // while not(System.Diagnostics.Debugger.IsAttached) do100        //   System.Threading.Thread.Sleep(100)101        // System.Diagnostics.Debugger.Break()102        let! result = receiveMessage cancellationToken DefaultBufferSize WebSocketMessageType.Text stream socket103        match result with104        | ReceiveStreamResult.Stream s ->105            return stream |> IO.MemoryStream.ToUTF8String |> String106        | ReceiveStreamResult.StreamClosed(status, reason) ->107            return ReceiveUTF8Result.StreamClosed(status, reason)108    }64    /// **Description**65    ///66    /// Same as the `DefaultReceiveBufferSize` and `DefaultClientSendBufferSize` from the internal [WebSocketHelpers]( h67    ///68    /// Current value: 1638469    ///70    /// **Output Type**71    ///   * `int`72    [<Literal>]73    let DefaultBufferSize  : int = 16384 // (16 * 1024)7475    /// **Description**76    ///77    /// Determines if the websocket is open78    ///79    /// **Parameters**80    ///   * `socket` - parameter of type `WebSocket`81    ///82    /// **Output Type**83    ///   * `bool`84    ///85    /// **Exceptions**86    ///87    let isWebsocketOpen (socket : #WebSocket) =88        socket.State = WebSocketState.Open89909192    /// **Description**93    ///94    /// Receives data from the `System.Net.WebSockets.WebSocket` connection asynchronously.95    ///96    /// **Parameters**97    ///   * `websocket` - parameter of type `WebSocket`98    ///   * `buffer` - parameter of type `ArraySegment<byte>`- References the application buffer that is the storage loc99    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh100    ///101    /// **Output Type**102    ///   * `Tasks.Task<WebSocketReceiveResult>` - An instance of this class represents the result of performing a singl103    ///104    /// **Exceptions**105    ///106    let receive (websocket : WebSocket) (buffer : ArraySegment<byte>) (cancellationToken : CancellationToken) =107        websocket.ReceiveAsync(buffer, cancellationToken)108  109  110111112module ThreadSafeWebSocket =113    open System114    open System.Threading115    open System.Net.WebSockets116    open Stream117    open System.Threading.Tasks118    open System.Threading.Tasks.Dataflow119    open FSharp.Control.Tasks.V2120121    type MessageSendResult = Result<unit, ExceptionDispatchInfo>122    type MessageReceiveResult = Result<WebSocket.ReceiveStreamResult, ExceptionDispatchInfo>123    type SendMessages =124    | Send of  bufferSize : CancellationToken * int * WebSocketMessageType *  IO.Stream * TaskCompletionSource<MessageSe125    | Close of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<MessageSendResult>126    | CloseOutput of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<MessageSendResult>127    type ReceiveMessage = CancellationToken * int * WebSocketMessageType * IO.Stream  * TaskCompletionSource<MessageRece128129    type ThreadSafeWebSocket =130        { websocket : WebSocket131          sendChannel : BufferBlock<SendMessages>132          receiveChannel : BufferBlock<ReceiveMessage>133        }134        interface IDisposable with135            member x.Dispose() = - 2136                x.websocket.Dispose()137138        member x.State = - 0139            x.websocket.State140141        member x.CloseStatus = - 0142            x.websocket.CloseStatus |> Option.ofNullable143144        member x.CloseStatusDescription = - 0145            x.websocket.CloseStatusDescription146147148    let createFromWebSocket dataflowBlockOptions (webSocket : WebSocket) = - 2149        let sendBuffer = BufferBlock<SendMessages>(dataflowBlockOptions) - 2150        let receiveBuffer = BufferBlock<ReceiveMessage>(dataflowBlockOptions)151152        /// handle executing a task in a try/catch and wrapping up the callstack info for later153        let inline wrap (action: unit -> Task<_>) (reply: TaskCompletionSource<_>) = task {154            try155                let! result = action ()156                reply.SetResult (Ok result)157            with158            | ex ->159                let dispatch = ExceptionDispatchInfo.Capture ex160                reply.SetResult(Error dispatch)161        }162 - 2163        let sendLoop () = task { - 2164            let mutable hasClosedBeenSent = false165 - 2166            while webSocket |> WebSocket.isWebsocketOpen && not hasClosedBeenSent do - 4167                let! message = sendBuffer.ReceiveAsync()168                match message with169                | Send (cancellationToken, buffer, messageType, stream, replyChannel) -> - 10170                    do! wrap (fun () -> WebSocket.sendMessage cancellationToken buffer messageType stream webSocket) rep171                | Close (cancellationToken, status, message, replyChannel) -> - 1172                    hasClosedBeenSent <- true - 5173                    do! wrap (fun ( ) -> WebSocket.close status message cancellationToken webSocket) replyChannel174                | CloseOutput (cancellationToken, status, message, replyChannel) -> - 1175                    hasClosedBeenSent <- true - 5176                    do! wrap (fun () -> WebSocket.closeOutput status message cancellationToken webSocket) replyChannel177        }178 - 2179        let receiveLoop () = task { - 6180            while webSocket |> WebSocket.isWebsocketOpen do - 4181                let! (cancellationToken, buffer, messageType, stream, replyChannel) = receiveBuffer.ReceiveAsync() - 10182                do! wrap (fun () -> WebSocket.receiveMessage cancellationToken buffer messageType stream webSocket) repl183        }184 - 4185        Task.Run<unit>(Func<Task<unit>>(sendLoop)) |> ignore - 4186        Task.Run<unit>(Func<Task<unit>>(receiveLoop)) |> ignore187 - 2188        { - 2189            websocket = webSocket - 2190            sendChannel = sendBuffer - 2191            receiveChannel = receiveBuffer - 2192        }193 - 2194    let sendMessage (wsts : ThreadSafeWebSocket) cancellationToken bufferSize messageType stream = task { - 2195        let reply = new TaskCompletionSource<_>() - 2196        let msg = Send(cancellationToken,bufferSize, messageType, stream, reply) - 2197        let! accepted = wsts.sendChannel.SendAsync msg - 2198        return! reply.Task199    }200 - 2201    let sendMessageAsUTF8(wsts : ThreadSafeWebSocket) cancellationToken (text : string) = task { - 2202        use stream = IO.MemoryStream.UTF8toMemoryStream text - 2203        return! sendMessage wsts cancellationToken WebSocket.DefaultBufferSize WebSocketMessageType.Text stream204    }205 - 2206    let receiveMessage (wsts : ThreadSafeWebSocket) cancellationToken bufferSize messageType stream = task { - 2207        let reply = new TaskCompletionSource<_>() - 2208        let msg = (cancellationToken, bufferSize, messageType, stream, reply) - 2209        let! accepted = wsts.receiveChannel.SendAsync(msg) - 2210        return! reply.Task211    }212 - 2213    let receiveMessageAsUTF8 (wsts : ThreadSafeWebSocket) cancellationToken = task { - 2214        use stream = new IO.MemoryStream() - 2215        let! response = receiveMessage wsts cancellationToken WebSocket.DefaultBufferSize WebSocketMessageType.Text stre216        match response with - 2217        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece - 0218        | Ok (WebSocket.ReceiveStreamResult.StreamClosed(status, reason)) -> return WebSocket.ReceiveUTF8Result.StreamCl - 0219        | Error ex -> return Error ex220221    }222 - 1223    let close (wsts : ThreadSafeWebSocket) cancellationToken status message = task { - 1224        let reply = new TaskCompletionSource<_>() - 1225        let msg = Close(cancellationToken,status, message, reply) - 1226        let! accepted = wsts.sendChannel.SendAsync msg - 1227        return! reply.Task228    }229 - 1230    let closeOutput (wsts : ThreadSafeWebSocket) cancellationToken status message = task { - 1231        let reply = new TaskCompletionSource<_>() - 1232        let msg = CloseOutput(cancellationToken,status, message, reply) - 1233        let! accepted = wsts.sendChannel.SendAsync msg - 1234        return! reply.Task235    }236111    /// **Description**112    ///113    ///  Sends data over the `System.Net.WebSockets.WebSocket` connection asynchronously.114    ///115    /// **Parameters**116    ///   * `buffer` - parameter of type `ArraySegment<byte>` - The buffer to be sent over the connection.117    ///   * `messageType` - parameter of type `WebSocketMessageType`- Indicates whether the application is sending a bin118    ///   * `endOfMessage` - parameter of type `bool` - Indicates whether the data in `buffer` is the last part of a mes119    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh120    ///121    /// **Output Type**122    ///   * `Tasks.Task<unit>`123    ///124    /// **Exceptions**125    ///126    let send (websocket : WebSocket) (buffer : ArraySegment<byte>) (messageType : WebSocketMessageType) (endOfMessage : 127        return! websocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken)128    }129130131    /// **Description**132    ///133    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too134    ///135    /// **Parameters**136    ///   * `websocket` - parameter of type `WebSocket`137    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co138    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con139    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh140    ///141    /// **Output Type**142    ///   * `Tasks.Task<unit>`143    ///144    /// **Exceptions**145    ///146    let close (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellationToke147        return! websocket.CloseAsync(closeStatus, statusDescription, cancellationToken)148    }149150151    /// **Description**152    ///153    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke154    ///155    /// **Parameters**156    ///   * `websocket` - parameter of type `WebSocket`157    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co158    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect159    ///   * `cancellationToken` - parameter of type `CancellationToken`  - Propagates the notification that operations s160    ///161    /// **Output Type**162    ///   * `Tasks.Task<unit>`163    ///164    /// **Exceptions**165    ///166    let closeOutput (websocket : WebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellat167        return! websocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken)168    }169170    /// Sends a whole message to the websocket read from the given stream171172    /// **Description**173    ///174    /// Sends a whole message to the websocket read from the given stream175    ///176    /// **Parameters**177    ///   * `socket` - parameter of type `WebSocket`    ///   * `bufferSize` - parameter of type `int` - How many bytes 178    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b179    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh180    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect181    ///182    /// **Output Type**183    ///   * `Tasks.Task<unit>`184    ///185    /// **Exceptions**186    ///187    let sendMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (cancellationToken : Ca188        let buffer = Array.create (bufferSize) Byte.MinValue189        let mutable moreToRead = true190        while moreToRead && isWebsocketOpen socket do191            let! read = readableStream.ReadAsync(buffer, 0, buffer.Length)192            if read > 0 then193                do!  send socket (ArraySegment(buffer |> Array.take read)) messageType false cancellationToken194            else195                moreToRead <- false196                do! send socket (ArraySegment(Array.empty)) messageType true cancellationToken197        }198199200    /// **Description**201    ///202    /// Sends a string as UTF8 over a websocket connection.203    ///204    /// **Parameters**205    ///   * `socket` - parameter of type `WebSocket`206    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh207    ///   * `text` - parameter of type `string` - The string to send over the websocket.208    ///209    /// **Output Type**210    ///   * `Tasks.Task<unit>`211    ///212    /// **Exceptions**213    ///214    let sendMessageAsUTF8 (socket : WebSocket) (cancellationToken : CancellationToken) (text : string) = task {215        use stream = IO.MemoryStream.UTF8toMemoryStream text216        do! sendMessage socket DefaultBufferSize  WebSocketMessageType.Text cancellationToken stream217    }218219    /// One of the possible results from reading a whole message from a websocket.220    type ReceiveStreamResult =221        /// Reading from the websocket completed222        | Stream of IO.Stream223        /// The websocket was closed during reading224        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string225226227228    /// **Description**229    ///230    /// Reads an entire message from a websocket.231    ///232    /// **Parameters**233    ///   * `socket` - parameter of type `WebSocket`234    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u235    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is receiving a236    ///   * `writeableStream` - parameter of type237    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh238    ///   * `writeableStream` - parameter of type `IO.Stream` -  A writeable stream that data from the websocket is writ239    ///240    /// **Output Type**241    ///   * `Tasks.Task<ReceiveStreamResult>`  - One of the possible results from reading a whole message from a websock242    ///243    /// **Exceptions**244    ///245    let receiveMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (cancellationToken :246        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)247        let mutable moreToRead = true248        let mutable mainResult = Unchecked.defaultof<ReceiveStreamResult>249        while moreToRead do250            let! result  = receive socket buffer cancellationToken251            match result with252            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived253                do! socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Close received", cancellationToken)254                moreToRead <- false255                mainResult <- Closed(socket.CloseStatus.Value, socket.CloseStatusDescription)256            | result ->257                if result.MessageType <> messageType then258                    failwithf "Invalid message type received %A, expected %A" result.MessageType messageType259                do! writeableStream.WriteAsync(buffer.Array, buffer.Offset, result.Count)260                if result.EndOfMessage then261                    moreToRead <- false262                    mainResult <- Stream writeableStream263        return mainResult264    }265266    /// One of the possible results from reading a whole message from a websocket.267    type ReceiveUTF8Result =268        /// Reading from the websocket completed.269        | String of string270        /// The websocket was closed during reading.271        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string272273274275276    /// **Description**277    ///278    /// Reads an entire message from a websocket as a string.279    ///280    /// **Parameters**281    ///   * `socket` - parameter of type `WebSocket`282    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh283    ///284    /// **Output Type**285    ///   * `Tasks.Task<ReceiveUTF8Result>`286    ///287    /// **Exceptions**288    ///289    let receiveMessageAsUTF8 (socket : WebSocket) (cancellationToken : CancellationToken)  = task {290        use stream =  new IO.MemoryStream()291        let! result = receiveMessage socket  DefaultBufferSize WebSocketMessageType.Text cancellationToken stream292        match result with293        | ReceiveStreamResult.Stream s ->294            return stream |> IO.MemoryStream.ToUTF8String |> String295        | ReceiveStreamResult.Closed(status, reason) ->296            return ReceiveUTF8Result.Closed(status, reason)297    }298299300301module ThreadSafeWebSocket =302    open System303    open System.Threading304    open System.Net.WebSockets305    open Stream306    open System.Threading.Tasks307    open System.Threading.Tasks.Dataflow308    open FSharp.Control.Tasks.V2309310    type SendMessages =311    | Send of  bufferSize : CancellationToken * int * WebSocketMessageType *  IO.Stream * TaskCompletionSource<Result<un312    | Close of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<Result<unit, ExceptionDispatchIn313    | CloseOutput of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<Result<unit, ExceptionDisp314    type ReceiveMessage = CancellationToken * int * WebSocketMessageType * IO.Stream  * TaskCompletionSource<Result<WebS315316     /// The ThreadSafeWebSocket record allows applications to send and receive data after the WebSocket upgrade has com317     ///318     /// `There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can 319    type ThreadSafeWebSocket =320        { websocket : WebSocket321          sendChannel : BufferBlock<SendMessages>322          receiveChannel : BufferBlock<ReceiveMessage>323        }324        interface IDisposable with325            /// Used to clean up unmanaged resources for ASP.NET and self-hosted implementations.326            member x.Dispose() = + 2327                x.websocket.Dispose()328        /// Returns the current state of the WebSocket connection.329        member x.State = + 0330            x.websocket.State331        /// Indicates the reason why the remote endpoint initiated the close handshake.332        member x.CloseStatus = + 0333            x.websocket.CloseStatus |> Option.ofNullable334        ///Allows the remote endpoint to describe the reason why the connection was closed.335        member x.CloseStatusDescription = + 0336            x.websocket.CloseStatusDescription337338339340    /// **Description**341    ///342    /// Creates a `ThreadSafeWebSocket` from an existing `WebSocket`.343    ///344    /// **Parameters**345    ///   * `webSocket` - parameter of type `WebSocket`346    ///347    /// **Output Type**348    ///   * `ThreadSafeWebSocket`349    ///350    /// **Exceptions**351    ///352    let createFromWebSocket dataflowBlockOptions (webSocket : WebSocket) = + 2353        let sendBuffer = BufferBlock<SendMessages>(dataflowBlockOptions) + 2354        let receiveBuffer = BufferBlock<ReceiveMessage>(dataflowBlockOptions)355356        /// handle executing a task in a try/catch and wrapping up the callstack info for later357        let inline wrap (action: unit -> Task<_>) (reply: TaskCompletionSource<_>) = task {358            try359                let! result = action ()360                reply.SetResult (Ok result)361            with362            | ex ->363                let dispatch = ExceptionDispatchInfo.Capture ex364                reply.SetResult(Error dispatch)365        }366 + 2367        let sendLoop () = task { + 2368            let mutable hasClosedBeenSent = false369 + 2370            while webSocket |> WebSocket.isWebsocketOpen && not hasClosedBeenSent do + 4371                let! message = sendBuffer.ReceiveAsync()372                match message with373                | Send (cancellationToken, buffer, messageType, stream, replyChannel) -> + 10374                    do! wrap (fun () -> WebSocket.sendMessage webSocket  buffer messageType cancellationToken stream ) r375                | Close (cancellationToken, status, message, replyChannel) -> + 1376                    hasClosedBeenSent <- true + 5377                    do! wrap (fun ( ) -> WebSocket.close webSocket status message cancellationToken ) replyChannel378                | CloseOutput (cancellationToken, status, message, replyChannel) -> + 1379                    hasClosedBeenSent <- true + 5380                    do! wrap (fun () -> WebSocket.closeOutput webSocket status message cancellationToken ) replyChannel381        }382 + 2383        let receiveLoop () = task { + 6384            while webSocket |> WebSocket.isWebsocketOpen do + 4385                let! (cancellationToken, buffer, messageType, stream, replyChannel) = receiveBuffer.ReceiveAsync() + 10386                do! wrap (fun () -> WebSocket.receiveMessage webSocket buffer messageType cancellationToken stream ) rep387        }388 + 4389        Task.Run<unit>(Func<Task<unit>>(sendLoop)) |> ignore + 4390        Task.Run<unit>(Func<Task<unit>>(receiveLoop)) |> ignore391 + 2392        { + 2393            websocket = webSocket + 2394            sendChannel = sendBuffer + 2395            receiveChannel = receiveBuffer + 2396        }397398399    /// **Description**400    ///401    /// Sends a whole message to the websocket read from the given stream.402    ///403    /// **Parameters**404    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`405    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u406    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b407    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh408    ///   * `readableStream` - parameter of type `IO.Stream`409    ///410    /// **Output Type**411    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`412    ///413    /// **Exceptions**414    /// + 2415    let sendMessage (threadSafeWebSocket : ThreadSafeWebSocket)  (bufferSize : int) (messageType : WebSocketMessageType) + 2416        let reply = new TaskCompletionSource<_>() + 2417        let msg = Send(cancellationToken,bufferSize, messageType, readableStream, reply) + 2418        let! accepted = threadSafeWebSocket.sendChannel.SendAsync msg + 2419        return! reply.Task420    }421422423    /// **Description**424    ///425    /// Sends a string as UTF8 over a websocket connection.426    ///427    /// **Parameters**428    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`429    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh430    ///   * `text` - parameter of type `string` - The string to send over the websocket.431    ///432    /// **Output Type**433    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`434    ///435    /// **Exceptions**436    /// + 2437    let sendMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) (cancellationToken : CancellationToken) (text : st + 2438        use stream = IO.MemoryStream.UTF8toMemoryStream text + 2439        return! sendMessage threadSafeWebSocket  WebSocket.DefaultBufferSize WebSocketMessageType.Text cancellationToken440    }441442443    /// **Description**444    ///445    /// Reads an entire message from a websocket as a string.446    ///447    /// **Parameters**448    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`449   ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to us450    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b451    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh452    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt453    ///454    /// **Output Type**455    ///   * `Task<Result<WebSocket.ReceiveStreamResult,ExceptionDispatchInfo>>`- One of the possible results from readin456    ///457    /// **Exceptions**458    /// + 2459    let receiveMessage (threadSafeWebSocket : ThreadSafeWebSocket)  (bufferSize : int) (messageType : WebSocketMessageTy + 2460        let reply = new TaskCompletionSource<_>() + 2461        let msg = (cancellationToken, bufferSize, messageType, writeableStream, reply) + 2462        let! accepted = threadSafeWebSocket.receiveChannel.SendAsync(msg) + 2463        return! reply.Task464    }465466467    /// **Description**468    ///469    /// Reads an entire message as a string.470    ///471    /// **Parameters**472    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`473    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh474    ///475    /// **Output Type**476    ///   * `Task<Result<WebSocket.ReceiveUTF8Result,ExceptionDispatchInfo>>`477    ///478    /// **Exceptions**479    /// + 2480    let receiveMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) (cancellationToken : CancellationToken) = task  + 2481        use stream = new IO.MemoryStream() + 2482        let! response = receiveMessage threadSafeWebSocket  WebSocket.DefaultBufferSize WebSocketMessageType.Text cancel483        match response with + 2484        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece + 0485        | Ok (WebSocket.ReceiveStreamResult.Closed(status, reason)) -> return WebSocket.ReceiveUTF8Result.Closed(status, + 0486        | Error ex -> return Error ex487488    }489490491    /// **Description**492    ///493    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too494    ///495    /// **Parameters**496    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`497    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co498    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con499    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh500    ///501    /// **Output Type**502    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`503    ///504    /// **Exceptions**505    /// + 1506    let close (threadSafeWebSocket : ThreadSafeWebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription : str + 1507        let reply = new TaskCompletionSource<_>() + 1508        let msg = Close(cancellationToken,closeStatus, statusDescription, reply) + 1509        let! accepted = threadSafeWebSocket.sendChannel.SendAsync msg + 1510        return! reply.Task511    }512513514    /// **Description**515    ///516    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too517    ///518    /// **Parameters**519    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`520///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket connec521    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con522    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh523    ///524    /// **Output Type**525    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`526    ///527    /// **Exceptions**528    /// + 1529    let closeOutput (threadSafeWebSocket : ThreadSafeWebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription  + 1530        let reply = new TaskCompletionSource<_>() + 1531        let msg = CloseOutput(cancellationToken,closeStatus, statusDescription, reply) + 1532        let! accepted = threadSafeWebSocket.sendChannel.SendAsync msg + 1533        return! reply.Task534    }535 - +

Methods/Properties

-System-IDisposable-Dispose()
-State()
-CloseStatus()
-CloseStatusDescription()
-createFromWebSocket(System.Threading.Tasks.Dataflow.DataflowBlockOptions,System.Net.WebSockets.WebSocket)
-sendLoop@163(System.Net.WebSockets.WebSocket,System.Threading.Tasks.Dataflow.BufferBlock`1<FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/SendMessages>,Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Exception)
-Invoke(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/SendMessages)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Exception)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Exception)
-receiveLoop@179(System.Net.WebSockets.WebSocket,System.Threading.Tasks.Dataflow.BufferBlock`1<System.Tuple`5<System.Threading.CancellationToken,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,System.Threading.Tasks.TaskCompletionSource`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.TPL.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>>>,Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(FSharp.Control.Websockets.TPL.WebSocket/ReceiveStreamResult)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Exception)
-Invoke(System.Tuple`5<System.Threading.CancellationToken,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,System.Threading.Tasks.TaskCompletionSource`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.TPL.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>>)
-Invoke()
-Invoke()
-sendMessage(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Threading.CancellationToken,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Boolean)
-sendMessageAsUTF8(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Threading.CancellationToken,System.String)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.IO.MemoryStream)
-receiveMessage(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Threading.CancellationToken,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Boolean)
-receiveMessageAsUTF8(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Threading.CancellationToken)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.IO.MemoryStream)
-Invoke(Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.TPL.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>)
-close(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Threading.CancellationToken,System.Net.WebSockets.WebSocketCloseStatus,System.String)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Boolean)
-closeOutput(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Threading.CancellationToken,System.Net.WebSockets.WebSocketCloseStatus,System.String)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Boolean)
+System-IDisposable-Dispose()
+State()
+CloseStatus()
+CloseStatusDescription()
+createFromWebSocket(System.Threading.Tasks.Dataflow.DataflowBlockOptions,System.Net.WebSockets.WebSocket)
+sendLoop@367(System.Net.WebSockets.WebSocket,System.Threading.Tasks.Dataflow.BufferBlock`1<FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/SendMessages>,Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Exception)
+Invoke(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/SendMessages)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Exception)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Exception)
+receiveLoop@383(System.Net.WebSockets.WebSocket,System.Threading.Tasks.Dataflow.BufferBlock`1<System.Tuple`5<System.Threading.CancellationToken,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,System.Threading.Tasks.TaskCompletionSource`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.TPL.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>>>,Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(FSharp.Control.Websockets.TPL.WebSocket/ReceiveStreamResult)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Exception)
+Invoke(System.Tuple`5<System.Threading.CancellationToken,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,System.Threading.Tasks.TaskCompletionSource`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.TPL.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>>)
+Invoke()
+Invoke()
+sendMessage(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.Threading.CancellationToken,a)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Boolean)
+sendMessageAsUTF8(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Threading.CancellationToken,System.String)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.IO.MemoryStream)
+receiveMessage(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.Threading.CancellationToken,System.IO.Stream)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Boolean)
+receiveMessageAsUTF8(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Threading.CancellationToken)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.IO.MemoryStream)
+Invoke(Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.TPL.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>)
+close(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Net.WebSockets.WebSocketCloseStatus,System.String,System.Threading.CancellationToken)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Boolean)
+closeOutput(FSharp.Control.Websockets.TPL.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Net.WebSockets.WebSocketCloseStatus,System.String,System.Threading.CancellationToken)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Boolean)

diff --git a/docs/coverage/FSharp.Control.Websockets.TPL_WebSocket.htm b/docs/coverage/FSharp.Control.Websockets.TPL_WebSocket.htm index 8849010..dc8c083 100644 --- a/docs/coverage/FSharp.Control.Websockets.TPL_WebSocket.htm +++ b/docs/coverage/FSharp.Control.Websockets.TPL_WebSocket.htm @@ -17,11 +17,11 @@

Summary

Class:FSharp.Control.Websockets.TPL.WebSocket Assembly:FSharp.Control.Websockets.TPL File(s):/Users/jimmybyrd/Documents/GitHub/FSharp.Control.Websockets/src/FSharp.Control.Websockets.TPL/FSharp.Control.Websockets.TPL.fs -Covered lines:31 -Uncovered lines:2 -Coverable lines:33 -Total lines:236 -Line coverage:93.9% +Covered lines:33 +Uncovered lines:3 +Coverable lines:36 +Total lines:535 +Line coverage:91.6% Branch coverage:62.5% @@ -29,38 +29,42 @@

Metrics

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
isWebsocketOpen(...)10000
close(...)1010001
closeOutput(...)1010001
sendMessage(...)1010001
sendMessageAsUTF8(...)1010001
receiveMessage(...)1010001
receiveMessageAsUTF8(...)1010001
Invoke(...)1010001
Invoke(...)2010002
Invoke(...)10000
Invoke(...)2010002
Invoke(...)10000
Invoke(...)10000
Invoke(...)421001004
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)10000
Invoke(...)10000
Invoke(...)10000
Invoke(...)2010002
Invoke(...)688057.146.29
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)2010002
Invoke(...)1010001
Invoke(...)1010001
isWebsocketOpen(...)10000
receive(...)10000
send(...)1010001
close(...)1010001
closeOutput(...)1010001
sendMessage(...)1010001
sendMessageAsUTF8(...)1010001
receiveMessage(...)1010001
receiveMessageAsUTF8(...)1010001
Invoke(...)10000
Invoke(...)2010002
Invoke(...)1010001
Invoke(...)2010002
Invoke(...)10000
Invoke(...)2010002
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)221001002
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)10000
Invoke(...)10000
Invoke(...)10000
Invoke(...)2010002
Invoke(...)688057.146.29
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)2010002
Invoke(...)1010001
Invoke(...)1010001

File(s)

@@ -76,268 +80,570 @@

 6module Stream =  7    open System  8    type System.IO.MemoryStream with9        static member UTF8toMemoryStream (text : string) =10            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)1112        static member ToUTF8String (stream : IO.MemoryStream) =13            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream14            stream.ToArray()15            |> Text.Encoding.UTF8.GetString16            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters1718        member stream.ToUTF8String () =19            stream |> System.IO.MemoryStream.ToUTF8String2021module WebSocket =22    open Stream23    open System24    open System.Net.WebSockets25    open FSharp.Control.Tasks.V22627    /// **Description**28    /// (16 * 1024) = 1638429    /// https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/WebSocketHelpers.cs,285b8b64a4da685130    /// **Output Type**31    ///   * `int`32    [<Literal>]33    let DefaultBufferSize  : int = 16384 // (16 * 1024)3435    let isWebsocketOpen (socket : #WebSocket) = - 036        socket.State = WebSocketState.Open37 - 138    let close (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellationToken: CancellationToken) (w - 239        return! ws.CloseAsync(closeStatus, statusDescription, cancellationToken)40    }41 - 142    let closeOutput (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellationToken: CancellationTok - 143        return! ws.CloseOutputAsync(closeStatus, statusDescription, cancellationToken)44    }4546    /// Sends a whole message to the websocket read from the given stream - 447    let sendMessage cancellationToken bufferSize messageType (readableStream : #IO.Stream) (socket : #WebSocket) = task  - 448        let buffer = Array.create (bufferSize) Byte.MinValue - 449        let mutable moreToRead = true - 450        while moreToRead && isWebsocketOpen socket do - 851            let! read = readableStream.ReadAsync(buffer, 0, buffer.Length) - 852            if read > 0 then - 453                do! socket.SendAsync(ArraySegment(buffer |> Array.take read), messageType, false, cancellationToken)54            else - 455                moreToRead <- false - 456                do! socket.SendAsync((ArraySegment(Array.empty)), messageType, true, cancellationToken)57        }58 - 259    let sendMessageAsUTF8 cancellationToken text socket = task { - 260        use stream = IO.MemoryStream.UTF8toMemoryStream text - 461        do! sendMessage cancellationToken DefaultBufferSize WebSocketMessageType.Text stream socket62    }910        /// **Description**11        ///12        /// Turns a string into a UTF8 MemoryStream13        ///14        /// **Parameters**15        ///   * `text` - parameter of type `string`16        ///17        /// **Output Type**18        ///   * `IO.MemoryStream`19        ///20        /// **Exceptions**21        ///22        static member UTF8toMemoryStream (text : string) =23            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)2425        /// **Description**26        ///27        /// Turns a `MemoryStream` into a a UTF8 string28        ///29        /// **Parameters**30        ///   * `stream` - parameter of type `IO.MemoryStream`31        ///32        /// **Output Type**33        ///   * `string`34        ///35        /// **Exceptions**36        ///37        static member ToUTF8String (stream : IO.MemoryStream) =38            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream39            stream.ToArray()40            |> Text.Encoding.UTF8.GetString41            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters4243        /// **Description**44        ///45        /// Turns a `MemoryStream` into a a UTF8 string46        ///47        /// **Parameters**48        ///49        ///50        /// **Output Type**51        ///   * `string`52        ///53        /// **Exceptions**54        ///55        member stream.ToUTF8String () =56            stream |> System.IO.MemoryStream.ToUTF8String5758module WebSocket =59    open Stream60    open System61    open System.Net.WebSockets62    open FSharp.Control.Tasks.V2  6364    type ReceiveStreamResult =65        | Stream of IO.Stream66        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string6768 - 669    let receiveMessage cancellationToken bufferSize messageType (writeableStream : IO.Stream) (socket : WebSocket) = tas - 670        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue) - 671        let mutable moreToRead = true - 672        let mutable mainResult = Unchecked.defaultof<ReceiveStreamResult> - 2273        while moreToRead do - 1074            let! result  = socket.ReceiveAsync(buffer, cancellationToken)75            match result with76            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived - 277                do! socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Close received", cancellationToken)78                moreToRead <- false79                mainResult <- StreamClosed(socket.CloseStatus.Value, socket.CloseStatusDescription)80            | result ->81                // printfn "result.MessageType -> %A" result.MessageType - 2482                if result.MessageType <> messageType then - 083                    failwithf "Invalid message type received %A, expected %A" result.MessageType messageType - 884                do! writeableStream.WriteAsync(buffer.Array, buffer.Offset, result.Count)85                if result.EndOfMessage then86                    moreToRead <- false87                    mainResult <- Stream writeableStream - 688        return mainResult89    }90    type ReceiveUTF8Result =91        | String of string92        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string9394 - 495    let receiveMessageAsUTF8 cancellationToken socket = task { - 496        use stream =  new IO.MemoryStream()97        // if not(System.Diagnostics.Debugger.IsAttached) then98        //   printfn "Please attach a debugger, PID: %d" (System.Diagnostics.Process.GetCurrentProcess().Id)99        // while not(System.Diagnostics.Debugger.IsAttached) do100        //   System.Threading.Thread.Sleep(100)101        // System.Diagnostics.Debugger.Break() - 4102        let! result = receiveMessage cancellationToken DefaultBufferSize WebSocketMessageType.Text stream socket103        match result with104        | ReceiveStreamResult.Stream s -> - 2105            return stream |> IO.MemoryStream.ToUTF8String |> String106        | ReceiveStreamResult.StreamClosed(status, reason) -> - 2107            return ReceiveUTF8Result.StreamClosed(status, reason)108    }64    /// **Description**65    ///66    /// Same as the `DefaultReceiveBufferSize` and `DefaultClientSendBufferSize` from the internal [WebSocketHelpers]( h67    ///68    /// Current value: 1638469    ///70    /// **Output Type**71    ///   * `int`72    [<Literal>]73    let DefaultBufferSize  : int = 16384 // (16 * 1024)7475    /// **Description**76    ///77    /// Determines if the websocket is open78    ///79    /// **Parameters**80    ///   * `socket` - parameter of type `WebSocket`81    ///82    /// **Output Type**83    ///   * `bool`84    ///85    /// **Exceptions**86    ///87    let isWebsocketOpen (socket : #WebSocket) = + 088        socket.State = WebSocketState.Open89909192    /// **Description**93    ///94    /// Receives data from the `System.Net.WebSockets.WebSocket` connection asynchronously.95    ///96    /// **Parameters**97    ///   * `websocket` - parameter of type `WebSocket`98    ///   * `buffer` - parameter of type `ArraySegment<byte>`- References the application buffer that is the storage loc99    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh100    ///101    /// **Output Type**102    ///   * `Tasks.Task<WebSocketReceiveResult>` - An instance of this class represents the result of performing a singl103    ///104    /// **Exceptions**105    ///106    let receive (websocket : WebSocket) (buffer : ArraySegment<byte>) (cancellationToken : CancellationToken) = + 0107        websocket.ReceiveAsync(buffer, cancellationToken)108  109  110111112module ThreadSafeWebSocket =113    open System114    open System.Threading115    open System.Net.WebSockets116    open Stream117    open System.Threading.Tasks118    open System.Threading.Tasks.Dataflow119    open FSharp.Control.Tasks.V2120121    type MessageSendResult = Result<unit, ExceptionDispatchInfo>122    type MessageReceiveResult = Result<WebSocket.ReceiveStreamResult, ExceptionDispatchInfo>123    type SendMessages =124    | Send of  bufferSize : CancellationToken * int * WebSocketMessageType *  IO.Stream * TaskCompletionSource<MessageSe125    | Close of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<MessageSendResult>126    | CloseOutput of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<MessageSendResult>127    type ReceiveMessage = CancellationToken * int * WebSocketMessageType * IO.Stream  * TaskCompletionSource<MessageRece128129    type ThreadSafeWebSocket =130        { websocket : WebSocket131          sendChannel : BufferBlock<SendMessages>132          receiveChannel : BufferBlock<ReceiveMessage>133        }134        interface IDisposable with135            member x.Dispose() =136                x.websocket.Dispose()137138        member x.State =139            x.websocket.State140141        member x.CloseStatus =142            x.websocket.CloseStatus |> Option.ofNullable143144        member x.CloseStatusDescription =145            x.websocket.CloseStatusDescription146147148    let createFromWebSocket dataflowBlockOptions (webSocket : WebSocket) =149        let sendBuffer = BufferBlock<SendMessages>(dataflowBlockOptions)150        let receiveBuffer = BufferBlock<ReceiveMessage>(dataflowBlockOptions)151152        /// handle executing a task in a try/catch and wrapping up the callstack info for later153        let inline wrap (action: unit -> Task<_>) (reply: TaskCompletionSource<_>) = task {154            try155                let! result = action ()156                reply.SetResult (Ok result)157            with158            | ex ->159                let dispatch = ExceptionDispatchInfo.Capture ex160                reply.SetResult(Error dispatch)161        }162163        let sendLoop () = task {164            let mutable hasClosedBeenSent = false165166            while webSocket |> WebSocket.isWebsocketOpen && not hasClosedBeenSent do167                let! message = sendBuffer.ReceiveAsync()168                match message with169                | Send (cancellationToken, buffer, messageType, stream, replyChannel) ->170                    do! wrap (fun () -> WebSocket.sendMessage cancellationToken buffer messageType stream webSocket) rep171                | Close (cancellationToken, status, message, replyChannel) ->172                    hasClosedBeenSent <- true173                    do! wrap (fun ( ) -> WebSocket.close status message cancellationToken webSocket) replyChannel174                | CloseOutput (cancellationToken, status, message, replyChannel) ->175                    hasClosedBeenSent <- true176                    do! wrap (fun () -> WebSocket.closeOutput status message cancellationToken webSocket) replyChannel177        }178179        let receiveLoop () = task {180            while webSocket |> WebSocket.isWebsocketOpen do181                let! (cancellationToken, buffer, messageType, stream, replyChannel) = receiveBuffer.ReceiveAsync()182                do! wrap (fun () -> WebSocket.receiveMessage cancellationToken buffer messageType stream webSocket) repl183        }184185        Task.Run<unit>(Func<Task<unit>>(sendLoop)) |> ignore186        Task.Run<unit>(Func<Task<unit>>(receiveLoop)) |> ignore187188        {189            websocket = webSocket190            sendChannel = sendBuffer191            receiveChannel = receiveBuffer192        }193194    let sendMessage (wsts : ThreadSafeWebSocket) cancellationToken bufferSize messageType stream = task {195        let reply = new TaskCompletionSource<_>()196        let msg = Send(cancellationToken,bufferSize, messageType, stream, reply)197        let! accepted = wsts.sendChannel.SendAsync msg198        return! reply.Task199    }200201    let sendMessageAsUTF8(wsts : ThreadSafeWebSocket) cancellationToken (text : string) = task {202        use stream = IO.MemoryStream.UTF8toMemoryStream text203        return! sendMessage wsts cancellationToken WebSocket.DefaultBufferSize WebSocketMessageType.Text stream204    }205206    let receiveMessage (wsts : ThreadSafeWebSocket) cancellationToken bufferSize messageType stream = task {207        let reply = new TaskCompletionSource<_>()208        let msg = (cancellationToken, bufferSize, messageType, stream, reply)209        let! accepted = wsts.receiveChannel.SendAsync(msg)210        return! reply.Task211    }212213    let receiveMessageAsUTF8 (wsts : ThreadSafeWebSocket) cancellationToken = task {214        use stream = new IO.MemoryStream()215        let! response = receiveMessage wsts cancellationToken WebSocket.DefaultBufferSize WebSocketMessageType.Text stre216        match response with217        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece218        | Ok (WebSocket.ReceiveStreamResult.StreamClosed(status, reason)) -> return WebSocket.ReceiveUTF8Result.StreamCl219        | Error ex -> return Error ex220221    }222223    let close (wsts : ThreadSafeWebSocket) cancellationToken status message = task {224        let reply = new TaskCompletionSource<_>()225        let msg = Close(cancellationToken,status, message, reply)226        let! accepted = wsts.sendChannel.SendAsync msg227        return! reply.Task228    }229230    let closeOutput (wsts : ThreadSafeWebSocket) cancellationToken status message = task {231        let reply = new TaskCompletionSource<_>()232        let msg = CloseOutput(cancellationToken,status, message, reply)233        let! accepted = wsts.sendChannel.SendAsync msg234        return! reply.Task235    }236111    /// **Description**112    ///113    ///  Sends data over the `System.Net.WebSockets.WebSocket` connection asynchronously.114    ///115    /// **Parameters**116    ///   * `buffer` - parameter of type `ArraySegment<byte>` - The buffer to be sent over the connection.117    ///   * `messageType` - parameter of type `WebSocketMessageType`- Indicates whether the application is sending a bin118    ///   * `endOfMessage` - parameter of type `bool` - Indicates whether the data in `buffer` is the last part of a mes119    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh120    ///121    /// **Output Type**122    ///   * `Tasks.Task<unit>`123    ///124    /// **Exceptions**125    /// + 8126    let send (websocket : WebSocket) (buffer : ArraySegment<byte>) (messageType : WebSocketMessageType) (endOfMessage :  + 8127        return! websocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken)128    }129130131    /// **Description**132    ///133    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too134    ///135    /// **Parameters**136    ///   * `websocket` - parameter of type `WebSocket`137    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co138    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con139    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh140    ///141    /// **Output Type**142    ///   * `Tasks.Task<unit>`143    ///144    /// **Exceptions**145    /// + 1146    let close (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellationToke + 2147        return! websocket.CloseAsync(closeStatus, statusDescription, cancellationToken)148    }149150151    /// **Description**152    ///153    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke154    ///155    /// **Parameters**156    ///   * `websocket` - parameter of type `WebSocket`157    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co158    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect159    ///   * `cancellationToken` - parameter of type `CancellationToken`  - Propagates the notification that operations s160    ///161    /// **Output Type**162    ///   * `Tasks.Task<unit>`163    ///164    /// **Exceptions**165    /// + 1166    let closeOutput (websocket : WebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription: string) (cancellat + 1167        return! websocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken)168    }169170    /// Sends a whole message to the websocket read from the given stream171172    /// **Description**173    ///174    /// Sends a whole message to the websocket read from the given stream175    ///176    /// **Parameters**177    ///   * `socket` - parameter of type `WebSocket`    ///   * `bufferSize` - parameter of type `int` - How many bytes 178    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b179    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh180    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect181    ///182    /// **Output Type**183    ///   * `Tasks.Task<unit>`184    ///185    /// **Exceptions**186    /// + 4187    let sendMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (cancellationToken : Ca + 4188        let buffer = Array.create (bufferSize) Byte.MinValue + 4189        let mutable moreToRead = true + 4190        while moreToRead && isWebsocketOpen socket do + 8191            let! read = readableStream.ReadAsync(buffer, 0, buffer.Length) + 8192            if read > 0 then + 8193                do!  send socket (ArraySegment(buffer |> Array.take read)) messageType false cancellationToken194            else + 4195                moreToRead <- false + 8196                do! send socket (ArraySegment(Array.empty)) messageType true cancellationToken197        }198199200    /// **Description**201    ///202    /// Sends a string as UTF8 over a websocket connection.203    ///204    /// **Parameters**205    ///   * `socket` - parameter of type `WebSocket`206    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh207    ///   * `text` - parameter of type `string` - The string to send over the websocket.208    ///209    /// **Output Type**210    ///   * `Tasks.Task<unit>`211    ///212    /// **Exceptions**213    /// + 2214    let sendMessageAsUTF8 (socket : WebSocket) (cancellationToken : CancellationToken) (text : string) = task { + 2215        use stream = IO.MemoryStream.UTF8toMemoryStream text + 4216        do! sendMessage socket DefaultBufferSize  WebSocketMessageType.Text cancellationToken stream217    }218219    /// One of the possible results from reading a whole message from a websocket.220    type ReceiveStreamResult =221        /// Reading from the websocket completed222        | Stream of IO.Stream223        /// The websocket was closed during reading224        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string225226227228    /// **Description**229    ///230    /// Reads an entire message from a websocket.231    ///232    /// **Parameters**233    ///   * `socket` - parameter of type `WebSocket`234    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u235    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is receiving a236    ///   * `writeableStream` - parameter of type237    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh238    ///   * `writeableStream` - parameter of type `IO.Stream` -  A writeable stream that data from the websocket is writ239    ///240    /// **Output Type**241    ///   * `Tasks.Task<ReceiveStreamResult>`  - One of the possible results from reading a whole message from a websock242    ///243    /// **Exceptions**244    /// + 6245    let receiveMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (cancellationToken : + 6246        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue) + 6247        let mutable moreToRead = true + 6248        let mutable mainResult = Unchecked.defaultof<ReceiveStreamResult> + 22249        while moreToRead do + 10250            let! result  = receive socket buffer cancellationToken251            match result with252            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived + 2253                do! socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Close received", cancellationToken)254                moreToRead <- false255                mainResult <- Closed(socket.CloseStatus.Value, socket.CloseStatusDescription)256            | result -> + 24257                if result.MessageType <> messageType then + 0258                    failwithf "Invalid message type received %A, expected %A" result.MessageType messageType + 8259                do! writeableStream.WriteAsync(buffer.Array, buffer.Offset, result.Count)260                if result.EndOfMessage then261                    moreToRead <- false262                    mainResult <- Stream writeableStream + 6263        return mainResult264    }265266    /// One of the possible results from reading a whole message from a websocket.267    type ReceiveUTF8Result =268        /// Reading from the websocket completed.269        | String of string270        /// The websocket was closed during reading.271        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string272273274275276    /// **Description**277    ///278    /// Reads an entire message from a websocket as a string.279    ///280    /// **Parameters**281    ///   * `socket` - parameter of type `WebSocket`282    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh283    ///284    /// **Output Type**285    ///   * `Tasks.Task<ReceiveUTF8Result>`286    ///287    /// **Exceptions**288    /// + 4289    let receiveMessageAsUTF8 (socket : WebSocket) (cancellationToken : CancellationToken)  = task { + 4290        use stream =  new IO.MemoryStream() + 4291        let! result = receiveMessage socket  DefaultBufferSize WebSocketMessageType.Text cancellationToken stream292        match result with293        | ReceiveStreamResult.Stream s -> + 2294            return stream |> IO.MemoryStream.ToUTF8String |> String295        | ReceiveStreamResult.Closed(status, reason) -> + 2296            return ReceiveUTF8Result.Closed(status, reason)297    }298299300301module ThreadSafeWebSocket =302    open System303    open System.Threading304    open System.Net.WebSockets305    open Stream306    open System.Threading.Tasks307    open System.Threading.Tasks.Dataflow308    open FSharp.Control.Tasks.V2309310    type SendMessages =311    | Send of  bufferSize : CancellationToken * int * WebSocketMessageType *  IO.Stream * TaskCompletionSource<Result<un312    | Close of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<Result<unit, ExceptionDispatchIn313    | CloseOutput of CancellationToken * WebSocketCloseStatus * string * TaskCompletionSource<Result<unit, ExceptionDisp314    type ReceiveMessage = CancellationToken * int * WebSocketMessageType * IO.Stream  * TaskCompletionSource<Result<WebS315316     /// The ThreadSafeWebSocket record allows applications to send and receive data after the WebSocket upgrade has com317     ///318     /// `There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can 319    type ThreadSafeWebSocket =320        { websocket : WebSocket321          sendChannel : BufferBlock<SendMessages>322          receiveChannel : BufferBlock<ReceiveMessage>323        }324        interface IDisposable with325            /// Used to clean up unmanaged resources for ASP.NET and self-hosted implementations.326            member x.Dispose() =327                x.websocket.Dispose()328        /// Returns the current state of the WebSocket connection.329        member x.State =330            x.websocket.State331        /// Indicates the reason why the remote endpoint initiated the close handshake.332        member x.CloseStatus =333            x.websocket.CloseStatus |> Option.ofNullable334        ///Allows the remote endpoint to describe the reason why the connection was closed.335        member x.CloseStatusDescription =336            x.websocket.CloseStatusDescription337338339340    /// **Description**341    ///342    /// Creates a `ThreadSafeWebSocket` from an existing `WebSocket`.343    ///344    /// **Parameters**345    ///   * `webSocket` - parameter of type `WebSocket`346    ///347    /// **Output Type**348    ///   * `ThreadSafeWebSocket`349    ///350    /// **Exceptions**351    ///352    let createFromWebSocket dataflowBlockOptions (webSocket : WebSocket) =353        let sendBuffer = BufferBlock<SendMessages>(dataflowBlockOptions)354        let receiveBuffer = BufferBlock<ReceiveMessage>(dataflowBlockOptions)355356        /// handle executing a task in a try/catch and wrapping up the callstack info for later357        let inline wrap (action: unit -> Task<_>) (reply: TaskCompletionSource<_>) = task {358            try359                let! result = action ()360                reply.SetResult (Ok result)361            with362            | ex ->363                let dispatch = ExceptionDispatchInfo.Capture ex364                reply.SetResult(Error dispatch)365        }366367        let sendLoop () = task {368            let mutable hasClosedBeenSent = false369370            while webSocket |> WebSocket.isWebsocketOpen && not hasClosedBeenSent do371                let! message = sendBuffer.ReceiveAsync()372                match message with373                | Send (cancellationToken, buffer, messageType, stream, replyChannel) ->374                    do! wrap (fun () -> WebSocket.sendMessage webSocket  buffer messageType cancellationToken stream ) r375                | Close (cancellationToken, status, message, replyChannel) ->376                    hasClosedBeenSent <- true377                    do! wrap (fun ( ) -> WebSocket.close webSocket status message cancellationToken ) replyChannel378                | CloseOutput (cancellationToken, status, message, replyChannel) ->379                    hasClosedBeenSent <- true380                    do! wrap (fun () -> WebSocket.closeOutput webSocket status message cancellationToken ) replyChannel381        }382383        let receiveLoop () = task {384            while webSocket |> WebSocket.isWebsocketOpen do385                let! (cancellationToken, buffer, messageType, stream, replyChannel) = receiveBuffer.ReceiveAsync()386                do! wrap (fun () -> WebSocket.receiveMessage webSocket buffer messageType cancellationToken stream ) rep387        }388389        Task.Run<unit>(Func<Task<unit>>(sendLoop)) |> ignore390        Task.Run<unit>(Func<Task<unit>>(receiveLoop)) |> ignore391392        {393            websocket = webSocket394            sendChannel = sendBuffer395            receiveChannel = receiveBuffer396        }397398399    /// **Description**400    ///401    /// Sends a whole message to the websocket read from the given stream.402    ///403    /// **Parameters**404    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`405    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u406    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b407    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh408    ///   * `readableStream` - parameter of type `IO.Stream`409    ///410    /// **Output Type**411    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`412    ///413    /// **Exceptions**414    ///415    let sendMessage (threadSafeWebSocket : ThreadSafeWebSocket)  (bufferSize : int) (messageType : WebSocketMessageType)416        let reply = new TaskCompletionSource<_>()417        let msg = Send(cancellationToken,bufferSize, messageType, readableStream, reply)418        let! accepted = threadSafeWebSocket.sendChannel.SendAsync msg419        return! reply.Task420    }421422423    /// **Description**424    ///425    /// Sends a string as UTF8 over a websocket connection.426    ///427    /// **Parameters**428    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`429    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh430    ///   * `text` - parameter of type `string` - The string to send over the websocket.431    ///432    /// **Output Type**433    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`434    ///435    /// **Exceptions**436    ///437    let sendMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) (cancellationToken : CancellationToken) (text : st438        use stream = IO.MemoryStream.UTF8toMemoryStream text439        return! sendMessage threadSafeWebSocket  WebSocket.DefaultBufferSize WebSocketMessageType.Text cancellationToken440    }441442443    /// **Description**444    ///445    /// Reads an entire message from a websocket as a string.446    ///447    /// **Parameters**448    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`449   ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to us450    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b451    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh452    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt453    ///454    /// **Output Type**455    ///   * `Task<Result<WebSocket.ReceiveStreamResult,ExceptionDispatchInfo>>`- One of the possible results from readin456    ///457    /// **Exceptions**458    ///459    let receiveMessage (threadSafeWebSocket : ThreadSafeWebSocket)  (bufferSize : int) (messageType : WebSocketMessageTy460        let reply = new TaskCompletionSource<_>()461        let msg = (cancellationToken, bufferSize, messageType, writeableStream, reply)462        let! accepted = threadSafeWebSocket.receiveChannel.SendAsync(msg)463        return! reply.Task464    }465466467    /// **Description**468    ///469    /// Reads an entire message as a string.470    ///471    /// **Parameters**472    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`473    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh474    ///475    /// **Output Type**476    ///   * `Task<Result<WebSocket.ReceiveUTF8Result,ExceptionDispatchInfo>>`477    ///478    /// **Exceptions**479    ///480    let receiveMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) (cancellationToken : CancellationToken) = task 481        use stream = new IO.MemoryStream()482        let! response = receiveMessage threadSafeWebSocket  WebSocket.DefaultBufferSize WebSocketMessageType.Text cancel483        match response with484        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece485        | Ok (WebSocket.ReceiveStreamResult.Closed(status, reason)) -> return WebSocket.ReceiveUTF8Result.Closed(status,486        | Error ex -> return Error ex487488    }489490491    /// **Description**492    ///493    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too494    ///495    /// **Parameters**496    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`497    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co498    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con499    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh500    ///501    /// **Output Type**502    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`503    ///504    /// **Exceptions**505    ///506    let close (threadSafeWebSocket : ThreadSafeWebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription : str507        let reply = new TaskCompletionSource<_>()508        let msg = Close(cancellationToken,closeStatus, statusDescription, reply)509        let! accepted = threadSafeWebSocket.sendChannel.SendAsync msg510        return! reply.Task511    }512513514    /// **Description**515    ///516    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too517    ///518    /// **Parameters**519    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`520///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket connec521    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con522    ///   * `cancellationToken` - parameter of type `CancellationToken` - Propagates the notification that operations sh523    ///524    /// **Output Type**525    ///   * `Task<Result<unit,ExceptionDispatchInfo>>`526    ///527    /// **Exceptions**528    ///529    let closeOutput (threadSafeWebSocket : ThreadSafeWebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription 530        let reply = new TaskCompletionSource<_>()531        let msg = CloseOutput(cancellationToken,closeStatus, statusDescription, reply)532        let! accepted = threadSafeWebSocket.sendChannel.SendAsync msg533        return! reply.Task534    }535 - +

Methods/Properties

-isWebsocketOpen(a)
-close(System.Net.WebSockets.WebSocketCloseStatus,System.String,System.Threading.CancellationToken,System.Net.WebSockets.WebSocket)
-Invoke(Microsoft.FSharp.Core.Unit)
-closeOutput(System.Net.WebSockets.WebSocketCloseStatus,System.String,System.Threading.CancellationToken,System.Net.WebSockets.WebSocket)
-Invoke(Microsoft.FSharp.Core.Unit)
-sendMessage(System.Threading.CancellationToken,System.Int32,System.Net.WebSockets.WebSocketMessageType,a,b)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Int32)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-sendMessageAsUTF8(System.Threading.CancellationToken,System.String,System.Net.WebSockets.WebSocket)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.IO.MemoryStream)
-receiveMessage(System.Threading.CancellationToken,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,System.Net.WebSockets.WebSocket)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Net.WebSockets.WebSocketReceiveResult)
-Invoke(System.Net.WebSockets.WebSocketMessageType)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-receiveMessageAsUTF8(System.Threading.CancellationToken,System.Net.WebSockets.WebSocket)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.IO.MemoryStream)
-Invoke(FSharp.Control.Websockets.TPL.WebSocket/ReceiveStreamResult)
+isWebsocketOpen(a)
+receive(System.Net.WebSockets.WebSocket,System.ArraySegment`1<System.Byte>,System.Threading.CancellationToken)
+send(System.Net.WebSockets.WebSocket,System.ArraySegment`1<System.Byte>,System.Net.WebSockets.WebSocketMessageType,System.Boolean,System.Threading.CancellationToken)
+Invoke(Microsoft.FSharp.Core.Unit)
+close(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketCloseStatus,System.String,System.Threading.CancellationToken)
+Invoke(Microsoft.FSharp.Core.Unit)
+closeOutput(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketCloseStatus,System.String,System.Threading.CancellationToken)
+Invoke(Microsoft.FSharp.Core.Unit)
+sendMessage(System.Net.WebSockets.WebSocket,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.Threading.CancellationToken,a)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Int32)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+sendMessageAsUTF8(System.Net.WebSockets.WebSocket,System.Threading.CancellationToken,System.String)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.IO.MemoryStream)
+receiveMessage(System.Net.WebSockets.WebSocket,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.Threading.CancellationToken,System.IO.Stream)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Net.WebSockets.WebSocketReceiveResult)
+Invoke(System.Net.WebSockets.WebSocketMessageType)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+receiveMessageAsUTF8(System.Net.WebSockets.WebSocket,System.Threading.CancellationToken)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.IO.MemoryStream)
+Invoke(FSharp.Control.Websockets.TPL.WebSocket/ReceiveStreamResult)

diff --git a/docs/coverage/FSharp.Control.Websockets_AssemblyVersionInformation.htm b/docs/coverage/FSharp.Control.Websockets_AssemblyVersionInformation.htm index 1b2de5c..fec5580 100644 --- a/docs/coverage/FSharp.Control.Websockets_AssemblyVersionInformation.htm +++ b/docs/coverage/FSharp.Control.Websockets_AssemblyVersionInformation.htm @@ -26,6 +26,6 @@

Summary

File(s)

No files found. This usually happens if a file isn't covered by a test or the class does not contain any sequence points (e.g. a class that only contains auto properties).

- + \ No newline at end of file diff --git a/docs/coverage/FSharp.Control.Websockets_Async.htm b/docs/coverage/FSharp.Control.Websockets_Async.htm index eb70fd8..d4aebe0 100644 --- a/docs/coverage/FSharp.Control.Websockets_Async.htm +++ b/docs/coverage/FSharp.Control.Websockets_Async.htm @@ -18,10 +18,10 @@

Summary

Assembly:FSharp.Control.Websockets File(s):/Users/jimmybyrd/Documents/GitHub/FSharp.Control.Websockets/src/FSharp.Control.Websockets/FSharp.Control.Websockets.fs Covered lines:6 -Uncovered lines:2 -Coverable lines:8 -Total lines:229 -Line coverage:75% +Uncovered lines:6 +Coverable lines:12 +Total lines:537 +Line coverage:50%

Metrics

@@ -34,12 +34,12 @@

Metrics

Invoke(...)10000 Invoke(...)10000 Invoke(...)10000 -AwaitTaskWithCancellation(...)1010001 -AwaitTaskWithCancellation(...)1010001 -Invoke(...)1010001 -Invoke(...)1010001 -Invoke(...)1010001 -Invoke(...)1010001 +AwaitTaskWithCancellation(...)1010001 +AwaitTaskWithCancellation(...)1010001 +Invoke(...)1010001 +Invoke(...)1010001 +Invoke(...)1010001 +Invoke(...)1010001

File(s)

@@ -54,246 +54,554 @@

 5open System.Runtime.ExceptionServices  6  07type Async = - 128    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async { - 129        let! ct = Async.CancellationToken - 1210        return! f ct |> Async.AwaitTask11    } - 012 - 1013    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async { - 1014        let! ct = Async.CancellationToken - 1015        return! f ct |> Async.AwaitTask16    }1718module Stream =19    open System20    type System.IO.MemoryStream with21        static member UTF8toMemoryStream (text : string) =22            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)2324        static member ToUTF8String (stream : IO.MemoryStream) =25            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream26            stream.ToArray()27            |> Text.Encoding.UTF8.GetString28            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters2930        member stream.ToUTF8String () =31            stream |> System.IO.MemoryStream.ToUTF8String3233module WebSocket =34    open Stream35    open System36    open System.Net.WebSockets3738    /// **Description**39    /// (16 * 1024) = 1638440    /// https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/WebSocketHelpers.cs,285b8b64a4da685141    /// **Output Type**42    ///   * `int`43    [<Literal>]44    let defaultBufferSize  : int = 16384 // (16 * 1024)4546    let asyncReceive  (buffer : ArraySegment<byte>) (websocket : #WebSocket) =47        fun ct ->  websocket.ReceiveAsync(buffer,ct)48        |> Async.AwaitTaskWithCancellation4950    let asyncSend (buffer : ArraySegment<byte>) messageType endOfMessage (websocket : #WebSocket) =51        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct)52        |> Async.AwaitTaskWithCancellation5354    let asyncClose status message (websocket : #WebSocket) =55        fun ct -> websocket.CloseAsync(status,message,ct)56        |> Async.AwaitTaskWithCancellation5758    let asyncCloseOutput status message (websocket : #WebSocket) =59        fun ct -> websocket.CloseOutputAsync(status,message,ct)60        |> Async.AwaitTaskWithCancellation6162    let isWebsocketOpen (socket : #WebSocket) =63        socket.State = WebSocketState.Open6465    /// Sends a whole message to the websocket read from the given stream66    let sendMessage bufferSize messageType (readableStream : #IO.Stream) (socket : #WebSocket) = async {67        let buffer = Array.create (bufferSize) Byte.MinValue6869        let rec sendMessage' () = async {70            if isWebsocketOpen socket then71                let! read =72                    readableStream.AsyncRead(buffer,0,buffer.Length)73                if read > 0 then74                    do! (socket |> asyncSend (ArraySegment(buffer |> Array.take read))  messageType false)75                    return! sendMessage'()76                else77                    do! (socket |> asyncSend (ArraySegment(Array.empty))  messageType true)78        }79        do! sendMessage'()80        } + 08 + 09    /// **Description**10    ///11    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async + 012    /// + 013    /// **Parameters** + 014    ///   * `f` - parameter of type `CancellationToken -> Task`15    ///16    /// **Output Type**17    ///   * `Async<unit>`18    ///19    /// **Exceptions**20    /// + 1221    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async { + 1222        let! ct = Async.CancellationToken + 1223        return! f ct |> Async.AwaitTask24    }252627    /// **Description**28    ///29    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async30    ///31    /// **Parameters**32    ///   * `f` - parameter of type `CancellationToken -> Task<'a>`33    ///34    /// **Output Type**35    ///   * `Async<'a>`36    ///37    /// **Exceptions**38    /// + 1039    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async { + 1040        let! ct = Async.CancellationToken + 1041        return! f ct |> Async.AwaitTask42    }4344module Stream =45    open System46    type System.IO.MemoryStream with4748        /// **Description**49        ///50        /// Turns a string into a UTF8 MemoryStream51        ///52        /// **Parameters**53        ///   * `text` - parameter of type `string`54        ///55        /// **Output Type**56        ///   * `IO.MemoryStream`57        ///58        /// **Exceptions**59        ///60        static member UTF8toMemoryStream (text : string) =61            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)626364        /// **Description**65        ///66        /// Turns a `MemoryStream` into a a UTF8 string67        ///68        /// **Parameters**69        ///   * `stream` - parameter of type `IO.MemoryStream`70        ///71        /// **Output Type**72        ///   * `string`73        ///74        /// **Exceptions**75        ///76        static member ToUTF8String (stream : IO.MemoryStream) =77            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream78            stream.ToArray()79            |> Text.Encoding.UTF8.GetString80            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters  8182    let sendMessageAsUTF8 text socket = async {83        use stream = IO.MemoryStream.UTF8toMemoryStream text84        do! sendMessage defaultBufferSize WebSocketMessageType.Text stream socket85    }86878889    type ReceiveStreamResult =90        | Stream of IO.Stream91        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string9293    let receiveMessage bufferSize messageType (writeableStream : IO.Stream) (socket : WebSocket) = async {94        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)9596        let rec readTillEnd' () = async {97            let! result  = socket |> asyncReceive buffer98            match result with99            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived100                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription101                do! asyncCloseOutput WebSocketCloseStatus.NormalClosure "Close received by client" socket102                return StreamClosed(socket.CloseStatus.Value, socket.CloseStatusDescription)103            | result ->104                // printfn "result.MessageType -> %A" result.MessageType105                if result.MessageType <> messageType then return ()106                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count)107                if result.EndOfMessage then108                    return Stream writeableStream109                else110                    return! readTillEnd' ()111        }112        return! readTillEnd' ()113    }114115    type ReceiveUTF8Result =116        | String of string117        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string118119    let receiveMessageAsUTF8 socket = async {120        use stream =  new IO.MemoryStream()121        let! result = receiveMessage defaultBufferSize WebSocketMessageType.Text stream socket122        match result with123        | ReceiveStreamResult.Stream s ->124            return stream |> IO.MemoryStream.ToUTF8String |> String125        | ReceiveStreamResult.StreamClosed(status, reason) ->126            return ReceiveUTF8Result.StreamClosed(status, reason)127    }128129module ThreadSafeWebSocket =130    open System131    open System.Threading132    open System.Net.WebSockets133    open Stream134135    type MessageSendResult = Result<unit, ExceptionDispatchInfo>136    type MessageReceiveResult = Result<WebSocket.ReceiveStreamResult, ExceptionDispatchInfo>137138    type SendMessages =139    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<MessageSendResult>140    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult>141    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult>142143    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<MessageReceiveResult>82        /// **Description**83        ///84        /// Turns a `MemoryStream` into a a UTF8 string85        ///86        /// **Parameters**87        ///88        ///89        /// **Output Type**90        ///   * `string`91        ///92        /// **Exceptions**93        ///94        member stream.ToUTF8String () =95            stream |> System.IO.MemoryStream.ToUTF8String9697module WebSocket =98    open Stream99    open System100    open System.Net.WebSockets101102    /// **Description**103    ///104    /// Same as the `DefaultReceiveBufferSize` and `DefaultClientSendBufferSize` from the internal [WebSocketHelpers]( h105    ///106    /// Current value: 16384107    ///108    /// **Output Type**109    ///   * `int`110    [<Literal>]111    let DefaultBufferSize  : int = 16384 // (16 * 1024)112113    /// **Description**114    ///115    /// Determines if the websocket is open116    ///117    /// **Parameters**118    ///   * `socket` - parameter of type `WebSocket`119    ///120    /// **Output Type**121    ///   * `bool`122    ///123    /// **Exceptions**124    ///125    let isWebsocketOpen (socket : WebSocket) =126        socket.State = WebSocketState.Open127128    /// **Description**129    ///130    /// Receives data from the `System.Net.WebSockets.WebSocket` connection asynchronously.131    ///132    /// **Parameters**133    ///   * `websocket` - parameter of type `WebSocket`134    ///   * `buffer` - parameter of type `ArraySegment<byte>` - References the application buffer that is the storage lo135    ///136    /// **Output Type**137    ///   * `Async<WebSocketReceiveResult>` - An instance of this class represents the result of performing a single Rec138    ///139    /// **Exceptions**140    ///141    let asyncReceive (websocket : WebSocket) (buffer : ArraySegment<byte>)  =142        fun ct ->  websocket.ReceiveAsync(buffer,ct)143        |> Async.AwaitTaskWithCancellation  144145    type ThreadSafeWebSocket =146        { websocket : WebSocket147          sendChannel : MailboxProcessor<SendMessages>148          receiveChannel : MailboxProcessor<ReceiveMessage>149        }150        interface IDisposable with151            member x.Dispose() =152                x.websocket.Dispose()153        member x.State =154            x.websocket.State155        member x.CloseStatus =156            x.websocket.CloseStatus |> Option.ofNullable157        member x.CloseStatusDescription =158            x.websocket.CloseStatusDescription159160    let createFromWebSocket (webSocket : WebSocket) =161        /// handle executing a task in a try/catch and wrapping up the callstack info for later162        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {163            try164                let! result = action165                reply.Reply(Ok result)166            with167            | ex ->168                let dispatch = ExceptionDispatchInfo.Capture ex169                reply.Reply(Error dispatch)170        }171172        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox ->173            let rec loop () = async {174                let! message = inbox.Receive()175                if webSocket |> WebSocket.isWebsocketOpen then176                    match message with177                    | Send (buffer, messageType, stream, replyChannel) ->178                        do! wrap (WebSocket.sendMessage buffer messageType stream webSocket) replyChannel179                        return! loop ()180                    | Close (status, message, replyChannel) ->181                        do! wrap (WebSocket.asyncClose status message webSocket) replyChannel182                    | CloseOutput (status, message, replyChannel) ->183                        do! wrap (WebSocket.asyncCloseOutput status message webSocket) replyChannel184            }185            loop ()186        )187        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox ->188            let rec loop () = async {189                let! (buffer, messageType, stream, replyChannel) = inbox.Receive()190                if webSocket |> WebSocket.isWebsocketOpen then191                    let! result = WebSocket.receiveMessage buffer messageType stream webSocket192                    replyChannel.Reply (Ok result)193                    do! loop ()194            }195            loop ()196        )197        {198            websocket = webSocket199            sendChannel = sendAgent200            receiveChannel = receiveAgent201        }145146    /// **Description**147    ///148    /// Sends data over the `System.Net.WebSockets.WebSocket` connection asynchronously.149    ///150    /// **Parameters**151    ///   * `websocket` - parameter of type `WebSocket`152    ///   * `buffer` - parameter of type `ArraySegment<byte>` - The buffer to be sent over the connection.153    ///   * `messageType` - parameter of type `WebSocketMessageType`- Indicates whether the application is sending a bin154    ///   * `endOfMessage` - parameter of type `bool` - Indicates whether the data in `buffer` is the last part of a mes155    ///156    /// **Output Type**157    ///   * `Async<unit>`158    ///159    /// **Exceptions**160    ///161    let asyncSend (websocket : WebSocket) (buffer : ArraySegment<byte>) (messageType : WebSocketMessageType) (endOfMessa162        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct)163        |> Async.AwaitTaskWithCancellation164165166    /// **Description**167    ///168    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too169    ///170    /// **Parameters**171    ///   * `websocket` - parameter of type `WebSocket`172    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co173    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con174    ///175    /// **Output Type**176    ///   * `Async<unit>`177    ///178    /// **Exceptions**179    ///180    let asyncClose (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  =181        fun ct -> websocket.CloseAsync(closeStatus, statusDescription, ct)182        |> Async.AwaitTaskWithCancellation183184185    /// **Description**186    ///187    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke188    ///189    /// **Parameters**190    ///   * `websocket` - parameter of type `WebSocket`191    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co192    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect193    ///194    /// **Output Type**195    ///   * `Async<unit>`196    ///197    /// **Exceptions**198    ///199    let asyncCloseOutput (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  =200        fun ct -> websocket.CloseOutputAsync(closeStatus, statusDescription, ct)201        |> Async.AwaitTaskWithCancellation  202203    let sendMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream =204        wsts.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, stream, reply))205206    let sendMessageAsUTF8(wsts : ThreadSafeWebSocket) (text : string) = async {207        use stream = IO.MemoryStream.UTF8toMemoryStream text208        return! sendMessage wsts  WebSocket.defaultBufferSize  WebSocketMessageType.Text stream209    }210211    let receiveMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream =212        wsts.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, stream, reply)213214    let receiveMessageAsUTF8 (wsts : ThreadSafeWebSocket) = async {215        use stream = new IO.MemoryStream()216        let! response = receiveMessage wsts WebSocket.defaultBufferSize  WebSocketMessageType.Text stream217        match response with218        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece219        | Ok (WebSocket.ReceiveStreamResult.StreamClosed(status, reason)) ->220            return Ok (WebSocket.ReceiveUTF8Result.StreamClosed(status, reason))221        | Error ex -> return Error ex222223    }224225    let close (wsts : ThreadSafeWebSocket) status message =226        wsts.sendChannel.PostAndAsyncReply(fun reply -> Close(status, message, reply))227228    let closeOutput (wsts : ThreadSafeWebSocket) status message =229        wsts.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(status, message, reply))203204    /// **Description**205    ///206    /// Sends a whole message to the websocket read from the given stream207    ///208    /// **Parameters**209    ///   * `socket` - parameter of type `WebSocket`210    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u211    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b212    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect213    ///214    /// **Output Type**215    ///   * `Async<unit>`216    ///217    /// **Exceptions**218    ///219    let sendMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (readableStream : #IO.S220        let buffer = Array.create (bufferSize) Byte.MinValue221222        let rec sendMessage' () = async {223            if isWebsocketOpen socket then224                let! read = readableStream.AsyncRead(buffer, 0, buffer.Length)225                if read > 0 then226                    do! asyncSend socket (ArraySegment(buffer |> Array.take read))  messageType false227                    return! sendMessage'()228                else229                    do! (asyncSend socket (ArraySegment(Array.empty))  messageType true)230        }231        do! sendMessage'()232    }233234235    /// **Description**236    ///237    /// Sends a string as UTF8 over a websocket connection.238    ///239    /// **Parameters**240    ///   * `socket` - parameter of type `WebSocket`241    ///   * `text` - parameter of type `string` - The string to send over the websocket.242    ///243    /// **Output Type**244    ///   * `Async<unit>`245    ///246    /// **Exceptions**247    ///248    let sendMessageAsUTF8 (socket : WebSocket) (text : string) = async {249        use stream = IO.MemoryStream.UTF8toMemoryStream text250        do! sendMessage socket DefaultBufferSize WebSocketMessageType.Text stream251    }252253254    /// One of the possible results from reading a whole message from a websocket.255    type ReceiveStreamResult =256        /// Reading from the websocket completed257        | Stream of IO.Stream258        /// The websocket was closed during reading259        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string260261262    /// **Description**263    ///264    /// Reads an entire message from a websocket.265    ///266    /// **Parameters**267    ///   * `socket` - parameter of type `WebSocket`268    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u269    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is receiving a270    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt271    ///272    /// **Output Type**273    ///   * `Async<ReceiveStreamResult>` - One of the possible results from reading a whole message from a websocket274    ///275    /// **Exceptions**276    ///277    let receiveMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (writeableStream : I278        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)279280        let rec readTillEnd' () = async {281            let! result  = asyncReceive socket buffer282            match result with283            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived284                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription285                do! asyncCloseOutput socket WebSocketCloseStatus.NormalClosure "Close received by client"286                return ReceiveStreamResult.Closed(socket.CloseStatus.Value, socket.CloseStatusDescription)287            | result ->288                // printfn "result.MessageType -> %A" result.MessageType289                if result.MessageType <> messageType then return ()290                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count)291                if result.EndOfMessage then292                    return Stream writeableStream293                else294                    return! readTillEnd' ()295        }296        return! readTillEnd' ()297    }298299300    /// One of the possible results from reading a whole message from a websocket.301    type ReceiveUTF8Result =302        /// Reading from the websocket completed.303        | String of string304        /// The websocket was closed during reading.305        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string306307308    /// **Description**309    ///310    /// Reads an entire message as a string.311    ///312    /// **Parameters**313    ///   * `socket` - parameter of type `WebSocket`314    ///315    /// **Output Type**316    ///   * `Async<ReceiveUTF8Result>`317    ///318    /// **Exceptions**319    ///320    let receiveMessageAsUTF8 (socket : WebSocket) = async {321        use stream =  new IO.MemoryStream()322        let! result = receiveMessage socket DefaultBufferSize WebSocketMessageType.Text stream323        match result with324        | ReceiveStreamResult.Stream s ->325            return stream |> IO.MemoryStream.ToUTF8String |> String326        | ReceiveStreamResult.Closed(status, reason) ->327            return ReceiveUTF8Result.Closed(status, reason)328    }329330module ThreadSafeWebSocket =331    open System332    open System.Threading333    open System.Net.WebSockets334    open Stream335336    type SendMessages =337    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<Result<unit, ExceptionDispatchIn338    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>339    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>340341    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<Result<WebSocket.ReceiveStreamRes342343    /// The ThreadSafeWebSocket record allows applications to send and receive data after the WebSocket upgrade has comp344    ///345    /// `There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can b346    type ThreadSafeWebSocket =347        { websocket : WebSocket348          sendChannel : MailboxProcessor<SendMessages>349          receiveChannel : MailboxProcessor<ReceiveMessage>350        }351        interface IDisposable with352            /// Used to clean up unmanaged resources for ASP.NET and self-hosted implementations.353            member x.Dispose() =354                x.websocket.Dispose()355        /// Returns the current state of the WebSocket connection.356        member x.State =357            x.websocket.State358        /// Indicates the reason why the remote endpoint initiated the close handshake.359        member x.CloseStatus =360            x.websocket.CloseStatus |> Option.ofNullable361        ///Allows the remote endpoint to describe the reason why the connection was closed.362        member x.CloseStatusDescription =363            x.websocket.CloseStatusDescription364365366    /// **Description**367    ///368    /// Creates a `ThreadSafeWebSocket` from an existing `WebSocket`.369    ///370    /// **Parameters**371    ///   * `webSocket` - parameter of type `WebSocket`372    ///373    /// **Output Type**374    ///   * `ThreadSafeWebSocket`375    ///376    /// **Exceptions**377    ///378    let createFromWebSocket (webSocket : WebSocket) =379        /// handle executing a task in a try/catch and wrapping up the callstack info for later380        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {381            try382                let! result = action383                reply.Reply(Ok result)384            with385            | ex ->386                let dispatch = ExceptionDispatchInfo.Capture ex387                reply.Reply(Error dispatch)388        }389390        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox ->391            let rec loop () = async {392                let! message = inbox.Receive()393                if webSocket |> WebSocket.isWebsocketOpen then394                    match message with395                    | Send (buffer, messageType, stream, replyChannel) ->396                        do! wrap (WebSocket.sendMessage webSocket buffer messageType stream ) replyChannel397                        return! loop ()398                    | Close (status, message, replyChannel) ->399                        do! wrap (WebSocket.asyncClose webSocket status message) replyChannel400                    | CloseOutput (status, message, replyChannel) ->401                        do! wrap (WebSocket.asyncCloseOutput webSocket status message) replyChannel402            }403            loop ()404        )405        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox ->406            let rec loop () = async {407                let! (buffer, messageType, stream, replyChannel) = inbox.Receive()408                if webSocket |> WebSocket.isWebsocketOpen then409                    do! wrap (WebSocket.receiveMessage webSocket buffer messageType stream) replyChannel410                    return! loop ()411            }412            loop ()413        )414        {415            websocket = webSocket416            sendChannel = sendAgent417            receiveChannel = receiveAgent418        }419420421422423    /// **Description**424    ///425    /// Sends a whole message to the websocket read from the given stream.426    ///427    /// **Parameters**428    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`429    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u430    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b431    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect432    ///433    /// **Output Type**434    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`435    ///436    /// **Exceptions**437    ///438    let sendMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) messageType (readableStream : #IO.Str439        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, readableStream, rep440441442    /// **Description**443    ///444    /// Sends a string as UTF8 over a websocket connection.445    ///446    /// **Parameters**447    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`448    ///   * `text` - parameter of type `string` - The string to send over the websocket.449    ///450    /// **Output Type**451    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`452    ///453    /// **Exceptions**454    ///455    let sendMessageAsUTF8(threadSafeWebSocket : ThreadSafeWebSocket) (text : string) = async {456        use stream = IO.MemoryStream.UTF8toMemoryStream text457        return! sendMessage threadSafeWebSocket  WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream458    }459460461    /// **Description**462    ///463    /// Reads an entire message from a websocket as a string.464    ///465    /// **Parameters**466    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`467    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u468    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b469    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt470    ///471    /// **Output Type**472    ///   * `Async<Result<WebSocket.ReceiveStreamResult,ExceptionDispatchInfo>>` - One of the possible results from read473    ///474    /// **Exceptions**475    ///476    let receiveMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) (messageType : WebSocketMessageTyp477        threadSafeWebSocket.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, writeableStream, repl478479480    /// **Description**481    ///482    /// Reads an entire message as a string.483    ///484    /// **Parameters**485    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`486    ///487    /// **Output Type**488    ///   * `Async<Result<WebSocket.ReceiveUTF8Result,ExceptionDispatchInfo>>`489    ///490    /// **Exceptions**491    ///492    let receiveMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) = async {493        use stream = new IO.MemoryStream()494        let! response = receiveMessage threadSafeWebSocket WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream495        match response with496        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece497        | Ok (WebSocket.Closed(status, reason)) ->498            return Ok (WebSocket.ReceiveUTF8Result.Closed(status, reason))499        | Error ex -> return Error ex500501    }502503504    /// **Description**505    ///506    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too507    ///508    /// **Parameters**509    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`510    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co511    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con512    ///513    /// **Output Type**514    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`515    ///516    /// **Exceptions**517    ///518    let close (threadSafeWebSocket : ThreadSafeWebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription : str519        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Close(closeStatus, statusDescription, reply))520521522    /// **Description**523    ///524    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke525    ///526    /// **Parameters**527    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`528    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co529    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect530    ///531    /// **Output Type**532    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`533    ///534    /// **Exceptions**535    ///536    let closeOutput (threadSafeWebSocket : ThreadSafeWebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription 537        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(closeStatus, statusDescription, reply - +
diff --git a/docs/coverage/FSharp.Control.Websockets_Stream.htm b/docs/coverage/FSharp.Control.Websockets_Stream.htm index 535b88f..71e9df9 100644 --- a/docs/coverage/FSharp.Control.Websockets_Stream.htm +++ b/docs/coverage/FSharp.Control.Websockets_Stream.htm @@ -18,10 +18,10 @@

Summary

Assembly:FSharp.Control.Websockets File(s):/Users/jimmybyrd/Documents/GitHub/FSharp.Control.Websockets/src/FSharp.Control.Websockets/FSharp.Control.Websockets.fs Covered lines:4 -Uncovered lines:5 -Coverable lines:9 -Total lines:229 -Line coverage:44.4% +Uncovered lines:8 +Coverable lines:12 +Total lines:537 +Line coverage:33.3%

Metrics

@@ -31,9 +31,9 @@

Metrics

MemoryStream.UTF8toMemoryStream.Static(...)10000 MemoryStream.ToUTF8String.Static(...)10000 MemoryStream.ToUTF8String(...)10000 -MemoryStream.UTF8toMemoryStream.Static(...)10000 -MemoryStream.ToUTF8String.Static(...)1010001 -MemoryStream.ToUTF8String(...)10000 +MemoryStream.UTF8toMemoryStream.Static(...)10000 +MemoryStream.ToUTF8String.Static(...)1010001 +MemoryStream.ToUTF8String(...)10000

File(s)

@@ -48,240 +48,548 @@

 5open System.Runtime.ExceptionServices  6  7type Async =8    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async {9        let! ct = Async.CancellationToken10        return! f ct |> Async.AwaitTask11    }1213    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async {14        let! ct = Async.CancellationToken15        return! f ct |> Async.AwaitTask16    }1718module Stream =19    open System20    type System.IO.MemoryStream with - 021        static member UTF8toMemoryStream (text : string) = - 022            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)23 - 024        static member ToUTF8String (stream : IO.MemoryStream) = - 425            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream - 426            stream.ToArray() - 427            |> Text.Encoding.UTF8.GetString - 428            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters29 - 030        member stream.ToUTF8String () = - 031            stream |> System.IO.MemoryStream.ToUTF8String3233module WebSocket =34    open Stream35    open System36    open System.Net.WebSockets3738    /// **Description**39    /// (16 * 1024) = 1638440    /// https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/WebSocketHelpers.cs,285b8b64a4da685141    /// **Output Type**42    ///   * `int`43    [<Literal>]44    let defaultBufferSize  : int = 16384 // (16 * 1024)4546    let asyncReceive  (buffer : ArraySegment<byte>) (websocket : #WebSocket) =47        fun ct ->  websocket.ReceiveAsync(buffer,ct)48        |> Async.AwaitTaskWithCancellation4950    let asyncSend (buffer : ArraySegment<byte>) messageType endOfMessage (websocket : #WebSocket) =51        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct)52        |> Async.AwaitTaskWithCancellation5354    let asyncClose status message (websocket : #WebSocket) =55        fun ct -> websocket.CloseAsync(status,message,ct)56        |> Async.AwaitTaskWithCancellation5758    let asyncCloseOutput status message (websocket : #WebSocket) =59        fun ct -> websocket.CloseOutputAsync(status,message,ct)60        |> Async.AwaitTaskWithCancellation6162    let isWebsocketOpen (socket : #WebSocket) =63        socket.State = WebSocketState.Open6465    /// Sends a whole message to the websocket read from the given stream66    let sendMessage bufferSize messageType (readableStream : #IO.Stream) (socket : #WebSocket) = async {67        let buffer = Array.create (bufferSize) Byte.MinValue6869        let rec sendMessage' () = async {70            if isWebsocketOpen socket then71                let! read =72                    readableStream.AsyncRead(buffer,0,buffer.Length)73                if read > 0 then74                    do! (socket |> asyncSend (ArraySegment(buffer |> Array.take read))  messageType false)75                    return! sendMessage'()76                else77                    do! (socket |> asyncSend (ArraySegment(Array.empty))  messageType true)78        }79        do! sendMessage'()80        }89    /// **Description**10    ///11    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async12    ///13    /// **Parameters**14    ///   * `f` - parameter of type `CancellationToken -> Task`15    ///16    /// **Output Type**17    ///   * `Async<unit>`18    ///19    /// **Exceptions**20    /// + 021    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async {22        let! ct = Async.CancellationToken23        return! f ct |> Async.AwaitTask + 024    } + 025 + 026 + 027    /// **Description**28    ///29    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async + 030    ///31    /// **Parameters**32    ///   * `f` - parameter of type `CancellationToken -> Task<'a>`33    ///34    /// **Output Type**35    ///   * `Async<'a>`36    ///37    /// **Exceptions**38    ///39    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async {40        let! ct = Async.CancellationToken41        return! f ct |> Async.AwaitTask42    }4344module Stream =45    open System46    type System.IO.MemoryStream with4748        /// **Description**49        ///50        /// Turns a string into a UTF8 MemoryStream51        ///52        /// **Parameters**53        ///   * `text` - parameter of type `string`54        ///55        /// **Output Type**56        ///   * `IO.MemoryStream`57        ///58        /// **Exceptions**59        ///60        static member UTF8toMemoryStream (text : string) = + 061            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)626364        /// **Description**65        ///66        /// Turns a `MemoryStream` into a a UTF8 string67        ///68        /// **Parameters**69        ///   * `stream` - parameter of type `IO.MemoryStream`70        ///71        /// **Output Type**72        ///   * `string`73        ///74        /// **Exceptions**75        ///76        static member ToUTF8String (stream : IO.MemoryStream) = + 477            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream + 478            stream.ToArray() + 479            |> Text.Encoding.UTF8.GetString + 480            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters  8182    let sendMessageAsUTF8 text socket = async {83        use stream = IO.MemoryStream.UTF8toMemoryStream text84        do! sendMessage defaultBufferSize WebSocketMessageType.Text stream socket85    }86878889    type ReceiveStreamResult =90        | Stream of IO.Stream91        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string9293    let receiveMessage bufferSize messageType (writeableStream : IO.Stream) (socket : WebSocket) = async {94        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)9596        let rec readTillEnd' () = async {97            let! result  = socket |> asyncReceive buffer98            match result with99            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived100                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription101                do! asyncCloseOutput WebSocketCloseStatus.NormalClosure "Close received by client" socket102                return StreamClosed(socket.CloseStatus.Value, socket.CloseStatusDescription)103            | result ->104                // printfn "result.MessageType -> %A" result.MessageType105                if result.MessageType <> messageType then return ()106                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count)107                if result.EndOfMessage then108                    return Stream writeableStream109                else110                    return! readTillEnd' ()111        }112        return! readTillEnd' ()113    }114115    type ReceiveUTF8Result =116        | String of string117        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string118119    let receiveMessageAsUTF8 socket = async {120        use stream =  new IO.MemoryStream()121        let! result = receiveMessage defaultBufferSize WebSocketMessageType.Text stream socket122        match result with123        | ReceiveStreamResult.Stream s ->124            return stream |> IO.MemoryStream.ToUTF8String |> String125        | ReceiveStreamResult.StreamClosed(status, reason) ->126            return ReceiveUTF8Result.StreamClosed(status, reason)127    }128129module ThreadSafeWebSocket =130    open System131    open System.Threading132    open System.Net.WebSockets133    open Stream134135    type MessageSendResult = Result<unit, ExceptionDispatchInfo>136    type MessageReceiveResult = Result<WebSocket.ReceiveStreamResult, ExceptionDispatchInfo>137138    type SendMessages =139    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<MessageSendResult>140    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult>141    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult>142143    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<MessageReceiveResult>82        /// **Description**83        ///84        /// Turns a `MemoryStream` into a a UTF8 string85        ///86        /// **Parameters**87        ///88        ///89        /// **Output Type**90        ///   * `string`91        ///92        /// **Exceptions**93        ///94        member stream.ToUTF8String () = + 095            stream |> System.IO.MemoryStream.ToUTF8String9697module WebSocket =98    open Stream99    open System100    open System.Net.WebSockets101102    /// **Description**103    ///104    /// Same as the `DefaultReceiveBufferSize` and `DefaultClientSendBufferSize` from the internal [WebSocketHelpers]( h105    ///106    /// Current value: 16384107    ///108    /// **Output Type**109    ///   * `int`110    [<Literal>]111    let DefaultBufferSize  : int = 16384 // (16 * 1024)112113    /// **Description**114    ///115    /// Determines if the websocket is open116    ///117    /// **Parameters**118    ///   * `socket` - parameter of type `WebSocket`119    ///120    /// **Output Type**121    ///   * `bool`122    ///123    /// **Exceptions**124    ///125    let isWebsocketOpen (socket : WebSocket) =126        socket.State = WebSocketState.Open127128    /// **Description**129    ///130    /// Receives data from the `System.Net.WebSockets.WebSocket` connection asynchronously.131    ///132    /// **Parameters**133    ///   * `websocket` - parameter of type `WebSocket`134    ///   * `buffer` - parameter of type `ArraySegment<byte>` - References the application buffer that is the storage lo135    ///136    /// **Output Type**137    ///   * `Async<WebSocketReceiveResult>` - An instance of this class represents the result of performing a single Rec138    ///139    /// **Exceptions**140    ///141    let asyncReceive (websocket : WebSocket) (buffer : ArraySegment<byte>)  =142        fun ct ->  websocket.ReceiveAsync(buffer,ct)143        |> Async.AwaitTaskWithCancellation  144145    type ThreadSafeWebSocket =146        { websocket : WebSocket147          sendChannel : MailboxProcessor<SendMessages>148          receiveChannel : MailboxProcessor<ReceiveMessage>149        }150        interface IDisposable with151            member x.Dispose() =152                x.websocket.Dispose()153        member x.State =154            x.websocket.State155        member x.CloseStatus =156            x.websocket.CloseStatus |> Option.ofNullable157        member x.CloseStatusDescription =158            x.websocket.CloseStatusDescription159160    let createFromWebSocket (webSocket : WebSocket) =161        /// handle executing a task in a try/catch and wrapping up the callstack info for later162        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {163            try164                let! result = action165                reply.Reply(Ok result)166            with167            | ex ->168                let dispatch = ExceptionDispatchInfo.Capture ex169                reply.Reply(Error dispatch)170        }171172        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox ->173            let rec loop () = async {174                let! message = inbox.Receive()175                if webSocket |> WebSocket.isWebsocketOpen then176                    match message with177                    | Send (buffer, messageType, stream, replyChannel) ->178                        do! wrap (WebSocket.sendMessage buffer messageType stream webSocket) replyChannel179                        return! loop ()180                    | Close (status, message, replyChannel) ->181                        do! wrap (WebSocket.asyncClose status message webSocket) replyChannel182                    | CloseOutput (status, message, replyChannel) ->183                        do! wrap (WebSocket.asyncCloseOutput status message webSocket) replyChannel184            }185            loop ()186        )187        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox ->188            let rec loop () = async {189                let! (buffer, messageType, stream, replyChannel) = inbox.Receive()190                if webSocket |> WebSocket.isWebsocketOpen then191                    let! result = WebSocket.receiveMessage buffer messageType stream webSocket192                    replyChannel.Reply (Ok result)193                    do! loop ()194            }195            loop ()196        )197        {198            websocket = webSocket199            sendChannel = sendAgent200            receiveChannel = receiveAgent201        }145146    /// **Description**147    ///148    /// Sends data over the `System.Net.WebSockets.WebSocket` connection asynchronously.149    ///150    /// **Parameters**151    ///   * `websocket` - parameter of type `WebSocket`152    ///   * `buffer` - parameter of type `ArraySegment<byte>` - The buffer to be sent over the connection.153    ///   * `messageType` - parameter of type `WebSocketMessageType`- Indicates whether the application is sending a bin154    ///   * `endOfMessage` - parameter of type `bool` - Indicates whether the data in `buffer` is the last part of a mes155    ///156    /// **Output Type**157    ///   * `Async<unit>`158    ///159    /// **Exceptions**160    ///161    let asyncSend (websocket : WebSocket) (buffer : ArraySegment<byte>) (messageType : WebSocketMessageType) (endOfMessa162        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct)163        |> Async.AwaitTaskWithCancellation164165166    /// **Description**167    ///168    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too169    ///170    /// **Parameters**171    ///   * `websocket` - parameter of type `WebSocket`172    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co173    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con174    ///175    /// **Output Type**176    ///   * `Async<unit>`177    ///178    /// **Exceptions**179    ///180    let asyncClose (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  =181        fun ct -> websocket.CloseAsync(closeStatus, statusDescription, ct)182        |> Async.AwaitTaskWithCancellation183184185    /// **Description**186    ///187    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke188    ///189    /// **Parameters**190    ///   * `websocket` - parameter of type `WebSocket`191    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co192    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect193    ///194    /// **Output Type**195    ///   * `Async<unit>`196    ///197    /// **Exceptions**198    ///199    let asyncCloseOutput (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  =200        fun ct -> websocket.CloseOutputAsync(closeStatus, statusDescription, ct)201        |> Async.AwaitTaskWithCancellation  202203    let sendMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream =204        wsts.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, stream, reply))205206    let sendMessageAsUTF8(wsts : ThreadSafeWebSocket) (text : string) = async {207        use stream = IO.MemoryStream.UTF8toMemoryStream text208        return! sendMessage wsts  WebSocket.defaultBufferSize  WebSocketMessageType.Text stream209    }210211    let receiveMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream =212        wsts.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, stream, reply)213214    let receiveMessageAsUTF8 (wsts : ThreadSafeWebSocket) = async {215        use stream = new IO.MemoryStream()216        let! response = receiveMessage wsts WebSocket.defaultBufferSize  WebSocketMessageType.Text stream217        match response with218        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece219        | Ok (WebSocket.ReceiveStreamResult.StreamClosed(status, reason)) ->220            return Ok (WebSocket.ReceiveUTF8Result.StreamClosed(status, reason))221        | Error ex -> return Error ex222223    }224225    let close (wsts : ThreadSafeWebSocket) status message =226        wsts.sendChannel.PostAndAsyncReply(fun reply -> Close(status, message, reply))227228    let closeOutput (wsts : ThreadSafeWebSocket) status message =229        wsts.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(status, message, reply))203204    /// **Description**205    ///206    /// Sends a whole message to the websocket read from the given stream207    ///208    /// **Parameters**209    ///   * `socket` - parameter of type `WebSocket`210    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u211    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b212    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect213    ///214    /// **Output Type**215    ///   * `Async<unit>`216    ///217    /// **Exceptions**218    ///219    let sendMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (readableStream : #IO.S220        let buffer = Array.create (bufferSize) Byte.MinValue221222        let rec sendMessage' () = async {223            if isWebsocketOpen socket then224                let! read = readableStream.AsyncRead(buffer, 0, buffer.Length)225                if read > 0 then226                    do! asyncSend socket (ArraySegment(buffer |> Array.take read))  messageType false227                    return! sendMessage'()228                else229                    do! (asyncSend socket (ArraySegment(Array.empty))  messageType true)230        }231        do! sendMessage'()232    }233234235    /// **Description**236    ///237    /// Sends a string as UTF8 over a websocket connection.238    ///239    /// **Parameters**240    ///   * `socket` - parameter of type `WebSocket`241    ///   * `text` - parameter of type `string` - The string to send over the websocket.242    ///243    /// **Output Type**244    ///   * `Async<unit>`245    ///246    /// **Exceptions**247    ///248    let sendMessageAsUTF8 (socket : WebSocket) (text : string) = async {249        use stream = IO.MemoryStream.UTF8toMemoryStream text250        do! sendMessage socket DefaultBufferSize WebSocketMessageType.Text stream251    }252253254    /// One of the possible results from reading a whole message from a websocket.255    type ReceiveStreamResult =256        /// Reading from the websocket completed257        | Stream of IO.Stream258        /// The websocket was closed during reading259        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string260261262    /// **Description**263    ///264    /// Reads an entire message from a websocket.265    ///266    /// **Parameters**267    ///   * `socket` - parameter of type `WebSocket`268    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u269    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is receiving a270    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt271    ///272    /// **Output Type**273    ///   * `Async<ReceiveStreamResult>` - One of the possible results from reading a whole message from a websocket274    ///275    /// **Exceptions**276    ///277    let receiveMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (writeableStream : I278        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)279280        let rec readTillEnd' () = async {281            let! result  = asyncReceive socket buffer282            match result with283            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived284                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription285                do! asyncCloseOutput socket WebSocketCloseStatus.NormalClosure "Close received by client"286                return ReceiveStreamResult.Closed(socket.CloseStatus.Value, socket.CloseStatusDescription)287            | result ->288                // printfn "result.MessageType -> %A" result.MessageType289                if result.MessageType <> messageType then return ()290                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count)291                if result.EndOfMessage then292                    return Stream writeableStream293                else294                    return! readTillEnd' ()295        }296        return! readTillEnd' ()297    }298299300    /// One of the possible results from reading a whole message from a websocket.301    type ReceiveUTF8Result =302        /// Reading from the websocket completed.303        | String of string304        /// The websocket was closed during reading.305        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string306307308    /// **Description**309    ///310    /// Reads an entire message as a string.311    ///312    /// **Parameters**313    ///   * `socket` - parameter of type `WebSocket`314    ///315    /// **Output Type**316    ///   * `Async<ReceiveUTF8Result>`317    ///318    /// **Exceptions**319    ///320    let receiveMessageAsUTF8 (socket : WebSocket) = async {321        use stream =  new IO.MemoryStream()322        let! result = receiveMessage socket DefaultBufferSize WebSocketMessageType.Text stream323        match result with324        | ReceiveStreamResult.Stream s ->325            return stream |> IO.MemoryStream.ToUTF8String |> String326        | ReceiveStreamResult.Closed(status, reason) ->327            return ReceiveUTF8Result.Closed(status, reason)328    }329330module ThreadSafeWebSocket =331    open System332    open System.Threading333    open System.Net.WebSockets334    open Stream335336    type SendMessages =337    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<Result<unit, ExceptionDispatchIn338    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>339    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>340341    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<Result<WebSocket.ReceiveStreamRes342343    /// The ThreadSafeWebSocket record allows applications to send and receive data after the WebSocket upgrade has comp344    ///345    /// `There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can b346    type ThreadSafeWebSocket =347        { websocket : WebSocket348          sendChannel : MailboxProcessor<SendMessages>349          receiveChannel : MailboxProcessor<ReceiveMessage>350        }351        interface IDisposable with352            /// Used to clean up unmanaged resources for ASP.NET and self-hosted implementations.353            member x.Dispose() =354                x.websocket.Dispose()355        /// Returns the current state of the WebSocket connection.356        member x.State =357            x.websocket.State358        /// Indicates the reason why the remote endpoint initiated the close handshake.359        member x.CloseStatus =360            x.websocket.CloseStatus |> Option.ofNullable361        ///Allows the remote endpoint to describe the reason why the connection was closed.362        member x.CloseStatusDescription =363            x.websocket.CloseStatusDescription364365366    /// **Description**367    ///368    /// Creates a `ThreadSafeWebSocket` from an existing `WebSocket`.369    ///370    /// **Parameters**371    ///   * `webSocket` - parameter of type `WebSocket`372    ///373    /// **Output Type**374    ///   * `ThreadSafeWebSocket`375    ///376    /// **Exceptions**377    ///378    let createFromWebSocket (webSocket : WebSocket) =379        /// handle executing a task in a try/catch and wrapping up the callstack info for later380        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {381            try382                let! result = action383                reply.Reply(Ok result)384            with385            | ex ->386                let dispatch = ExceptionDispatchInfo.Capture ex387                reply.Reply(Error dispatch)388        }389390        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox ->391            let rec loop () = async {392                let! message = inbox.Receive()393                if webSocket |> WebSocket.isWebsocketOpen then394                    match message with395                    | Send (buffer, messageType, stream, replyChannel) ->396                        do! wrap (WebSocket.sendMessage webSocket buffer messageType stream ) replyChannel397                        return! loop ()398                    | Close (status, message, replyChannel) ->399                        do! wrap (WebSocket.asyncClose webSocket status message) replyChannel400                    | CloseOutput (status, message, replyChannel) ->401                        do! wrap (WebSocket.asyncCloseOutput webSocket status message) replyChannel402            }403            loop ()404        )405        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox ->406            let rec loop () = async {407                let! (buffer, messageType, stream, replyChannel) = inbox.Receive()408                if webSocket |> WebSocket.isWebsocketOpen then409                    do! wrap (WebSocket.receiveMessage webSocket buffer messageType stream) replyChannel410                    return! loop ()411            }412            loop ()413        )414        {415            websocket = webSocket416            sendChannel = sendAgent417            receiveChannel = receiveAgent418        }419420421422423    /// **Description**424    ///425    /// Sends a whole message to the websocket read from the given stream.426    ///427    /// **Parameters**428    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`429    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u430    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b431    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect432    ///433    /// **Output Type**434    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`435    ///436    /// **Exceptions**437    ///438    let sendMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) messageType (readableStream : #IO.Str439        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, readableStream, rep440441442    /// **Description**443    ///444    /// Sends a string as UTF8 over a websocket connection.445    ///446    /// **Parameters**447    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`448    ///   * `text` - parameter of type `string` - The string to send over the websocket.449    ///450    /// **Output Type**451    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`452    ///453    /// **Exceptions**454    ///455    let sendMessageAsUTF8(threadSafeWebSocket : ThreadSafeWebSocket) (text : string) = async {456        use stream = IO.MemoryStream.UTF8toMemoryStream text457        return! sendMessage threadSafeWebSocket  WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream458    }459460461    /// **Description**462    ///463    /// Reads an entire message from a websocket as a string.464    ///465    /// **Parameters**466    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`467    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u468    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b469    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt470    ///471    /// **Output Type**472    ///   * `Async<Result<WebSocket.ReceiveStreamResult,ExceptionDispatchInfo>>` - One of the possible results from read473    ///474    /// **Exceptions**475    ///476    let receiveMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) (messageType : WebSocketMessageTyp477        threadSafeWebSocket.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, writeableStream, repl478479480    /// **Description**481    ///482    /// Reads an entire message as a string.483    ///484    /// **Parameters**485    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`486    ///487    /// **Output Type**488    ///   * `Async<Result<WebSocket.ReceiveUTF8Result,ExceptionDispatchInfo>>`489    ///490    /// **Exceptions**491    ///492    let receiveMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) = async {493        use stream = new IO.MemoryStream()494        let! response = receiveMessage threadSafeWebSocket WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream495        match response with496        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece497        | Ok (WebSocket.Closed(status, reason)) ->498            return Ok (WebSocket.ReceiveUTF8Result.Closed(status, reason))499        | Error ex -> return Error ex500501    }502503504    /// **Description**505    ///506    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too507    ///508    /// **Parameters**509    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`510    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co511    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con512    ///513    /// **Output Type**514    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`515    ///516    /// **Exceptions**517    ///518    let close (threadSafeWebSocket : ThreadSafeWebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription : str519        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Close(closeStatus, statusDescription, reply))520521522    /// **Description**523    ///524    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke525    ///526    /// **Parameters**527    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`528    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co529    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect530    ///531    /// **Output Type**532    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`533    ///534    /// **Exceptions**535    ///536    let closeOutput (threadSafeWebSocket : ThreadSafeWebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription 537        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(closeStatus, statusDescription, reply - +
diff --git a/docs/coverage/FSharp.Control.Websockets_ThreadSafeWebSocket2.htm b/docs/coverage/FSharp.Control.Websockets_ThreadSafeWebSocket2.htm index 858f81b..d3dd85b 100644 --- a/docs/coverage/FSharp.Control.Websockets_ThreadSafeWebSocket2.htm +++ b/docs/coverage/FSharp.Control.Websockets_ThreadSafeWebSocket2.htm @@ -17,11 +17,11 @@

Summary

Class:FSharp.Control.Websockets.ThreadSafeWebSocket Assembly:FSharp.Control.Websockets File(s):/Users/jimmybyrd/Documents/GitHub/FSharp.Control.Websockets/src/FSharp.Control.Websockets/FSharp.Control.Websockets.fs -Covered lines:32 +Covered lines:31 Uncovered lines:5 -Coverable lines:37 -Total lines:229 -Line coverage:86.4% +Coverable lines:36 +Total lines:537 +Line coverage:86.1% Branch coverage:80% @@ -29,48 +29,51 @@

Metrics

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
loop@173(...)1010001
loop@188-18(...)1010001
createFromWebSocket(...)1010001
sendMessage(...)1010001
sendMessageAsUTF8(...)1010001
receiveMessage(...)1010001
receiveMessageAsUTF8(...)1010001
close(...)1010001
closeOutput(...)1010001
System-IDisposable-Dispose()1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)4685.7185.714.05
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)2266.6766.672.15
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)3233.331005.67
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
loop@391(...)1010001
loop@406-18(...)1010001
createFromWebSocket(...)1010001
sendMessage(...)1010001
sendMessageAsUTF8(...)1010001
receiveMessage(...)1010001
receiveMessageAsUTF8(...)1010001
close(...)1010001
closeOutput(...)1010001
System-IDisposable-Dispose()1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)4685.7185.714.05
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)10000
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)2266.6766.672.15
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)3233.331005.67
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001

File(s)

@@ -85,271 +88,581 @@

 5open System.Runtime.ExceptionServices  6  7type Async =8    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async {9        let! ct = Async.CancellationToken10        return! f ct |> Async.AwaitTask11    }1213    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async {14        let! ct = Async.CancellationToken15        return! f ct |> Async.AwaitTask16    }1718module Stream =19    open System20    type System.IO.MemoryStream with21        static member UTF8toMemoryStream (text : string) =22            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)2324        static member ToUTF8String (stream : IO.MemoryStream) =25            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream26            stream.ToArray()27            |> Text.Encoding.UTF8.GetString28            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters2930        member stream.ToUTF8String () =31            stream |> System.IO.MemoryStream.ToUTF8String3233module WebSocket =34    open Stream35    open System36    open System.Net.WebSockets3738    /// **Description**39    /// (16 * 1024) = 1638440    /// https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/WebSocketHelpers.cs,285b8b64a4da685141    /// **Output Type**42    ///   * `int`43    [<Literal>]44    let defaultBufferSize  : int = 16384 // (16 * 1024)4546    let asyncReceive  (buffer : ArraySegment<byte>) (websocket : #WebSocket) =47        fun ct ->  websocket.ReceiveAsync(buffer,ct)48        |> Async.AwaitTaskWithCancellation4950    let asyncSend (buffer : ArraySegment<byte>) messageType endOfMessage (websocket : #WebSocket) =51        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct)52        |> Async.AwaitTaskWithCancellation5354    let asyncClose status message (websocket : #WebSocket) =55        fun ct -> websocket.CloseAsync(status,message,ct)56        |> Async.AwaitTaskWithCancellation5758    let asyncCloseOutput status message (websocket : #WebSocket) =59        fun ct -> websocket.CloseOutputAsync(status,message,ct)60        |> Async.AwaitTaskWithCancellation6162    let isWebsocketOpen (socket : #WebSocket) =63        socket.State = WebSocketState.Open6465    /// Sends a whole message to the websocket read from the given stream66    let sendMessage bufferSize messageType (readableStream : #IO.Stream) (socket : #WebSocket) = async {67        let buffer = Array.create (bufferSize) Byte.MinValue6869        let rec sendMessage' () = async {70            if isWebsocketOpen socket then71                let! read =72                    readableStream.AsyncRead(buffer,0,buffer.Length)73                if read > 0 then74                    do! (socket |> asyncSend (ArraySegment(buffer |> Array.take read))  messageType false)75                    return! sendMessage'()76                else77                    do! (socket |> asyncSend (ArraySegment(Array.empty))  messageType true)78        }79        do! sendMessage'()80        }89    /// **Description**10    ///11    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async12    ///13    /// **Parameters**14    ///   * `f` - parameter of type `CancellationToken -> Task`15    ///16    /// **Output Type**17    ///   * `Async<unit>`18    ///19    /// **Exceptions**20    ///21    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async {22        let! ct = Async.CancellationToken23        return! f ct |> Async.AwaitTask24    }252627    /// **Description**28    ///29    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async30    ///31    /// **Parameters**32    ///   * `f` - parameter of type `CancellationToken -> Task<'a>`33    ///34    /// **Output Type**35    ///   * `Async<'a>`36    ///37    /// **Exceptions**38    ///39    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async {40        let! ct = Async.CancellationToken41        return! f ct |> Async.AwaitTask42    }4344module Stream =45    open System46    type System.IO.MemoryStream with4748        /// **Description**49        ///50        /// Turns a string into a UTF8 MemoryStream51        ///52        /// **Parameters**53        ///   * `text` - parameter of type `string`54        ///55        /// **Output Type**56        ///   * `IO.MemoryStream`57        ///58        /// **Exceptions**59        ///60        static member UTF8toMemoryStream (text : string) =61            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)626364        /// **Description**65        ///66        /// Turns a `MemoryStream` into a a UTF8 string67        ///68        /// **Parameters**69        ///   * `stream` - parameter of type `IO.MemoryStream`70        ///71        /// **Output Type**72        ///   * `string`73        ///74        /// **Exceptions**75        ///76        static member ToUTF8String (stream : IO.MemoryStream) =77            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream78            stream.ToArray()79            |> Text.Encoding.UTF8.GetString80            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters  8182    let sendMessageAsUTF8 text socket = async {83        use stream = IO.MemoryStream.UTF8toMemoryStream text84        do! sendMessage defaultBufferSize WebSocketMessageType.Text stream socket85    }86878889    type ReceiveStreamResult =90        | Stream of IO.Stream91        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string9293    let receiveMessage bufferSize messageType (writeableStream : IO.Stream) (socket : WebSocket) = async {94        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)9596        let rec readTillEnd' () = async {97            let! result  = socket |> asyncReceive buffer98            match result with99            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived100                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription101                do! asyncCloseOutput WebSocketCloseStatus.NormalClosure "Close received by client" socket102                return StreamClosed(socket.CloseStatus.Value, socket.CloseStatusDescription)103            | result ->104                // printfn "result.MessageType -> %A" result.MessageType105                if result.MessageType <> messageType then return ()106                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count)107                if result.EndOfMessage then108                    return Stream writeableStream109                else110                    return! readTillEnd' ()111        }112        return! readTillEnd' ()113    }114115    type ReceiveUTF8Result =116        | String of string117        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string118119    let receiveMessageAsUTF8 socket = async {120        use stream =  new IO.MemoryStream()121        let! result = receiveMessage defaultBufferSize WebSocketMessageType.Text stream socket122        match result with123        | ReceiveStreamResult.Stream s ->124            return stream |> IO.MemoryStream.ToUTF8String |> String125        | ReceiveStreamResult.StreamClosed(status, reason) ->126            return ReceiveUTF8Result.StreamClosed(status, reason)127    }128129module ThreadSafeWebSocket =130    open System131    open System.Threading132    open System.Net.WebSockets133    open Stream134135    type MessageSendResult = Result<unit, ExceptionDispatchInfo>136    type MessageReceiveResult = Result<WebSocket.ReceiveStreamResult, ExceptionDispatchInfo>137138    type SendMessages =139    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<MessageSendResult>140    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult>141    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult>142143    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<MessageReceiveResult>82        /// **Description**83        ///84        /// Turns a `MemoryStream` into a a UTF8 string85        ///86        /// **Parameters**87        ///88        ///89        /// **Output Type**90        ///   * `string`91        ///92        /// **Exceptions**93        ///94        member stream.ToUTF8String () =95            stream |> System.IO.MemoryStream.ToUTF8String9697module WebSocket =98    open Stream99    open System100    open System.Net.WebSockets101102    /// **Description**103    ///104    /// Same as the `DefaultReceiveBufferSize` and `DefaultClientSendBufferSize` from the internal [WebSocketHelpers]( h105    ///106    /// Current value: 16384107    ///108    /// **Output Type**109    ///   * `int`110    [<Literal>]111    let DefaultBufferSize  : int = 16384 // (16 * 1024)112113    /// **Description**114    ///115    /// Determines if the websocket is open116    ///117    /// **Parameters**118    ///   * `socket` - parameter of type `WebSocket`119    ///120    /// **Output Type**121    ///   * `bool`122    ///123    /// **Exceptions**124    ///125    let isWebsocketOpen (socket : WebSocket) =126        socket.State = WebSocketState.Open127128    /// **Description**129    ///130    /// Receives data from the `System.Net.WebSockets.WebSocket` connection asynchronously.131    ///132    /// **Parameters**133    ///   * `websocket` - parameter of type `WebSocket`134    ///   * `buffer` - parameter of type `ArraySegment<byte>` - References the application buffer that is the storage lo135    ///136    /// **Output Type**137    ///   * `Async<WebSocketReceiveResult>` - An instance of this class represents the result of performing a single Rec138    ///139    /// **Exceptions**140    ///141    let asyncReceive (websocket : WebSocket) (buffer : ArraySegment<byte>)  =142        fun ct ->  websocket.ReceiveAsync(buffer,ct)143        |> Async.AwaitTaskWithCancellation  144145    type ThreadSafeWebSocket =146        { websocket : WebSocket147          sendChannel : MailboxProcessor<SendMessages>148          receiveChannel : MailboxProcessor<ReceiveMessage>149        }150        interface IDisposable with151            member x.Dispose() = - 2152                x.websocket.Dispose()153        member x.State = - 0154            x.websocket.State155        member x.CloseStatus = - 0156            x.websocket.CloseStatus |> Option.ofNullable157        member x.CloseStatusDescription = - 0158            x.websocket.CloseStatusDescription159160    let createFromWebSocket (webSocket : WebSocket) =161        /// handle executing a task in a try/catch and wrapping up the callstack info for later162        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {163            try164                let! result = action165                reply.Reply(Ok result)166            with167            | ex ->168                let dispatch = ExceptionDispatchInfo.Capture ex169                reply.Reply(Error dispatch)170        }171172        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox -> - 4173            let rec loop () = async { - 4174                let! message = inbox.Receive() - 6175                if webSocket |> WebSocket.isWebsocketOpen then176                    match message with177                    | Send (buffer, messageType, stream, replyChannel) -> - 8178                        do! wrap (WebSocket.sendMessage buffer messageType stream webSocket) replyChannel - 2179                        return! loop ()180                    | Close (status, message, replyChannel) -> - 5181                        do! wrap (WebSocket.asyncClose status message webSocket) replyChannel182                    | CloseOutput (status, message, replyChannel) -> - 5183                        do! wrap (WebSocket.asyncCloseOutput status message webSocket) replyChannel184            } - 4185            loop ()186        )187        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox -> - 4188            let rec loop () = async { - 4189                let! (buffer, messageType, stream, replyChannel) = inbox.Receive() - 2190                if webSocket |> WebSocket.isWebsocketOpen then - 2191                    let! result = WebSocket.receiveMessage buffer messageType stream webSocket - 2192                    replyChannel.Reply (Ok result) - 2193                    do! loop ()194            } - 4195            loop ()196        ) - 2197        { - 2198            websocket = webSocket - 2199            sendChannel = sendAgent - 2200            receiveChannel = receiveAgent - 2201        }145146    /// **Description**147    ///148    /// Sends data over the `System.Net.WebSockets.WebSocket` connection asynchronously.149    ///150    /// **Parameters**151    ///   * `websocket` - parameter of type `WebSocket`152    ///   * `buffer` - parameter of type `ArraySegment<byte>` - The buffer to be sent over the connection.153    ///   * `messageType` - parameter of type `WebSocketMessageType`- Indicates whether the application is sending a bin154    ///   * `endOfMessage` - parameter of type `bool` - Indicates whether the data in `buffer` is the last part of a mes155    ///156    /// **Output Type**157    ///   * `Async<unit>`158    ///159    /// **Exceptions**160    ///161    let asyncSend (websocket : WebSocket) (buffer : ArraySegment<byte>) (messageType : WebSocketMessageType) (endOfMessa162        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct)163        |> Async.AwaitTaskWithCancellation164165166    /// **Description**167    ///168    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too169    ///170    /// **Parameters**171    ///   * `websocket` - parameter of type `WebSocket`172    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co173    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con174    ///175    /// **Output Type**176    ///   * `Async<unit>`177    ///178    /// **Exceptions**179    ///180    let asyncClose (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  =181        fun ct -> websocket.CloseAsync(closeStatus, statusDescription, ct)182        |> Async.AwaitTaskWithCancellation183184185    /// **Description**186    ///187    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke188    ///189    /// **Parameters**190    ///   * `websocket` - parameter of type `WebSocket`191    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co192    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect193    ///194    /// **Output Type**195    ///   * `Async<unit>`196    ///197    /// **Exceptions**198    ///199    let asyncCloseOutput (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  =200        fun ct -> websocket.CloseOutputAsync(closeStatus, statusDescription, ct)201        |> Async.AwaitTaskWithCancellation  202203    let sendMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream = - 4204        wsts.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, stream, reply))205 - 2206    let sendMessageAsUTF8(wsts : ThreadSafeWebSocket) (text : string) = async { - 2207        use stream = IO.MemoryStream.UTF8toMemoryStream text - 2208        return! sendMessage wsts  WebSocket.defaultBufferSize  WebSocketMessageType.Text stream209    }210211    let receiveMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream = - 4212        wsts.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, stream, reply)213 - 2214    let receiveMessageAsUTF8 (wsts : ThreadSafeWebSocket) = async { - 2215        use stream = new IO.MemoryStream() - 2216        let! response = receiveMessage wsts WebSocket.defaultBufferSize  WebSocketMessageType.Text stream217        match response with - 2218        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece219        | Ok (WebSocket.ReceiveStreamResult.StreamClosed(status, reason)) -> - 0220            return Ok (WebSocket.ReceiveUTF8Result.StreamClosed(status, reason)) - 0221        | Error ex -> return Error ex222223    }224225    let close (wsts : ThreadSafeWebSocket) status message = - 2226        wsts.sendChannel.PostAndAsyncReply(fun reply -> Close(status, message, reply))227228    let closeOutput (wsts : ThreadSafeWebSocket) status message = - 2229        wsts.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(status, message, reply))203204    /// **Description**205    ///206    /// Sends a whole message to the websocket read from the given stream207    ///208    /// **Parameters**209    ///   * `socket` - parameter of type `WebSocket`210    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u211    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b212    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect213    ///214    /// **Output Type**215    ///   * `Async<unit>`216    ///217    /// **Exceptions**218    ///219    let sendMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (readableStream : #IO.S220        let buffer = Array.create (bufferSize) Byte.MinValue221222        let rec sendMessage' () = async {223            if isWebsocketOpen socket then224                let! read = readableStream.AsyncRead(buffer, 0, buffer.Length)225                if read > 0 then226                    do! asyncSend socket (ArraySegment(buffer |> Array.take read))  messageType false227                    return! sendMessage'()228                else229                    do! (asyncSend socket (ArraySegment(Array.empty))  messageType true)230        }231        do! sendMessage'()232    }233234235    /// **Description**236    ///237    /// Sends a string as UTF8 over a websocket connection.238    ///239    /// **Parameters**240    ///   * `socket` - parameter of type `WebSocket`241    ///   * `text` - parameter of type `string` - The string to send over the websocket.242    ///243    /// **Output Type**244    ///   * `Async<unit>`245    ///246    /// **Exceptions**247    ///248    let sendMessageAsUTF8 (socket : WebSocket) (text : string) = async {249        use stream = IO.MemoryStream.UTF8toMemoryStream text250        do! sendMessage socket DefaultBufferSize WebSocketMessageType.Text stream251    }252253254    /// One of the possible results from reading a whole message from a websocket.255    type ReceiveStreamResult =256        /// Reading from the websocket completed257        | Stream of IO.Stream258        /// The websocket was closed during reading259        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string260261262    /// **Description**263    ///264    /// Reads an entire message from a websocket.265    ///266    /// **Parameters**267    ///   * `socket` - parameter of type `WebSocket`268    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u269    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is receiving a270    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt271    ///272    /// **Output Type**273    ///   * `Async<ReceiveStreamResult>` - One of the possible results from reading a whole message from a websocket274    ///275    /// **Exceptions**276    ///277    let receiveMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (writeableStream : I278        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)279280        let rec readTillEnd' () = async {281            let! result  = asyncReceive socket buffer282            match result with283            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived284                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription285                do! asyncCloseOutput socket WebSocketCloseStatus.NormalClosure "Close received by client"286                return ReceiveStreamResult.Closed(socket.CloseStatus.Value, socket.CloseStatusDescription)287            | result ->288                // printfn "result.MessageType -> %A" result.MessageType289                if result.MessageType <> messageType then return ()290                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count)291                if result.EndOfMessage then292                    return Stream writeableStream293                else294                    return! readTillEnd' ()295        }296        return! readTillEnd' ()297    }298299300    /// One of the possible results from reading a whole message from a websocket.301    type ReceiveUTF8Result =302        /// Reading from the websocket completed.303        | String of string304        /// The websocket was closed during reading.305        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string306307308    /// **Description**309    ///310    /// Reads an entire message as a string.311    ///312    /// **Parameters**313    ///   * `socket` - parameter of type `WebSocket`314    ///315    /// **Output Type**316    ///   * `Async<ReceiveUTF8Result>`317    ///318    /// **Exceptions**319    ///320    let receiveMessageAsUTF8 (socket : WebSocket) = async {321        use stream =  new IO.MemoryStream()322        let! result = receiveMessage socket DefaultBufferSize WebSocketMessageType.Text stream323        match result with324        | ReceiveStreamResult.Stream s ->325            return stream |> IO.MemoryStream.ToUTF8String |> String326        | ReceiveStreamResult.Closed(status, reason) ->327            return ReceiveUTF8Result.Closed(status, reason)328    }329330module ThreadSafeWebSocket =331    open System332    open System.Threading333    open System.Net.WebSockets334    open Stream335336    type SendMessages =337    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<Result<unit, ExceptionDispatchIn338    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>339    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>340341    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<Result<WebSocket.ReceiveStreamRes342343    /// The ThreadSafeWebSocket record allows applications to send and receive data after the WebSocket upgrade has comp344    ///345    /// `There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can b346    type ThreadSafeWebSocket =347        { websocket : WebSocket348          sendChannel : MailboxProcessor<SendMessages>349          receiveChannel : MailboxProcessor<ReceiveMessage>350        }351        interface IDisposable with352            /// Used to clean up unmanaged resources for ASP.NET and self-hosted implementations.353            member x.Dispose() = + 2354                x.websocket.Dispose()355        /// Returns the current state of the WebSocket connection.356        member x.State = + 0357            x.websocket.State358        /// Indicates the reason why the remote endpoint initiated the close handshake.359        member x.CloseStatus = + 0360            x.websocket.CloseStatus |> Option.ofNullable361        ///Allows the remote endpoint to describe the reason why the connection was closed.362        member x.CloseStatusDescription = + 0363            x.websocket.CloseStatusDescription364365366    /// **Description**367    ///368    /// Creates a `ThreadSafeWebSocket` from an existing `WebSocket`.369    ///370    /// **Parameters**371    ///   * `webSocket` - parameter of type `WebSocket`372    ///373    /// **Output Type**374    ///   * `ThreadSafeWebSocket`375    ///376    /// **Exceptions**377    ///378    let createFromWebSocket (webSocket : WebSocket) =379        /// handle executing a task in a try/catch and wrapping up the callstack info for later380        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {381            try382                let! result = action383                reply.Reply(Ok result)384            with385            | ex ->386                let dispatch = ExceptionDispatchInfo.Capture ex387                reply.Reply(Error dispatch)388        }389390        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox -> + 4391            let rec loop () = async { + 4392                let! message = inbox.Receive() + 6393                if webSocket |> WebSocket.isWebsocketOpen then394                    match message with395                    | Send (buffer, messageType, stream, replyChannel) -> + 8396                        do! wrap (WebSocket.sendMessage webSocket buffer messageType stream ) replyChannel + 2397                        return! loop ()398                    | Close (status, message, replyChannel) -> + 5399                        do! wrap (WebSocket.asyncClose webSocket status message) replyChannel400                    | CloseOutput (status, message, replyChannel) -> + 5401                        do! wrap (WebSocket.asyncCloseOutput webSocket status message) replyChannel402            } + 4403            loop ()404        )405        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox -> + 4406            let rec loop () = async { + 4407                let! (buffer, messageType, stream, replyChannel) = inbox.Receive() + 2408                if webSocket |> WebSocket.isWebsocketOpen then + 8409                    do! wrap (WebSocket.receiveMessage webSocket buffer messageType stream) replyChannel + 2410                    return! loop ()411            } + 4412            loop ()413        ) + 2414        { + 2415            websocket = webSocket + 2416            sendChannel = sendAgent + 2417            receiveChannel = receiveAgent + 2418        }419420421422423    /// **Description**424    ///425    /// Sends a whole message to the websocket read from the given stream.426    ///427    /// **Parameters**428    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`429    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u430    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b431    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect432    ///433    /// **Output Type**434    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`435    ///436    /// **Exceptions**437    ///438    let sendMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) messageType (readableStream : #IO.Str + 4439        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, readableStream, rep440441442    /// **Description**443    ///444    /// Sends a string as UTF8 over a websocket connection.445    ///446    /// **Parameters**447    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`448    ///   * `text` - parameter of type `string` - The string to send over the websocket.449    ///450    /// **Output Type**451    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`452    ///453    /// **Exceptions**454    /// + 2455    let sendMessageAsUTF8(threadSafeWebSocket : ThreadSafeWebSocket) (text : string) = async { + 2456        use stream = IO.MemoryStream.UTF8toMemoryStream text + 2457        return! sendMessage threadSafeWebSocket  WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream458    }459460461    /// **Description**462    ///463    /// Reads an entire message from a websocket as a string.464    ///465    /// **Parameters**466    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`467    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u468    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b469    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt470    ///471    /// **Output Type**472    ///   * `Async<Result<WebSocket.ReceiveStreamResult,ExceptionDispatchInfo>>` - One of the possible results from read473    ///474    /// **Exceptions**475    ///476    let receiveMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) (messageType : WebSocketMessageTyp + 4477        threadSafeWebSocket.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, writeableStream, repl478479480    /// **Description**481    ///482    /// Reads an entire message as a string.483    ///484    /// **Parameters**485    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`486    ///487    /// **Output Type**488    ///   * `Async<Result<WebSocket.ReceiveUTF8Result,ExceptionDispatchInfo>>`489    ///490    /// **Exceptions**491    /// + 2492    let receiveMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) = async { + 2493        use stream = new IO.MemoryStream() + 2494        let! response = receiveMessage threadSafeWebSocket WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream495        match response with + 2496        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece497        | Ok (WebSocket.Closed(status, reason)) -> + 0498            return Ok (WebSocket.ReceiveUTF8Result.Closed(status, reason)) + 0499        | Error ex -> return Error ex500501    }502503504    /// **Description**505    ///506    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too507    ///508    /// **Parameters**509    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`510    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co511    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con512    ///513    /// **Output Type**514    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`515    ///516    /// **Exceptions**517    ///518    let close (threadSafeWebSocket : ThreadSafeWebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription : str + 2519        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Close(closeStatus, statusDescription, reply))520521522    /// **Description**523    ///524    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke525    ///526    /// **Parameters**527    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`528    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co529    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect530    ///531    /// **Output Type**532    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`533    ///534    /// **Exceptions**535    ///536    let closeOutput (threadSafeWebSocket : ThreadSafeWebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription  + 2537        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(closeStatus, statusDescription, reply - +

Methods/Properties

-System-IDisposable-Dispose()
-State()
-CloseStatus()
-CloseStatusDescription()
-loop@173(System.Net.WebSockets.WebSocket,Microsoft.FSharp.Control.FSharpMailboxProcessor`1<FSharp.Control.Websockets.ThreadSafeWebSocket/SendMessages>,Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(FSharp.Control.Websockets.ThreadSafeWebSocket/SendMessages)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Exception)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Exception)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Exception)
-Invoke(Microsoft.FSharp.Control.FSharpMailboxProcessor`1<FSharp.Control.Websockets.ThreadSafeWebSocket/SendMessages>)
-loop@188-18(System.Net.WebSockets.WebSocket,Microsoft.FSharp.Control.FSharpMailboxProcessor`1<System.Tuple`4<System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>>>,Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Tuple`4<System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>>)
-Invoke(FSharp.Control.Websockets.WebSocket/ReceiveStreamResult)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Control.FSharpMailboxProcessor`1<System.Tuple`4<System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>>>)
-createFromWebSocket(System.Net.WebSockets.WebSocket)
-sendMessage(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream)
-Invoke(Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<Microsoft.FSharp.Core.Unit,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>)
-sendMessageAsUTF8(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket,System.String)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.IO.MemoryStream)
-receiveMessage(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream)
-Invoke(Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>)
-receiveMessageAsUTF8(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.IO.MemoryStream)
-Invoke(Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>)
-close(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Net.WebSockets.WebSocketCloseStatus,System.String)
-Invoke(Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<Microsoft.FSharp.Core.Unit,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>)
-closeOutput(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Net.WebSockets.WebSocketCloseStatus,System.String)
-Invoke(Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<Microsoft.FSharp.Core.Unit,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>)
+System-IDisposable-Dispose()
+State()
+CloseStatus()
+CloseStatusDescription()
+loop@391(System.Net.WebSockets.WebSocket,Microsoft.FSharp.Control.FSharpMailboxProcessor`1<FSharp.Control.Websockets.ThreadSafeWebSocket/SendMessages>,Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(FSharp.Control.Websockets.ThreadSafeWebSocket/SendMessages)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Exception)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Exception)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Exception)
+Invoke(Microsoft.FSharp.Control.FSharpMailboxProcessor`1<FSharp.Control.Websockets.ThreadSafeWebSocket/SendMessages>)
+loop@406-18(System.Net.WebSockets.WebSocket,Microsoft.FSharp.Control.FSharpMailboxProcessor`1<System.Tuple`4<System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>>>,Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Tuple`4<System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>>)
+Invoke(FSharp.Control.Websockets.WebSocket/ReceiveStreamResult)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Exception)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Control.FSharpMailboxProcessor`1<System.Tuple`4<System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>>>)
+createFromWebSocket(System.Net.WebSockets.WebSocket)
+sendMessage(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Int32,System.Net.WebSockets.WebSocketMessageType,a)
+Invoke(Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<Microsoft.FSharp.Core.Unit,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>)
+sendMessageAsUTF8(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket,System.String)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.IO.MemoryStream)
+receiveMessage(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream)
+Invoke(Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>)
+receiveMessageAsUTF8(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.IO.MemoryStream)
+Invoke(Microsoft.FSharp.Core.FSharpResult`2<FSharp.Control.Websockets.WebSocket/ReceiveStreamResult,System.Runtime.ExceptionServices.ExceptionDispatchInfo>)
+close(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Net.WebSockets.WebSocketCloseStatus,System.String)
+Invoke(Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<Microsoft.FSharp.Core.Unit,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>)
+closeOutput(FSharp.Control.Websockets.ThreadSafeWebSocket/ThreadSafeWebSocket,System.Net.WebSockets.WebSocketCloseStatus,System.String)
+Invoke(Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1<Microsoft.FSharp.Core.FSharpResult`2<Microsoft.FSharp.Core.Unit,System.Runtime.ExceptionServices.ExceptionDispatchInfo>>)

diff --git a/docs/coverage/FSharp.Control.Websockets_ThreadSafeWebsocket.htm b/docs/coverage/FSharp.Control.Websockets_ThreadSafeWebsocket.htm index 8dc469f..5d89ffb 100644 --- a/docs/coverage/FSharp.Control.Websockets_ThreadSafeWebsocket.htm +++ b/docs/coverage/FSharp.Control.Websockets_ThreadSafeWebsocket.htm @@ -20,7 +20,7 @@

Summary

Covered lines:0 Uncovered lines:49 Coverable lines:49 -Total lines:229 +Total lines:537 Line coverage:0% Branch coverage:0% @@ -74,231 +74,539 @@

 5open System.Runtime.ExceptionServices  6  7type Async =8    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async {9        let! ct = Async.CancellationToken10        return! f ct |> Async.AwaitTask11    }1213    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async {14        let! ct = Async.CancellationToken15        return! f ct |> Async.AwaitTask16    }1718module Stream =19    open System20    type System.IO.MemoryStream with21        static member UTF8toMemoryStream (text : string) =22            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)2324        static member ToUTF8String (stream : IO.MemoryStream) =25            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream26            stream.ToArray()27            |> Text.Encoding.UTF8.GetString28            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters2930        member stream.ToUTF8String () =31            stream |> System.IO.MemoryStream.ToUTF8String3233module WebSocket =34    open Stream35    open System36    open System.Net.WebSockets3738    /// **Description**39    /// (16 * 1024) = 1638440    /// https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/WebSocketHelpers.cs,285b8b64a4da685141    /// **Output Type**42    ///   * `int`43    [<Literal>]44    let defaultBufferSize  : int = 16384 // (16 * 1024)4546    let asyncReceive  (buffer : ArraySegment<byte>) (websocket : #WebSocket) =47        fun ct ->  websocket.ReceiveAsync(buffer,ct)48        |> Async.AwaitTaskWithCancellation4950    let asyncSend (buffer : ArraySegment<byte>) messageType endOfMessage (websocket : #WebSocket) =51        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct)52        |> Async.AwaitTaskWithCancellation5354    let asyncClose status message (websocket : #WebSocket) =55        fun ct -> websocket.CloseAsync(status,message,ct)56        |> Async.AwaitTaskWithCancellation5758    let asyncCloseOutput status message (websocket : #WebSocket) =59        fun ct -> websocket.CloseOutputAsync(status,message,ct)60        |> Async.AwaitTaskWithCancellation6162    let isWebsocketOpen (socket : #WebSocket) =63        socket.State = WebSocketState.Open6465    /// Sends a whole message to the websocket read from the given stream66    let sendMessage bufferSize messageType (readableStream : #IO.Stream) (socket : #WebSocket) = async {67        let buffer = Array.create (bufferSize) Byte.MinValue6869        let rec sendMessage' () = async {70            if isWebsocketOpen socket then71                let! read =72                    readableStream.AsyncRead(buffer,0,buffer.Length)73                if read > 0 then74                    do! (socket |> asyncSend (ArraySegment(buffer |> Array.take read))  messageType false)75                    return! sendMessage'()76                else77                    do! (socket |> asyncSend (ArraySegment(Array.empty))  messageType true)78        }79        do! sendMessage'()80        }89    /// **Description**10    ///11    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async12    ///13    /// **Parameters**14    ///   * `f` - parameter of type `CancellationToken -> Task`15    ///16    /// **Output Type**17    ///   * `Async<unit>`18    ///19    /// **Exceptions**20    ///21    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async {22        let! ct = Async.CancellationToken23        return! f ct |> Async.AwaitTask24    }252627    /// **Description**28    ///29    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async30    ///31    /// **Parameters**32    ///   * `f` - parameter of type `CancellationToken -> Task<'a>`33    ///34    /// **Output Type**35    ///   * `Async<'a>`36    ///37    /// **Exceptions**38    ///39    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async {40        let! ct = Async.CancellationToken41        return! f ct |> Async.AwaitTask42    }4344module Stream =45    open System46    type System.IO.MemoryStream with4748        /// **Description**49        ///50        /// Turns a string into a UTF8 MemoryStream51        ///52        /// **Parameters**53        ///   * `text` - parameter of type `string`54        ///55        /// **Output Type**56        ///   * `IO.MemoryStream`57        ///58        /// **Exceptions**59        ///60        static member UTF8toMemoryStream (text : string) =61            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)626364        /// **Description**65        ///66        /// Turns a `MemoryStream` into a a UTF8 string67        ///68        /// **Parameters**69        ///   * `stream` - parameter of type `IO.MemoryStream`70        ///71        /// **Output Type**72        ///   * `string`73        ///74        /// **Exceptions**75        ///76        static member ToUTF8String (stream : IO.MemoryStream) =77            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream78            stream.ToArray()79            |> Text.Encoding.UTF8.GetString80            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters  8182    let sendMessageAsUTF8 text socket = async {83        use stream = IO.MemoryStream.UTF8toMemoryStream text84        do! sendMessage defaultBufferSize WebSocketMessageType.Text stream socket85    }86878889    type ReceiveStreamResult =90        | Stream of IO.Stream91        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string9293    let receiveMessage bufferSize messageType (writeableStream : IO.Stream) (socket : WebSocket) = async {94        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)9596        let rec readTillEnd' () = async {97            let! result  = socket |> asyncReceive buffer98            match result with99            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived100                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription101                do! asyncCloseOutput WebSocketCloseStatus.NormalClosure "Close received by client" socket102                return StreamClosed(socket.CloseStatus.Value, socket.CloseStatusDescription)103            | result ->104                // printfn "result.MessageType -> %A" result.MessageType105                if result.MessageType <> messageType then return ()106                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count)107                if result.EndOfMessage then108                    return Stream writeableStream109                else110                    return! readTillEnd' ()111        }112        return! readTillEnd' ()113    }114115    type ReceiveUTF8Result =116        | String of string117        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string118119    let receiveMessageAsUTF8 socket = async {120        use stream =  new IO.MemoryStream()121        let! result = receiveMessage defaultBufferSize WebSocketMessageType.Text stream socket122        match result with123        | ReceiveStreamResult.Stream s ->124            return stream |> IO.MemoryStream.ToUTF8String |> String125        | ReceiveStreamResult.StreamClosed(status, reason) ->126            return ReceiveUTF8Result.StreamClosed(status, reason)127    }128129module ThreadSafeWebSocket =130    open System131    open System.Threading - 0132    open System.Net.WebSockets133    open Stream - 0134135    type MessageSendResult = Result<unit, ExceptionDispatchInfo> - 0136    type MessageReceiveResult = Result<WebSocket.ReceiveStreamResult, ExceptionDispatchInfo>137 - 0138    type SendMessages =139    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<MessageSendResult>140    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult> - 0141    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult> - 0142 - 0143    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<MessageReceiveResult>82        /// **Description**83        ///84        /// Turns a `MemoryStream` into a a UTF8 string85        ///86        /// **Parameters**87        ///88        ///89        /// **Output Type**90        ///   * `string`91        ///92        /// **Exceptions**93        ///94        member stream.ToUTF8String () =95            stream |> System.IO.MemoryStream.ToUTF8String9697module WebSocket =98    open Stream99    open System100    open System.Net.WebSockets101102    /// **Description**103    ///104    /// Same as the `DefaultReceiveBufferSize` and `DefaultClientSendBufferSize` from the internal [WebSocketHelpers]( h105    ///106    /// Current value: 16384107    ///108    /// **Output Type**109    ///   * `int`110    [<Literal>]111    let DefaultBufferSize  : int = 16384 // (16 * 1024)112113    /// **Description**114    ///115    /// Determines if the websocket is open116    ///117    /// **Parameters**118    ///   * `socket` - parameter of type `WebSocket`119    ///120    /// **Output Type**121    ///   * `bool`122    ///123    /// **Exceptions**124    ///125    let isWebsocketOpen (socket : WebSocket) =126        socket.State = WebSocketState.Open127128    /// **Description**129    ///130    /// Receives data from the `System.Net.WebSockets.WebSocket` connection asynchronously.131    /// + 0132    /// **Parameters**133    ///   * `websocket` - parameter of type `WebSocket` + 0134    ///   * `buffer` - parameter of type `ArraySegment<byte>` - References the application buffer that is the storage lo135    /// + 0136    /// **Output Type**137    ///   * `Async<WebSocketReceiveResult>` - An instance of this class represents the result of performing a single Rec + 0138    ///139    /// **Exceptions**140    /// + 0141    let asyncReceive (websocket : WebSocket) (buffer : ArraySegment<byte>)  = + 0142        fun ct ->  websocket.ReceiveAsync(buffer,ct) + 0143        |> Async.AwaitTaskWithCancellation  0144 - 0145    type ThreadSafeWebSocket = - 0146        { websocket : WebSocket - 0147          sendChannel : MailboxProcessor<SendMessages> - 0148          receiveChannel : MailboxProcessor<ReceiveMessage> - 0149        } - 0150        interface IDisposable with - 0151            member x.Dispose() = - 0152                x.websocket.Dispose() - 0153        member x.State = - 0154            x.websocket.State - 0155        member x.CloseStatus = - 0156            x.websocket.CloseStatus |> Option.ofNullable - 0157        member x.CloseStatusDescription = - 0158            x.websocket.CloseStatusDescription - 0159 - 0160    let createFromWebSocket (webSocket : WebSocket) = - 0161        /// handle executing a task in a try/catch and wrapping up the callstack info for later - 0162        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async { - 0163            try - 0164                let! result = action - 0165                reply.Reply(Ok result) - 0166            with - 0167            | ex -> - 0168                let dispatch = ExceptionDispatchInfo.Capture ex - 0169                reply.Reply(Error dispatch) - 0170        } - 0171 - 0172        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox -> - 0173            let rec loop () = async { - 0174                let! message = inbox.Receive()175                if webSocket |> WebSocket.isWebsocketOpen then176                    match message with - 0177                    | Send (buffer, messageType, stream, replyChannel) ->178                        do! wrap (WebSocket.sendMessage buffer messageType stream webSocket) replyChannel - 0179                        return! loop () - 0180                    | Close (status, message, replyChannel) -> - 0181                        do! wrap (WebSocket.asyncClose status message webSocket) replyChannel182                    | CloseOutput (status, message, replyChannel) ->183                        do! wrap (WebSocket.asyncCloseOutput status message webSocket) replyChannel184            } - 0185            loop ()186        ) - 0187        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox -> - 0188            let rec loop () = async { - 0189                let! (buffer, messageType, stream, replyChannel) = inbox.Receive() - 0190                if webSocket |> WebSocket.isWebsocketOpen then191                    let! result = WebSocket.receiveMessage buffer messageType stream webSocket192                    replyChannel.Reply (Ok result)193                    do! loop () - 0194            }195            loop ()196        ) - 0197        {198            websocket = webSocket199            sendChannel = sendAgent200            receiveChannel = receiveAgent201        } + 0145 + 0146    /// **Description** + 0147    /// + 0148    /// Sends data over the `System.Net.WebSockets.WebSocket` connection asynchronously. + 0149    /// + 0150    /// **Parameters** + 0151    ///   * `websocket` - parameter of type `WebSocket` + 0152    ///   * `buffer` - parameter of type `ArraySegment<byte>` - The buffer to be sent over the connection. + 0153    ///   * `messageType` - parameter of type `WebSocketMessageType`- Indicates whether the application is sending a bin + 0154    ///   * `endOfMessage` - parameter of type `bool` - Indicates whether the data in `buffer` is the last part of a mes + 0155    /// + 0156    /// **Output Type** + 0157    ///   * `Async<unit>` + 0158    /// + 0159    /// **Exceptions** + 0160    /// + 0161    let asyncSend (websocket : WebSocket) (buffer : ArraySegment<byte>) (messageType : WebSocketMessageType) (endOfMessa + 0162        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct) + 0163        |> Async.AwaitTaskWithCancellation + 0164 + 0165 + 0166    /// **Description** + 0167    /// + 0168    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too + 0169    /// + 0170    /// **Parameters** + 0171    ///   * `websocket` - parameter of type `WebSocket` + 0172    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co + 0173    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con + 0174    ///175    /// **Output Type**176    ///   * `Async<unit>` + 0177    ///178    /// **Exceptions** + 0179    /// + 0180    let asyncClose (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  = + 0181        fun ct -> websocket.CloseAsync(closeStatus, statusDescription, ct)182        |> Async.AwaitTaskWithCancellation183184 + 0185    /// **Description**186    /// + 0187    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke + 0188    /// + 0189    /// **Parameters** + 0190    ///   * `websocket` - parameter of type `WebSocket`191    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co192    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect193    /// + 0194    /// **Output Type**195    ///   * `Async<unit>`196    /// + 0197    /// **Exceptions**198    ///199    let asyncCloseOutput (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  =200        fun ct -> websocket.CloseOutputAsync(closeStatus, statusDescription, ct)201        |> Async.AwaitTaskWithCancellation  202203    let sendMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream =204        wsts.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, stream, reply))205206    let sendMessageAsUTF8(wsts : ThreadSafeWebSocket) (text : string) = async {207        use stream = IO.MemoryStream.UTF8toMemoryStream text208        return! sendMessage wsts  WebSocket.defaultBufferSize  WebSocketMessageType.Text stream209    }210211    let receiveMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream =212        wsts.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, stream, reply)213214    let receiveMessageAsUTF8 (wsts : ThreadSafeWebSocket) = async {215        use stream = new IO.MemoryStream()216        let! response = receiveMessage wsts WebSocket.defaultBufferSize  WebSocketMessageType.Text stream217        match response with218        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece219        | Ok (WebSocket.ReceiveStreamResult.StreamClosed(status, reason)) ->220            return Ok (WebSocket.ReceiveUTF8Result.StreamClosed(status, reason))221        | Error ex -> return Error ex222223    }224225    let close (wsts : ThreadSafeWebSocket) status message =226        wsts.sendChannel.PostAndAsyncReply(fun reply -> Close(status, message, reply))227228    let closeOutput (wsts : ThreadSafeWebSocket) status message =229        wsts.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(status, message, reply))203204    /// **Description**205    ///206    /// Sends a whole message to the websocket read from the given stream207    ///208    /// **Parameters**209    ///   * `socket` - parameter of type `WebSocket`210    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u211    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b212    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect213    ///214    /// **Output Type**215    ///   * `Async<unit>`216    ///217    /// **Exceptions**218    ///219    let sendMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (readableStream : #IO.S220        let buffer = Array.create (bufferSize) Byte.MinValue221222        let rec sendMessage' () = async {223            if isWebsocketOpen socket then224                let! read = readableStream.AsyncRead(buffer, 0, buffer.Length)225                if read > 0 then226                    do! asyncSend socket (ArraySegment(buffer |> Array.take read))  messageType false227                    return! sendMessage'()228                else229                    do! (asyncSend socket (ArraySegment(Array.empty))  messageType true)230        }231        do! sendMessage'()232    }233234235    /// **Description**236    ///237    /// Sends a string as UTF8 over a websocket connection.238    ///239    /// **Parameters**240    ///   * `socket` - parameter of type `WebSocket`241    ///   * `text` - parameter of type `string` - The string to send over the websocket.242    ///243    /// **Output Type**244    ///   * `Async<unit>`245    ///246    /// **Exceptions**247    ///248    let sendMessageAsUTF8 (socket : WebSocket) (text : string) = async {249        use stream = IO.MemoryStream.UTF8toMemoryStream text250        do! sendMessage socket DefaultBufferSize WebSocketMessageType.Text stream251    }252253254    /// One of the possible results from reading a whole message from a websocket.255    type ReceiveStreamResult =256        /// Reading from the websocket completed257        | Stream of IO.Stream258        /// The websocket was closed during reading259        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string260261262    /// **Description**263    ///264    /// Reads an entire message from a websocket.265    ///266    /// **Parameters**267    ///   * `socket` - parameter of type `WebSocket`268    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u269    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is receiving a270    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt271    ///272    /// **Output Type**273    ///   * `Async<ReceiveStreamResult>` - One of the possible results from reading a whole message from a websocket274    ///275    /// **Exceptions**276    ///277    let receiveMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (writeableStream : I278        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)279280        let rec readTillEnd' () = async {281            let! result  = asyncReceive socket buffer282            match result with283            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived284                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription285                do! asyncCloseOutput socket WebSocketCloseStatus.NormalClosure "Close received by client"286                return ReceiveStreamResult.Closed(socket.CloseStatus.Value, socket.CloseStatusDescription)287            | result ->288                // printfn "result.MessageType -> %A" result.MessageType289                if result.MessageType <> messageType then return ()290                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count)291                if result.EndOfMessage then292                    return Stream writeableStream293                else294                    return! readTillEnd' ()295        }296        return! readTillEnd' ()297    }298299300    /// One of the possible results from reading a whole message from a websocket.301    type ReceiveUTF8Result =302        /// Reading from the websocket completed.303        | String of string304        /// The websocket was closed during reading.305        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string306307308    /// **Description**309    ///310    /// Reads an entire message as a string.311    ///312    /// **Parameters**313    ///   * `socket` - parameter of type `WebSocket`314    ///315    /// **Output Type**316    ///   * `Async<ReceiveUTF8Result>`317    ///318    /// **Exceptions**319    ///320    let receiveMessageAsUTF8 (socket : WebSocket) = async {321        use stream =  new IO.MemoryStream()322        let! result = receiveMessage socket DefaultBufferSize WebSocketMessageType.Text stream323        match result with324        | ReceiveStreamResult.Stream s ->325            return stream |> IO.MemoryStream.ToUTF8String |> String326        | ReceiveStreamResult.Closed(status, reason) ->327            return ReceiveUTF8Result.Closed(status, reason)328    }329330module ThreadSafeWebSocket =331    open System332    open System.Threading333    open System.Net.WebSockets334    open Stream335336    type SendMessages =337    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<Result<unit, ExceptionDispatchIn338    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>339    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>340341    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<Result<WebSocket.ReceiveStreamRes342343    /// The ThreadSafeWebSocket record allows applications to send and receive data after the WebSocket upgrade has comp344    ///345    /// `There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can b346    type ThreadSafeWebSocket =347        { websocket : WebSocket348          sendChannel : MailboxProcessor<SendMessages>349          receiveChannel : MailboxProcessor<ReceiveMessage>350        }351        interface IDisposable with352            /// Used to clean up unmanaged resources for ASP.NET and self-hosted implementations.353            member x.Dispose() =354                x.websocket.Dispose()355        /// Returns the current state of the WebSocket connection.356        member x.State =357            x.websocket.State358        /// Indicates the reason why the remote endpoint initiated the close handshake.359        member x.CloseStatus =360            x.websocket.CloseStatus |> Option.ofNullable361        ///Allows the remote endpoint to describe the reason why the connection was closed.362        member x.CloseStatusDescription =363            x.websocket.CloseStatusDescription364365366    /// **Description**367    ///368    /// Creates a `ThreadSafeWebSocket` from an existing `WebSocket`.369    ///370    /// **Parameters**371    ///   * `webSocket` - parameter of type `WebSocket`372    ///373    /// **Output Type**374    ///   * `ThreadSafeWebSocket`375    ///376    /// **Exceptions**377    ///378    let createFromWebSocket (webSocket : WebSocket) =379        /// handle executing a task in a try/catch and wrapping up the callstack info for later380        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {381            try382                let! result = action383                reply.Reply(Ok result)384            with385            | ex ->386                let dispatch = ExceptionDispatchInfo.Capture ex387                reply.Reply(Error dispatch)388        }389390        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox ->391            let rec loop () = async {392                let! message = inbox.Receive()393                if webSocket |> WebSocket.isWebsocketOpen then394                    match message with395                    | Send (buffer, messageType, stream, replyChannel) ->396                        do! wrap (WebSocket.sendMessage webSocket buffer messageType stream ) replyChannel397                        return! loop ()398                    | Close (status, message, replyChannel) ->399                        do! wrap (WebSocket.asyncClose webSocket status message) replyChannel400                    | CloseOutput (status, message, replyChannel) ->401                        do! wrap (WebSocket.asyncCloseOutput webSocket status message) replyChannel402            }403            loop ()404        )405        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox ->406            let rec loop () = async {407                let! (buffer, messageType, stream, replyChannel) = inbox.Receive()408                if webSocket |> WebSocket.isWebsocketOpen then409                    do! wrap (WebSocket.receiveMessage webSocket buffer messageType stream) replyChannel410                    return! loop ()411            }412            loop ()413        )414        {415            websocket = webSocket416            sendChannel = sendAgent417            receiveChannel = receiveAgent418        }419420421422423    /// **Description**424    ///425    /// Sends a whole message to the websocket read from the given stream.426    ///427    /// **Parameters**428    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`429    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u430    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b431    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect432    ///433    /// **Output Type**434    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`435    ///436    /// **Exceptions**437    ///438    let sendMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) messageType (readableStream : #IO.Str439        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, readableStream, rep440441442    /// **Description**443    ///444    /// Sends a string as UTF8 over a websocket connection.445    ///446    /// **Parameters**447    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`448    ///   * `text` - parameter of type `string` - The string to send over the websocket.449    ///450    /// **Output Type**451    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`452    ///453    /// **Exceptions**454    ///455    let sendMessageAsUTF8(threadSafeWebSocket : ThreadSafeWebSocket) (text : string) = async {456        use stream = IO.MemoryStream.UTF8toMemoryStream text457        return! sendMessage threadSafeWebSocket  WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream458    }459460461    /// **Description**462    ///463    /// Reads an entire message from a websocket as a string.464    ///465    /// **Parameters**466    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`467    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u468    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b469    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt470    ///471    /// **Output Type**472    ///   * `Async<Result<WebSocket.ReceiveStreamResult,ExceptionDispatchInfo>>` - One of the possible results from read473    ///474    /// **Exceptions**475    ///476    let receiveMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) (messageType : WebSocketMessageTyp477        threadSafeWebSocket.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, writeableStream, repl478479480    /// **Description**481    ///482    /// Reads an entire message as a string.483    ///484    /// **Parameters**485    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`486    ///487    /// **Output Type**488    ///   * `Async<Result<WebSocket.ReceiveUTF8Result,ExceptionDispatchInfo>>`489    ///490    /// **Exceptions**491    ///492    let receiveMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) = async {493        use stream = new IO.MemoryStream()494        let! response = receiveMessage threadSafeWebSocket WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream495        match response with496        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece497        | Ok (WebSocket.Closed(status, reason)) ->498            return Ok (WebSocket.ReceiveUTF8Result.Closed(status, reason))499        | Error ex -> return Error ex500501    }502503504    /// **Description**505    ///506    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too507    ///508    /// **Parameters**509    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`510    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co511    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con512    ///513    /// **Output Type**514    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`515    ///516    /// **Exceptions**517    ///518    let close (threadSafeWebSocket : ThreadSafeWebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription : str519        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Close(closeStatus, statusDescription, reply))520521522    /// **Description**523    ///524    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke525    ///526    /// **Parameters**527    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`528    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co529    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect530    ///531    /// **Output Type**532    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`533    ///534    /// **Exceptions**535    ///536    let closeOutput (threadSafeWebSocket : ThreadSafeWebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription 537        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(closeStatus, statusDescription, reply - +

Methods/Properties

diff --git a/docs/coverage/FSharp.Control.Websockets_WebSocket.htm b/docs/coverage/FSharp.Control.Websockets_WebSocket.htm index 66e87ab..2095aa8 100644 --- a/docs/coverage/FSharp.Control.Websockets_WebSocket.htm +++ b/docs/coverage/FSharp.Control.Websockets_WebSocket.htm @@ -17,11 +17,11 @@

Summary

Class:FSharp.Control.Websockets.WebSocket Assembly:FSharp.Control.Websockets File(s):/Users/jimmybyrd/Documents/GitHub/FSharp.Control.Websockets/src/FSharp.Control.Websockets/FSharp.Control.Websockets.fs -Covered lines:39 +Covered lines:38 Uncovered lines:1 -Coverable lines:40 -Total lines:229 -Line coverage:97.5% +Coverable lines:39 +Total lines:537 +Line coverage:97.4% Branch coverage:66.6% @@ -29,39 +29,39 @@

Metrics

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage Crap Score
asyncReceive(...)1010001
asyncSend(...)1010001
asyncClose(...)1010001
asyncCloseOutput(...)1010001
isWebsocketOpen(...)10000
sendMessage'@69(...)1010001
sendMessage(...)1010001
sendMessageAsUTF8(...)1010001
readTillEnd'@96(...)1010001
receiveMessage(...)1010001
receiveMessageAsUTF8(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)221001002
Invoke(...)2266.6766.672.15
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)221001002
Invoke(...)1010001
Invoke(...)588057.145.2
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)2010002
Invoke(...)1010001
Invoke(...)1010001
isWebsocketOpen(...)10000
asyncReceive(...)1010001
asyncSend(...)1010001
asyncClose(...)1010001
asyncCloseOutput(...)1010001
sendMessage'@222(...)1010001
sendMessage(...)1010001
sendMessageAsUTF8(...)1010001
readTillEnd'@280(...)1010001
receiveMessage(...)1010001
receiveMessageAsUTF8(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)221001002
Invoke(...)2266.6766.672.15
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)221001002
Invoke(...)1010001
Invoke(...)588057.145.2
Invoke(...)1010001
Invoke(...)1010001
Invoke(...)2010002
Invoke(...)1010001
Invoke(...)1010001

File(s)

@@ -76,267 +76,575 @@

 5open System.Runtime.ExceptionServices  6  7type Async =8    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async {9        let! ct = Async.CancellationToken10        return! f ct |> Async.AwaitTask11    }1213    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async {14        let! ct = Async.CancellationToken15        return! f ct |> Async.AwaitTask16    }1718module Stream =19    open System20    type System.IO.MemoryStream with21        static member UTF8toMemoryStream (text : string) =22            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)2324        static member ToUTF8String (stream : IO.MemoryStream) =25            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream26            stream.ToArray()27            |> Text.Encoding.UTF8.GetString28            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters2930        member stream.ToUTF8String () =31            stream |> System.IO.MemoryStream.ToUTF8String3233module WebSocket =34    open Stream35    open System36    open System.Net.WebSockets3738    /// **Description**39    /// (16 * 1024) = 1638440    /// https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/WebSocketHelpers.cs,285b8b64a4da685141    /// **Output Type**42    ///   * `int`43    [<Literal>]44    let defaultBufferSize  : int = 16384 // (16 * 1024)4546    let asyncReceive  (buffer : ArraySegment<byte>) (websocket : #WebSocket) = - 2047        fun ct ->  websocket.ReceiveAsync(buffer,ct) - 1048        |> Async.AwaitTaskWithCancellation4950    let asyncSend (buffer : ArraySegment<byte>) messageType endOfMessage (websocket : #WebSocket) = - 1651        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct) - 852        |> Async.AwaitTaskWithCancellation5354    let asyncClose status message (websocket : #WebSocket) = - 255        fun ct -> websocket.CloseAsync(status,message,ct) - 156        |> Async.AwaitTaskWithCancellation5758    let asyncCloseOutput status message (websocket : #WebSocket) = - 659        fun ct -> websocket.CloseOutputAsync(status,message,ct) - 360        |> Async.AwaitTaskWithCancellation6162    let isWebsocketOpen (socket : #WebSocket) = - 063        socket.State = WebSocketState.Open6465    /// Sends a whole message to the websocket read from the given stream - 466    let sendMessage bufferSize messageType (readableStream : #IO.Stream) (socket : #WebSocket) = async { - 467        let buffer = Array.create (bufferSize) Byte.MinValue68 - 869        let rec sendMessage' () = async { - 870            if isWebsocketOpen socket then - 871                let! read = - 872                    readableStream.AsyncRead(buffer,0,buffer.Length) - 873                if read > 0 then - 474                    do! (socket |> asyncSend (ArraySegment(buffer |> Array.take read))  messageType false) - 475                    return! sendMessage'()76                else - 877                    do! (socket |> asyncSend (ArraySegment(Array.empty))  messageType true)78        } - 1279        do! sendMessage'()80        }89    /// **Description**10    ///11    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async12    ///13    /// **Parameters**14    ///   * `f` - parameter of type `CancellationToken -> Task`15    ///16    /// **Output Type**17    ///   * `Async<unit>`18    ///19    /// **Exceptions**20    ///21    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async {22        let! ct = Async.CancellationToken23        return! f ct |> Async.AwaitTask24    }252627    /// **Description**28    ///29    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async30    ///31    /// **Parameters**32    ///   * `f` - parameter of type `CancellationToken -> Task<'a>`33    ///34    /// **Output Type**35    ///   * `Async<'a>`36    ///37    /// **Exceptions**38    ///39    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async {40        let! ct = Async.CancellationToken41        return! f ct |> Async.AwaitTask42    }4344module Stream =45    open System46    type System.IO.MemoryStream with4748        /// **Description**49        ///50        /// Turns a string into a UTF8 MemoryStream51        ///52        /// **Parameters**53        ///   * `text` - parameter of type `string`54        ///55        /// **Output Type**56        ///   * `IO.MemoryStream`57        ///58        /// **Exceptions**59        ///60        static member UTF8toMemoryStream (text : string) =61            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)626364        /// **Description**65        ///66        /// Turns a `MemoryStream` into a a UTF8 string67        ///68        /// **Parameters**69        ///   * `stream` - parameter of type `IO.MemoryStream`70        ///71        /// **Output Type**72        ///   * `string`73        ///74        /// **Exceptions**75        ///76        static member ToUTF8String (stream : IO.MemoryStream) =77            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream78            stream.ToArray()79            |> Text.Encoding.UTF8.GetString80            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters  81 - 282    let sendMessageAsUTF8 text socket = async { - 283        use stream = IO.MemoryStream.UTF8toMemoryStream text - 484        do! sendMessage defaultBufferSize WebSocketMessageType.Text stream socket85    }86878889    type ReceiveStreamResult =90        | Stream of IO.Stream91        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string92 - 693    let receiveMessage bufferSize messageType (writeableStream : IO.Stream) (socket : WebSocket) = async { - 694        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)95 - 1096        let rec readTillEnd' () = async { - 1097            let! result  = socket |> asyncReceive buffer98            match result with99            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived100                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription - 2101                do! asyncCloseOutput WebSocketCloseStatus.NormalClosure "Close received by client" socket - 2102                return StreamClosed(socket.CloseStatus.Value, socket.CloseStatusDescription)103            | result ->104                // printfn "result.MessageType -> %A" result.MessageType - 24105                if result.MessageType <> messageType then return () - 8106                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count) - 8107                if result.EndOfMessage then - 4108                    return Stream writeableStream109                else - 4110                    return! readTillEnd' ()111        } - 12112        return! readTillEnd' ()113    }114115    type ReceiveUTF8Result =116        | String of string117        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string118 - 4119    let receiveMessageAsUTF8 socket = async { - 4120        use stream =  new IO.MemoryStream() - 4121        let! result = receiveMessage defaultBufferSize WebSocketMessageType.Text stream socket122        match result with123        | ReceiveStreamResult.Stream s -> - 2124            return stream |> IO.MemoryStream.ToUTF8String |> String125        | ReceiveStreamResult.StreamClosed(status, reason) -> - 2126            return ReceiveUTF8Result.StreamClosed(status, reason)127    }128129module ThreadSafeWebSocket =130    open System131    open System.Threading132    open System.Net.WebSockets133    open Stream134135    type MessageSendResult = Result<unit, ExceptionDispatchInfo>136    type MessageReceiveResult = Result<WebSocket.ReceiveStreamResult, ExceptionDispatchInfo>137138    type SendMessages =139    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<MessageSendResult>140    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult>141    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult>142143    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<MessageReceiveResult>82        /// **Description**83        ///84        /// Turns a `MemoryStream` into a a UTF8 string85        ///86        /// **Parameters**87        ///88        ///89        /// **Output Type**90        ///   * `string`91        ///92        /// **Exceptions**93        ///94        member stream.ToUTF8String () =95            stream |> System.IO.MemoryStream.ToUTF8String9697module WebSocket =98    open Stream99    open System100    open System.Net.WebSockets101102    /// **Description**103    ///104    /// Same as the `DefaultReceiveBufferSize` and `DefaultClientSendBufferSize` from the internal [WebSocketHelpers]( h105    ///106    /// Current value: 16384107    ///108    /// **Output Type**109    ///   * `int`110    [<Literal>]111    let DefaultBufferSize  : int = 16384 // (16 * 1024)112113    /// **Description**114    ///115    /// Determines if the websocket is open116    ///117    /// **Parameters**118    ///   * `socket` - parameter of type `WebSocket`119    ///120    /// **Output Type**121    ///   * `bool`122    ///123    /// **Exceptions**124    ///125    let isWebsocketOpen (socket : WebSocket) = + 0126        socket.State = WebSocketState.Open127128    /// **Description**129    ///130    /// Receives data from the `System.Net.WebSockets.WebSocket` connection asynchronously.131    ///132    /// **Parameters**133    ///   * `websocket` - parameter of type `WebSocket`134    ///   * `buffer` - parameter of type `ArraySegment<byte>` - References the application buffer that is the storage lo135    ///136    /// **Output Type**137    ///   * `Async<WebSocketReceiveResult>` - An instance of this class represents the result of performing a single Rec138    ///139    /// **Exceptions**140    ///141    let asyncReceive (websocket : WebSocket) (buffer : ArraySegment<byte>)  = + 20142        fun ct ->  websocket.ReceiveAsync(buffer,ct) + 10143        |> Async.AwaitTaskWithCancellation  144145    type ThreadSafeWebSocket =146        { websocket : WebSocket147          sendChannel : MailboxProcessor<SendMessages>148          receiveChannel : MailboxProcessor<ReceiveMessage>149        }150        interface IDisposable with151            member x.Dispose() =152                x.websocket.Dispose()153        member x.State =154            x.websocket.State155        member x.CloseStatus =156            x.websocket.CloseStatus |> Option.ofNullable157        member x.CloseStatusDescription =158            x.websocket.CloseStatusDescription159160    let createFromWebSocket (webSocket : WebSocket) =161        /// handle executing a task in a try/catch and wrapping up the callstack info for later162        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {163            try164                let! result = action165                reply.Reply(Ok result)166            with167            | ex ->168                let dispatch = ExceptionDispatchInfo.Capture ex169                reply.Reply(Error dispatch)170        }171172        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox ->173            let rec loop () = async {174                let! message = inbox.Receive()175                if webSocket |> WebSocket.isWebsocketOpen then176                    match message with177                    | Send (buffer, messageType, stream, replyChannel) ->178                        do! wrap (WebSocket.sendMessage buffer messageType stream webSocket) replyChannel179                        return! loop ()180                    | Close (status, message, replyChannel) ->181                        do! wrap (WebSocket.asyncClose status message webSocket) replyChannel182                    | CloseOutput (status, message, replyChannel) ->183                        do! wrap (WebSocket.asyncCloseOutput status message webSocket) replyChannel184            }185            loop ()186        )187        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox ->188            let rec loop () = async {189                let! (buffer, messageType, stream, replyChannel) = inbox.Receive()190                if webSocket |> WebSocket.isWebsocketOpen then191                    let! result = WebSocket.receiveMessage buffer messageType stream webSocket192                    replyChannel.Reply (Ok result)193                    do! loop ()194            }195            loop ()196        )197        {198            websocket = webSocket199            sendChannel = sendAgent200            receiveChannel = receiveAgent201        }145146    /// **Description**147    ///148    /// Sends data over the `System.Net.WebSockets.WebSocket` connection asynchronously.149    ///150    /// **Parameters**151    ///   * `websocket` - parameter of type `WebSocket`152    ///   * `buffer` - parameter of type `ArraySegment<byte>` - The buffer to be sent over the connection.153    ///   * `messageType` - parameter of type `WebSocketMessageType`- Indicates whether the application is sending a bin154    ///   * `endOfMessage` - parameter of type `bool` - Indicates whether the data in `buffer` is the last part of a mes155    ///156    /// **Output Type**157    ///   * `Async<unit>`158    ///159    /// **Exceptions**160    ///161    let asyncSend (websocket : WebSocket) (buffer : ArraySegment<byte>) (messageType : WebSocketMessageType) (endOfMessa + 16162        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct) + 8163        |> Async.AwaitTaskWithCancellation164165166    /// **Description**167    ///168    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too169    ///170    /// **Parameters**171    ///   * `websocket` - parameter of type `WebSocket`172    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co173    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con174    ///175    /// **Output Type**176    ///   * `Async<unit>`177    ///178    /// **Exceptions**179    ///180    let asyncClose (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  = + 2181        fun ct -> websocket.CloseAsync(closeStatus, statusDescription, ct) + 1182        |> Async.AwaitTaskWithCancellation183184185    /// **Description**186    ///187    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke188    ///189    /// **Parameters**190    ///   * `websocket` - parameter of type `WebSocket`191    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co192    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect193    ///194    /// **Output Type**195    ///   * `Async<unit>`196    ///197    /// **Exceptions**198    ///199    let asyncCloseOutput (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  = + 6200        fun ct -> websocket.CloseOutputAsync(closeStatus, statusDescription, ct) + 3201        |> Async.AwaitTaskWithCancellation  202203    let sendMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream =204        wsts.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, stream, reply))205206    let sendMessageAsUTF8(wsts : ThreadSafeWebSocket) (text : string) = async {207        use stream = IO.MemoryStream.UTF8toMemoryStream text208        return! sendMessage wsts  WebSocket.defaultBufferSize  WebSocketMessageType.Text stream209    }210211    let receiveMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream =212        wsts.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, stream, reply)213214    let receiveMessageAsUTF8 (wsts : ThreadSafeWebSocket) = async {215        use stream = new IO.MemoryStream()216        let! response = receiveMessage wsts WebSocket.defaultBufferSize  WebSocketMessageType.Text stream217        match response with218        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece219        | Ok (WebSocket.ReceiveStreamResult.StreamClosed(status, reason)) ->220            return Ok (WebSocket.ReceiveUTF8Result.StreamClosed(status, reason))221        | Error ex -> return Error ex222223    }224225    let close (wsts : ThreadSafeWebSocket) status message =226        wsts.sendChannel.PostAndAsyncReply(fun reply -> Close(status, message, reply))227228    let closeOutput (wsts : ThreadSafeWebSocket) status message =229        wsts.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(status, message, reply))203204    /// **Description**205    ///206    /// Sends a whole message to the websocket read from the given stream207    ///208    /// **Parameters**209    ///   * `socket` - parameter of type `WebSocket`210    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u211    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b212    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect213    ///214    /// **Output Type**215    ///   * `Async<unit>`216    ///217    /// **Exceptions**218    /// + 4219    let sendMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (readableStream : #IO.S + 4220        let buffer = Array.create (bufferSize) Byte.MinValue221 + 8222        let rec sendMessage' () = async { + 8223            if isWebsocketOpen socket then + 8224                let! read = readableStream.AsyncRead(buffer, 0, buffer.Length) + 8225                if read > 0 then + 4226                    do! asyncSend socket (ArraySegment(buffer |> Array.take read))  messageType false + 4227                    return! sendMessage'()228                else + 8229                    do! (asyncSend socket (ArraySegment(Array.empty))  messageType true)230        } + 12231        do! sendMessage'()232    }233234235    /// **Description**236    ///237    /// Sends a string as UTF8 over a websocket connection.238    ///239    /// **Parameters**240    ///   * `socket` - parameter of type `WebSocket`241    ///   * `text` - parameter of type `string` - The string to send over the websocket.242    ///243    /// **Output Type**244    ///   * `Async<unit>`245    ///246    /// **Exceptions**247    /// + 2248    let sendMessageAsUTF8 (socket : WebSocket) (text : string) = async { + 2249        use stream = IO.MemoryStream.UTF8toMemoryStream text + 4250        do! sendMessage socket DefaultBufferSize WebSocketMessageType.Text stream251    }252253254    /// One of the possible results from reading a whole message from a websocket.255    type ReceiveStreamResult =256        /// Reading from the websocket completed257        | Stream of IO.Stream258        /// The websocket was closed during reading259        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string260261262    /// **Description**263    ///264    /// Reads an entire message from a websocket.265    ///266    /// **Parameters**267    ///   * `socket` - parameter of type `WebSocket`268    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u269    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is receiving a270    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt271    ///272    /// **Output Type**273    ///   * `Async<ReceiveStreamResult>` - One of the possible results from reading a whole message from a websocket274    ///275    /// **Exceptions**276    /// + 6277    let receiveMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (writeableStream : I + 6278        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)279 + 10280        let rec readTillEnd' () = async { + 10281            let! result  = asyncReceive socket buffer282            match result with283            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived284                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription + 2285                do! asyncCloseOutput socket WebSocketCloseStatus.NormalClosure "Close received by client" + 2286                return ReceiveStreamResult.Closed(socket.CloseStatus.Value, socket.CloseStatusDescription)287            | result ->288                // printfn "result.MessageType -> %A" result.MessageType + 24289                if result.MessageType <> messageType then return () + 8290                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count) + 8291                if result.EndOfMessage then + 4292                    return Stream writeableStream293                else + 4294                    return! readTillEnd' ()295        } + 12296        return! readTillEnd' ()297    }298299300    /// One of the possible results from reading a whole message from a websocket.301    type ReceiveUTF8Result =302        /// Reading from the websocket completed.303        | String of string304        /// The websocket was closed during reading.305        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string306307308    /// **Description**309    ///310    /// Reads an entire message as a string.311    ///312    /// **Parameters**313    ///   * `socket` - parameter of type `WebSocket`314    ///315    /// **Output Type**316    ///   * `Async<ReceiveUTF8Result>`317    ///318    /// **Exceptions**319    /// + 4320    let receiveMessageAsUTF8 (socket : WebSocket) = async { + 4321        use stream =  new IO.MemoryStream() + 4322        let! result = receiveMessage socket DefaultBufferSize WebSocketMessageType.Text stream323        match result with324        | ReceiveStreamResult.Stream s -> + 2325            return stream |> IO.MemoryStream.ToUTF8String |> String326        | ReceiveStreamResult.Closed(status, reason) -> + 2327            return ReceiveUTF8Result.Closed(status, reason)328    }329330module ThreadSafeWebSocket =331    open System332    open System.Threading333    open System.Net.WebSockets334    open Stream335336    type SendMessages =337    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<Result<unit, ExceptionDispatchIn338    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>339    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>340341    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<Result<WebSocket.ReceiveStreamRes342343    /// The ThreadSafeWebSocket record allows applications to send and receive data after the WebSocket upgrade has comp344    ///345    /// `There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can b346    type ThreadSafeWebSocket =347        { websocket : WebSocket348          sendChannel : MailboxProcessor<SendMessages>349          receiveChannel : MailboxProcessor<ReceiveMessage>350        }351        interface IDisposable with352            /// Used to clean up unmanaged resources for ASP.NET and self-hosted implementations.353            member x.Dispose() =354                x.websocket.Dispose()355        /// Returns the current state of the WebSocket connection.356        member x.State =357            x.websocket.State358        /// Indicates the reason why the remote endpoint initiated the close handshake.359        member x.CloseStatus =360            x.websocket.CloseStatus |> Option.ofNullable361        ///Allows the remote endpoint to describe the reason why the connection was closed.362        member x.CloseStatusDescription =363            x.websocket.CloseStatusDescription364365366    /// **Description**367    ///368    /// Creates a `ThreadSafeWebSocket` from an existing `WebSocket`.369    ///370    /// **Parameters**371    ///   * `webSocket` - parameter of type `WebSocket`372    ///373    /// **Output Type**374    ///   * `ThreadSafeWebSocket`375    ///376    /// **Exceptions**377    ///378    let createFromWebSocket (webSocket : WebSocket) =379        /// handle executing a task in a try/catch and wrapping up the callstack info for later380        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {381            try382                let! result = action383                reply.Reply(Ok result)384            with385            | ex ->386                let dispatch = ExceptionDispatchInfo.Capture ex387                reply.Reply(Error dispatch)388        }389390        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox ->391            let rec loop () = async {392                let! message = inbox.Receive()393                if webSocket |> WebSocket.isWebsocketOpen then394                    match message with395                    | Send (buffer, messageType, stream, replyChannel) ->396                        do! wrap (WebSocket.sendMessage webSocket buffer messageType stream ) replyChannel397                        return! loop ()398                    | Close (status, message, replyChannel) ->399                        do! wrap (WebSocket.asyncClose webSocket status message) replyChannel400                    | CloseOutput (status, message, replyChannel) ->401                        do! wrap (WebSocket.asyncCloseOutput webSocket status message) replyChannel402            }403            loop ()404        )405        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox ->406            let rec loop () = async {407                let! (buffer, messageType, stream, replyChannel) = inbox.Receive()408                if webSocket |> WebSocket.isWebsocketOpen then409                    do! wrap (WebSocket.receiveMessage webSocket buffer messageType stream) replyChannel410                    return! loop ()411            }412            loop ()413        )414        {415            websocket = webSocket416            sendChannel = sendAgent417            receiveChannel = receiveAgent418        }419420421422423    /// **Description**424    ///425    /// Sends a whole message to the websocket read from the given stream.426    ///427    /// **Parameters**428    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`429    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u430    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b431    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect432    ///433    /// **Output Type**434    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`435    ///436    /// **Exceptions**437    ///438    let sendMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) messageType (readableStream : #IO.Str439        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, readableStream, rep440441442    /// **Description**443    ///444    /// Sends a string as UTF8 over a websocket connection.445    ///446    /// **Parameters**447    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`448    ///   * `text` - parameter of type `string` - The string to send over the websocket.449    ///450    /// **Output Type**451    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`452    ///453    /// **Exceptions**454    ///455    let sendMessageAsUTF8(threadSafeWebSocket : ThreadSafeWebSocket) (text : string) = async {456        use stream = IO.MemoryStream.UTF8toMemoryStream text457        return! sendMessage threadSafeWebSocket  WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream458    }459460461    /// **Description**462    ///463    /// Reads an entire message from a websocket as a string.464    ///465    /// **Parameters**466    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`467    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u468    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b469    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt470    ///471    /// **Output Type**472    ///   * `Async<Result<WebSocket.ReceiveStreamResult,ExceptionDispatchInfo>>` - One of the possible results from read473    ///474    /// **Exceptions**475    ///476    let receiveMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) (messageType : WebSocketMessageTyp477        threadSafeWebSocket.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, writeableStream, repl478479480    /// **Description**481    ///482    /// Reads an entire message as a string.483    ///484    /// **Parameters**485    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`486    ///487    /// **Output Type**488    ///   * `Async<Result<WebSocket.ReceiveUTF8Result,ExceptionDispatchInfo>>`489    ///490    /// **Exceptions**491    ///492    let receiveMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) = async {493        use stream = new IO.MemoryStream()494        let! response = receiveMessage threadSafeWebSocket WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream495        match response with496        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece497        | Ok (WebSocket.Closed(status, reason)) ->498            return Ok (WebSocket.ReceiveUTF8Result.Closed(status, reason))499        | Error ex -> return Error ex500501    }502503504    /// **Description**505    ///506    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too507    ///508    /// **Parameters**509    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`510    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co511    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con512    ///513    /// **Output Type**514    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`515    ///516    /// **Exceptions**517    ///518    let close (threadSafeWebSocket : ThreadSafeWebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription : str519        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Close(closeStatus, statusDescription, reply))520521522    /// **Description**523    ///524    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke525    ///526    /// **Parameters**527    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`528    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co529    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect530    ///531    /// **Output Type**532    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`533    ///534    /// **Exceptions**535    ///536    let closeOutput (threadSafeWebSocket : ThreadSafeWebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription 537        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(closeStatus, statusDescription, reply -

+

Methods/Properties

-asyncReceive(System.ArraySegment`1<System.Byte>,a)
-Invoke(System.Threading.CancellationToken)
-asyncSend(System.ArraySegment`1<System.Byte>,System.Net.WebSockets.WebSocketMessageType,System.Boolean,a)
-Invoke(System.Threading.CancellationToken)
-asyncClose(System.Net.WebSockets.WebSocketCloseStatus,System.String,a)
-Invoke(System.Threading.CancellationToken)
-asyncCloseOutput(System.Net.WebSockets.WebSocketCloseStatus,System.String,a)
-Invoke(System.Threading.CancellationToken)
-isWebsocketOpen(a)
-sendMessage(System.Int32,System.Net.WebSockets.WebSocketMessageType,a,b)
-Invoke(Microsoft.FSharp.Core.Unit)
-sendMessage'@69(System.Net.WebSockets.WebSocketMessageType,a,b,System.Byte[],Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Int32)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-sendMessageAsUTF8(System.String,System.Net.WebSockets.WebSocket)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.IO.MemoryStream)
-receiveMessage(System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,System.Net.WebSockets.WebSocket)
-Invoke(Microsoft.FSharp.Core.Unit)
-readTillEnd'@96(System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,System.Net.WebSockets.WebSocket,System.ArraySegment`1<System.Byte>,Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.Net.WebSockets.WebSocketReceiveResult)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(Microsoft.FSharp.Core.Unit)
-receiveMessageAsUTF8(System.Net.WebSockets.WebSocket)
-Invoke(Microsoft.FSharp.Core.Unit)
-Invoke(System.IO.MemoryStream)
-Invoke(FSharp.Control.Websockets.WebSocket/ReceiveStreamResult)
+isWebsocketOpen(System.Net.WebSockets.WebSocket)
+asyncReceive(System.Net.WebSockets.WebSocket,System.ArraySegment`1<System.Byte>)
+Invoke(System.Threading.CancellationToken)
+asyncSend(System.Net.WebSockets.WebSocket,System.ArraySegment`1<System.Byte>,System.Net.WebSockets.WebSocketMessageType,System.Boolean)
+Invoke(System.Threading.CancellationToken)
+asyncClose(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketCloseStatus,System.String)
+Invoke(System.Threading.CancellationToken)
+asyncCloseOutput(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketCloseStatus,System.String)
+Invoke(System.Threading.CancellationToken)
+sendMessage(System.Net.WebSockets.WebSocket,System.Int32,System.Net.WebSockets.WebSocketMessageType,a)
+Invoke(Microsoft.FSharp.Core.Unit)
+sendMessage'@222(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketMessageType,a,System.Byte[],Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Int32)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+sendMessageAsUTF8(System.Net.WebSockets.WebSocket,System.String)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.IO.MemoryStream)
+receiveMessage(System.Net.WebSockets.WebSocket,System.Int32,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream)
+Invoke(Microsoft.FSharp.Core.Unit)
+readTillEnd'@280(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketMessageType,System.IO.Stream,System.ArraySegment`1<System.Byte>,Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.Net.WebSockets.WebSocketReceiveResult)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(Microsoft.FSharp.Core.Unit)
+receiveMessageAsUTF8(System.Net.WebSockets.WebSocket)
+Invoke(Microsoft.FSharp.Core.Unit)
+Invoke(System.IO.MemoryStream)
+Invoke(FSharp.Control.Websockets.WebSocket/ReceiveStreamResult)

diff --git a/docs/coverage/FSharp.Control.Websockets_Websocket2.htm b/docs/coverage/FSharp.Control.Websockets_Websocket2.htm index 50b32b7..4e3470c 100644 --- a/docs/coverage/FSharp.Control.Websockets_Websocket2.htm +++ b/docs/coverage/FSharp.Control.Websockets_Websocket2.htm @@ -20,7 +20,7 @@

Summary

Covered lines:0 Uncovered lines:34 Coverable lines:34 -Total lines:229 +Total lines:537 Line coverage:0% Branch coverage:0% @@ -72,231 +72,539 @@

 5open System.Runtime.ExceptionServices  6  7type Async =8    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async {9        let! ct = Async.CancellationToken10        return! f ct |> Async.AwaitTask11    }1213    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async {14        let! ct = Async.CancellationToken15        return! f ct |> Async.AwaitTask16    }1718module Stream =19    open System20    type System.IO.MemoryStream with21        static member UTF8toMemoryStream (text : string) =22            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text)2324        static member ToUTF8String (stream : IO.MemoryStream) =25            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream26            stream.ToArray()27            |> Text.Encoding.UTF8.GetString28            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters2930        member stream.ToUTF8String () =31            stream |> System.IO.MemoryStream.ToUTF8String3233module WebSocket =34    open Stream35    open System36    open System.Net.WebSockets3738    /// **Description**39    /// (16 * 1024) = 1638440    /// https://referencesource.microsoft.com/#System/net/System/Net/WebSockets/WebSocketHelpers.cs,285b8b64a4da685141    /// **Output Type**42    ///   * `int`43    [<Literal>]44    let defaultBufferSize  : int = 16384 // (16 * 1024)45 - 046    let asyncReceive  (buffer : ArraySegment<byte>) (websocket : #WebSocket) =47        fun ct ->  websocket.ReceiveAsync(buffer,ct)48        |> Async.AwaitTaskWithCancellation49 - 050    let asyncSend (buffer : ArraySegment<byte>) messageType endOfMessage (websocket : #WebSocket) =51        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct)52        |> Async.AwaitTaskWithCancellation53 - 054    let asyncClose status message (websocket : #WebSocket) =55        fun ct -> websocket.CloseAsync(status,message,ct)56        |> Async.AwaitTaskWithCancellation57 - 058    let asyncCloseOutput status message (websocket : #WebSocket) =59        fun ct -> websocket.CloseOutputAsync(status,message,ct)60        |> Async.AwaitTaskWithCancellation61 - 062    let isWebsocketOpen (socket : #WebSocket) =63        socket.State = WebSocketState.Open64 - 065    /// Sends a whole message to the websocket read from the given stream - 066    let sendMessage bufferSize messageType (readableStream : #IO.Stream) (socket : #WebSocket) = async {67        let buffer = Array.create (bufferSize) Byte.MinValue - 068 - 069        let rec sendMessage' () = async { - 070            if isWebsocketOpen socket then - 071                let! read = - 072                    readableStream.AsyncRead(buffer,0,buffer.Length) - 073                if read > 0 then74                    do! (socket |> asyncSend (ArraySegment(buffer |> Array.take read))  messageType false) - 075                    return! sendMessage'()76                else - 077                    do! (socket |> asyncSend (ArraySegment(Array.empty))  messageType true)78        }79        do! sendMessage'() - 080        }89    /// **Description**10    ///11    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async12    ///13    /// **Parameters**14    ///   * `f` - parameter of type `CancellationToken -> Task`15    ///16    /// **Output Type**17    ///   * `Async<unit>`18    ///19    /// **Exceptions**20    ///21    static member AwaitTaskWithCancellation (f: CancellationToken -> Task) : Async<unit> = async {22        let! ct = Async.CancellationToken23        return! f ct |> Async.AwaitTask24    }252627    /// **Description**28    ///29    /// Turn a function return a task that uses a cancelltionToken into an FSharp Async30    ///31    /// **Parameters**32    ///   * `f` - parameter of type `CancellationToken -> Task<'a>`33    ///34    /// **Output Type**35    ///   * `Async<'a>`36    ///37    /// **Exceptions**38    ///39    static member AwaitTaskWithCancellation (f: CancellationToken -> Task<'a>) : Async<'a> = async {40        let! ct = Async.CancellationToken41        return! f ct |> Async.AwaitTask42    }4344module Stream =45    open System + 046    type System.IO.MemoryStream with4748        /// **Description**49        /// + 050        /// Turns a string into a UTF8 MemoryStream51        ///52        /// **Parameters**53        ///   * `text` - parameter of type `string` + 054        ///55        /// **Output Type**56        ///   * `IO.MemoryStream`57        /// + 058        /// **Exceptions**59        ///60        static member UTF8toMemoryStream (text : string) =61            new IO.MemoryStream(Text.Encoding.UTF8.GetBytes text) + 0626364        /// **Description** + 065        /// + 066        /// Turns a `MemoryStream` into a a UTF8 string67        /// + 068        /// **Parameters** + 069        ///   * `stream` - parameter of type `IO.MemoryStream` + 070        /// + 071        /// **Output Type** + 072        ///   * `string` + 073        ///74        /// **Exceptions** + 075        ///76        static member ToUTF8String (stream : IO.MemoryStream) = + 077            stream.Seek(0L,IO.SeekOrigin.Begin) |> ignore //ensure start of stream78            stream.ToArray()79            |> Text.Encoding.UTF8.GetString + 080            |> fun s -> s.TrimEnd(char 0) // remove null teriminating characters  081 - 082    let sendMessageAsUTF8 text socket = async {83        use stream = IO.MemoryStream.UTF8toMemoryStream text84        do! sendMessage defaultBufferSize WebSocketMessageType.Text stream socket - 085    } - 08687 - 088 - 089    type ReceiveStreamResult = - 090        | Stream of IO.Stream91        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string92 - 093    let receiveMessage bufferSize messageType (writeableStream : IO.Stream) (socket : WebSocket) = async {94        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)95 - 096        let rec readTillEnd' () = async { - 097            let! result  = socket |> asyncReceive buffer - 098            match result with - 099            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived100                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription - 0101                do! asyncCloseOutput WebSocketCloseStatus.NormalClosure "Close received by client" socket102                return StreamClosed(socket.CloseStatus.Value, socket.CloseStatusDescription) - 0103            | result ->104                // printfn "result.MessageType -> %A" result.MessageType105                if result.MessageType <> messageType then return () - 0106                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count) - 0107                if result.EndOfMessage then - 0108                    return Stream writeableStream - 0109                else110                    return! readTillEnd' ()111        }112        return! readTillEnd' ()113    }114115    type ReceiveUTF8Result =116        | String of string117        | StreamClosed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string118119    let receiveMessageAsUTF8 socket = async {120        use stream =  new IO.MemoryStream()121        let! result = receiveMessage defaultBufferSize WebSocketMessageType.Text stream socket122        match result with123        | ReceiveStreamResult.Stream s ->124            return stream |> IO.MemoryStream.ToUTF8String |> String125        | ReceiveStreamResult.StreamClosed(status, reason) ->126            return ReceiveUTF8Result.StreamClosed(status, reason)127    }128129module ThreadSafeWebSocket =130    open System131    open System.Threading132    open System.Net.WebSockets133    open Stream134135    type MessageSendResult = Result<unit, ExceptionDispatchInfo>136    type MessageReceiveResult = Result<WebSocket.ReceiveStreamResult, ExceptionDispatchInfo>137138    type SendMessages =139    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<MessageSendResult>140    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult>141    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<MessageSendResult>142143    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<MessageReceiveResult> + 082        /// **Description**83        ///84        /// Turns a `MemoryStream` into a a UTF8 string + 085        /// + 086        /// **Parameters**87        /// + 088        /// + 089        /// **Output Type** + 090        ///   * `string`91        ///92        /// **Exceptions** + 093        ///94        member stream.ToUTF8String () =95            stream |> System.IO.MemoryStream.ToUTF8String + 096 + 097module WebSocket = + 098    open Stream + 099    open System100    open System.Net.WebSockets + 0101102    /// **Description** + 0103    ///104    /// Same as the `DefaultReceiveBufferSize` and `DefaultClientSendBufferSize` from the internal [WebSocketHelpers]( h105    /// + 0106    /// Current value: 16384 + 0107    /// + 0108    /// **Output Type** + 0109    ///   * `int`110    [<Literal>]111    let DefaultBufferSize  : int = 16384 // (16 * 1024)112113    /// **Description**114    ///115    /// Determines if the websocket is open116    ///117    /// **Parameters**118    ///   * `socket` - parameter of type `WebSocket`119    ///120    /// **Output Type**121    ///   * `bool`122    ///123    /// **Exceptions**124    ///125    let isWebsocketOpen (socket : WebSocket) =126        socket.State = WebSocketState.Open127128    /// **Description**129    ///130    /// Receives data from the `System.Net.WebSockets.WebSocket` connection asynchronously.131    ///132    /// **Parameters**133    ///   * `websocket` - parameter of type `WebSocket`134    ///   * `buffer` - parameter of type `ArraySegment<byte>` - References the application buffer that is the storage lo135    ///136    /// **Output Type**137    ///   * `Async<WebSocketReceiveResult>` - An instance of this class represents the result of performing a single Rec138    ///139    /// **Exceptions**140    ///141    let asyncReceive (websocket : WebSocket) (buffer : ArraySegment<byte>)  =142        fun ct ->  websocket.ReceiveAsync(buffer,ct)143        |> Async.AwaitTaskWithCancellation  144145    type ThreadSafeWebSocket =146        { websocket : WebSocket147          sendChannel : MailboxProcessor<SendMessages>148          receiveChannel : MailboxProcessor<ReceiveMessage>149        }150        interface IDisposable with151            member x.Dispose() =152                x.websocket.Dispose()153        member x.State =154            x.websocket.State155        member x.CloseStatus =156            x.websocket.CloseStatus |> Option.ofNullable157        member x.CloseStatusDescription =158            x.websocket.CloseStatusDescription159160    let createFromWebSocket (webSocket : WebSocket) =161        /// handle executing a task in a try/catch and wrapping up the callstack info for later162        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {163            try164                let! result = action165                reply.Reply(Ok result)166            with167            | ex ->168                let dispatch = ExceptionDispatchInfo.Capture ex169                reply.Reply(Error dispatch)170        }171172        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox ->173            let rec loop () = async {174                let! message = inbox.Receive()175                if webSocket |> WebSocket.isWebsocketOpen then176                    match message with177                    | Send (buffer, messageType, stream, replyChannel) ->178                        do! wrap (WebSocket.sendMessage buffer messageType stream webSocket) replyChannel179                        return! loop ()180                    | Close (status, message, replyChannel) ->181                        do! wrap (WebSocket.asyncClose status message webSocket) replyChannel182                    | CloseOutput (status, message, replyChannel) ->183                        do! wrap (WebSocket.asyncCloseOutput status message webSocket) replyChannel184            }185            loop ()186        )187        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox ->188            let rec loop () = async {189                let! (buffer, messageType, stream, replyChannel) = inbox.Receive()190                if webSocket |> WebSocket.isWebsocketOpen then191                    let! result = WebSocket.receiveMessage buffer messageType stream webSocket192                    replyChannel.Reply (Ok result)193                    do! loop ()194            }195            loop ()196        )197        {198            websocket = webSocket199            sendChannel = sendAgent200            receiveChannel = receiveAgent201        }145146    /// **Description**147    ///148    /// Sends data over the `System.Net.WebSockets.WebSocket` connection asynchronously.149    ///150    /// **Parameters**151    ///   * `websocket` - parameter of type `WebSocket`152    ///   * `buffer` - parameter of type `ArraySegment<byte>` - The buffer to be sent over the connection.153    ///   * `messageType` - parameter of type `WebSocketMessageType`- Indicates whether the application is sending a bin154    ///   * `endOfMessage` - parameter of type `bool` - Indicates whether the data in `buffer` is the last part of a mes155    ///156    /// **Output Type**157    ///   * `Async<unit>`158    ///159    /// **Exceptions**160    ///161    let asyncSend (websocket : WebSocket) (buffer : ArraySegment<byte>) (messageType : WebSocketMessageType) (endOfMessa162        fun ct ->  websocket.SendAsync(buffer, messageType, endOfMessage, ct)163        |> Async.AwaitTaskWithCancellation164165166    /// **Description**167    ///168    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too169    ///170    /// **Parameters**171    ///   * `websocket` - parameter of type `WebSocket`172    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co173    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con174    ///175    /// **Output Type**176    ///   * `Async<unit>`177    ///178    /// **Exceptions**179    ///180    let asyncClose (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  =181        fun ct -> websocket.CloseAsync(closeStatus, statusDescription, ct)182        |> Async.AwaitTaskWithCancellation183184185    /// **Description**186    ///187    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke188    ///189    /// **Parameters**190    ///   * `websocket` - parameter of type `WebSocket`191    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co192    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect193    ///194    /// **Output Type**195    ///   * `Async<unit>`196    ///197    /// **Exceptions**198    ///199    let asyncCloseOutput (websocket : WebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription : string)  =200        fun ct -> websocket.CloseOutputAsync(closeStatus, statusDescription, ct)201        |> Async.AwaitTaskWithCancellation  202203    let sendMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream =204        wsts.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, stream, reply))205206    let sendMessageAsUTF8(wsts : ThreadSafeWebSocket) (text : string) = async {207        use stream = IO.MemoryStream.UTF8toMemoryStream text208        return! sendMessage wsts  WebSocket.defaultBufferSize  WebSocketMessageType.Text stream209    }210211    let receiveMessage (wsts : ThreadSafeWebSocket) bufferSize messageType stream =212        wsts.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, stream, reply)213214    let receiveMessageAsUTF8 (wsts : ThreadSafeWebSocket) = async {215        use stream = new IO.MemoryStream()216        let! response = receiveMessage wsts WebSocket.defaultBufferSize  WebSocketMessageType.Text stream217        match response with218        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece219        | Ok (WebSocket.ReceiveStreamResult.StreamClosed(status, reason)) ->220            return Ok (WebSocket.ReceiveUTF8Result.StreamClosed(status, reason))221        | Error ex -> return Error ex222223    }224225    let close (wsts : ThreadSafeWebSocket) status message =226        wsts.sendChannel.PostAndAsyncReply(fun reply -> Close(status, message, reply))227228    let closeOutput (wsts : ThreadSafeWebSocket) status message =229        wsts.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(status, message, reply))203204    /// **Description**205    ///206    /// Sends a whole message to the websocket read from the given stream207    ///208    /// **Parameters**209    ///   * `socket` - parameter of type `WebSocket`210    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u211    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b212    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect213    ///214    /// **Output Type**215    ///   * `Async<unit>`216    ///217    /// **Exceptions**218    ///219    let sendMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (readableStream : #IO.S220        let buffer = Array.create (bufferSize) Byte.MinValue221222        let rec sendMessage' () = async {223            if isWebsocketOpen socket then224                let! read = readableStream.AsyncRead(buffer, 0, buffer.Length)225                if read > 0 then226                    do! asyncSend socket (ArraySegment(buffer |> Array.take read))  messageType false227                    return! sendMessage'()228                else229                    do! (asyncSend socket (ArraySegment(Array.empty))  messageType true)230        }231        do! sendMessage'()232    }233234235    /// **Description**236    ///237    /// Sends a string as UTF8 over a websocket connection.238    ///239    /// **Parameters**240    ///   * `socket` - parameter of type `WebSocket`241    ///   * `text` - parameter of type `string` - The string to send over the websocket.242    ///243    /// **Output Type**244    ///   * `Async<unit>`245    ///246    /// **Exceptions**247    ///248    let sendMessageAsUTF8 (socket : WebSocket) (text : string) = async {249        use stream = IO.MemoryStream.UTF8toMemoryStream text250        do! sendMessage socket DefaultBufferSize WebSocketMessageType.Text stream251    }252253254    /// One of the possible results from reading a whole message from a websocket.255    type ReceiveStreamResult =256        /// Reading from the websocket completed257        | Stream of IO.Stream258        /// The websocket was closed during reading259        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string260261262    /// **Description**263    ///264    /// Reads an entire message from a websocket.265    ///266    /// **Parameters**267    ///   * `socket` - parameter of type `WebSocket`268    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u269    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is receiving a270    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt271    ///272    /// **Output Type**273    ///   * `Async<ReceiveStreamResult>` - One of the possible results from reading a whole message from a websocket274    ///275    /// **Exceptions**276    ///277    let receiveMessage (socket : WebSocket) (bufferSize : int) (messageType : WebSocketMessageType) (writeableStream : I278        let buffer = new ArraySegment<Byte>( Array.create (bufferSize) Byte.MinValue)279280        let rec readTillEnd' () = async {281            let! result  = asyncReceive socket buffer282            match result with283            | result when result.MessageType = WebSocketMessageType.Close || socket.State = WebSocketState.CloseReceived284                // printfn "Close received! %A - %A" socket.CloseStatus socket.CloseStatusDescription285                do! asyncCloseOutput socket WebSocketCloseStatus.NormalClosure "Close received by client"286                return ReceiveStreamResult.Closed(socket.CloseStatus.Value, socket.CloseStatusDescription)287            | result ->288                // printfn "result.MessageType -> %A" result.MessageType289                if result.MessageType <> messageType then return ()290                do! writeableStream.AsyncWrite(buffer.Array, buffer.Offset, result.Count)291                if result.EndOfMessage then292                    return Stream writeableStream293                else294                    return! readTillEnd' ()295        }296        return! readTillEnd' ()297    }298299300    /// One of the possible results from reading a whole message from a websocket.301    type ReceiveUTF8Result =302        /// Reading from the websocket completed.303        | String of string304        /// The websocket was closed during reading.305        | Closed of closeStatus: WebSocketCloseStatus * closeStatusDescription:string306307308    /// **Description**309    ///310    /// Reads an entire message as a string.311    ///312    /// **Parameters**313    ///   * `socket` - parameter of type `WebSocket`314    ///315    /// **Output Type**316    ///   * `Async<ReceiveUTF8Result>`317    ///318    /// **Exceptions**319    ///320    let receiveMessageAsUTF8 (socket : WebSocket) = async {321        use stream =  new IO.MemoryStream()322        let! result = receiveMessage socket DefaultBufferSize WebSocketMessageType.Text stream323        match result with324        | ReceiveStreamResult.Stream s ->325            return stream |> IO.MemoryStream.ToUTF8String |> String326        | ReceiveStreamResult.Closed(status, reason) ->327            return ReceiveUTF8Result.Closed(status, reason)328    }329330module ThreadSafeWebSocket =331    open System332    open System.Threading333    open System.Net.WebSockets334    open Stream335336    type SendMessages =337    | Send of  bufferSize : int * WebSocketMessageType *  IO.Stream * AsyncReplyChannel<Result<unit, ExceptionDispatchIn338    | Close of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>339    | CloseOutput of  WebSocketCloseStatus * string * AsyncReplyChannel<Result<unit, ExceptionDispatchInfo>>340341    type ReceiveMessage =  int * WebSocketMessageType * IO.Stream  * AsyncReplyChannel<Result<WebSocket.ReceiveStreamRes342343    /// The ThreadSafeWebSocket record allows applications to send and receive data after the WebSocket upgrade has comp344    ///345    /// `There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can b346    type ThreadSafeWebSocket =347        { websocket : WebSocket348          sendChannel : MailboxProcessor<SendMessages>349          receiveChannel : MailboxProcessor<ReceiveMessage>350        }351        interface IDisposable with352            /// Used to clean up unmanaged resources for ASP.NET and self-hosted implementations.353            member x.Dispose() =354                x.websocket.Dispose()355        /// Returns the current state of the WebSocket connection.356        member x.State =357            x.websocket.State358        /// Indicates the reason why the remote endpoint initiated the close handshake.359        member x.CloseStatus =360            x.websocket.CloseStatus |> Option.ofNullable361        ///Allows the remote endpoint to describe the reason why the connection was closed.362        member x.CloseStatusDescription =363            x.websocket.CloseStatusDescription364365366    /// **Description**367    ///368    /// Creates a `ThreadSafeWebSocket` from an existing `WebSocket`.369    ///370    /// **Parameters**371    ///   * `webSocket` - parameter of type `WebSocket`372    ///373    /// **Output Type**374    ///   * `ThreadSafeWebSocket`375    ///376    /// **Exceptions**377    ///378    let createFromWebSocket (webSocket : WebSocket) =379        /// handle executing a task in a try/catch and wrapping up the callstack info for later380        let inline wrap (action: Async<'a>) (reply: AsyncReplyChannel<_>) = async {381            try382                let! result = action383                reply.Reply(Ok result)384            with385            | ex ->386                let dispatch = ExceptionDispatchInfo.Capture ex387                reply.Reply(Error dispatch)388        }389390        let sendAgent = MailboxProcessor<SendMessages>.Start(fun inbox ->391            let rec loop () = async {392                let! message = inbox.Receive()393                if webSocket |> WebSocket.isWebsocketOpen then394                    match message with395                    | Send (buffer, messageType, stream, replyChannel) ->396                        do! wrap (WebSocket.sendMessage webSocket buffer messageType stream ) replyChannel397                        return! loop ()398                    | Close (status, message, replyChannel) ->399                        do! wrap (WebSocket.asyncClose webSocket status message) replyChannel400                    | CloseOutput (status, message, replyChannel) ->401                        do! wrap (WebSocket.asyncCloseOutput webSocket status message) replyChannel402            }403            loop ()404        )405        let receiveAgent = MailboxProcessor<ReceiveMessage>.Start(fun inbox ->406            let rec loop () = async {407                let! (buffer, messageType, stream, replyChannel) = inbox.Receive()408                if webSocket |> WebSocket.isWebsocketOpen then409                    do! wrap (WebSocket.receiveMessage webSocket buffer messageType stream) replyChannel410                    return! loop ()411            }412            loop ()413        )414        {415            websocket = webSocket416            sendChannel = sendAgent417            receiveChannel = receiveAgent418        }419420421422423    /// **Description**424    ///425    /// Sends a whole message to the websocket read from the given stream.426    ///427    /// **Parameters**428    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`429    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the stream at a time.  Recommended to u430    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b431    ///   * `readableStream` - parameter of type `Stream` - A readable stream of data to send over the websocket connect432    ///433    /// **Output Type**434    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`435    ///436    /// **Exceptions**437    ///438    let sendMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) messageType (readableStream : #IO.Str439        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Send(bufferSize, messageType, readableStream, rep440441442    /// **Description**443    ///444    /// Sends a string as UTF8 over a websocket connection.445    ///446    /// **Parameters**447    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`448    ///   * `text` - parameter of type `string` - The string to send over the websocket.449    ///450    /// **Output Type**451    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`452    ///453    /// **Exceptions**454    ///455    let sendMessageAsUTF8(threadSafeWebSocket : ThreadSafeWebSocket) (text : string) = async {456        use stream = IO.MemoryStream.UTF8toMemoryStream text457        return! sendMessage threadSafeWebSocket  WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream458    }459460461    /// **Description**462    ///463    /// Reads an entire message from a websocket as a string.464    ///465    /// **Parameters**466    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`467    ///   * `bufferSize` - parameter of type `int` - How many bytes to read from the socket at a time.  Recommended to u468    ///   * `messageType` - parameter of type `WebSocketMessageType` -  Indicates whether the application is sending a b469    ///   * `writeableStream` - parameter of type `IO.Stream` - A writeable stream that data from the websocket is writt470    ///471    /// **Output Type**472    ///   * `Async<Result<WebSocket.ReceiveStreamResult,ExceptionDispatchInfo>>` - One of the possible results from read473    ///474    /// **Exceptions**475    ///476    let receiveMessage (threadSafeWebSocket : ThreadSafeWebSocket) (bufferSize : int) (messageType : WebSocketMessageTyp477        threadSafeWebSocket.receiveChannel.PostAndAsyncReply(fun reply -> bufferSize, messageType, writeableStream, repl478479480    /// **Description**481    ///482    /// Reads an entire message as a string.483    ///484    /// **Parameters**485    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`486    ///487    /// **Output Type**488    ///   * `Async<Result<WebSocket.ReceiveUTF8Result,ExceptionDispatchInfo>>`489    ///490    /// **Exceptions**491    ///492    let receiveMessageAsUTF8 (threadSafeWebSocket : ThreadSafeWebSocket) = async {493        use stream = new IO.MemoryStream()494        let! response = receiveMessage threadSafeWebSocket WebSocket.DefaultBufferSize  WebSocketMessageType.Text stream495        match response with496        | Ok (WebSocket.ReceiveStreamResult.Stream s) -> return stream |> IO.MemoryStream.ToUTF8String |> WebSocket.Rece497        | Ok (WebSocket.Closed(status, reason)) ->498            return Ok (WebSocket.ReceiveUTF8Result.Closed(status, reason))499        | Error ex -> return Error ex500501    }502503504    /// **Description**505    ///506    /// Closes the WebSocket connection as an asynchronous operation using the close handshake defined in the http://too507    ///508    /// **Parameters**509    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`510    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co511    ///   * `statusDescription` - parameter of type `string` -  Specifies a human readable explanation as to why the con512    ///513    /// **Output Type**514    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`515    ///516    /// **Exceptions**517    ///518    let close (threadSafeWebSocket : ThreadSafeWebSocket)  (closeStatus : WebSocketCloseStatus) (statusDescription : str519        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> Close(closeStatus, statusDescription, reply))520521522    /// **Description**523    ///524    /// Initiates or completes the close handshake defined in the http://tools.ietf.org/html/draft-ietf-hybi-thewebsocke525    ///526    /// **Parameters**527    ///   * `threadSafeWebSocket` - parameter of type `ThreadSafeWebSocket`528    ///   * `closeStatus` - parameter of type `WebSocketCloseStatus` - Indicates the reason for closing the WebSocket co529    ///   * `statusDescription` - parameter of type `string`Specifies a human readable explanation as to why the connect530    ///531    /// **Output Type**532    ///   * `Async<Result<unit,ExceptionDispatchInfo>>`533    ///534    /// **Exceptions**535    ///536    let closeOutput (threadSafeWebSocket : ThreadSafeWebSocket) (closeStatus : WebSocketCloseStatus) (statusDescription 537        threadSafeWebSocket.sendChannel.PostAndAsyncReply(fun reply -> CloseOutput(closeStatus, statusDescription, reply - +

Methods/Properties

diff --git a/docs/coverage/combined.js b/docs/coverage/combined.js index a8c7de9..5b4bd0d 100644 --- a/docs/coverage/combined.js +++ b/docs/coverage/combined.js @@ -325,20 +325,20 @@ var assemblies = [ { "name": "FSharp.Control.Websockets", "classes": [ - { "name": "FSharp.Control.Websockets.Async", "reportPath": "FSharp.Control.Websockets_Async.htm", "coveredLines": 6, "uncoveredLines": 2, "coverableLines": 8, "totalLines": 229, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 0, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, - { "name": "FSharp.Control.Websockets.Stream", "reportPath": "FSharp.Control.Websockets_Stream.htm", "coveredLines": 4, "uncoveredLines": 5, "coverableLines": 9, "totalLines": 229, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 0, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, - { "name": "FSharp.Control.Websockets.ThreadSafeWebsocket", "reportPath": "FSharp.Control.Websockets_ThreadSafeWebsocket.htm", "coveredLines": 0, "uncoveredLines": 49, "coverableLines": 49, "totalLines": 229, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 8, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, - { "name": "FSharp.Control.Websockets.ThreadSafeWebSocket", "reportPath": "FSharp.Control.Websockets_ThreadSafeWebSocket2.htm", "coveredLines": 32, "uncoveredLines": 5, "coverableLines": 37, "totalLines": 229, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 8, "totalBranches": 10, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, - { "name": "FSharp.Control.Websockets.Websocket", "reportPath": "FSharp.Control.Websockets_Websocket2.htm", "coveredLines": 0, "uncoveredLines": 34, "coverableLines": 34, "totalLines": 229, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 8, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, - { "name": "FSharp.Control.Websockets.WebSocket", "reportPath": "FSharp.Control.Websockets_WebSocket.htm", "coveredLines": 39, "uncoveredLines": 1, "coverableLines": 40, "totalLines": 229, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 8, "totalBranches": 12, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, + { "name": "FSharp.Control.Websockets.Async", "reportPath": "FSharp.Control.Websockets_Async.htm", "coveredLines": 6, "uncoveredLines": 6, "coverableLines": 12, "totalLines": 537, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 0, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, + { "name": "FSharp.Control.Websockets.Stream", "reportPath": "FSharp.Control.Websockets_Stream.htm", "coveredLines": 4, "uncoveredLines": 8, "coverableLines": 12, "totalLines": 537, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 0, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, + { "name": "FSharp.Control.Websockets.ThreadSafeWebsocket", "reportPath": "FSharp.Control.Websockets_ThreadSafeWebsocket.htm", "coveredLines": 0, "uncoveredLines": 49, "coverableLines": 49, "totalLines": 537, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 8, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, + { "name": "FSharp.Control.Websockets.ThreadSafeWebSocket", "reportPath": "FSharp.Control.Websockets_ThreadSafeWebSocket2.htm", "coveredLines": 31, "uncoveredLines": 5, "coverableLines": 36, "totalLines": 537, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 8, "totalBranches": 10, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, + { "name": "FSharp.Control.Websockets.Websocket", "reportPath": "FSharp.Control.Websockets_Websocket2.htm", "coveredLines": 0, "uncoveredLines": 34, "coverableLines": 34, "totalLines": 537, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 8, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, + { "name": "FSharp.Control.Websockets.WebSocket", "reportPath": "FSharp.Control.Websockets_WebSocket.htm", "coveredLines": 38, "uncoveredLines": 1, "coverableLines": 39, "totalLines": 537, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 8, "totalBranches": 12, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, { "name": "System.AssemblyVersionInformation", "reportPath": "FSharp.Control.Websockets_AssemblyVersionInformation.htm", "coveredLines": 0, "uncoveredLines": 0, "coverableLines": 0, "totalLines": 0, "coverageType": "MethodCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 0, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, ]}, { "name": "FSharp.Control.Websockets.TPL", "classes": [ - { "name": "FSharp.Control.Websockets.TPL.Stream", "reportPath": "FSharp.Control.Websockets.TPL_Stream.htm", "coveredLines": 4, "uncoveredLines": 2, "coverableLines": 6, "totalLines": 236, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 0, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, - { "name": "FSharp.Control.Websockets.TPL.ThreadSafeWebSocket", "reportPath": "FSharp.Control.Websockets.TPL_ThreadSafeWebSocket.htm", "coveredLines": 50, "uncoveredLines": 5, "coverableLines": 55, "totalLines": 236, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 2, "totalBranches": 2, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, - { "name": "FSharp.Control.Websockets.TPL.WebSocket", "reportPath": "FSharp.Control.Websockets.TPL_WebSocket.htm", "coveredLines": 31, "uncoveredLines": 2, "coverableLines": 33, "totalLines": 236, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 5, "totalBranches": 8, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, + { "name": "FSharp.Control.Websockets.TPL.Stream", "reportPath": "FSharp.Control.Websockets.TPL_Stream.htm", "coveredLines": 4, "uncoveredLines": 2, "coverableLines": 6, "totalLines": 535, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 0, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, + { "name": "FSharp.Control.Websockets.TPL.ThreadSafeWebSocket", "reportPath": "FSharp.Control.Websockets.TPL_ThreadSafeWebSocket.htm", "coveredLines": 50, "uncoveredLines": 5, "coverableLines": 55, "totalLines": 535, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 2, "totalBranches": 2, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, + { "name": "FSharp.Control.Websockets.TPL.WebSocket", "reportPath": "FSharp.Control.Websockets.TPL_WebSocket.htm", "coveredLines": 33, "uncoveredLines": 3, "coverableLines": 36, "totalLines": 535, "coverageType": "LineCoverage", "methodCoverage": "-", "coveredBranches": 5, "totalBranches": 8, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, { "name": "System.AssemblyVersionInformation", "reportPath": "FSharp.Control.Websockets.TPL_AssemblyVersionInformation.htm", "coveredLines": 0, "uncoveredLines": 0, "coverableLines": 0, "totalLines": 0, "coverageType": "MethodCoverage", "methodCoverage": "-", "coveredBranches": 0, "totalBranches": 0, "lineCoverageHistory": [], "branchCoverageHistory": [], "historicCoverages": [] }, ]}, ]; diff --git a/docs/coverage/index.htm b/docs/coverage/index.htm index 875b7ba..aba5369 100644 --- a/docs/coverage/index.htm +++ b/docs/coverage/index.htm @@ -14,16 +14,16 @@

Summary

-Generated on:5/24/19 - 12:07:21 AM +Generated on:5/24/19 - 1:30:52 PM Parser:MultiReportParser (4x OpenCoverParser) Assemblies:2 Classes:11 Files:2 Covered lines:166 -Uncovered lines:105 -Coverable lines:271 -Total lines:465 -Line coverage:61.2% +Uncovered lines:113 +Coverable lines:279 +Total lines:1072 +Line coverage:59.4% Branch coverage:47.9% @@ -48,21 +48,21 @@

Coverage

NameCoveredUncoveredCoverableTotalLine coverageBranch coverage -FSharp.Control.Websockets8196177137445.7%
  
42.1%
  
-FSharp.Control.Websockets.Async62822975%
  
 
-FSharp.Control.Websockets.Stream45922944.4%
  
 
-FSharp.Control.Websockets.ThreadSafeWebsocket049492290%
 
0%
 
-FSharp.Control.Websockets.ThreadSafeWebSocket3253722986.4%
  
80%
  
-FSharp.Control.Websockets.Websocket034342290%
 
0%
 
-FSharp.Control.Websockets.WebSocket3914022997.5%
  
66.6%
  
+FSharp.Control.Websockets79103182322243.4%
  
42.1%
  
+FSharp.Control.Websockets.Async661253750%
  
 
+FSharp.Control.Websockets.Stream481253733.3%
  
 
+FSharp.Control.Websockets.ThreadSafeWebsocket049495370%
 
0%
 
+FSharp.Control.Websockets.ThreadSafeWebSocket3153653786.1%
  
80%
  
+FSharp.Control.Websockets.Websocket034345370%
 
0%
 
+FSharp.Control.Websockets.WebSocket3813953797.4%
  
66.6%
  
System.AssemblyVersionInformation0000
 
 
-FSharp.Control.Websockets.TPL8599470890.4%
  
70%
  
-FSharp.Control.Websockets.TPL.Stream42623666.6%
  
 
-FSharp.Control.Websockets.TPL.ThreadSafeWebSocket5055523690.9%
  
100%
 
-FSharp.Control.Websockets.TPL.WebSocket3123323693.9%
  
62.5%
  
+FSharp.Control.Websockets.TPL871097160589.6%
  
70%
  
+FSharp.Control.Websockets.TPL.Stream42653566.6%
  
 
+FSharp.Control.Websockets.TPL.ThreadSafeWebSocket5055553590.9%
  
100%
 
+FSharp.Control.Websockets.TPL.WebSocket3333653591.6%
  
62.5%
  
System.AssemblyVersionInformation0000
 
 
-
+ \ No newline at end of file diff --git a/src/FSharp.Control.Websockets.TPL/AssemblyInfo.fs b/src/FSharp.Control.Websockets.TPL/AssemblyInfo.fs index 5d8c2b9..a270dc8 100644 --- a/src/FSharp.Control.Websockets.TPL/AssemblyInfo.fs +++ b/src/FSharp.Control.Websockets.TPL/AssemblyInfo.fs @@ -4,20 +4,20 @@ open System.Reflection [] [] -[] -[] -[] -[] +[] +[] +[] +[] [] -[] +[] do () module internal AssemblyVersionInformation = let [] AssemblyTitle = "FSharp.Control.Websockets.TPL" let [] AssemblyProduct = "FSharp.Control.Websockets" - let [] AssemblyVersion = "0.1.2" - let [] AssemblyMetadata_ReleaseDate = "2019-05-23T00:00:00.0000000" - let [] AssemblyFileVersion = "0.1.2" - let [] AssemblyInformationalVersion = "0.1.2" + let [] AssemblyVersion = "0.2.0" + let [] AssemblyMetadata_ReleaseDate = "2019-05-24T00:00:00.0000000" + let [] AssemblyFileVersion = "0.2.0" + let [] AssemblyInformationalVersion = "0.2.0" let [] AssemblyMetadata_ReleaseChannel = "release" - let [] AssemblyMetadata_GitHash = "fb87fe1690e778194917d7778aedc1cea557d8b0" + let [] AssemblyMetadata_GitHash = "a1e88136d97b12a3b87a24207e56abdfcd3d9ad8" diff --git a/src/FSharp.Control.Websockets/AssemblyInfo.fs b/src/FSharp.Control.Websockets/AssemblyInfo.fs index fedfdc3..5295ec9 100644 --- a/src/FSharp.Control.Websockets/AssemblyInfo.fs +++ b/src/FSharp.Control.Websockets/AssemblyInfo.fs @@ -4,20 +4,20 @@ open System.Reflection [] [] -[] -[] -[] -[] +[] +[] +[] +[] [] -[] +[] do () module internal AssemblyVersionInformation = let [] AssemblyTitle = "FSharp.Control.Websockets" let [] AssemblyProduct = "FSharp.Control.Websockets" - let [] AssemblyVersion = "0.1.2" - let [] AssemblyMetadata_ReleaseDate = "2019-05-23T00:00:00.0000000" - let [] AssemblyFileVersion = "0.1.2" - let [] AssemblyInformationalVersion = "0.1.2" + let [] AssemblyVersion = "0.2.0" + let [] AssemblyMetadata_ReleaseDate = "2019-05-24T00:00:00.0000000" + let [] AssemblyFileVersion = "0.2.0" + let [] AssemblyInformationalVersion = "0.2.0" let [] AssemblyMetadata_ReleaseChannel = "release" - let [] AssemblyMetadata_GitHash = "fb87fe1690e778194917d7778aedc1cea557d8b0" + let [] AssemblyMetadata_GitHash = "a1e88136d97b12a3b87a24207e56abdfcd3d9ad8" diff --git a/tests/FSharp.Control.Websockets.TPL.Tests/AssemblyInfo.fs b/tests/FSharp.Control.Websockets.TPL.Tests/AssemblyInfo.fs index 918011e..312819f 100644 --- a/tests/FSharp.Control.Websockets.TPL.Tests/AssemblyInfo.fs +++ b/tests/FSharp.Control.Websockets.TPL.Tests/AssemblyInfo.fs @@ -4,20 +4,20 @@ open System.Reflection [] [] -[] -[] -[] -[] +[] +[] +[] +[] [] -[] +[] do () module internal AssemblyVersionInformation = let [] AssemblyTitle = "FSharp.Control.Websockets.TPL.Tests" let [] AssemblyProduct = "FSharp.Control.Websockets" - let [] AssemblyVersion = "0.1.2" - let [] AssemblyMetadata_ReleaseDate = "2019-05-23T00:00:00.0000000" - let [] AssemblyFileVersion = "0.1.2" - let [] AssemblyInformationalVersion = "0.1.2" + let [] AssemblyVersion = "0.2.0" + let [] AssemblyMetadata_ReleaseDate = "2019-05-24T00:00:00.0000000" + let [] AssemblyFileVersion = "0.2.0" + let [] AssemblyInformationalVersion = "0.2.0" let [] AssemblyMetadata_ReleaseChannel = "release" - let [] AssemblyMetadata_GitHash = "fb87fe1690e778194917d7778aedc1cea557d8b0" + let [] AssemblyMetadata_GitHash = "a1e88136d97b12a3b87a24207e56abdfcd3d9ad8" diff --git a/tests/FSharp.Control.Websockets.Tests/AssemblyInfo.fs b/tests/FSharp.Control.Websockets.Tests/AssemblyInfo.fs index 6a64020..3e96c88 100644 --- a/tests/FSharp.Control.Websockets.Tests/AssemblyInfo.fs +++ b/tests/FSharp.Control.Websockets.Tests/AssemblyInfo.fs @@ -4,20 +4,20 @@ open System.Reflection [] [] -[] -[] -[] -[] +[] +[] +[] +[] [] -[] +[] do () module internal AssemblyVersionInformation = let [] AssemblyTitle = "FSharp.Control.Websockets.Tests" let [] AssemblyProduct = "FSharp.Control.Websockets" - let [] AssemblyVersion = "0.1.2" - let [] AssemblyMetadata_ReleaseDate = "2019-05-23T00:00:00.0000000" - let [] AssemblyFileVersion = "0.1.2" - let [] AssemblyInformationalVersion = "0.1.2" + let [] AssemblyVersion = "0.2.0" + let [] AssemblyMetadata_ReleaseDate = "2019-05-24T00:00:00.0000000" + let [] AssemblyFileVersion = "0.2.0" + let [] AssemblyInformationalVersion = "0.2.0" let [] AssemblyMetadata_ReleaseChannel = "release" - let [] AssemblyMetadata_GitHash = "fb87fe1690e778194917d7778aedc1cea557d8b0" + let [] AssemblyMetadata_GitHash = "a1e88136d97b12a3b87a24207e56abdfcd3d9ad8"