├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ └── ci.yaml
├── .gitignore
├── BedrockFramework.sln
├── Directory.Build.props
├── LICENSE
├── README.md
├── docs
├── .gitignore
├── docfx.json
├── index.md
└── toc.yml
├── global.json
├── nuget.config
├── samples
├── Certs
│ └── testcert.pfx
├── ClientApplication
│ ├── ClientApplication.csproj
│ ├── DnsCachingConnectionFactory.cs
│ ├── HubBuilderExtensions.cs
│ └── Program.cs
├── Directory.Build.props
└── ServerApplication
│ ├── Chat.cs
│ ├── EchoServerApplication.cs
│ ├── HttpApplication.cs
│ ├── LengthPrefixedProtocol.cs
│ ├── MqttApplication.cs
│ ├── MyCustomProtocol.cs
│ ├── Program.cs
│ └── ServerApplication.csproj
├── src
├── Bedrock.Framework.Experimental
│ ├── Bedrock.Framework.Experimental.csproj
│ ├── Client
│ │ └── ClientExtensions.cs
│ ├── Infrastructure
│ │ ├── BufferExtensions.cs
│ │ ├── BufferWriter.cs
│ │ ├── SequenceReaderExtensions.cs
│ │ └── TaskCompletionSourceWithCancellation.cs
│ ├── Protocols
│ │ ├── ChunkedHttpBodyReader.cs
│ │ ├── ContentLengthHttpBodyReader.cs
│ │ ├── Http1Header.cs
│ │ ├── Http1HeaderReader.cs
│ │ ├── Http1RequestMessageReader.cs
│ │ ├── Http1RequestMessageWriter.cs
│ │ ├── Http1ResponseMessageReader.cs
│ │ ├── Http1ResponseMessageWriter.cs
│ │ ├── Http2
│ │ │ ├── Bitshifter.cs
│ │ │ ├── FlowControl
│ │ │ │ ├── FlowControl.cs
│ │ │ │ ├── InputFlowControl.cs
│ │ │ │ ├── OutputFlowControl.cs
│ │ │ │ ├── OutputFlowControlAwaitable.cs
│ │ │ │ ├── StreamInputFlowControl.cs
│ │ │ │ └── StreamOutputFlowControl.cs
│ │ │ ├── HPack
│ │ │ │ ├── DynamicTable.cs
│ │ │ │ ├── HPackDecoder.cs
│ │ │ │ ├── HPackDecodingException.cs
│ │ │ │ ├── HPackEncoder.cs
│ │ │ │ ├── HPackEncodingException.cs
│ │ │ │ ├── HeaderField.cs
│ │ │ │ ├── Huffman.cs
│ │ │ │ ├── HuffmanDecodingException.cs
│ │ │ │ ├── IntegerDecoder.cs
│ │ │ │ ├── IntegerEncoder.cs
│ │ │ │ ├── StaticTable.cs
│ │ │ │ └── StatusCodes.cs
│ │ │ ├── Http2ConnectionErrorException.cs
│ │ │ ├── Http2ContinuationFrameFlags.cs
│ │ │ ├── Http2DataFrameFlags.cs
│ │ │ ├── Http2ErrorCode.cs
│ │ │ ├── Http2Frame.Continuation.cs
│ │ │ ├── Http2Frame.Data.cs
│ │ │ ├── Http2Frame.GoAway.cs
│ │ │ ├── Http2Frame.Headers.cs
│ │ │ ├── Http2Frame.Ping.cs
│ │ │ ├── Http2Frame.Priority.cs
│ │ │ ├── Http2Frame.RstStream.cs
│ │ │ ├── Http2Frame.Settings.cs
│ │ │ ├── Http2Frame.WindowUpdate.cs
│ │ │ ├── Http2Frame.cs
│ │ │ ├── Http2FrameReader.cs
│ │ │ ├── Http2FrameType.cs
│ │ │ ├── Http2FrameWriter.cs
│ │ │ ├── Http2HeadersFrameFlags.cs
│ │ │ ├── Http2PeerSetting.cs
│ │ │ ├── Http2PeerSettings.cs
│ │ │ ├── Http2PingFrameFlags.cs
│ │ │ ├── Http2SettingsFrameFlags.cs
│ │ │ ├── Http2SettingsParameter.cs
│ │ │ └── Http2SettingsParameterOutOfRangeException.cs
│ │ ├── Http2ClientProtocol.cs
│ │ ├── Http2Protocol.cs
│ │ ├── Http2ServerProtocol.cs
│ │ ├── HttpBodyContent.cs
│ │ ├── HttpBodyStream.cs
│ │ ├── HttpClientProtocol.cs
│ │ ├── HttpServerProtocol.cs
│ │ ├── HubHandshakeMessageReader.cs
│ │ ├── HubMessageReader.cs
│ │ ├── HubMessageWriter.cs
│ │ ├── HubProtocol.cs
│ │ ├── Memcached
│ │ │ ├── Constants.cs
│ │ │ ├── Enums.cs
│ │ │ ├── Expiration.cs
│ │ │ ├── MemcachedMessageReader.cs
│ │ │ ├── MemcachedMessageWriter.cs
│ │ │ ├── MemcachedProtocol.cs
│ │ │ ├── MemcachedRequest.cs
│ │ │ ├── MemcachedRequestHeader.cs
│ │ │ ├── MemcachedResponse.cs
│ │ │ └── MemcachedResponseHeader.cs
│ │ ├── ParseResult.cs
│ │ ├── RabbitMQ
│ │ │ ├── FrameType.cs
│ │ │ ├── IRabbitMQMessage.cs
│ │ │ ├── Methods
│ │ │ │ ├── ChannelOpen.cs
│ │ │ │ ├── ChannelOpenOk.cs
│ │ │ │ ├── ConnectionOk.cs
│ │ │ │ ├── ConnectionOpen.cs
│ │ │ │ ├── ConnectionOpenOk.cs
│ │ │ │ ├── ConnectionStart.cs
│ │ │ │ ├── ConnectionTune.cs
│ │ │ │ ├── ConnectionTuneOk.cs
│ │ │ │ ├── MethodBase.cs
│ │ │ │ ├── QueueDeclare.cs
│ │ │ │ ├── QueueDeclareOk.cs
│ │ │ │ ├── QueueDelete.cs
│ │ │ │ └── QueueDeleteOk.cs
│ │ │ ├── ProtocolHelper.cs
│ │ │ ├── RabbitMQClientProtocol.cs
│ │ │ ├── RabbitMQMessageFormatter.cs
│ │ │ └── RabbitMQProtocolVersionHeader.cs
│ │ └── WebSocketProtocol.cs
│ └── Transports
│ │ ├── Http2
│ │ ├── Http2ConnectionFactory.cs
│ │ ├── Http2ConnectionListener.cs
│ │ └── Http2ConnectionListenerFactory.cs
│ │ ├── Pipes
│ │ ├── NamedPipeConnectionContext.cs
│ │ ├── NamedPipeConnectionFactory.cs
│ │ ├── NamedPipeConnectionListener.cs
│ │ ├── NamedPipeConnectionListenerFactory.cs
│ │ └── NamedPipeEndPoint.cs
│ │ ├── Pooling
│ │ ├── ConnectionPoolingFactory.cs
│ │ ├── EndPointPool.cs
│ │ ├── HttpConnectionKind.cs
│ │ ├── HttpEndPoint.cs
│ │ ├── IEndPointPool.cs
│ │ ├── IMaxConnectionFeature.cs
│ │ └── PooledConnectionContext.cs
│ │ ├── ServerBuilderExtensions.cs
│ │ └── WebSockets
│ │ ├── WebSocketConnectionFactory.cs
│ │ ├── WebSocketConnectionListener.cs
│ │ └── WebSocketConnectionListenerFactory.cs
└── Bedrock.Framework
│ ├── Bedrock.Framework.csproj
│ ├── Client
│ ├── Client.cs
│ └── ClientBuilder.cs
│ ├── ConnectionContextWithDelegate.cs
│ ├── Hosting
│ ├── ServerHostedService.cs
│ ├── ServerHostedServiceOptions.cs
│ └── ServiceCollectionExtensions.cs
│ ├── Infrastructure
│ ├── DuplexPipe.cs
│ ├── DuplexPipeStream.cs
│ ├── DuplexPipeStreamAdapter.cs
│ ├── EmptyServiceProvder.cs
│ ├── MemoryPoolExtensions.cs
│ ├── TaskExtensions.cs
│ └── TaskToApm.cs
│ ├── InternalsVisibleTo.cs
│ ├── Middleware
│ ├── ConnectionBuilderExtensions.cs
│ ├── ConnectionLimitMiddleware.cs
│ ├── Internal
│ │ └── LoggingStream.cs
│ ├── LoggingConnectionMiddleware.cs
│ └── Tls
│ │ ├── CertificateLoader.cs
│ │ ├── ITlsApplicationProtocolFeature.cs
│ │ ├── ITlsConnectionFeature.cs
│ │ ├── ITlsHandshakeFeature.cs
│ │ ├── RemoteCertificateMode.cs
│ │ ├── SslDuplexPipe.cs
│ │ ├── TlsClientConnectionMiddleware.cs
│ │ ├── TlsConnectionFeature.cs
│ │ ├── TlsOptions.cs
│ │ └── TlsServerConnectionMiddleware.cs
│ ├── Protocols
│ ├── ConsumableArrayBufferWriter.cs
│ ├── IMessageReader.cs
│ ├── IMessageWriter.cs
│ ├── MessagePipeReader.cs
│ ├── Protocol.cs
│ ├── ProtocolReadResult.cs
│ ├── ProtocolReader.cs
│ ├── ProtocolWriter.cs
│ └── WebSockets
│ │ ├── WebSocketFrameException.cs
│ │ ├── WebSocketFrameReader.cs
│ │ ├── WebSocketFrameWriter.cs
│ │ ├── WebSocketHeader.cs
│ │ ├── WebSocketOpcode.cs
│ │ ├── WebSocketPayloadEncoder.cs
│ │ ├── WebSocketPayloadReader.cs
│ │ ├── WebSocketReadFrame.cs
│ │ └── WebSocketWriteFrame.cs
│ ├── Server
│ ├── EndPointBinding.cs
│ ├── LocalHostBinding.cs
│ ├── Server.cs
│ ├── ServerBinding.cs
│ ├── ServerBuilder.cs
│ ├── ServerConnection.cs
│ └── ServiceProviderExtensions.cs
│ └── Transports
│ ├── Memory
│ ├── MemoryEndPoint.cs
│ └── MemoryTransport.cs
│ ├── ServerBuilderExtensions.cs
│ └── Sockets
│ ├── BufferExtensions.cs
│ ├── ServerBuilderExtensions.cs
│ ├── SocketAwaitable.cs
│ ├── SocketConnection.cs
│ ├── SocketConnectionFactory.cs
│ ├── SocketReceiver.cs
│ ├── SocketSender.cs
│ └── SocketsServerBuilder.cs
├── tests
├── Bedrock.Framework.Benchmarks
│ ├── Bedrock.Framework.Benchmarks.csproj
│ ├── BenchmarkConfig.cs
│ ├── HttpHeaderReaderBenchmarks.cs
│ ├── MessagePipeReaderBenchmarks.cs
│ ├── Program.cs
│ └── ProtocolReaderBenchmarks.cs
├── Bedrock.Framework.Tests
│ ├── Bedrock.Framework.Tests.csproj
│ ├── ConnectionPoolingTests.cs
│ ├── ConsumableArrayBufferWriterTests.cs
│ ├── Http1Tests.cs
│ ├── Infrastructure
│ │ └── TestSequenceSegment.cs
│ ├── MessagePipeReaderTests.cs
│ ├── ProtocolTests.cs
│ └── Protocols
│ │ ├── Http1HeaderReaderTests.cs
│ │ ├── WebSocketFrameReaderTests.cs
│ │ ├── WebSocketFrameWriterTests.cs
│ │ └── WebSocketPayloadReaderTests.cs
└── Directory.Build.props
└── version.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.props, *.targets, *.csproj]
4 | indent_style = space
5 | indent_size = 2
6 |
7 | [*.cs]
8 | indent_style = space
9 | indent_size = 4
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Make sh files under the build directory always have LF as line endings
8 | ###############################################################################
9 | *.sh eol=lf
10 |
11 | ###############################################################################
12 | # Set default behavior for command prompt diff.
13 | #
14 | # This is need for earlier builds of msysgit that does not have it on by
15 | # default for csharp files.
16 | # Note: This is only used by command line
17 | ###############################################################################
18 | #*.cs diff=csharp
19 |
20 | ###############################################################################
21 | # Set the merge driver for project and solution files
22 | #
23 | # Merging from the command prompt will add diff markers to the files if there
24 | # are conflicts (Merging from VS is not affected by the settings below, in VS
25 | # the diff markers are never inserted). Diff markers may cause the following
26 | # file extensions to fail to load in VS. An alternative would be to treat
27 | # these files as binary and thus will always conflict and require user
28 | # intervention with every merge. To do so, just uncomment the entries below
29 | ###############################################################################
30 | #*.sln merge=binary
31 | #*.csproj merge=binary
32 | #*.vbproj merge=binary
33 | #*.vcxproj merge=binary
34 | #*.vcproj merge=binary
35 | #*.dbproj merge=binary
36 | #*.fsproj merge=binary
37 | #*.lsproj merge=binary
38 | #*.wixproj merge=binary
39 | #*.modelproj merge=binary
40 | #*.sqlproj merge=binary
41 | #*.wwaproj merge=binary
42 |
43 | ###############################################################################
44 | # behavior for image files
45 | #
46 | # image files are treated as binary by default.
47 | ###############################################################################
48 | #*.jpg binary
49 | #*.png binary
50 | #*.gif binary
51 |
52 | ###############################################################################
53 | # diff behavior for common document formats
54 | #
55 | # Convert binary document formats to text before diffing them. This feature
56 | # is only available from the command line. Turn it on by uncommenting the
57 | # entries below.
58 | ###############################################################################
59 | #*.doc diff=astextplain
60 | #*.DOC diff=astextplain
61 | #*.docx diff=astextplain
62 | #*.DOCX diff=astextplain
63 | #*.dot diff=astextplain
64 | #*.DOT diff=astextplain
65 | #*.pdf diff=astextplain
66 | #*.PDF diff=astextplain
67 | #*.rtf diff=astextplain
68 | #*.RTF diff=astextplain
69 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: windows-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v4
12 | with:
13 | fetch-depth: 0
14 |
15 | - uses: dotnet/nbgv@v0.4.2
16 | with:
17 | setAllVars: true
18 |
19 | - name: Setup .NET Core SDK
20 | uses: actions/setup-dotnet@v4
21 | with:
22 | dotnet-version: |
23 | 8.0.x
24 | 9.0.x
25 |
26 | - name: dotnet build
27 | run: dotnet build BedrockFramework.sln -c Release
28 |
29 | - name: dotnet test
30 | run: dotnet test BedrockFramework.sln -c Release --no-build
31 |
32 | - name: dotnet pack
33 | run: dotnet pack BedrockFramework.sln -c Release --no-build --include-source --include-symbols
34 |
35 | - name: setup nuget
36 | if: github.event_name == 'push' && github.ref == 'refs/heads/main'
37 | uses: NuGet/setup-nuget@v1.0.5
38 | with:
39 | nuget-version: latest
40 |
41 | - name: Set API key
42 | if: github.event_name == 'push' && github.ref == 'refs/heads/main'
43 | run: nuget setapikey ${{ secrets.FEEDZ_TOKEN }} -Config nuget.config -Source https://f.feedz.io/davidfowl/bedrockframework/nuget/index.json
44 |
45 | - name: Set symbols API key
46 | if: github.event_name == 'push' && github.ref == 'refs/heads/main'
47 | run: nuget setapikey ${{ secrets.FEEDZ_TOKEN }} -Config nuget.config -Source https://f.feedz.io/davidfowl/bedrockframework/symbols
48 |
49 | - name: push packages
50 | if: github.event_name == 'push' && github.ref == 'refs/heads/main'
51 | run: dotnet nuget push **/*.nupkg -s https://f.feedz.io/davidfowl/bedrockframework/nuget/index.json -ss https://f.feedz.io/davidfowl/bedrockframework/symbols --skip-duplicate
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Folders
2 | artifacts/
3 | bin/
4 | obj/
5 | .dotnet/
6 | .nuget/
7 | .packages/
8 | .tools/
9 | .vs/
10 | .vscode/
11 | node_modules/
12 | BenchmarkDotNet.Artifacts/
13 | .gradle/
14 | src/SignalR/clients/**/dist/
15 | modules/
16 |
17 | # File extensions
18 | *.aps
19 | *.binlog
20 | *.dll
21 | *.DS_Store
22 | *.exe
23 | *.idb
24 | *.lib
25 | *.log
26 | *.pch
27 | *.pdb
28 | *.pidb
29 | *.psess
30 | *.res
31 | *.snk
32 | *.suo
33 | *.tlog
34 | *.user
35 | *.userprefs
36 | *.vspx
37 |
38 | # Specific files, typically generated by tools
39 | launchSettings.json
40 | msbuild.ProjectImports.zip
41 | StyleCop.Cache
42 | UpgradeLog.htm
43 |
44 | # DocFX site generation (for now)
45 | _site/
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 | $(NoWarn);NU5105
6 | https://github.com/davidfowl/BedrockFramework
7 | https://github.com/davidfowl/BedrockFramework
8 | https://github.com/davidfowl/BedrockFramework/releases
9 | MIT
10 | git
11 | cloud microservice networking sockets websockets tcp
12 |
13 |
14 |
15 | 3.6.146
16 | all
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 David Fowler
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Bedrock Framework
2 |
3 | [](https://f.feedz.io/davidfowl/bedrockframework/packages/Bedrock.Framework/latest/download)
4 |
5 | [](https://gitter.im/BedrockFramework/BedrockFramework?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
6 |
7 | [Project Bedrock](https://github.com/aspnet/AspNetCore/issues/4772) is a set of .NET Core APIs for doing transport agnostic networking. In .NET Core 3.0 we've introduced some new abstractions
8 | as part of [Microsoft.AspNetCore.Connections.Abstractions](https://www.nuget.org/packages/Microsoft.AspNetCore.Connections.Abstractions) for client-server communication.
9 |
10 | See the presentation [here](https://speakerdeck.com/davidfowl/project-bedrock)
11 |
12 | This project is split into 2 packages:
13 | - **Bedrock.Framework** - The core framework, server and client builder APIs, built in middleware and transports (sockets and memory).
14 | - **Bedrock.Framework.Experimental** - A set of protocol and transport implementations that may eventually make their way into core. Some of them are incomplete at this time.
15 |
16 | ## Using CI builds
17 |
18 | To use CI builds add the following nuget feed:
19 |
20 | ```xml
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | ```
30 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | ###############
2 | # folder #
3 | ###############
4 | /**/DROP/
5 | /**/TEMP/
6 | /**/packages/
7 | /**/bin/
8 | /**/obj/
9 | _site
10 |
--------------------------------------------------------------------------------
/docs/docfx.json:
--------------------------------------------------------------------------------
1 | {
2 | "metadata": [
3 | {
4 | "src": [
5 | {
6 | "files": [ "**/**.csproj" ],
7 | "exclude": [ "**/bin/**", "**/obj/**" ],
8 | "src": "../src"
9 | }
10 | ],
11 | "dest": "obj/api"
12 | }
13 | ],
14 | "build": {
15 | "content": [
16 | {
17 | "files": [ "**/*.yml" ],
18 | "src": "obj/api",
19 | "dest": "api"
20 | },
21 | {
22 | "files": [
23 | "*.md",
24 | "toc.yml"
25 | ]
26 | }
27 | ],
28 | "resource": [
29 | {
30 | "files": [
31 | "images/**"
32 | ]
33 | }
34 | ],
35 | "globalMetadata": {
36 | "_appTitle": "Bedrock Framework docs",
37 | "_enableSearch": true
38 | },
39 | "dest": "_site",
40 | "xrefService": [ "https://xref.docs.microsoft.com/query?uid={uid}" ],
41 | "template": [
42 | "default"
43 | ]
44 | }
45 | }
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Bedrock Framework
2 |
3 | Project Bedrock is a set of .NET Core APIs for doing transport agnostic networking. In .NET Core 3.0 we've introduced some new abstractions as part of Microsoft.AspNetCore.Connections.Abstractions for client-server communication.
4 |
5 | Check out the full [API documentation available online](/api/Bedrock.Framework.html).
--------------------------------------------------------------------------------
/docs/toc.yml:
--------------------------------------------------------------------------------
1 | - name: Api Documentation
2 | href: api/
3 |
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "9.0.100"
4 | }
5 | }
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/samples/Certs/testcert.pfx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidfowl/BedrockFramework/58e81e930a5d7a26095336dc91dfb935aca021f2/samples/Certs/testcert.pfx
--------------------------------------------------------------------------------
/samples/ClientApplication/ClientApplication.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/samples/ClientApplication/HubBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net;
4 | using System.Text;
5 | using Bedrock.Framework;
6 | using Microsoft.AspNetCore.Connections;
7 | using Microsoft.AspNetCore.SignalR.Client;
8 | using Microsoft.Extensions.DependencyInjection;
9 |
10 | namespace Microsoft.AspNetCore.SignalR.Client
11 | {
12 | public static class HubBuilderExtensions
13 | {
14 | public static IHubConnectionBuilder WithConnectionFactory(this IHubConnectionBuilder hubConnectionBuilder, IConnectionFactory connectionFactory, EndPoint endPoint)
15 | {
16 | hubConnectionBuilder.Services.AddSingleton(connectionFactory);
17 | hubConnectionBuilder.Services.AddSingleton(endPoint);
18 | return hubConnectionBuilder;
19 | }
20 |
21 | public static IHubConnectionBuilder WithClientBuilder(this IHubConnectionBuilder hubConnectionBuilder, EndPoint endPoint, Action configure)
22 | {
23 | hubConnectionBuilder.Services.AddSingleton(sp =>
24 | {
25 | var builder = new ClientBuilder(sp);
26 | configure(builder);
27 | return builder.Build();
28 | });
29 |
30 | hubConnectionBuilder.Services.AddSingleton(endPoint);
31 | return hubConnectionBuilder;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/samples/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 |
6 |
--------------------------------------------------------------------------------
/samples/ServerApplication/Chat.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.SignalR;
6 |
7 | namespace ServerApplication
8 | {
9 | public class Chat : Hub
10 | {
11 | public Task Send(string message)
12 | {
13 | return Clients.All.SendAsync("Send", message);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/samples/ServerApplication/EchoServerApplication.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Bedrock.Framework.Middleware.Tls;
6 | using Microsoft.AspNetCore.Connections;
7 | using Microsoft.Extensions.Logging;
8 |
9 | namespace Bedrock.Framework
10 | {
11 | public class EchoServerApplication : ConnectionHandler
12 | {
13 | private readonly ILogger _logger;
14 |
15 | public EchoServerApplication(ILogger logger)
16 | {
17 | _logger = logger;
18 | }
19 |
20 | public override async Task OnConnectedAsync(ConnectionContext connection)
21 | {
22 | try
23 | {
24 | _logger.LogInformation("{ConnectionId} connected", connection.ConnectionId);
25 |
26 | var handshake = connection.Features.Get();
27 |
28 | if (handshake != null)
29 | {
30 | _logger.LogInformation("TLS enabled, TLS Verson={TLSVersion}, HashAlgorithm={HashAlgorithm}", handshake.Protocol, handshake.HashAlgorithm);
31 | }
32 |
33 | await connection.Transport.Input.CopyToAsync(connection.Transport.Output);
34 | }
35 | finally
36 | {
37 | _logger.LogInformation("{ConnectionId} disconnected", connection.ConnectionId);
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/samples/ServerApplication/HttpApplication.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net;
4 | using System.Net.Http;
5 | using System.Threading.Tasks;
6 | using Bedrock.Framework.Protocols;
7 | using Microsoft.AspNetCore.Connections;
8 |
9 | namespace ServerApplication
10 | {
11 | public class HttpApplication : ConnectionHandler
12 | {
13 | public override async Task OnConnectedAsync(ConnectionContext connection)
14 | {
15 | var httpConnection = new HttpServerProtocol(connection);
16 |
17 | while (true)
18 | {
19 | var request = await httpConnection.ReadRequestAsync();
20 |
21 | Console.WriteLine(request);
22 |
23 | // Consume the request body
24 | await request.Content.CopyToAsync(Stream.Null);
25 |
26 | await httpConnection.WriteResponseAsync(new HttpResponseMessage(HttpStatusCode.OK)
27 | {
28 | Content = new StringContent("Hello World")
29 | });
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/samples/ServerApplication/LengthPrefixedProtocol.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using Bedrock.Framework.Protocols;
5 |
6 | namespace Protocols
7 | {
8 | public class LengthPrefixedProtocol : IMessageReader, IMessageWriter
9 | {
10 | public bool TryParseMessage(in ReadOnlySequence input, ref SequencePosition consumed, ref SequencePosition examined, out Message message)
11 | {
12 | var reader = new SequenceReader(input);
13 | if (!reader.TryReadBigEndian(out int length) || reader.Remaining < length)
14 | {
15 | message = default;
16 | return false;
17 | }
18 |
19 | var payload = input.Slice(reader.Position, length);
20 | message = new Message(payload);
21 |
22 | consumed = payload.End;
23 | examined = consumed;
24 | return true;
25 | }
26 |
27 | public void WriteMessage(Message message, IBufferWriter output)
28 | {
29 | var lengthBuffer = output.GetSpan(4);
30 | BinaryPrimitives.WriteInt32BigEndian(lengthBuffer, (int)message.Payload.Length);
31 | output.Advance(4);
32 | foreach (var memory in message.Payload)
33 | {
34 | output.Write(memory.Span);
35 | }
36 | }
37 | }
38 |
39 | public struct Message
40 | {
41 | public Message(byte[] payload) : this(new ReadOnlySequence(payload))
42 | {
43 |
44 | }
45 |
46 | public Message(ReadOnlySequence payload)
47 | {
48 | Payload = payload;
49 | }
50 |
51 | public ReadOnlySequence Payload { get; }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/samples/ServerApplication/MqttApplication.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Connections;
3 | using MQTTnet.Adapter;
4 | using MQTTnet.AspNetCore;
5 | using MQTTnet.Packets;
6 | using MQTTnet.Protocol;
7 |
8 | namespace ServerApplication
9 | {
10 | public class MqttApplication : ConnectionHandler
11 | {
12 | private MqttConnectionHandler _mqttHandler = new();
13 |
14 | public MqttApplication()
15 | {
16 | _mqttHandler.ClientHandler = OnClientConnectedAsync;
17 | }
18 |
19 | public override async Task OnConnectedAsync(ConnectionContext connection)
20 | {
21 | await _mqttHandler.OnConnectedAsync(connection);
22 | }
23 |
24 | private async Task OnClientConnectedAsync(IMqttChannelAdapter adapter)
25 | {
26 | while (true)
27 | {
28 | var packet = await adapter.ReceivePacketAsync(default);
29 |
30 | switch (packet)
31 | {
32 | case MqttConnectPacket connectPacket:
33 | await adapter.SendPacketAsync(new MqttConnAckPacket
34 | {
35 | ReturnCode = MqttConnectReturnCode.ConnectionAccepted,
36 | ReasonCode = MqttConnectReasonCode.Success,
37 | IsSessionPresent = false
38 | },
39 | default);
40 | break;
41 | case MqttDisconnectPacket disconnectPacket:
42 | break;
43 | case MqttAuthPacket mqttAuthPacket:
44 | break;
45 | case MqttConnAckPacket connAckPacket:
46 | break;
47 | case MqttPublishPacket mqttPublishPacket:
48 | break;
49 | case MqttSubscribePacket mqttSubscribePacket:
50 | var ack = new MqttSubAckPacket
51 | {
52 | PacketIdentifier = mqttSubscribePacket.PacketIdentifier
53 | };
54 | ack.ReasonCodes.Add(MqttSubscribeReasonCode.GrantedQoS0);
55 |
56 | await adapter.SendPacketAsync(ack, default);
57 | break;
58 | default:
59 | break;
60 | }
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/samples/ServerApplication/MyCustomProtocol.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Bedrock.Framework.Protocols;
3 | using Microsoft.AspNetCore.Connections;
4 | using Microsoft.Extensions.Logging;
5 | using Protocols;
6 |
7 | namespace ServerApplication
8 | {
9 | public class MyCustomProtocol : ConnectionHandler
10 | {
11 | private readonly ILogger _logger;
12 |
13 | public MyCustomProtocol(ILogger logger)
14 | {
15 | _logger = logger;
16 | }
17 |
18 | public override async Task OnConnectedAsync(ConnectionContext connection)
19 | {
20 | // Use a length prefixed protocol
21 | var protocol = new LengthPrefixedProtocol();
22 | var reader = connection.CreateReader();
23 | var writer = connection.CreateWriter();
24 |
25 | while (true)
26 | {
27 | try
28 | {
29 | var result = await reader.ReadAsync(protocol);
30 | var message = result.Message;
31 |
32 | _logger.LogInformation("Received a message of {Length} bytes", message.Payload.Length);
33 |
34 | if (result.IsCompleted)
35 | {
36 | break;
37 | }
38 |
39 | await writer.WriteAsync(protocol, message);
40 | }
41 | finally
42 | {
43 | reader.Advance();
44 | }
45 | }
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/samples/ServerApplication/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Security.Cryptography.X509Certificates;
3 | using Bedrock.Framework;
4 | using Microsoft.AspNetCore.Connections;
5 | using Microsoft.AspNetCore.SignalR;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Microsoft.Extensions.Hosting;
8 | using Microsoft.Extensions.Logging;
9 | using ServerApplication;
10 |
11 | var builder = Host.CreateApplicationBuilder();
12 |
13 | builder.Logging.SetMinimumLevel(LogLevel.Debug);
14 |
15 | builder.Services.AddSignalR();
16 |
17 | builder.ConfigureServer(server =>
18 | {
19 | server.UseSockets(sockets =>
20 | {
21 | // Echo server
22 | sockets.ListenLocalhost(5000,
23 | builder => builder.UseConnectionLogging().UseConnectionHandler());
24 |
25 | // HTTP/1.1 server
26 | sockets.Listen(IPAddress.Loopback, 5001,
27 | builder => builder.UseConnectionLogging().UseConnectionHandler());
28 |
29 | // SignalR Hub
30 | sockets.Listen(IPAddress.Loopback, 5002,
31 | builder => builder.UseConnectionLogging().UseHub());
32 |
33 | // MQTT application
34 | sockets.Listen(IPAddress.Loopback, 5003,
35 | builder => builder.UseConnectionLogging().UseConnectionHandler());
36 |
37 | // Echo Server with TLS
38 | sockets.Listen(IPAddress.Loopback, 5004,
39 | builder => builder.UseServerTls(options =>
40 | {
41 | options.LocalCertificate = X509CertificateLoader.LoadPkcs12FromFile("testcert.pfx", "testcert");
42 |
43 | // NOTE: Do not do this in a production environment
44 | options.AllowAnyRemoteCertificate();
45 | })
46 | .UseConnectionLogging().UseConnectionHandler());
47 |
48 | sockets.Listen(IPAddress.Loopback, 5005,
49 | builder => builder.UseConnectionLogging().UseConnectionHandler());
50 | });
51 | });
52 |
53 | var host = builder.Build();
54 |
55 | host.Run();
--------------------------------------------------------------------------------
/samples/ServerApplication/ServerApplication.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Bedrock.Framework.Experimental.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0;net9.0
5 | true
6 | Experimental protocols and transports for Bedrock.Framework.
7 | David Fowler
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Client/ClientExtensions.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Bedrock.Framework
3 | {
4 | public static class ClientExtensions
5 | {
6 | public static ClientBuilder UseConnectionPooling(this ClientBuilder builder)
7 | {
8 | // TODO right now, this must be called after UseSockets
9 | builder.Use(factory => new ConnectionPoolingFactory(factory));
10 | return builder;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Infrastructure/BufferWriter.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Buffers;
6 | using System.Runtime.CompilerServices;
7 |
8 | namespace Bedrock.Framework.Infrastructure
9 | {
10 | public ref struct BufferWriter where T : IBufferWriter
11 | {
12 | private T _output;
13 | private Span _span;
14 | private int _buffered;
15 |
16 | public BufferWriter(T output)
17 | {
18 | _buffered = 0;
19 | _output = output;
20 | _span = output.GetSpan();
21 | }
22 |
23 | public Span Span => _span;
24 |
25 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
26 | public void Commit()
27 | {
28 | var buffered = _buffered;
29 | if (buffered > 0)
30 | {
31 | _buffered = 0;
32 | _output.Advance(buffered);
33 | }
34 | }
35 |
36 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
37 | public void Advance(int count)
38 | {
39 | _buffered += count;
40 | _span = _span.Slice(count);
41 | }
42 |
43 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
44 | public void Write(ReadOnlySpan source)
45 | {
46 | if (_span.Length >= source.Length)
47 | {
48 | source.CopyTo(_span);
49 | Advance(source.Length);
50 | }
51 | else
52 | {
53 | WriteMultiBuffer(source);
54 | }
55 | }
56 |
57 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
58 | public void Ensure(int count = 1)
59 | {
60 | if (_span.Length < count)
61 | {
62 | EnsureMore(count);
63 | }
64 | }
65 |
66 | [MethodImpl(MethodImplOptions.NoInlining)]
67 | private void EnsureMore(int count = 0)
68 | {
69 | if (_buffered > 0)
70 | {
71 | Commit();
72 | }
73 |
74 | _span = _output.GetSpan(count);
75 | }
76 |
77 | private void WriteMultiBuffer(ReadOnlySpan source)
78 | {
79 | while (source.Length > 0)
80 | {
81 | if (_span.Length == 0)
82 | {
83 | EnsureMore();
84 | }
85 |
86 | var writable = Math.Min(source.Length, _span.Length);
87 | source.Slice(0, writable).CopyTo(_span);
88 | source = source.Slice(writable);
89 | Advance(writable);
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Infrastructure/SequenceReaderExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace System.Buffers
2 | {
3 | internal static class SequenceReaderExtensions
4 | {
5 | public static bool TryReadTo(this ref SequenceReader sequenceReader, out ReadOnlySpan span, ReadOnlySpan delimiter, bool advancePastDelimiter = true) where T : unmanaged, IEquatable
6 | {
7 | if (sequenceReader.TryReadTo(out ReadOnlySequence sequence, delimiter, advancePastDelimiter))
8 | {
9 | span = sequence.IsSingleSegment ? sequence.FirstSpan : sequence.ToArray();
10 | return true;
11 | }
12 | span = default;
13 | return false;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Infrastructure/TaskCompletionSourceWithCancellation.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace Bedrock.Framework.Infrastructure
5 | {
6 | internal class TaskCompletionSourceWithCancellation : TaskCompletionSource
7 | {
8 | private CancellationToken _cancellationToken;
9 |
10 | public TaskCompletionSourceWithCancellation() : base(TaskCreationOptions.RunContinuationsAsynchronously)
11 | {
12 | }
13 |
14 | private void OnCancellation()
15 | {
16 | TrySetCanceled(_cancellationToken);
17 | }
18 |
19 | public async ValueTask WaitWithCancellationAsync(CancellationToken cancellationToken)
20 | {
21 | _cancellationToken = cancellationToken;
22 | using (cancellationToken.UnsafeRegister(s => ((TaskCompletionSourceWithCancellation)s).OnCancellation(), this))
23 | {
24 | return await Task.ConfigureAwait(false);
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/ContentLengthHttpBodyReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Bedrock.Framework.Protocols
7 | {
8 | public class ContentLengthHttpBodyReader : IMessageReader>
9 | {
10 | private long _remaining;
11 |
12 | public ContentLengthHttpBodyReader(long contentLength)
13 | {
14 | _remaining = contentLength;
15 | }
16 |
17 | public bool TryParseMessage(in ReadOnlySequence input, ref SequencePosition consumed, ref SequencePosition examined, out ReadOnlySequence message)
18 | {
19 | if (_remaining == 0)
20 | {
21 | message = default;
22 | return true;
23 | }
24 |
25 | var read = Math.Min(_remaining, input.Length);
26 |
27 | _remaining -= read;
28 |
29 | message = input.Slice(0, read);
30 | consumed = message.End;
31 | examined = consumed;
32 |
33 | return message.Length > 0;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http1Header.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Bedrock.Framework.Protocols.Http.Http1
4 | {
5 | public readonly struct Http1Header
6 | {
7 | private readonly ReadOnlyMemory _name;
8 | private readonly ReadOnlyMemory _value;
9 |
10 | public Http1Header(ReadOnlyMemory name, ReadOnlyMemory value)
11 | {
12 | _name = name;
13 | _value = value;
14 | }
15 |
16 | public ReadOnlySpan Name => _name.Span;
17 | public ReadOnlySpan Value => _value.Span;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http1RequestMessageWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Diagnostics;
4 | using System.Net.Http;
5 | using System.Runtime.InteropServices;
6 | using System.Text;
7 | using Bedrock.Framework.Infrastructure;
8 |
9 | namespace Bedrock.Framework.Protocols
10 | {
11 | public class Http1RequestMessageWriter : IMessageWriter
12 | {
13 | private ReadOnlySpan Http11 => new byte[] { (byte)'H', (byte)'T', (byte)'T', (byte)'P', (byte)'/', (byte)'1', (byte)'.', (byte)'1' };
14 | private ReadOnlySpan NewLine => new byte[] { (byte)'\r', (byte)'\n' };
15 | private ReadOnlySpan Space => new byte[] { (byte)' ' };
16 | private ReadOnlySpan Colon => new byte[] { (byte)':' };
17 | private ReadOnlySpan Host => new byte[] { (byte)'H', (byte)'o', (byte)'s', (byte)'t' };
18 |
19 | private readonly byte[] _hostHeaderValueBytes;
20 |
21 | public Http1RequestMessageWriter(string host, int port)
22 | {
23 | // Precalculate ASCII bytes for Host header
24 | string hostHeader = $"{host}:{port}";
25 | _hostHeaderValueBytes = Encoding.ASCII.GetBytes(hostHeader);
26 | }
27 |
28 | public void WriteMessage(HttpRequestMessage message, IBufferWriter output)
29 | {
30 | Debug.Assert(message.Method != null);
31 | Debug.Assert(message.RequestUri != null);
32 |
33 | var writer = new BufferWriter>(output);
34 | writer.WriteAsciiNoValidation(message.Method.Method);
35 | writer.Write(Space);
36 | // REVIEW: This isn't right
37 | writer.WriteAsciiNoValidation(message.RequestUri.ToString());
38 | writer.Write(Space);
39 | writer.Write(Http11);
40 | writer.Write(NewLine);
41 |
42 | if (message.Headers == null || message.Headers.Host == null)
43 | {
44 | writer.Write(Host);
45 | writer.Write(Colon);
46 | writer.Write(Space);
47 | writer.Write(_hostHeaderValueBytes);
48 | writer.Write(NewLine);
49 | }
50 |
51 | foreach (var header in message.Headers)
52 | {
53 | foreach (var value in header.Value)
54 | {
55 | writer.WriteAsciiNoValidation(header.Key);
56 | writer.Write(Colon);
57 | writer.Write(Space);
58 | writer.WriteAsciiNoValidation(value);
59 | writer.Write(NewLine);
60 | }
61 | }
62 |
63 | if (message.Content != null)
64 | {
65 | foreach (var header in message.Content.Headers)
66 | {
67 | foreach (var value in header.Value)
68 | {
69 | writer.WriteAsciiNoValidation(header.Key);
70 | writer.Write(Colon);
71 | writer.Write(Space);
72 | writer.WriteAsciiNoValidation(value);
73 | writer.Write(NewLine);
74 | }
75 | }
76 | }
77 |
78 | writer.Write(NewLine);
79 | writer.Commit();
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http1ResponseMessageWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Net.Http;
4 | using System.Runtime.InteropServices;
5 | using Bedrock.Framework.Infrastructure;
6 |
7 | namespace Bedrock.Framework.Protocols
8 | {
9 | public class Http1ResponseMessageWriter : IMessageWriter
10 | {
11 | private ReadOnlySpan NewLine => new byte[] { (byte)'\r', (byte)'\n' };
12 | private ReadOnlySpan Space => new byte[] { (byte)' ' };
13 |
14 | public void WriteMessage(HttpResponseMessage message, IBufferWriter output)
15 | {
16 | var writer = new BufferWriter>(output);
17 | writer.WriteAsciiNoValidation("HTTP/1.1");
18 | writer.Write(Space);
19 | writer.WriteNumeric((uint)message.StatusCode);
20 | writer.Write(Space);
21 | writer.WriteAsciiNoValidation(message.StatusCode.ToString());
22 | writer.Write(NewLine);
23 |
24 | var colon = (byte)':';
25 |
26 | foreach (var header in message.Headers)
27 | {
28 | foreach (var value in header.Value)
29 | {
30 | writer.WriteAsciiNoValidation(header.Key);
31 | writer.Write(MemoryMarshal.CreateReadOnlySpan(ref colon, 1));
32 | writer.Write(Space);
33 | writer.WriteAsciiNoValidation(value);
34 | writer.Write(NewLine);
35 | }
36 | }
37 |
38 | if (message.Content != null)
39 | {
40 | // Access the property to compute the content length (if there is any)
41 | _ = message.Content.Headers.ContentLength;
42 |
43 | foreach (var header in message.Content.Headers)
44 | {
45 | foreach (var value in header.Value)
46 | {
47 | writer.WriteAsciiNoValidation(header.Key);
48 | writer.Write(MemoryMarshal.CreateReadOnlySpan(ref colon, 1));
49 | writer.Write(Space);
50 | writer.WriteAsciiNoValidation(value);
51 | writer.Write(NewLine);
52 | }
53 | }
54 | }
55 |
56 | writer.Write(NewLine);
57 | writer.Commit();
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Bitshifter.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Buffers.Binary;
6 | using System.Diagnostics;
7 | using System.Runtime.CompilerServices;
8 |
9 | namespace Bedrock.Framework.Protocols.Http2
10 | {
11 | // Mimics BinaryPrimitives with oddly sized units
12 | internal static class Bitshifter
13 | {
14 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
15 | public static uint ReadUInt24BigEndian(ReadOnlySpan source)
16 | {
17 | return (uint)((source[0] << 16) | (source[1] << 8) | source[2]);
18 | }
19 |
20 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
21 | public static void WriteUInt24BigEndian(Span destination, uint value)
22 | {
23 | Debug.Assert(value <= 0xFF_FF_FF, value.ToString());
24 | destination[0] = (byte)((value & 0xFF_00_00) >> 16);
25 | destination[1] = (byte)((value & 0x00_FF_00) >> 8);
26 | destination[2] = (byte)(value & 0x00_00_FF);
27 | }
28 |
29 | // Drops the highest order bit
30 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
31 | public static uint ReadUInt31BigEndian(ReadOnlySpan source)
32 | {
33 | return BinaryPrimitives.ReadUInt32BigEndian(source) & 0x7F_FF_FF_FF;
34 | }
35 |
36 | // Does not overwrite the highest order bit
37 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
38 | public static void WriteUInt31BigEndian(Span destination, uint value)
39 | => WriteUInt31BigEndian(destination, value, true);
40 |
41 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
42 | public static void WriteUInt31BigEndian(Span destination, uint value, bool preserveHighestBit)
43 | {
44 | Debug.Assert(value <= 0x7F_FF_FF_FF, value.ToString());
45 |
46 | if (preserveHighestBit)
47 | {
48 | // Keep the highest bit
49 | value |= (destination[0] & 0x80u) << 24;
50 | }
51 |
52 | BinaryPrimitives.WriteUInt32BigEndian(destination, value);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/FlowControl/FlowControl.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System.Diagnostics;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2.FlowControl
7 | {
8 | internal struct FlowControl
9 | {
10 | public FlowControl(uint initialWindowSize)
11 | {
12 | Debug.Assert(initialWindowSize <= Http2PeerSettings.MaxWindowSize, $"{nameof(initialWindowSize)} too large.");
13 |
14 | Available = (int)initialWindowSize;
15 | IsAborted = false;
16 | }
17 |
18 | public int Available { get; private set; }
19 | public bool IsAborted { get; private set; }
20 |
21 | public void Advance(int bytes)
22 | {
23 | Debug.Assert(!IsAborted, $"({nameof(Advance)} called after abort.");
24 | Debug.Assert(bytes == 0 || (bytes > 0 && bytes <= Available), $"{nameof(Advance)}({bytes}) called with {Available} bytes available.");
25 |
26 | Available -= bytes;
27 | }
28 |
29 | // bytes can be negative when SETTINGS_INITIAL_WINDOW_SIZE decreases mid-connection.
30 | // This can also cause Available to become negative which MUST be allowed.
31 | // https://httpwg.org/specs/rfc7540.html#rfc.section.6.9.2
32 | public bool TryUpdateWindow(int bytes)
33 | {
34 | var maxUpdate = Http2PeerSettings.MaxWindowSize - Available;
35 |
36 | if (bytes > maxUpdate)
37 | {
38 | return false;
39 | }
40 |
41 | Available += bytes;
42 |
43 | return true;
44 | }
45 |
46 | public void Abort()
47 | {
48 | IsAborted = true;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/FlowControl/OutputFlowControl.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 |
7 | namespace Bedrock.Framework.Protocols.Http2.FlowControl
8 | {
9 | internal class OutputFlowControl
10 | {
11 | private FlowControl _flow;
12 | private Queue _awaitableQueue;
13 |
14 | public OutputFlowControl(uint initialWindowSize)
15 | {
16 | _flow = new FlowControl(initialWindowSize);
17 | }
18 |
19 | public int Available => _flow.Available;
20 | public bool IsAborted => _flow.IsAborted;
21 |
22 | public OutputFlowControlAwaitable AvailabilityAwaitable
23 | {
24 | get
25 | {
26 | Debug.Assert(!_flow.IsAborted, $"({nameof(AvailabilityAwaitable)} accessed after abort.");
27 | Debug.Assert(_flow.Available <= 0, $"({nameof(AvailabilityAwaitable)} accessed with {Available} bytes available.");
28 |
29 | if (_awaitableQueue == null)
30 | {
31 | _awaitableQueue = new Queue();
32 | }
33 |
34 | var awaitable = new OutputFlowControlAwaitable();
35 | _awaitableQueue.Enqueue(awaitable);
36 | return awaitable;
37 | }
38 | }
39 |
40 | public void Advance(int bytes)
41 | {
42 | _flow.Advance(bytes);
43 | }
44 |
45 | // bytes can be negative when SETTINGS_INITIAL_WINDOW_SIZE decreases mid-connection.
46 | // This can also cause Available to become negative which MUST be allowed.
47 | // https://httpwg.org/specs/rfc7540.html#rfc.section.6.9.2
48 | public bool TryUpdateWindow(int bytes)
49 | {
50 | if (_flow.TryUpdateWindow(bytes))
51 | {
52 | while (_flow.Available > 0 && _awaitableQueue?.Count > 0)
53 | {
54 | _awaitableQueue.Dequeue().Complete();
55 | }
56 |
57 | return true;
58 | }
59 |
60 | return false;
61 | }
62 |
63 | public void Abort()
64 | {
65 | // Make sure to set the aborted flag before running any continuations.
66 | _flow.Abort();
67 |
68 | while (_awaitableQueue?.Count > 0)
69 | {
70 | _awaitableQueue.Dequeue().Complete();
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/FlowControl/OutputFlowControlAwaitable.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Diagnostics;
6 | using System.Runtime.CompilerServices;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace Bedrock.Framework.Protocols.Http2.FlowControl
11 | {
12 | internal class OutputFlowControlAwaitable : ICriticalNotifyCompletion
13 | {
14 | private static readonly Action _callbackCompleted = () => { };
15 |
16 | private Action _callback;
17 |
18 | public OutputFlowControlAwaitable GetAwaiter() => this;
19 | public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);
20 |
21 | public void GetResult()
22 | {
23 | Debug.Assert(ReferenceEquals(_callback, _callbackCompleted));
24 |
25 | _callback = null;
26 | }
27 |
28 | public void OnCompleted(Action continuation)
29 | {
30 | if (ReferenceEquals(_callback, _callbackCompleted) ||
31 | ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
32 | {
33 | Task.Run(continuation);
34 | }
35 | }
36 |
37 | public void UnsafeOnCompleted(Action continuation)
38 | {
39 | OnCompleted(continuation);
40 | }
41 |
42 | public void Complete()
43 | {
44 | var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);
45 |
46 | continuation?.Invoke();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/HPack/HPackDecodingException.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2.HPack
7 | {
8 | internal sealed class HPackDecodingException : Exception
9 | {
10 | public HPackDecodingException(string message)
11 | : base(message)
12 | {
13 | }
14 | public HPackDecodingException(string message, Exception innerException)
15 | : base(message, innerException)
16 | {
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/HPack/HPackEncodingException.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2.HPack
7 | {
8 | internal sealed class HPackEncodingException : Exception
9 | {
10 | public HPackEncodingException(string message)
11 | : base(message)
12 | {
13 | }
14 | public HPackEncodingException(string message, Exception innerException)
15 | : base(message, innerException)
16 | {
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/HPack/HeaderField.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2.HPack
7 | {
8 | internal readonly struct HeaderField
9 | {
10 | // http://httpwg.org/specs/rfc7541.html#rfc.section.4.1
11 | public const int RfcOverhead = 32;
12 |
13 | public HeaderField(Span name, Span value)
14 | {
15 | Name = new byte[name.Length];
16 | name.CopyTo(Name);
17 |
18 | Value = new byte[value.Length];
19 | value.CopyTo(Value);
20 | }
21 |
22 | public byte[] Name { get; }
23 |
24 | public byte[] Value { get; }
25 |
26 | public int Length => GetLength(Name.Length, Value.Length);
27 |
28 | public static int GetLength(int nameLength, int valueLength) => nameLength + valueLength + 32;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/HPack/HuffmanDecodingException.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2.HPack
7 | {
8 | internal sealed class HuffmanDecodingException : Exception
9 | {
10 | public HuffmanDecodingException(string message)
11 | : base(message)
12 | {
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/HPack/IntegerDecoder.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2.HPack
5 | {
6 | ///
7 | /// The maximum we will decode is Int32.MaxValue, which is also the maximum request header field size.
8 | ///
9 | internal class IntegerDecoder
10 | {
11 | private int _i;
12 | private int _m;
13 |
14 | ///
15 | /// Callers must ensure higher bits above the prefix are cleared before calling this method.
16 | ///
17 | ///
18 | ///
19 | ///
20 | ///
21 | public bool BeginTryDecode(byte b, int prefixLength, out int result)
22 | {
23 | if (b < ((1 << prefixLength) - 1))
24 | {
25 | result = b;
26 | return true;
27 | }
28 |
29 | _i = b;
30 | _m = 0;
31 | result = 0;
32 | return false;
33 | }
34 |
35 | public bool TryDecode(byte b, out int result)
36 | {
37 | var m = _m; // Enregister
38 | var i = _i + ((b & 0x7f) << m); // Enregister
39 |
40 | if ((b & 0x80) == 0)
41 | {
42 | // Int32.MaxValue only needs a maximum of 5 bytes to represent and the last byte cannot have any value set larger than 0x7
43 | if ((m > 21 && b > 0x7) || i < 0)
44 | {
45 | ThrowIntegerTooBigException();
46 | }
47 |
48 | result = i;
49 | return true;
50 | }
51 | else if (m > 21)
52 | {
53 | // Int32.MaxValue only needs a maximum of 5 bytes to represent
54 | ThrowIntegerTooBigException();
55 | }
56 |
57 | _m = m + 7;
58 | _i = i;
59 |
60 | result = 0;
61 | return false;
62 | }
63 |
64 | public static void ThrowIntegerTooBigException()
65 | => throw new HPackDecodingException("CoreStrings.HPackErrorIntegerTooBig");
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/HPack/IntegerEncoder.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Diagnostics;
6 |
7 | namespace Bedrock.Framework.Protocols.Http2.HPack
8 | {
9 | internal static class IntegerEncoder
10 | {
11 | public static bool Encode(int i, int n, Span buffer, out int length)
12 | {
13 | Debug.Assert(i >= 0);
14 | Debug.Assert(n >= 1 && n <= 8);
15 |
16 | var j = 0;
17 | length = 0;
18 |
19 | if (buffer.Length == 0)
20 | {
21 | return false;
22 | }
23 |
24 | if (i < (1 << n) - 1)
25 | {
26 | buffer[j] &= MaskHigh(8 - n);
27 | buffer[j++] |= (byte)i;
28 | }
29 | else
30 | {
31 | buffer[j] &= MaskHigh(8 - n);
32 | buffer[j++] |= (byte)((1 << n) - 1);
33 |
34 | if (j == buffer.Length)
35 | {
36 | return false;
37 | }
38 |
39 | i -= ((1 << n) - 1);
40 | while (i >= 128)
41 | {
42 | var ui = (uint)i; // Use unsigned for optimizations
43 | buffer[j++] = (byte)((ui % 128) + 128);
44 |
45 | if (j >= buffer.Length)
46 | {
47 | return false;
48 | }
49 |
50 | i = (int)(ui / 128); // Jit converts unsigned divide by power-of-2 constant to clean shift
51 | }
52 | buffer[j++] = (byte)i;
53 | }
54 |
55 | length = j;
56 | return true;
57 | }
58 |
59 | private static byte MaskHigh(int n)
60 | {
61 | return (byte)(sbyte.MinValue >> (n - 1));
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2ConnectionErrorException.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2
7 | {
8 | internal sealed class Http2ConnectionErrorException : Exception
9 | {
10 | public Http2ConnectionErrorException(string message, Http2ErrorCode errorCode)
11 | : base($"HTTP/2 connection error ({errorCode}): {message}")
12 | {
13 | ErrorCode = errorCode;
14 | }
15 |
16 | public Http2ErrorCode ErrorCode { get; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2ContinuationFrameFlags.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2
7 | {
8 | [Flags]
9 | internal enum Http2ContinuationFrameFlags : byte
10 | {
11 | NONE = 0x0,
12 | END_HEADERS = 0x4,
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2DataFrameFlags.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2
7 | {
8 | [Flags]
9 | internal enum Http2DataFrameFlags : byte
10 | {
11 | NONE = 0x0,
12 | END_STREAM = 0x1,
13 | PADDED = 0x8
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2ErrorCode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 |
5 | namespace Bedrock.Framework.Protocols.Http2
6 | {
7 | internal enum Http2ErrorCode : uint
8 | {
9 | NO_ERROR = 0x0,
10 | PROTOCOL_ERROR = 0x1,
11 | INTERNAL_ERROR = 0x2,
12 | FLOW_CONTROL_ERROR = 0x3,
13 | SETTINGS_TIMEOUT = 0x4,
14 | STREAM_CLOSED = 0x5,
15 | FRAME_SIZE_ERROR = 0x6,
16 | REFUSED_STREAM = 0x7,
17 | CANCEL = 0x8,
18 | COMPRESSION_ERROR = 0x9,
19 | CONNECT_ERROR = 0xa,
20 | ENHANCE_YOUR_CALM = 0xb,
21 | INADEQUATE_SECURITY = 0xc,
22 | HTTP_1_1_REQUIRED = 0xd,
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2Frame.Continuation.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | /* https://tools.ietf.org/html/rfc7540#section-6.10
7 | +---------------------------------------------------------------+
8 | | Header Block Fragment (*) ...
9 | +---------------------------------------------------------------+
10 | */
11 | internal partial class Http2Frame
12 | {
13 | public Http2ContinuationFrameFlags ContinuationFlags
14 | {
15 | get => (Http2ContinuationFrameFlags)Flags;
16 | set => Flags = (byte)value;
17 | }
18 |
19 | public bool ContinuationEndHeaders => (ContinuationFlags & Http2ContinuationFrameFlags.END_HEADERS) == Http2ContinuationFrameFlags.END_HEADERS;
20 |
21 | public void PrepareContinuation(Http2ContinuationFrameFlags flags, int streamId)
22 | {
23 | PayloadLength = 0;
24 | Type = Http2FrameType.CONTINUATION;
25 | ContinuationFlags = flags;
26 | StreamId = streamId;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2Frame.Data.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | /*
7 | +---------------+
8 | |Pad Length? (8)|
9 | +---------------+-----------------------------------------------+
10 | | Data (*) ...
11 | +---------------------------------------------------------------+
12 | | Padding (*) ...
13 | +---------------------------------------------------------------+
14 | */
15 | internal partial class Http2Frame
16 | {
17 | public Http2DataFrameFlags DataFlags
18 | {
19 | get => (Http2DataFrameFlags)Flags;
20 | set => Flags = (byte)value;
21 | }
22 |
23 | public bool DataEndStream => (DataFlags & Http2DataFrameFlags.END_STREAM) == Http2DataFrameFlags.END_STREAM;
24 |
25 | public bool DataHasPadding => (DataFlags & Http2DataFrameFlags.PADDED) == Http2DataFrameFlags.PADDED;
26 |
27 | public byte DataPadLength { get; set; }
28 |
29 | private int DataPayloadOffset => DataHasPadding ? 1 : 0;
30 |
31 | public int DataPayloadLength => PayloadLength - DataPayloadOffset - DataPadLength;
32 |
33 | public void PrepareData(int streamId, byte? padLength = null)
34 | {
35 | PayloadLength = 0;
36 | Type = Http2FrameType.DATA;
37 | DataFlags = padLength.HasValue ? Http2DataFrameFlags.PADDED : Http2DataFrameFlags.NONE;
38 | StreamId = streamId;
39 | DataPadLength = padLength ?? 0;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2Frame.GoAway.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | /* https://tools.ietf.org/html/rfc7540#section-6.8
7 | +-+-------------------------------------------------------------+
8 | |R| Last-Stream-ID (31) |
9 | +-+-------------------------------------------------------------+
10 | | Error Code (32) |
11 | +---------------------------------------------------------------+
12 | | Additional Debug Data (*) |
13 | +---------------------------------------------------------------+
14 | */
15 | internal partial class Http2Frame
16 | {
17 | public int GoAwayLastStreamId { get; set; }
18 |
19 | public Http2ErrorCode GoAwayErrorCode { get; set; }
20 |
21 | public void PrepareGoAway(int lastStreamId, Http2ErrorCode errorCode)
22 | {
23 | PayloadLength = 8;
24 | Type = Http2FrameType.GOAWAY;
25 | Flags = 0;
26 | StreamId = 0;
27 | GoAwayLastStreamId = lastStreamId;
28 | GoAwayErrorCode = errorCode;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2Frame.Headers.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | /* https://tools.ietf.org/html/rfc7540#section-6.2
7 | +---------------+
8 | |Pad Length? (8)|
9 | +-+-------------+-----------------------------------------------+
10 | |E| Stream Dependency? (31) |
11 | +-+-------------+-----------------------------------------------+
12 | | Weight? (8) |
13 | +-+-------------+-----------------------------------------------+
14 | | Header Block Fragment (*) ...
15 | +---------------------------------------------------------------+
16 | | Padding (*) ...
17 | +---------------------------------------------------------------+
18 | */
19 | internal partial class Http2Frame
20 | {
21 | public Http2HeadersFrameFlags HeadersFlags
22 | {
23 | get => (Http2HeadersFrameFlags)Flags;
24 | set => Flags = (byte)value;
25 | }
26 |
27 | public bool HeadersEndHeaders => (HeadersFlags & Http2HeadersFrameFlags.END_HEADERS) == Http2HeadersFrameFlags.END_HEADERS;
28 |
29 | public bool HeadersEndStream => (HeadersFlags & Http2HeadersFrameFlags.END_STREAM) == Http2HeadersFrameFlags.END_STREAM;
30 |
31 | public bool HeadersHasPadding => (HeadersFlags & Http2HeadersFrameFlags.PADDED) == Http2HeadersFrameFlags.PADDED;
32 |
33 | public bool HeadersHasPriority => (HeadersFlags & Http2HeadersFrameFlags.PRIORITY) == Http2HeadersFrameFlags.PRIORITY;
34 |
35 | public byte HeadersPadLength { get; set; }
36 |
37 | public int HeadersStreamDependency { get; set; }
38 |
39 | public byte HeadersPriorityWeight { get; set; }
40 |
41 | private int HeadersPayloadOffset => (HeadersHasPadding ? 1 : 0) + (HeadersHasPriority ? 5 : 0);
42 |
43 | public int HeadersPayloadLength => PayloadLength - HeadersPayloadOffset - HeadersPadLength;
44 |
45 | public void PrepareHeaders(Http2HeadersFrameFlags flags, int streamId)
46 | {
47 | PayloadLength = 0;
48 | Type = Http2FrameType.HEADERS;
49 | HeadersFlags = flags;
50 | StreamId = streamId;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2Frame.Ping.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | /* https://tools.ietf.org/html/rfc7540#section-6.7
7 | +---------------------------------------------------------------+
8 | | |
9 | | Opaque Data (64) |
10 | | |
11 | +---------------------------------------------------------------+
12 | */
13 | internal partial class Http2Frame
14 | {
15 | public Http2PingFrameFlags PingFlags
16 | {
17 | get => (Http2PingFrameFlags)Flags;
18 | set => Flags = (byte)value;
19 | }
20 |
21 | public bool PingAck => (PingFlags & Http2PingFrameFlags.ACK) == Http2PingFrameFlags.ACK;
22 |
23 | public void PreparePing(Http2PingFrameFlags flags)
24 | {
25 | PayloadLength = 8;
26 | Type = Http2FrameType.PING;
27 | PingFlags = flags;
28 | StreamId = 0;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2Frame.Priority.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | /* https://tools.ietf.org/html/rfc7540#section-6.3
7 | +-+-------------------------------------------------------------+
8 | |E| Stream Dependency (31) |
9 | +-+-------------+-----------------------------------------------+
10 | | Weight (8) |
11 | +-+-------------+
12 | */
13 | internal partial class Http2Frame
14 | {
15 | public int PriorityStreamDependency { get; set; }
16 |
17 | public bool PriorityIsExclusive { get; set; }
18 |
19 | public byte PriorityWeight { get; set; }
20 |
21 | public void PreparePriority(int streamId, int streamDependency, bool exclusive, byte weight)
22 | {
23 | PayloadLength = 5;
24 | Type = Http2FrameType.PRIORITY;
25 | StreamId = streamId;
26 | PriorityStreamDependency = streamDependency;
27 | PriorityIsExclusive = exclusive;
28 | PriorityWeight = weight;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2Frame.RstStream.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | /* https://tools.ietf.org/html/rfc7540#section-6.4
7 | +---------------------------------------------------------------+
8 | | Error Code (32) |
9 | +---------------------------------------------------------------+
10 | */
11 | internal partial class Http2Frame
12 | {
13 | public Http2ErrorCode RstStreamErrorCode { get; set; }
14 |
15 | public void PrepareRstStream(int streamId, Http2ErrorCode errorCode)
16 | {
17 | PayloadLength = 4;
18 | Type = Http2FrameType.RST_STREAM;
19 | Flags = 0;
20 | StreamId = streamId;
21 | RstStreamErrorCode = errorCode;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2Frame.Settings.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | /* https://tools.ietf.org/html/rfc7540#section-6.5.1
7 | List of:
8 | +-------------------------------+
9 | | Identifier (16) |
10 | +-------------------------------+-------------------------------+
11 | | Value (32) |
12 | +---------------------------------------------------------------+
13 | */
14 | internal partial class Http2Frame
15 | {
16 | public Http2SettingsFrameFlags SettingsFlags
17 | {
18 | get => (Http2SettingsFrameFlags)Flags;
19 | set => Flags = (byte)value;
20 | }
21 |
22 | public bool SettingsAck => (SettingsFlags & Http2SettingsFrameFlags.ACK) == Http2SettingsFrameFlags.ACK;
23 |
24 | public void PrepareSettings(Http2SettingsFrameFlags flags)
25 | {
26 | PayloadLength = 0;
27 | Type = Http2FrameType.SETTINGS;
28 | SettingsFlags = flags;
29 | StreamId = 0;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2Frame.WindowUpdate.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | /* https://tools.ietf.org/html/rfc7540#section-6.9
7 | +-+-------------------------------------------------------------+
8 | |R| Window Size Increment (31) |
9 | +-+-------------------------------------------------------------+
10 | */
11 | internal partial class Http2Frame
12 | {
13 | public int WindowUpdateSizeIncrement { get; set; }
14 |
15 | public void PrepareWindowUpdate(int streamId, int sizeIncrement)
16 | {
17 | PayloadLength = 4;
18 | Type = Http2FrameType.WINDOW_UPDATE;
19 | Flags = 0;
20 | StreamId = streamId;
21 | WindowUpdateSizeIncrement = sizeIncrement;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2Frame.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | /* https://tools.ietf.org/html/rfc7540#section-4.1
7 | +-----------------------------------------------+
8 | | Length (24) |
9 | +---------------+---------------+---------------+
10 | | Type (8) | Flags (8) |
11 | +-+-------------+---------------+-------------------------------+
12 | |R| Stream Identifier (31) |
13 | +=+=============================================================+
14 | | Frame Payload (0...) ...
15 | +---------------------------------------------------------------+
16 | */
17 | internal partial class Http2Frame
18 | {
19 | public int PayloadLength { get; set; }
20 |
21 | public Http2FrameType Type { get; set; }
22 |
23 | public byte Flags { get; set; }
24 |
25 | public int StreamId { get; set; }
26 |
27 | internal object ShowFlags()
28 | {
29 | switch (Type)
30 | {
31 | case Http2FrameType.CONTINUATION:
32 | return ContinuationFlags;
33 | case Http2FrameType.DATA:
34 | return DataFlags;
35 | case Http2FrameType.HEADERS:
36 | return HeadersFlags;
37 | case Http2FrameType.SETTINGS:
38 | return SettingsFlags;
39 | case Http2FrameType.PING:
40 | return PingFlags;
41 |
42 | // Not Implemented
43 | case Http2FrameType.PUSH_PROMISE:
44 |
45 | // No flags defined
46 | case Http2FrameType.PRIORITY:
47 | case Http2FrameType.RST_STREAM:
48 | case Http2FrameType.GOAWAY:
49 | case Http2FrameType.WINDOW_UPDATE:
50 | default:
51 | return $"0x{Flags:x}";
52 | }
53 | }
54 |
55 | public override string ToString()
56 | {
57 | return $"{Type} Stream: {StreamId} Length: {PayloadLength} Flags: {ShowFlags()}";
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2FrameType.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | internal enum Http2FrameType : byte
7 | {
8 | DATA = 0x0,
9 | HEADERS = 0x1,
10 | PRIORITY = 0x2,
11 | RST_STREAM = 0x3,
12 | SETTINGS = 0x4,
13 | PUSH_PROMISE = 0x5,
14 | PING = 0x6,
15 | GOAWAY = 0x7,
16 | WINDOW_UPDATE = 0x8,
17 | CONTINUATION = 0x9
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2HeadersFrameFlags.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2
7 | {
8 | [Flags]
9 | internal enum Http2HeadersFrameFlags : byte
10 | {
11 | NONE = 0x0,
12 | END_STREAM = 0x1,
13 | END_HEADERS = 0x4,
14 | PADDED = 0x8,
15 | PRIORITY = 0x20
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2PeerSetting.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | internal readonly struct Http2PeerSetting
7 | {
8 | public Http2PeerSetting(Http2SettingsParameter parameter, uint value)
9 | {
10 | Parameter = parameter;
11 | Value = value;
12 | }
13 |
14 | public Http2SettingsParameter Parameter { get; }
15 |
16 | public uint Value { get; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2PingFrameFlags.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2
7 | {
8 | [Flags]
9 | internal enum Http2PingFrameFlags : byte
10 | {
11 | NONE = 0x0,
12 | ACK = 0x1
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2SettingsFrameFlags.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2
7 | {
8 | [Flags]
9 | internal enum Http2SettingsFrameFlags : byte
10 | {
11 | NONE = 0x0,
12 | ACK = 0x1,
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2SettingsParameter.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace Bedrock.Framework.Protocols.Http2
5 | {
6 | internal enum Http2SettingsParameter : ushort
7 | {
8 | SETTINGS_HEADER_TABLE_SIZE = 0x1,
9 | SETTINGS_ENABLE_PUSH = 0x2,
10 | SETTINGS_MAX_CONCURRENT_STREAMS = 0x3,
11 | SETTINGS_INITIAL_WINDOW_SIZE = 0x4,
12 | SETTINGS_MAX_FRAME_SIZE = 0x5,
13 | SETTINGS_MAX_HEADER_LIST_SIZE = 0x6,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2/Http2SettingsParameterOutOfRangeException.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) .NET Foundation. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 |
6 | namespace Bedrock.Framework.Protocols.Http2
7 | {
8 | internal sealed class Http2SettingsParameterOutOfRangeException : Exception
9 | {
10 | public Http2SettingsParameterOutOfRangeException(Http2SettingsParameter parameter, long lowerBound, long upperBound)
11 | : base($"HTTP/2 SETTINGS parameter {parameter} must be set to a value between {lowerBound} and {upperBound}")
12 | {
13 | Parameter = parameter;
14 | }
15 |
16 | public Http2SettingsParameter Parameter { get; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Http2ClientProtocol.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Collections.Generic;
4 | using System.Net.Http;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Bedrock.Framework.Protocols.Http2;
8 | using Microsoft.AspNetCore.Connections;
9 |
10 | namespace Bedrock.Framework.Protocols
11 | {
12 | internal class Http2ClientProtocol : Http2Protocol
13 | {
14 | private const int DefaultInitialWindowSize = 65535;
15 |
16 | // We don't really care about limiting control flow at the connection level.
17 | // We limit it per stream, and the user controls how many streams are created.
18 | // So set the connection window size to a large value.
19 | private const int ConnectionWindowSize = 64 * 1024 * 1024;
20 |
21 | private bool _expectingSettingsAck;
22 |
23 | public Http2ClientProtocol(ConnectionContext connection) : base(connection)
24 | {
25 | _ = ProcessFramesAsync();
26 | }
27 |
28 | public ValueTask SendAsync(HttpRequestMessage requestMessage, HttpCompletionOption completionOption = HttpCompletionOption.ResponseHeadersRead)
29 | {
30 | return default;
31 | }
32 |
33 | protected override async ValueTask ProcessFramesAsync()
34 | {
35 | _connection.Transport.Output.Write(ClientPreface);
36 |
37 | await FrameWriter.WriteSettingsAsync(new List
38 | {
39 | // First setting: Disable push promise
40 | new Http2PeerSetting(Http2SettingsParameter.SETTINGS_ENABLE_PUSH, 0),
41 | // Second setting: Set header table size to 0 to disable dynamic header compression
42 | new Http2PeerSetting(Http2SettingsParameter.SETTINGS_HEADER_TABLE_SIZE, 0)
43 | });
44 |
45 | await FrameWriter.WriteWindowUpdateAsync(0, ConnectionWindowSize - DefaultInitialWindowSize);
46 |
47 | await FrameWriter.FlushAsync();
48 |
49 | _expectingSettingsAck = true;
50 |
51 | await base.ProcessFramesAsync();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/HttpBodyContent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Net;
6 | using System.Net.Http;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Bedrock.Framework.Protocols
11 | {
12 | internal class HttpBodyContent : HttpContent
13 | {
14 | private Stream _stream;
15 |
16 | protected override Task CreateContentReadStreamAsync()
17 | {
18 | return Task.FromResult(_stream);
19 | }
20 |
21 | protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
22 | {
23 | return _stream.CopyToAsync(stream);
24 | }
25 |
26 | protected override bool TryComputeLength(out long length)
27 | {
28 | length = 0;
29 | return false;
30 | }
31 |
32 | internal void SetStream(Stream stream)
33 | {
34 | _stream = stream;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/HttpBodyStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.IO;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace Bedrock.Framework.Protocols
8 | {
9 | internal class HttpBodyStream : Stream
10 | {
11 | private ProtocolReader _reader;
12 | private IMessageReader> _bodyReader;
13 |
14 | public HttpBodyStream(ProtocolReader reader, IMessageReader> bodyReader)
15 | {
16 | _reader = reader;
17 | _bodyReader = bodyReader;
18 | }
19 |
20 | public override bool CanRead => true;
21 |
22 | public override bool CanSeek => false;
23 |
24 | public override bool CanWrite => false;
25 |
26 | public override long Length => throw new System.NotImplementedException();
27 |
28 | public override long Position { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
29 |
30 | public override void Flush()
31 | {
32 | throw new System.NotImplementedException();
33 | }
34 |
35 | public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default)
36 | {
37 | var result = await _reader.ReadAsync(_bodyReader, maximumMessageSize: buffer.Length, cancellationToken).ConfigureAwait(false);
38 | var data = result.Message;
39 |
40 | // Connection died
41 | if (result.IsCompleted)
42 | {
43 | return 0;
44 | }
45 |
46 | if (data.Length > 0)
47 | {
48 | data.CopyTo(buffer.Span);
49 | }
50 |
51 | _reader.Advance();
52 |
53 | return (int)data.Length;
54 | }
55 |
56 | public override int Read(byte[] buffer, int offset, int count)
57 | {
58 | throw new System.NotImplementedException();
59 | }
60 |
61 | public override long Seek(long offset, SeekOrigin origin)
62 | {
63 | throw new System.NotImplementedException();
64 | }
65 |
66 | public override void SetLength(long value)
67 | {
68 | throw new System.NotImplementedException();
69 | }
70 |
71 | public override void Write(byte[] buffer, int offset, int count)
72 | {
73 | throw new System.NotImplementedException();
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/HttpClientProtocol.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Http;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Connections;
6 |
7 | namespace Bedrock.Framework.Protocols
8 | {
9 | public class HttpClientProtocol
10 | {
11 | private readonly ConnectionContext _connection;
12 | private readonly ProtocolReader _reader;
13 | private readonly Http1RequestMessageWriter _messageWriter;
14 |
15 | public HttpClientProtocol(ConnectionContext connection)
16 | {
17 | _connection = connection;
18 | _reader = connection.CreateReader();
19 |
20 | (string host, int port) = connection.RemoteEndPoint switch
21 | {
22 | UriEndPoint uriEndPoint => (uriEndPoint.Uri.Host, uriEndPoint.Uri.Port),
23 | IPEndPoint ip => (ip.Address.ToString(), ip.Port),
24 | NamedPipeEndPoint np => (np.PipeName, 80),
25 | _ => throw new NotSupportedException($"{connection.RemoteEndPoint} not supported")
26 | };
27 | _messageWriter = new Http1RequestMessageWriter(host, port);
28 | }
29 |
30 | public async ValueTask SendAsync(HttpRequestMessage requestMessage, HttpCompletionOption completionOption = HttpCompletionOption.ResponseHeadersRead, System.Threading.CancellationToken cancellationToken = default)
31 | {
32 | // Write request message headers
33 | _messageWriter.WriteMessage(requestMessage, _connection.Transport.Output);
34 |
35 | // Write the body directly
36 | if (requestMessage.Content != null)
37 | {
38 | await requestMessage.Content.CopyToAsync(_connection.Transport.Output.AsStream()).ConfigureAwait(false);
39 | }
40 |
41 | await _connection.Transport.Output.FlushAsync(cancellationToken).ConfigureAwait(false);
42 |
43 | var content = new HttpBodyContent();
44 | var headerReader = new Http1ResponseMessageReader(content);
45 |
46 | var result = await _reader.ReadAsync(headerReader, cancellationToken).ConfigureAwait(false);
47 |
48 | if (result.IsCompleted)
49 | {
50 | throw new ConnectionAbortedException();
51 | }
52 |
53 | var response = result.Message;
54 |
55 | // TODO: Handle upgrade
56 | if (content.Headers.ContentLength != null)
57 | {
58 | content.SetStream(new HttpBodyStream(_reader, new ContentLengthHttpBodyReader(response.Content.Headers.ContentLength.Value)));
59 | }
60 | else if (response.Headers.TransferEncodingChunked.HasValue)
61 | {
62 | content.SetStream(new HttpBodyStream(_reader, new ChunkedHttpBodyReader()));
63 | }
64 | else
65 | {
66 | content.SetStream(new HttpBodyStream(_reader, new ContentLengthHttpBodyReader(0)));
67 | }
68 |
69 | _reader.Advance();
70 |
71 | return response;
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/HttpServerProtocol.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Connections;
4 |
5 | namespace Bedrock.Framework.Protocols
6 | {
7 | public class HttpServerProtocol
8 | {
9 | private readonly ConnectionContext _connection;
10 | private readonly ProtocolReader _reader;
11 |
12 | private readonly Http1ResponseMessageWriter _writer = new Http1ResponseMessageWriter();
13 |
14 | public HttpServerProtocol(ConnectionContext connection)
15 | {
16 | _connection = connection;
17 | _reader = connection.CreateReader();
18 | }
19 |
20 | public async ValueTask ReadRequestAsync(System.Threading.CancellationToken cancellationToken = default)
21 | {
22 | var content = new HttpBodyContent();
23 | var headerReader = new Http1RequestMessageReader(content);
24 |
25 | var result = await _reader.ReadAsync(headerReader, cancellationToken).ConfigureAwait(false);
26 |
27 | if (result.IsCompleted)
28 | {
29 | throw new ConnectionAbortedException();
30 | }
31 |
32 | var request = result.Message;
33 |
34 | // TODO: Handle upgrade
35 | if (content.Headers.ContentLength != null)
36 | {
37 | content.SetStream(new HttpBodyStream(_reader, new ContentLengthHttpBodyReader(request.Content.Headers.ContentLength.Value)));
38 | }
39 | else if (request.Headers.TransferEncodingChunked.HasValue)
40 | {
41 | content.SetStream(new HttpBodyStream(_reader, new ChunkedHttpBodyReader()));
42 | }
43 | else
44 | {
45 | content.SetStream(new HttpBodyStream(_reader, new ContentLengthHttpBodyReader(0)));
46 | }
47 |
48 | _reader.Advance();
49 |
50 | return request;
51 | }
52 |
53 | public async ValueTask WriteResponseAsync(HttpResponseMessage responseMessage, System.Threading.CancellationToken cancellationToken = default)
54 | {
55 | _writer.WriteMessage(responseMessage, _connection.Transport.Output);
56 |
57 | if (responseMessage.Content != null)
58 | {
59 | await responseMessage.Content.CopyToAsync(_connection.Transport.Output.AsStream()).ConfigureAwait(false);
60 | }
61 | }
62 | }
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/HubHandshakeMessageReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using Microsoft.AspNetCore.SignalR.Protocol;
4 |
5 | namespace Bedrock.Framework.Protocols
6 | {
7 | public class HubHandshakeMessageReader : IMessageReader
8 | {
9 | public bool TryParseMessage(in ReadOnlySequence input, ref SequencePosition consumed, ref SequencePosition examined, out HandshakeRequestMessage message)
10 | {
11 | var buffer = input;
12 | if (HandshakeProtocol.TryParseRequestMessage(ref buffer, out message))
13 | {
14 | consumed = buffer.End;
15 | examined = consumed;
16 | return true;
17 | }
18 |
19 | return false;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/HubMessageReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using Microsoft.AspNetCore.SignalR;
4 | using Microsoft.AspNetCore.SignalR.Protocol;
5 |
6 | namespace Bedrock.Framework.Protocols
7 | {
8 | public class HubMessageReader : IMessageReader
9 | {
10 | private readonly IHubProtocol _hubProtocol;
11 | private readonly IInvocationBinder _invocationBinder;
12 |
13 | public HubMessageReader(IHubProtocol hubProtocol, IInvocationBinder invocationBinder)
14 | {
15 | _hubProtocol = hubProtocol;
16 | _invocationBinder = invocationBinder;
17 | }
18 |
19 | public bool TryParseMessage(in ReadOnlySequence input, ref SequencePosition consumed, ref SequencePosition examined, out HubMessage message)
20 | {
21 | var buffer = input;
22 | if (_hubProtocol.TryParseMessage(ref buffer, _invocationBinder, out message))
23 | {
24 | consumed = buffer.End;
25 | examined = consumed;
26 | return true;
27 | }
28 |
29 | return false;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/HubMessageWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Collections.Generic;
4 | using System.Text;
5 | using Microsoft.AspNetCore.SignalR.Protocol;
6 |
7 | namespace Bedrock.Framework.Protocols
8 | {
9 | public class HubMessageWriter : IMessageWriter
10 | {
11 | private readonly IHubProtocol _hubProtocol;
12 |
13 | public HubMessageWriter(IHubProtocol hubProtocol)
14 | {
15 | _hubProtocol = hubProtocol;
16 | }
17 |
18 | public void WriteMessage(HubMessage message, IBufferWriter output)
19 | {
20 | _hubProtocol.WriteMessage(message, output);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/HubProtocol.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Connections;
4 | using Microsoft.AspNetCore.SignalR;
5 | using Microsoft.AspNetCore.SignalR.Protocol;
6 |
7 | namespace Bedrock.Framework.Protocols
8 | {
9 | public class HubProtocol
10 | {
11 | private readonly ConnectionContext _connection;
12 | private readonly ProtocolReader _protocolReader;
13 | private readonly ProtocolWriter _protocolWriter;
14 | private readonly IMessageReader _hubMessageReader;
15 | private readonly IMessageWriter _hubMessageWriter;
16 | private readonly int? _maximumMessageSize;
17 |
18 | private HubProtocol(ConnectionContext connection, int? maximumMessageSize, IHubProtocol hubProtocol, IInvocationBinder invocationBinder)
19 | {
20 | _connection = connection;
21 | _protocolReader = connection.CreateReader();
22 | _protocolWriter = connection.CreateWriter();
23 | _hubMessageReader = new HubMessageReader(hubProtocol, invocationBinder);
24 | _hubMessageWriter = new HubMessageWriter(hubProtocol);
25 | _maximumMessageSize = maximumMessageSize;
26 | }
27 |
28 | public static HubProtocol CreateFromConnection(ConnectionContext connection, IHubProtocol hubProtocol, IInvocationBinder invocationBinder, int? maximumMessageSize = null)
29 | {
30 | return new HubProtocol(connection, maximumMessageSize, hubProtocol, invocationBinder);
31 | }
32 |
33 | public async ValueTask ReadHandshakeAsync(CancellationToken cancellationToken = default)
34 | {
35 | var result = await _protocolReader.ReadAsync(new HubHandshakeMessageReader(), _maximumMessageSize, cancellationToken).ConfigureAwait(false);
36 |
37 | var message = result.Message;
38 |
39 | _protocolReader.Advance();
40 |
41 | return message;
42 | }
43 |
44 | public async ValueTask ReadAsync(CancellationToken cancellationToken = default)
45 | {
46 | var result = await _protocolReader.ReadAsync(_hubMessageReader, cancellationToken).ConfigureAwait(false);
47 |
48 | var message = result.Message;
49 |
50 | _protocolReader.Advance();
51 |
52 | return message;
53 | }
54 |
55 | public ValueTask WriteAsync(HubMessage message, CancellationToken cancellationToken = default)
56 | {
57 | return _protocolWriter.WriteAsync(_hubMessageWriter, message, cancellationToken);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Memcached/Constants.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Bedrock.Framework.Experimental.Protocols.Memcached
6 | {
7 | public class Constants
8 | {
9 | public const int HeaderLength = 24;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Memcached/Enums.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Bedrock.Framework.Experimental.Protocols.Memcached
6 | {
7 | public class Enums
8 | {
9 | ///
10 | /// see https://github.com/memcached/memcached/wiki/BinaryProtocolRevamped#data-types
11 | ///
12 | public enum Opcode : byte
13 | {
14 | Get = 0x00,
15 | Set = 0x01,
16 | Add = 0x02,
17 | Replace = 0x03,
18 | Delete = 0x04
19 | }
20 |
21 | public enum ResponseStatus : byte
22 | {
23 | NoError = 0x0000,
24 | KeyNotFound = 0x0001,
25 | KeyExists = 0x0002,
26 | ValueTooLarge = 0x0003,
27 | InvalidArguments = 0x0004,
28 | ItemNotStored = 0x0005,
29 | IncrOrDecrInvalid = 0x0006,
30 | VBucketBelongToAnotherServer = 0x0007,
31 | AuthenticationError = 0x0008,
32 | AuthenticationContinue = 0x0009,
33 | UnknowCommand = 0x0081,
34 | OutOfMemory = 0x0082,
35 | NotSupported = 0x0083,
36 | InternalError = 0x0084,
37 | Busy = 0x0085,
38 | TemporaryFailure = 0x0086
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Memcached/Expiration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Bedrock.Framework.Experimental.Protocols.Memcached
6 | {
7 | public readonly struct Expiration
8 | {
9 | public readonly uint Value { get; }
10 |
11 | private Expiration(uint value)
12 | {
13 | Value = value;
14 | }
15 |
16 | public static implicit operator Expiration(TimeSpan? expireIn) => SetExpiration(expireIn);
17 |
18 | private static Expiration SetExpiration(TimeSpan? expireIn)
19 | {
20 | uint value = 0;
21 | if (expireIn != null)
22 | {
23 | if (expireIn < TimeSpan.FromDays(30))
24 | {
25 | value = (uint)expireIn.Value.TotalSeconds;
26 | }
27 | else
28 | {
29 | value = (uint)new DateTimeOffset(DateTime.UtcNow.Add(expireIn.Value)).ToUnixTimeSeconds();
30 | }
31 | }
32 | return new Expiration(value);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Memcached/MemcachedMessageReader.cs:
--------------------------------------------------------------------------------
1 | using Bedrock.Framework.Infrastructure;
2 | using Bedrock.Framework.Protocols;
3 | using System;
4 | using System.Buffers;
5 | using System.Buffers.Binary;
6 | using System.Collections.Generic;
7 | using System.Text;
8 | using static Bedrock.Framework.Experimental.Protocols.Memcached.Enums;
9 |
10 | namespace Bedrock.Framework.Experimental.Protocols.Memcached
11 | {
12 | public class MemcachedMessageReader : IMessageReader
13 | {
14 | public bool TryParseMessage(in ReadOnlySequence input, ref SequencePosition consumed, ref SequencePosition examined, out MemcachedResponse message)
15 | {
16 | if (input.Length < Constants.HeaderLength)
17 | {
18 | message = default;
19 | return false;
20 | }
21 | message = new MemcachedResponse();
22 | if (input.First.Length >= Constants.HeaderLength)
23 | {
24 | message.ReadHeader(input.First.Span);
25 | }
26 | else
27 | {
28 | Span header = stackalloc byte[Constants.HeaderLength];
29 | input.Slice(0, Constants.HeaderLength).CopyTo(header);
30 | message.ReadHeader(header);
31 | }
32 |
33 | if (input.Length < message.Header.TotalBodyLength + Constants.HeaderLength)
34 | {
35 | message = default;
36 | return false;
37 | }
38 |
39 | message.ReadBody(input.Slice(Constants.HeaderLength, message.Header.TotalBodyLength));
40 | consumed = input.Slice(Constants.HeaderLength + message.Header.TotalBodyLength).End;
41 | examined = consumed;
42 | return true;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Memcached/MemcachedMessageWriter.cs:
--------------------------------------------------------------------------------
1 | using Bedrock.Framework.Infrastructure;
2 | using Bedrock.Framework.Protocols;
3 | using System;
4 | using System.Buffers;
5 | using System.Buffers.Binary;
6 | using System.Collections.Generic;
7 | using System.Text;
8 |
9 | namespace Bedrock.Framework.Experimental.Protocols.Memcached
10 | {
11 | public class MemcachedMessageWriter : IMessageWriter
12 | {
13 | public void WriteMessage(MemcachedRequest message, IBufferWriter output)
14 | {
15 | Span headerSpan = stackalloc byte[Constants.HeaderLength];
16 | byte extraLength = 0;
17 |
18 | if(message.Flags != TypeCode.Empty)
19 | {
20 | extraLength = 8;
21 | }
22 |
23 | var messageValueLength = 0;
24 |
25 | if (message.Value != null)
26 | {
27 | messageValueLength = message.Value.Length;
28 | }
29 |
30 | var header = new MemcachedRequestHeader()
31 | {
32 | KeyLength = (ushort)message.Key.Length,
33 | Opaque = message.Opaque,
34 | TotalBodyLength = (uint)(extraLength + message.Key.Length + messageValueLength),
35 | ExtraLength = extraLength
36 | };
37 |
38 | if(extraLength != 0)
39 | {
40 | header.Extras = (message.Flags, message.ExpireIn);
41 | }
42 |
43 | headerSpan[0] = MemcachedRequestHeader.Magic;
44 | headerSpan[1] = (byte)message.Opcode;
45 | BinaryPrimitives.WriteUInt16BigEndian(headerSpan.Slice(2), header.KeyLength);
46 | headerSpan[4] = header.ExtraLength;
47 | headerSpan[5] = header.DataType;
48 | BinaryPrimitives.WriteUInt16BigEndian(headerSpan.Slice(6), header.VBucket);
49 | BinaryPrimitives.WriteUInt32BigEndian(headerSpan.Slice(8), header.TotalBodyLength);
50 | BinaryPrimitives.WriteUInt32BigEndian(headerSpan.Slice(12), header.Opaque);
51 | BinaryPrimitives.WriteUInt64BigEndian(headerSpan.Slice(16), header.Cas);
52 |
53 | output.Write(headerSpan);
54 |
55 | var body = output.GetSpan((int)header.TotalBodyLength);
56 | BinaryPrimitives.WriteUInt32BigEndian(body.Slice(0), (uint)header.Extras.Flags);
57 | BinaryPrimitives.WriteUInt32BigEndian(body.Slice(4), (uint)header.Extras.Expiration.Value);
58 |
59 | message.Key.CopyTo(body.Slice(header.ExtraLength));
60 | message.Value.CopyTo(body.Slice(header.ExtraLength + message.Key.Length));
61 | output.Advance((int)header.TotalBodyLength);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Memcached/MemcachedRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using static Bedrock.Framework.Experimental.Protocols.Memcached.Enums;
5 |
6 | namespace Bedrock.Framework.Experimental.Protocols.Memcached
7 | {
8 | public class MemcachedRequest
9 | {
10 | public Opcode Opcode { get; }
11 | public byte[] Key { get; }
12 | public uint Opaque { get; }
13 | public byte[] Value { get; set; }
14 | public TypeCode Flags { get; }
15 | public TimeSpan? ExpireIn { get; }
16 |
17 | public MemcachedRequest(Opcode opcode, byte[] key, uint opaque,byte[] value, TypeCode flags, TimeSpan ? expireIn=null)
18 | {
19 | Opcode = opcode;
20 | Key = key;
21 | Opaque = opaque;
22 | Value = value;
23 | Flags = flags;
24 | ExpireIn = expireIn;
25 | }
26 |
27 | public MemcachedRequest(Opcode opcode, byte[] key, uint opaque)
28 | {
29 | Opcode = opcode;
30 | Key = key;
31 | Opaque = opaque;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Memcached/MemcachedRequestHeader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Bedrock.Framework.Experimental.Protocols.Memcached
6 | {
7 | public class MemcachedRequestHeader
8 | {
9 | public const byte Magic = 0x80;
10 | public ushort KeyLength { get; set; }
11 | public byte ExtraLength { get; set; }
12 | public byte DataType { get; set; }
13 | public ushort VBucket { get; set; }
14 | public uint TotalBodyLength { get; set; }
15 | public uint Opaque { get; set; }
16 | public ulong Cas { get; set; }
17 | public (TypeCode Flags, Expiration Expiration) Extras;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Memcached/MemcachedResponse.cs:
--------------------------------------------------------------------------------
1 | using Bedrock.Framework.Infrastructure;
2 | using System;
3 | using System.Buffers;
4 | using System.Buffers.Binary;
5 | using System.Collections.Generic;
6 | using System.Text;
7 | using static Bedrock.Framework.Experimental.Protocols.Memcached.Enums;
8 |
9 | namespace Bedrock.Framework.Experimental.Protocols.Memcached
10 | {
11 | public class MemcachedResponse
12 | {
13 | public ReadOnlySequence Data { get; private set; }
14 | public TypeCode Flags { get; set; }
15 | public MemcachedResponseHeader Header { get; private set; }
16 |
17 | public void ReadHeader(ReadOnlySpan buffer)
18 | {
19 | if (buffer[0] != MemcachedResponseHeader.Magic)
20 | {
21 | throw new ArgumentException("Magic mismatch");
22 | }
23 |
24 | this.Header = new MemcachedResponseHeader()
25 | {
26 | Opcode = (Opcode)buffer[1],
27 | KeyLength = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(2)),
28 | ExtraLength = buffer[4],
29 | DataType = buffer[5],
30 | ResponseStatus = (ResponseStatus)BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(6)),
31 | TotalBodyLength = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(8)),
32 | Opaque = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(12)),
33 | Cas = BinaryPrimitives.ReadUInt64BigEndian(buffer.Slice(16)),
34 | };
35 | }
36 |
37 | public void ReadBody(ReadOnlySequence sequence)
38 | {
39 | if (sequence.Length == 0)
40 | {
41 | return;
42 | }
43 |
44 | Data = sequence.Slice(Header.KeyLength + Header.ExtraLength);
45 |
46 | Span buffer = stackalloc byte[4];
47 | sequence.Slice(0, 4).CopyTo(buffer);
48 | Flags = (TypeCode)BinaryPrimitives.ReadUInt32BigEndian(buffer);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/Memcached/MemcachedResponseHeader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using static Bedrock.Framework.Experimental.Protocols.Memcached.Enums;
5 |
6 | namespace Bedrock.Framework.Experimental.Protocols.Memcached
7 | {
8 | public class MemcachedResponseHeader
9 | {
10 | public const byte Magic = 0x81;
11 | public Opcode Opcode { get; set; }
12 | public ushort KeyLength { get; set; }
13 | public byte ExtraLength { get; set; }
14 | public byte DataType { get; set; }
15 | public ResponseStatus ResponseStatus { get; set; }
16 | public uint TotalBodyLength { get; set; }
17 | public uint Opaque { get; set; }
18 | public ulong Cas { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/ParseResult.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | namespace Bedrock.Framework.Protocols.Http.Http1
4 | {
5 | public struct ParseResult
6 | {
7 | private readonly T _value;
8 | private readonly ParseError _error;
9 |
10 | public ParseResult(T value) : this()
11 | {
12 | _value = value;
13 | }
14 |
15 | public ParseResult(ParseError error) : this()
16 | {
17 | _error = error;
18 | }
19 |
20 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
21 | public bool TryGetValue(out T value)
22 | {
23 | value = _value;
24 | return _error is null;
25 | }
26 |
27 | public bool TryGetError(out ParseError error)
28 | {
29 | error = _error;
30 | return error is object;
31 | }
32 | }
33 |
34 | public class ParseError
35 | {
36 | public ParseError(string reason, string line)
37 | {
38 | Reason = reason;
39 | Line = line;
40 | }
41 |
42 | public string Reason { get; }
43 | public string Line { get; }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/FrameType.cs:
--------------------------------------------------------------------------------
1 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ
2 | {
3 | internal enum FrameType : byte
4 | {
5 | Method = 1,
6 | Header = 2,
7 | Body = 3,
8 | HeartBeat = 8,
9 | End = 206
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/IRabbitMQMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ
7 | {
8 | public interface IAmqpMessage
9 | {
10 | void Write(IBufferWriter output);
11 | bool TryParse(in ReadOnlySequence input, out SequencePosition end);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/ChannelOpen.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class ChannelOpen : MethodBase, IAmqpMessage
10 | {
11 | public override byte ClassId => 20;
12 | public override byte MethodId => 10;
13 |
14 | public ReadOnlyMemory Reserved1 { get; }
15 | public ushort Channel { get; private set; }
16 |
17 | public ChannelOpen(ReadOnlyMemory reserved1, ushort channel)
18 | {
19 | Reserved1 = reserved1;
20 | Channel = channel;
21 | }
22 |
23 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
24 | {
25 | throw new NotImplementedException();
26 | }
27 |
28 | public void Write(IBufferWriter output)
29 | {
30 | var payloadLength = 1 + Reserved1.Length + MethodHeaderLength;
31 | var buffer = output.GetSpan(RabbitMQMessageFormatter.HeaderLength + payloadLength + 1);
32 |
33 | WriteHeader(ref buffer, Channel, payloadLength);
34 |
35 | BinaryPrimitives.WriteUInt16BigEndian(buffer, ClassId);
36 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(2), MethodId);
37 | buffer[4] = (byte)Reserved1.Length;
38 | Reserved1.Span.CopyTo(buffer.Slice(5));
39 | buffer[payloadLength]= (byte)FrameType.End;
40 |
41 | output.Advance(RabbitMQMessageFormatter.HeaderLength + payloadLength + sizeof(byte));
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/ChannelOpenOk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class ChannelOpenOk : MethodBase, IAmqpMessage
10 | {
11 | public override byte ClassId => 20;
12 | public override byte MethodId => 11;
13 |
14 | public ReadOnlyMemory Reserved1 { get; private set; }
15 |
16 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
17 | {
18 | var reader = new SequenceReader(input);
19 | try
20 | {
21 | Reserved1 = ProtocolHelper.ReadLongString(ref reader);
22 | end = reader.Position;
23 | return true;
24 | }
25 | catch (Exception ex)
26 | {
27 | //TODO trace error
28 | end = default;
29 | return false;
30 | }
31 | }
32 |
33 | public void Write(IBufferWriter output)
34 | {
35 | throw new NotImplementedException();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/ConnectionOk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class ConnectionOk : MethodBase, IAmqpMessage
10 | {
11 | public ReadOnlyMemory SecurityMechanism { get; private set; }
12 | public ReadOnlyMemory Credentials { get; private set; }
13 | public ReadOnlyMemory Locale { get; private set; }
14 |
15 | public override byte ClassId => 10;
16 | public override byte MethodId => 11;
17 |
18 | public ConnectionOk(ReadOnlyMemory securityMechanism, ReadOnlyMemory credentials, ReadOnlyMemory locale)
19 | {
20 | SecurityMechanism = securityMechanism;
21 | Credentials = credentials;
22 | Locale = locale;
23 | }
24 |
25 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
26 | {
27 | throw new NotImplementedException();
28 | }
29 |
30 | public void Write(IBufferWriter output)
31 | {
32 | int PayloadLength = SecurityMechanism.Length + Credentials.Length + Locale.Length + 14;
33 | var buffer = output.GetSpan(RabbitMQMessageFormatter.HeaderLength + PayloadLength + 1);
34 |
35 | WriteHeader(ref buffer, 0, PayloadLength);
36 |
37 | BinaryPrimitives.WriteUInt16BigEndian(buffer, ClassId);
38 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(2), MethodId);
39 | //TO DO replace by client properties
40 | BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(4), 0);
41 | buffer = buffer.Slice(8);
42 | buffer[0] = (byte)SecurityMechanism.Length;
43 | SecurityMechanism.Span.CopyTo(buffer.Slice(1));
44 | buffer = buffer.Slice(SecurityMechanism.Length+1);
45 | BinaryPrimitives.WriteInt32BigEndian(buffer, Credentials.Length);
46 | buffer = buffer.Slice(4);
47 | Credentials.Span.CopyTo(buffer);
48 | buffer = buffer.Slice(Credentials.Length);
49 | buffer[0] = (byte)Locale.Length;
50 | buffer = buffer.Slice(1);
51 | Locale.Span.CopyTo(buffer);
52 | buffer = buffer.Slice(Locale.Length);
53 | buffer[0] = (byte)FrameType.End;
54 |
55 | output.Advance(RabbitMQMessageFormatter.HeaderLength + PayloadLength + sizeof(byte));
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/ConnectionOpen.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class ConnectionOpen : MethodBase, IAmqpMessage
10 | {
11 | public override byte ClassId => 10;
12 | public override byte MethodId => 40;
13 |
14 | public ReadOnlyMemory Vhost { get; private set; }
15 | public ReadOnlyMemory Reserved1 { get; private set; }
16 |
17 | public byte Reserved2 { get; }
18 |
19 | public ConnectionOpen(ReadOnlyMemory vhost, ReadOnlyMemory reserved1, byte reserved2)
20 | {
21 | Vhost = vhost;
22 | Reserved1 = reserved1;
23 | Reserved2 = reserved2;
24 | }
25 |
26 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
27 | {
28 | throw new NotImplementedException();
29 | }
30 |
31 | public void Write(IBufferWriter output)
32 | {
33 | var payloadLength = 1 + Vhost.Length + 1 + Reserved1.Length + MethodHeaderLength + sizeof(byte);
34 | var buffer = output.GetSpan(RabbitMQMessageFormatter.HeaderLength + payloadLength + 1);
35 |
36 | WriteHeader(ref buffer, 0, payloadLength);
37 |
38 | BinaryPrimitives.WriteUInt16BigEndian(buffer, ClassId);
39 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(2), MethodId);
40 | buffer[4] = (byte)Vhost.Length;
41 | Vhost.Span.CopyTo(buffer.Slice(5));
42 | buffer[5 + Vhost.Length] = (byte)Reserved1.Length;
43 | Reserved1.Span.CopyTo(buffer.Slice(5 + Vhost.Length + 1));
44 | buffer[5 + Vhost.Length + 1 + Reserved1.Length] = Reserved2;
45 | buffer[payloadLength] = (byte)FrameType.End;
46 |
47 | output.Advance(RabbitMQMessageFormatter.HeaderLength + payloadLength + sizeof(byte));
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/ConnectionOpenOk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class ConnectionOpenOk : MethodBase, IAmqpMessage
10 | {
11 | public override byte ClassId => 10;
12 | public override byte MethodId => 41;
13 | public ReadOnlyMemory Reserved1 { get; private set; }
14 |
15 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
16 | {
17 | var reader = new SequenceReader(input);
18 | try
19 | {
20 | Reserved1 = ProtocolHelper.ReadShortString(ref reader);
21 | end = reader.Position;
22 | return true;
23 | }
24 | catch (Exception ex)
25 | {
26 | //TODO trace error
27 | end = default;
28 | return false;
29 | }
30 | }
31 |
32 | public void Write(IBufferWriter output)
33 | {
34 | throw new NotImplementedException();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/ConnectionStart.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
7 | {
8 | public class ConnectionStart : MethodBase, IAmqpMessage
9 | {
10 | public override byte ClassId => 10;
11 | public override byte MethodId => 10;
12 |
13 | public byte VersionMajor { get; private set; }
14 | public byte VersionMinor { get; private set; }
15 | public Dictionary ServerProperties { get; private set; }
16 | public ReadOnlyMemory SecurityMechanims { get; private set; }
17 | public ReadOnlyMemory Locale { get; private set; }
18 |
19 | private ReadOnlySpan PlainAMQPPlain => new byte[] { (byte)'P', (byte)'L', (byte)'A', (byte)'I', (byte)'N', (byte)' ', (byte)'A', (byte)'M', (byte)'Q', (byte)'P', (byte)'L', (byte)'A', (byte)'I', (byte)'N' };
20 | private ReadOnlyMemory Plain = new byte[] { (byte)'P', (byte)'L', (byte)'A', (byte)'I', (byte)'N' };
21 |
22 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
23 | {
24 | var reader = new SequenceReader(input);
25 |
26 | if (!reader.TryRead(out var verionMajor))
27 | {
28 | end = default;
29 | return false;
30 | }
31 | if (!reader.TryRead(out var versionMinor))
32 | {
33 | end = default;
34 | return false;
35 | }
36 |
37 | VersionMajor = verionMajor;
38 | VersionMinor = versionMinor;
39 | try
40 | {
41 | ServerProperties = ProtocolHelper.ReadTable(ref reader);
42 | var security = ProtocolHelper.ReadLongString(ref reader);
43 | if(security.Span.SequenceEqual(PlainAMQPPlain))
44 | {
45 | SecurityMechanims = Plain;
46 | }
47 | else
48 | {
49 | throw new Exception($"Unsupported security mechanism");
50 | }
51 | Locale = ProtocolHelper.ReadLongString(ref reader);
52 | end = reader.Position;
53 | return true;
54 | }
55 | catch(Exception ex)
56 | {
57 | //TODO trace error
58 | end = default;
59 | return false;
60 | }
61 | }
62 |
63 | public void Write(IBufferWriter output)
64 | {
65 | throw new NotImplementedException();
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/ConnectionTune.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class ConnectionTune : MethodBase, IAmqpMessage
10 | {
11 | public override byte ClassId => 10;
12 | public override byte MethodId => 30;
13 |
14 | public short MaxChannel { get; private set; }
15 | public int MaxFrame { get; private set; }
16 | public short HeartBeat { get; private set; }
17 |
18 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
19 | {
20 | var reader = new SequenceReader(input);
21 | try
22 | {
23 | reader.TryReadBigEndian(out short maxChannel);
24 | reader.TryReadBigEndian(out int maxFrame);
25 | reader.TryReadBigEndian(out short heartBeat);
26 |
27 | MaxChannel = maxChannel;
28 | MaxFrame = maxFrame;
29 | HeartBeat = heartBeat;
30 |
31 | end = reader.Position;
32 | return true;
33 | }
34 | catch (Exception ex)
35 | {
36 | //TODO trace error
37 | end = default;
38 | return false;
39 | }
40 | }
41 |
42 | public void Write(IBufferWriter output)
43 | {
44 | throw new NotImplementedException();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/ConnectionTuneOk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class ConnectionTuneOk : MethodBase, IAmqpMessage
10 | {
11 | public override byte ClassId => 10;
12 | public override byte MethodId => 31;
13 |
14 | public short MaxChannel { get; private set; }
15 | public int MaxFrame { get; private set; }
16 | public short HeartBeat { get; private set; }
17 |
18 | public ConnectionTuneOk(short maxChannel, int maxFrame, short heartBeat)
19 | {
20 | this.MaxChannel = maxChannel;
21 | this.MaxFrame = maxFrame;
22 | this.HeartBeat = heartBeat;
23 | }
24 |
25 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
26 | {
27 | throw new NotImplementedException();
28 | }
29 |
30 | public void Write(IBufferWriter output)
31 | {
32 | var payloadLength = sizeof(ushort) + sizeof(uint) + sizeof(ushort) + MethodHeaderLength;
33 | var buffer = output.GetSpan(RabbitMQMessageFormatter.HeaderLength + payloadLength + 1);
34 |
35 | WriteHeader(ref buffer, 0, payloadLength);
36 |
37 | BinaryPrimitives.WriteUInt16BigEndian(buffer, ClassId);
38 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(2), MethodId);
39 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(4), (ushort)this.MaxChannel);
40 | BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(6), (uint)this.MaxFrame);
41 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(10), (ushort)this.HeartBeat);
42 | buffer[payloadLength]= (byte)FrameType.End;
43 |
44 | output.Advance(RabbitMQMessageFormatter.HeaderLength + payloadLength + sizeof(byte));
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/MethodBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
7 | {
8 | public abstract class MethodBase
9 | {
10 | public abstract byte ClassId { get; }
11 | public abstract byte MethodId { get; }
12 |
13 | public const int MethodHeaderLength = 4;
14 |
15 | public void WriteHeader(ref Span buffer, ushort channel, int payloadLength)
16 | {
17 | buffer[0] = (byte)FrameType.Method;
18 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(1), channel);
19 | BinaryPrimitives.WriteInt32BigEndian(buffer.Slice(3), payloadLength);
20 | buffer = buffer.Slice(7);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/QueueDeclare.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class QueueDeclare : MethodBase, IAmqpMessage
10 | {
11 | public override byte ClassId => 50;
12 | public override byte MethodId => 10;
13 |
14 | public ushort Channel { get; private set; }
15 | public ushort Reserved1 { get; private set; }
16 | public string QueueName { get; private set; }
17 | public bool Passive { get; private set; }
18 | public bool Durable { get; private set; }
19 | public bool Exclusive { get; private set; }
20 | public bool AutoDelete { get; private set; }
21 | public bool NoWait { get; private set; }
22 | public Dictionary Arguments { get; private set; }
23 | public ReadOnlyMemory Options { get; private set; }
24 |
25 | public QueueDeclare(ushort channel,ushort reserved1, string queueName, bool passive = false, bool durable = true, bool exclusive = false, bool autoDelete = false, bool noWait = false, Dictionary arguments = null )
26 | {
27 | Channel = channel;
28 | Reserved1 = reserved1;
29 | QueueName = queueName;
30 | Passive = passive;
31 | Durable = durable;
32 | Exclusive = exclusive;
33 | AutoDelete = autoDelete;
34 | NoWait = noWait;
35 | Arguments = arguments;
36 | Options = new ReadOnlyMemory(new byte[] { Convert.ToByte(passive), Convert.ToByte(durable), Convert.ToByte(exclusive), Convert.ToByte(autoDelete), Convert.ToByte(noWait) });
37 | }
38 |
39 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
40 | {
41 | throw new NotImplementedException();
42 | }
43 |
44 | public void Write(IBufferWriter output)
45 | {
46 | var payloadLength = 6 + 1 + QueueName.Length + sizeof(byte) + MethodHeaderLength;
47 | var buffer = output.GetSpan(RabbitMQMessageFormatter.HeaderLength + payloadLength + 1);
48 |
49 | WriteHeader(ref buffer, this.Channel, payloadLength);
50 |
51 | BinaryPrimitives.WriteUInt16BigEndian(buffer, ClassId);
52 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(2), MethodId);
53 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(4), Reserved1);
54 | buffer[6] = (byte)QueueName.Length;
55 | Encoding.UTF8.GetBytes(QueueName).CopyTo(buffer.Slice(7));
56 | var bytes = ProtocolHelper.BoolArrayToByte(Options);
57 | buffer = buffer.Slice(7 + QueueName.Length);
58 | buffer[0] = bytes;
59 | //TO DO write table
60 | BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(1), 0);
61 | buffer[5] = (byte)FrameType.End;
62 |
63 | output.Advance(RabbitMQMessageFormatter.HeaderLength + payloadLength + sizeof(byte));
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/QueueDeclareOk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class QueueDeclareOk : MethodBase, IAmqpMessage
10 | {
11 | public override byte ClassId => 50;
12 | public override byte MethodId => 11;
13 | public ReadOnlyMemory QueueName { get; private set; }
14 | public uint MessageCount { get; private set; }
15 | public uint ConsumerCount { get; private set; }
16 |
17 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
18 | {
19 | var reader = new SequenceReader(input);
20 | try
21 | {
22 | this.QueueName = ProtocolHelper.ReadShortString(ref reader);
23 | if (reader.TryReadBigEndian(out int messageCount))
24 | this.MessageCount = (uint)messageCount;
25 | if (reader.TryReadBigEndian(out int consumerCount))
26 | this.ConsumerCount = (uint)consumerCount;
27 |
28 | end = reader.Position;
29 | return true;
30 | }
31 | catch (Exception ex)
32 | {
33 | //TODO trace error
34 | end = default;
35 | return false;
36 | }
37 | }
38 |
39 | public void Write(IBufferWriter output)
40 | {
41 | throw new NotImplementedException();
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/QueueDelete.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class QueueDelete : MethodBase, IAmqpMessage
10 | {
11 | public override byte ClassId => 50;
12 | public override byte MethodId => 40;
13 |
14 | public ushort Channel { get; private set; }
15 | public ushort Reserved1 { get; private set; }
16 | public string QueueName { get; private set; }
17 | public bool DeleteIfUnused { get; private set; }
18 | public bool DeleteIfEmpty { get; private set; }
19 |
20 | public ReadOnlyMemory Options { get; private set; }
21 |
22 | public QueueDelete(ushort channel,ushort reserved1, string queueName, bool deleteIfUnused = false, bool deleteIfEmpty = false )
23 | {
24 | Channel = channel;
25 | Reserved1 = reserved1;
26 | QueueName = queueName;
27 |
28 | Options = new ReadOnlyMemory(new byte[] { Convert.ToByte(deleteIfUnused), Convert.ToByte(deleteIfEmpty) });
29 | }
30 |
31 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
32 | {
33 | throw new NotImplementedException();
34 | }
35 |
36 | public void Write(IBufferWriter output)
37 | {
38 | var payloadLength = MethodHeaderLength + 2 + 1 + QueueName.Length + sizeof(byte);
39 | var buffer = output.GetSpan(RabbitMQMessageFormatter.HeaderLength + payloadLength + 1);
40 |
41 | WriteHeader(ref buffer, this.Channel, payloadLength);
42 |
43 | BinaryPrimitives.WriteUInt16BigEndian(buffer, ClassId);
44 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(2), MethodId);
45 | BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(4), Reserved1);
46 | buffer[6] = (byte)QueueName.Length;
47 | Encoding.UTF8.GetBytes(QueueName).CopyTo(buffer.Slice(7));
48 | buffer[7 + QueueName.Length] = ProtocolHelper.BoolArrayToByte(Options);
49 | buffer[payloadLength] = (byte)FrameType.End;
50 |
51 | output.Advance(RabbitMQMessageFormatter.HeaderLength + payloadLength + sizeof(byte));
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/Methods/QueueDeleteOk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Buffers.Binary;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ.Methods
8 | {
9 | public class QueueDeleteOk : MethodBase, IAmqpMessage
10 | {
11 | public override byte ClassId => 50;
12 | public override byte MethodId => 41;
13 |
14 | public uint MessageCount { get; private set; }
15 |
16 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
17 | {
18 | var reader = new SequenceReader(input);
19 | try
20 | {
21 | if (reader.TryReadBigEndian(out int messageCount))
22 | this.MessageCount = (uint)messageCount;
23 |
24 | end = reader.Position;
25 | return true;
26 | }
27 | catch (Exception ex)
28 | {
29 | //TODO trace error
30 | end = default;
31 | return false;
32 | }
33 | }
34 |
35 | public void Write(IBufferWriter output)
36 | {
37 | throw new NotImplementedException();
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/RabbitMQClientProtocol.cs:
--------------------------------------------------------------------------------
1 | using Bedrock.Framework.Protocols;
2 | using Microsoft.AspNetCore.Connections;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ
9 | {
10 | public class RabbitMQClientProtocol
11 | {
12 | private readonly ProtocolWriter _writer;
13 | private readonly ProtocolReader _reader;
14 | private readonly RabbitMQMessageFormatter _formatter;
15 |
16 | public RabbitMQClientProtocol(ConnectionContext connection)
17 | {
18 | _writer = connection.CreateWriter();
19 | _reader = connection.CreateReader();
20 | _formatter = new RabbitMQMessageFormatter();
21 | }
22 |
23 | public ValueTask SendAsync(IAmqpMessage message)
24 | {
25 | return _writer.WriteAsync(_formatter, message);
26 | }
27 |
28 | public async Task ReceiveAsync() where T : IAmqpMessage
29 | {
30 | var result = await _reader.ReadAsync(_formatter);
31 | _reader.Advance();
32 | return (T)result.Message;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/RabbitMQ/RabbitMQProtocolVersionHeader.cs:
--------------------------------------------------------------------------------
1 | using Bedrock.Framework.Infrastructure;
2 | using System;
3 | using System.Buffers;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Bedrock.Framework.Experimental.Protocols.RabbitMQ
8 | {
9 | public class RabbitMQProtocolVersionHeader : IAmqpMessage
10 | {
11 | private ReadOnlySpan ProtocolHeader => new byte[] { (byte)'A', (byte)'M', (byte)'Q', (byte)'P', 0, 0, 9, 1 };
12 |
13 | public bool TryParse(in ReadOnlySequence input, out SequencePosition end)
14 | {
15 | throw new NotImplementedException();
16 | }
17 |
18 | public void Write(IBufferWriter output)
19 | {
20 | var writer = new BufferWriter>(output);
21 | writer.Write(ProtocolHeader);
22 | writer.Commit();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Protocols/WebSocketProtocol.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.WebSockets;
3 | using System.Threading;
4 | using System.Threading.Channels;
5 | using System.Threading.Tasks;
6 | using Bedrock.Framework.Infrastructure;
7 | using Microsoft.AspNetCore.Connections;
8 |
9 | namespace Bedrock.Framework.Protocols
10 | {
11 | public class WebSocketProtocol
12 | {
13 | public WebSocketProtocol(WebSocket websocket, ConnectionContext connection)
14 | {
15 | WebSocket = websocket;
16 | Connection = connection;
17 | }
18 |
19 | private SemaphoreSlim _semaphore = new SemaphoreSlim(1);
20 |
21 | private WebSocket WebSocket { get; }
22 | public ConnectionContext Connection { get; }
23 |
24 | public ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default)
25 | {
26 | return WebSocket.ReceiveAsync(buffer, cancellationToken);
27 | }
28 |
29 | public async ValueTask WriteAsync(ReadOnlyMemory buffer, WebSocketMessageType webSocketMessageType, bool endOfMessage, CancellationToken cancellationToken = default)
30 | {
31 | await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
32 |
33 | try
34 | {
35 | await WebSocket.SendAsync(buffer, webSocketMessageType, endOfMessage, cancellationToken).ConfigureAwait(false);
36 | }
37 | finally
38 | {
39 | _semaphore.Release();
40 | }
41 | }
42 |
43 | public static WebSocketProtocol CreateFromConnection(ConnectionContext connection, bool isServer, string subProtocol, TimeSpan keepAliveInterval)
44 | {
45 | var websocket = WebSocket.CreateFromStream(new DuplexPipeStream(connection.Transport.Input, connection.Transport.Output), isServer, subProtocol, keepAliveInterval);
46 | return new WebSocketProtocol(websocket, connection);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Transports/Http2/Http2ConnectionListenerFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Connections;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.AspNetCore.Server.Kestrel.Core;
8 | using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
9 | using Microsoft.Extensions.DependencyInjection;
10 | using Microsoft.Extensions.Logging;
11 | using Microsoft.Extensions.Options;
12 |
13 | namespace Bedrock.Framework
14 | {
15 | public partial class Http2ConnectionListenerFactory : IConnectionListenerFactory
16 | {
17 | private readonly ILoggerFactory _loggerFactory;
18 |
19 | public Http2ConnectionListenerFactory(ILoggerFactory loggerFactory)
20 | {
21 | _loggerFactory = loggerFactory;
22 | }
23 |
24 | public async ValueTask BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default)
25 | {
26 | IPEndPoint iPEndPoint = null;
27 | switch (endpoint)
28 | {
29 | // Kestrel doesn't natively support UriEndpoints as yet
30 | case UriEndPoint uriEndPoint:
31 | IPAddress address = null;
32 | if (uriEndPoint.Uri.Host == "localhost")
33 | {
34 | address = IPAddress.Loopback;
35 | }
36 | else
37 | {
38 | IPAddress.Parse(uriEndPoint.Uri.Host);
39 | }
40 | iPEndPoint = new IPEndPoint(address, uriEndPoint.Uri.Port);
41 | break;
42 | case IPEndPoint ip:
43 | iPEndPoint = ip;
44 | break;
45 | default:
46 | throw new NotSupportedException($"{endpoint} not supported");
47 | }
48 |
49 | var services = new ServiceCollection();
50 | services.AddSingleton(_loggerFactory);
51 | services.AddLogging();
52 | var serverOptions = Options.Create(new KestrelServerOptions() { ApplicationServices = services.BuildServiceProvider() }); ;
53 | var socketOptions = Options.Create(new SocketTransportOptions());
54 | var socketTransportFactory = new SocketTransportFactory(socketOptions, _loggerFactory);
55 | var server = new KestrelServer(serverOptions, socketTransportFactory, _loggerFactory);
56 | ListenOptions listenOptions = null;
57 |
58 | // Bind an HTTP/2 endpoint
59 | server.Options.Listen(iPEndPoint, options =>
60 | {
61 | options.UseHttps();
62 | options.Protocols = HttpProtocols.Http2;
63 | // Storing the options so we can get the resolved EndPoint later
64 | listenOptions = options;
65 | });
66 |
67 | var listener = new Http2ConnectionListener(server);
68 |
69 | await listener.BindAsync(cancellationToken).ConfigureAwait(false);
70 |
71 | listener.EndPoint = listenOptions.IPEndPoint;
72 |
73 | return listener;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Bedrock.Framework.Experimental/Transports/Pipes/NamedPipeConnectionContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO.Pipelines;
4 | using System.IO.Pipes;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using Microsoft.AspNetCore.Connections;
8 | using Microsoft.AspNetCore.Http.Features;
9 |
10 | namespace Bedrock.Framework
11 | {
12 | internal class NamedPipeConnectionContext : ConnectionContext, IDuplexPipe
13 | {
14 | private readonly PipeStream _stream;
15 |
16 | public NamedPipeConnectionContext(PipeStream stream, NamedPipeEndPoint endPoint)
17 | {
18 | _stream = stream;
19 | Transport = this;
20 | ConnectionId = Guid.NewGuid().ToString();
21 | RemoteEndPoint = endPoint;
22 |
23 | Input = PipeReader.Create(stream);
24 | Output = PipeWriter.Create(stream);
25 | }
26 |
27 | public PipeReader Input { get; }
28 |
29 | public PipeWriter Output { get; }
30 | public override string ConnectionId { get; set; }
31 |
32 | public override IFeatureCollection Features { get; } = new FeatureCollection();
33 |
34 | public override IDictionary