{"id":18602976,"url":"https://github.com/devlooped/websocketpipe","last_synced_at":"2025-10-25T04:42:28.986Z","repository":{"id":40281762,"uuid":"408549469","full_name":"devlooped/WebSocketPipe","owner":"devlooped","description":"System.IO.Pipelines API adapter for System.Net.WebSockets","archived":false,"fork":false,"pushed_at":"2025-10-08T00:21:31.000Z","size":134,"stargazers_count":32,"open_issues_count":5,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-10-08T02:37:14.538Z","etag":null,"topics":["csharp","dotnet","networking","pipelines","websocket","websockets"],"latest_commit_sha":null,"homepage":"https://clarius.org/WebSocketPipe","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devlooped.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"devlooped"}},"created_at":"2021-09-20T18:03:42.000Z","updated_at":"2025-09-08T01:18:11.000Z","dependencies_parsed_at":"2025-02-18T00:23:27.897Z","dependency_job_id":"1579c90f-cf72-4439-99fc-3f2378cc2121","html_url":"https://github.com/devlooped/WebSocketPipe","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/devlooped/WebSocketPipe","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FWebSocketPipe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FWebSocketPipe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FWebSocketPipe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FWebSocketPipe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devlooped","download_url":"https://codeload.github.com/devlooped/WebSocketPipe/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FWebSocketPipe/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280906490,"owners_count":26411412,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","networking","pipelines","websocket","websockets"],"created_at":"2024-11-07T02:13:07.965Z","updated_at":"2025-10-25T04:42:28.980Z","avatar_url":"https://github.com/devlooped.png","language":"C#","funding_links":["https://github.com/sponsors/devlooped","https://github.com/sponsors"],"categories":[],"sub_categories":[],"readme":"![Icon](https://raw.githubusercontent.com/devlooped/WebSocketPipe/main/assets/img/icon.png) WebSocketPipe\n============\n\nHigh-performance System.IO.Pipelines API adapter for System.Net.WebSockets\n\n[![Version](https://img.shields.io/nuget/vpre/WebSocketPipe.svg?color=royalblue)](https://www.nuget.org/packages/WebSocketPipe)\n[![Downloads](https://img.shields.io/nuget/dt/WebSocketPipe.svg?color=green)](https://www.nuget.org/packages/WebSocketPipe)\n[![License](https://img.shields.io/github/license/devlooped/WebSocketPipe.svg?color=blue)](https://github.com/devlooped/WebSocketPipe/blob/main/license.txt)\n[![Build](https://github.com/devlooped/WebSocketPipe/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/devlooped/WebSocketPipe/actions)\n\n\u003c!-- #content --\u003e\n# Usage\n\n```csharp\nusing Devlooped.Net;\n\nvar client = new ClientWebSocket();\nawait client.ConnectAsync(serverUri, CancellationToken.None);\n\nusing IWebSocketPipe pipe = WebSocketPipe.Create(client, closeWhenCompleted: true);\n\n// Start the pipe before hooking up the processing\nvar run = pipe.RunAsync();\n```\n\nThe `IWebSocketPipe` interface extends [IDuplexPipe](https://docs.microsoft.com/en-us/dotnet/api/system.io.pipelines.iduplexpipe?view=dotnet-plat-ext-5.0), \nexposing `Input` and `Output` properties that can be used to \nread incoming messages and write outgoing ones.\n\nFor example, to read incoming data and write it to the console, \nwe could write the following code:\n\n```csharp\nawait ReadIncoming(pipe.Input);\n\nasync Task ReadIncoming(PipeReader reader)\n{\n    while (await reader.ReadAsync() is var result \u0026\u0026 !result.IsCompleted)\n    {\n        Console.WriteLine($\"Received: {Encoding.UTF8.GetString(result.Buffer)}\");\n        reader.AdvanceTo(result.Buffer.End);\n    }\n    Console.WriteLine($\"Done reading.\");\n}\n```\n\nSimilarly, to write to the underlying websocket the input \nentered in the console, we use code like the following: \n\n```csharp\nawait SendOutgoing(pipe.Output);\n\nasync Task SendOutgoing(PipeWriter writer)\n{\n    while (Console.ReadLine() is var line \u0026\u0026 line?.Length \u003e 0)\n    {\n        Encoding.UTF8.GetBytes(line, writer);\n    }\n    await writer.CompleteAsync();\n    Console.WriteLine($\"Done writing.\");\n}\n```\n\nIf we wanted to simultaneously read and write and wait for \ncompletion of both operations, we could just wait for both \ntasks:\n\n```csharp\n// Wait for completion of processing code\nawait Task.WhenAny(\n    ReadIncoming(pipe.Input),\n    SendOutgoing(pipe.Output));\n```\n\nNote that completing the `PipeWriter` automatically causes the \nreader to reveive a completed result and exit the loop. In addition, \nthe overall `IWebSocketPipe.RunAsync` task will also run to completion. \n\n\nThe `IWebSocketPipe` takes care of gracefully closing the connection \nwhen the input or output are completed, if `closeWhenCompleted` is set \nto true when creating it. \n\nAlternatively, it's also possible to complete the entire pipe explicitly, \nwhile setting an optional socket close status and status description for \nthe server to act on:\n\n```csharp\nawait pipe.CompleteAsync(WebSocketCloseStatus.NormalClosure, \"Done processing\");\n```\n\nSpecifying a close status will always close the underlying socket.\n\nThe `WebSocketPipe` can also be used on the server. The following example is basically \ntaken from the documentation on [WebSockets in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-5.0#configure-the-middleware) \nand adapted to use a `WebSocketPipe` to echo messages to the client:\n\n```csharp\napp.Use(async (context, next) =\u003e\n{\n    if (context.Request.Path == \"/ws\")\n    {\n        if (context.WebSockets.IsWebSocketRequest)\n        {\n            using var websocket = await context.WebSockets.AcceptWebSocketAsync();\n            using var pipe = WebSocketPipe.Create(websocket, true);\n            await Task.WhenAll(Echo(pipe), pipe.RunAsync(context.RequestAborted));\n        }\n        else\n        {\n            context.Response.StatusCode = (int) HttpStatusCode.BadRequest;\n        }\n    }\n    else\n    {\n        await next();\n    }\n});\n```\n\nThe sample `Echo` method is simply:\n\n```csharp\nasync Task Echo(IDuplexPipe pipe)\n{\n    while (await pipe.Input.ReadAsync() is var result \u0026\u0026 !result.IsCompleted)\n    {\n        // Just assume we get a single-segment entry, for simplicity\n        await pipe.Output.WriteAsync(result.Buffer.First);\n        pipe.Input.AdvanceTo(result.Buffer.End);\n    }\n}\n```\n\u003c!-- #content --\u003e\n\n# Dogfooding\n\n[![CI Version](https://img.shields.io/endpoint?url=https://shields.kzu.app/vpre/WebSocketPipe/main\u0026label=nuget.ci\u0026color=brightgreen)](https://pkg.kzu.app/index.json)\n[![Build](https://github.com/devlooped/WebSocketPipe/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/devlooped/WebSocketPipe/actions)\n\nWe also produce CI packages from branches and pull requests so you can dogfood builds as quickly as they are produced. \n\nThe CI feed is `https://pkg.kzu.app/index.json`. \n\nThe versioning scheme for packages is:\n\n- PR builds: *42.42.42-pr*`[NUMBER]`\n- Branch builds: *42.42.42-*`[BRANCH]`.`[COMMITS]`\n\n\u003c!-- include https://github.com/devlooped/sponsors/raw/main/footer.md --\u003e\n# Sponsors \n\n\u003c!-- sponsors.md --\u003e\n[![Clarius Org](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/clarius.png \"Clarius Org\")](https://github.com/clarius)\n[![MFB Technologies, Inc.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/MFB-Technologies-Inc.png \"MFB Technologies, Inc.\")](https://github.com/MFB-Technologies-Inc)\n[![Torutek](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/torutek-gh.png \"Torutek\")](https://github.com/torutek-gh)\n[![DRIVE.NET, Inc.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/drivenet.png \"DRIVE.NET, Inc.\")](https://github.com/drivenet)\n[![Keith Pickford](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Keflon.png \"Keith Pickford\")](https://github.com/Keflon)\n[![Thomas Bolon](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/tbolon.png \"Thomas Bolon\")](https://github.com/tbolon)\n[![Kori Francis](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/kfrancis.png \"Kori Francis\")](https://github.com/kfrancis)\n[![Toni Wenzel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/twenzel.png \"Toni Wenzel\")](https://github.com/twenzel)\n[![Uno Platform](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/unoplatform.png \"Uno Platform\")](https://github.com/unoplatform)\n[![Dan Siegel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/dansiegel.png \"Dan Siegel\")](https://github.com/dansiegel)\n[![Reuben Swartz](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/rbnswartz.png \"Reuben Swartz\")](https://github.com/rbnswartz)\n[![Jacob Foshee](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jfoshee.png \"Jacob Foshee\")](https://github.com/jfoshee)\n[![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Mrxx99.png \"\")](https://github.com/Mrxx99)\n[![Eric Johnson](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/eajhnsn1.png \"Eric Johnson\")](https://github.com/eajhnsn1)\n[![Ix Technologies B.V.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/IxTechnologies.png \"Ix Technologies B.V.\")](https://github.com/IxTechnologies)\n[![David JENNI](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/davidjenni.png \"David JENNI\")](https://github.com/davidjenni)\n[![Jonathan ](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Jonathan-Hickey.png \"Jonathan \")](https://github.com/Jonathan-Hickey)\n[![Charley Wu](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/akunzai.png \"Charley Wu\")](https://github.com/akunzai)\n[![Ken Bonny](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/KenBonny.png \"Ken Bonny\")](https://github.com/KenBonny)\n[![Simon Cropp](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/SimonCropp.png \"Simon Cropp\")](https://github.com/SimonCropp)\n[![agileworks-eu](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/agileworks-eu.png \"agileworks-eu\")](https://github.com/agileworks-eu)\n[![sorahex](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/sorahex.png \"sorahex\")](https://github.com/sorahex)\n[![Zheyu Shen](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/arsdragonfly.png \"Zheyu Shen\")](https://github.com/arsdragonfly)\n[![Vezel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/vezel-dev.png \"Vezel\")](https://github.com/vezel-dev)\n[![ChilliCream](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/ChilliCream.png \"ChilliCream\")](https://github.com/ChilliCream)\n[![4OTC](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/4OTC.png \"4OTC\")](https://github.com/4OTC)\n[![Vincent Limo](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/v-limo.png \"Vincent Limo\")](https://github.com/v-limo)\n[![Jordan S. Jones](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jordansjones.png \"Jordan S. Jones\")](https://github.com/jordansjones)\n[![domischell](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/DominicSchell.png \"domischell\")](https://github.com/DominicSchell)\n\n\n\u003c!-- sponsors.md --\u003e\n\n[![Sponsor this project](https://raw.githubusercontent.com/devlooped/sponsors/main/sponsor.png \"Sponsor this project\")](https://github.com/sponsors/devlooped)\n\u0026nbsp;\n\n[Learn more about GitHub Sponsors](https://github.com/sponsors)\n\n\u003c!-- https://github.com/devlooped/sponsors/raw/main/footer.md --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevlooped%2Fwebsocketpipe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevlooped%2Fwebsocketpipe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevlooped%2Fwebsocketpipe/lists"}