|
1 | 1 | // Copyright (c) .NET Foundation. All rights reserved.
|
2 | 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
3 | 3 |
|
| 4 | +using System; |
| 5 | +using System.Collections.Generic; |
4 | 6 | using System.Diagnostics;
|
| 7 | +using System.IO; |
| 8 | +using System.IO.Pipelines; |
5 | 9 | using System.Net;
|
| 10 | +using System.Net.Security; |
| 11 | +using System.Security.Authentication; |
6 | 12 | using System.Text;
|
| 13 | +using System.Threading; |
7 | 14 | using System.Threading.Tasks;
|
8 | 15 | using Microsoft.AspNetCore.Connections;
|
| 16 | +using Microsoft.AspNetCore.Connections.Features; |
| 17 | +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; |
9 | 18 | using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
|
10 | 19 | using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Client;
|
11 | 20 | using Microsoft.Extensions.DependencyInjection;
|
@@ -46,65 +55,106 @@ public Http2CatHostedService(IConnectionFactory connectionFactory, ILogger<Http2
|
46 | 55 |
|
47 | 56 | public async Task RunAsync()
|
48 | 57 | {
|
49 |
| - var endpoint = new IPEndPoint(IPAddress.Loopback, 5005); |
| 58 | + var endpoint = new IPEndPoint(IPAddress.Loopback, 5001); |
50 | 59 |
|
51 | 60 | _logger.LogInformation($"Connecting to '{endpoint}'.");
|
52 | 61 |
|
53 |
| - await using var connectionContext = await _connectionFactory.ConnectAsync(endpoint); |
| 62 | + await using var context = await _connectionFactory.ConnectAsync(endpoint); |
54 | 63 |
|
55 |
| - _logger.LogInformation($"Connected to '{endpoint}'."); |
| 64 | + _logger.LogInformation($"Connected to '{endpoint}'. Starting TLS handshake."); |
56 | 65 |
|
57 |
| - var http2Utilities = new Http2Utilities(connectionContext); |
| 66 | + var memoryPool = context.Features.Get<IMemoryPoolFeature>()?.MemoryPool; |
| 67 | + var inputPipeOptions = new StreamPipeReaderOptions(memoryPool, memoryPool.GetMinimumSegmentSize(), memoryPool.GetMinimumAllocSize(), leaveOpen: true); |
| 68 | + var outputPipeOptions = new StreamPipeWriterOptions(pool: memoryPool, leaveOpen: true); |
58 | 69 |
|
59 |
| - await http2Utilities.InitializeConnectionAsync(); |
| 70 | + await using var sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions); |
| 71 | + await using var sslStream = sslDuplexPipe.Stream; |
60 | 72 |
|
61 |
| - _logger.LogInformation("Initialized http2 connection. Starting stream 1."); |
| 73 | + var originalTransport = context.Transport; |
| 74 | + context.Transport = sslDuplexPipe; |
62 | 75 |
|
63 |
| - await http2Utilities.StartStreamAsync(1, Http2Utilities._browserRequestHeaders, endStream: true); |
| 76 | + try |
| 77 | + { |
| 78 | + await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions |
| 79 | + { |
| 80 | + TargetHost = "localhost", |
| 81 | + RemoteCertificateValidationCallback = (_, __, ___, ____) => true, |
| 82 | + ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http2 }, |
| 83 | + EnabledSslProtocols = SslProtocols.Tls12, |
| 84 | + }, CancellationToken.None); |
64 | 85 |
|
65 |
| - var headersFrame = await http2Utilities.ReceiveFrameAsync(); |
| 86 | + _logger.LogInformation($"TLS handshake completed successfully."); |
66 | 87 |
|
67 |
| - Trace.Assert(headersFrame.Type == Http2FrameType.HEADERS); |
68 |
| - Trace.Assert((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_HEADERS) != 0); |
69 |
| - Trace.Assert((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_STREAM) == 0); |
| 88 | + var http2Utilities = new Http2Utilities(context); |
70 | 89 |
|
71 |
| - _logger.LogInformation("Received headers in a single frame."); |
| 90 | + await http2Utilities.InitializeConnectionAsync(); |
72 | 91 |
|
73 |
| - var decodedHeaders = http2Utilities.DecodeHeaders(headersFrame); |
74 |
| - |
75 |
| - foreach (var header in decodedHeaders) |
76 |
| - { |
77 |
| - _logger.LogInformation($"{header.Key}: {header.Value}"); |
78 |
| - } |
| 92 | + _logger.LogInformation("Initialized http2 connection. Starting stream 1."); |
| 93 | + |
| 94 | + await http2Utilities.StartStreamAsync(1, Http2Utilities._browserRequestHeaders, endStream: true); |
| 95 | + |
| 96 | + var headersFrame = await http2Utilities.ReceiveFrameAsync(); |
| 97 | + |
| 98 | + Trace.Assert(headersFrame.Type == Http2FrameType.HEADERS); |
| 99 | + Trace.Assert((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_HEADERS) != 0); |
| 100 | + Trace.Assert((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_STREAM) == 0); |
| 101 | + |
| 102 | + _logger.LogInformation("Received headers in a single frame."); |
| 103 | + |
| 104 | + var decodedHeaders = http2Utilities.DecodeHeaders(headersFrame); |
| 105 | + |
| 106 | + foreach (var header in decodedHeaders) |
| 107 | + { |
| 108 | + _logger.LogInformation($"{header.Key}: {header.Value}"); |
| 109 | + } |
| 110 | + |
| 111 | + var dataFrame = await http2Utilities.ReceiveFrameAsync(); |
79 | 112 |
|
80 |
| - var dataFrame = await http2Utilities.ReceiveFrameAsync(); |
| 113 | + Trace.Assert(dataFrame.Type == Http2FrameType.DATA); |
| 114 | + Trace.Assert((dataFrame.Flags & (byte)Http2DataFrameFlags.END_STREAM) == 0); |
81 | 115 |
|
82 |
| - Trace.Assert(dataFrame.Type == Http2FrameType.DATA); |
83 |
| - Trace.Assert((dataFrame.Flags & (byte)Http2DataFrameFlags.END_STREAM) == 0); |
| 116 | + _logger.LogInformation("Received data in a single frame."); |
84 | 117 |
|
85 |
| - _logger.LogInformation("Received data in a single frame."); |
| 118 | + _logger.LogInformation(Encoding.UTF8.GetString(dataFrame.Payload.ToArray())); |
86 | 119 |
|
87 |
| - _logger.LogInformation(Encoding.UTF8.GetString(dataFrame.Payload.ToArray())); |
| 120 | + var trailersFrame = await http2Utilities.ReceiveFrameAsync(); |
88 | 121 |
|
89 |
| - var trailersFrame = await http2Utilities.ReceiveFrameAsync(); |
| 122 | + Trace.Assert(trailersFrame.Type == Http2FrameType.HEADERS); |
| 123 | + Trace.Assert((trailersFrame.Flags & (byte)Http2DataFrameFlags.END_STREAM) == 1); |
90 | 124 |
|
91 |
| - Trace.Assert(trailersFrame.Type == Http2FrameType.HEADERS); |
92 |
| - Trace.Assert((trailersFrame.Flags & (byte)Http2DataFrameFlags.END_STREAM) == 1); |
| 125 | + _logger.LogInformation("Received trailers in a single frame."); |
93 | 126 |
|
94 |
| - _logger.LogInformation("Received trailers in a single frame."); |
| 127 | + http2Utilities._decodedHeaders.Clear(); |
95 | 128 |
|
96 |
| - http2Utilities._decodedHeaders.Clear(); |
| 129 | + var decodedTrailers = http2Utilities.DecodeHeaders(trailersFrame); |
97 | 130 |
|
98 |
| - var decodedTrailers = http2Utilities.DecodeHeaders(trailersFrame); |
| 131 | + foreach (var header in decodedHeaders) |
| 132 | + { |
| 133 | + _logger.LogInformation($"{header.Key}: {header.Value}"); |
| 134 | + } |
99 | 135 |
|
100 |
| - foreach (var header in decodedHeaders) |
| 136 | + await http2Utilities.StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); |
| 137 | + |
| 138 | + _logger.LogInformation("Connection stopped."); |
| 139 | + } |
| 140 | + finally |
101 | 141 | {
|
102 |
| - _logger.LogInformation($"{header.Key}: {header.Value}"); |
| 142 | + context.Transport = originalTransport; |
103 | 143 | }
|
| 144 | + } |
| 145 | + } |
104 | 146 |
|
105 |
| - await http2Utilities.StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); |
| 147 | + private class SslDuplexPipe : DuplexPipeStreamAdapter<SslStream> |
| 148 | + { |
| 149 | + public SslDuplexPipe(IDuplexPipe transport, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions) |
| 150 | + : this(transport, readerOptions, writerOptions, s => new SslStream(s)) |
| 151 | + { |
| 152 | + |
| 153 | + } |
106 | 154 |
|
107 |
| - _logger.LogInformation("Connection stopped."); |
| 155 | + public SslDuplexPipe(IDuplexPipe transport, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions, Func<Stream, SslStream> factory) : |
| 156 | + base(transport, readerOptions, writerOptions, factory) |
| 157 | + { |
108 | 158 | }
|
109 | 159 | }
|
110 | 160 | }
|
|
0 commit comments