-
Notifications
You must be signed in to change notification settings - Fork 1
完善OpenSslSocketsHttpHandler #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # CodeGraph data files — local to each machine, not for committing. | ||
| # Ignore everything in .codegraph/ except this file itself, so transient | ||
| # files (the database, daemon.pid, sockets, logs) never show up in git. | ||
| * | ||
| !.gitignore |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| namespace DotNetCampus.HttpClientOverOpenSsl | ||
| { | ||
| sealed class ConnectionOptions | ||
| { | ||
| private static readonly HttpRequestOptionsKey<ConnectionOptions> key = new(nameof(ConnectionOptions)); | ||
|
|
||
| /// <summary> | ||
| /// 是否为安全传输 | ||
| /// </summary> | ||
| public bool IsSecurity { get; } | ||
|
|
||
| /// <summary> | ||
| /// 原始请求Uri | ||
| /// </summary> | ||
| public Uri OriginalUri { get; } | ||
|
|
||
| /// <summary> | ||
| /// 连接选项 | ||
| /// </summary> | ||
| /// <param name="isSecurity"></param> | ||
| /// <param name="originalUri"></param> | ||
| public ConnectionOptions(bool isSecurity, Uri originalUri) | ||
| { | ||
| this.IsSecurity = isSecurity; | ||
| this.OriginalUri = originalUri; | ||
| } | ||
|
|
||
|
|
||
| /// <summary> | ||
| /// 获取自定义连接选项 | ||
| /// </summary> | ||
| /// <param name="request"></param> | ||
| /// <returns></returns> | ||
| public static ConnectionOptions Get(HttpRequestMessage request) | ||
| { | ||
| return request.Options.TryGetValue(key, out var options) | ||
| ? options | ||
| : throw new InvalidOperationException("必须先 Set()"); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// 设置使用自定义连接 | ||
| /// </summary> | ||
| /// <param name="request"></param> | ||
| public static void Set(HttpRequestMessage request) | ||
| { | ||
| if (request.Options.TryGetValue(key, out _)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var originalUri = request.RequestUri ?? throw new HttpRequestException("必须指定请求的URI"); | ||
| var isSecurity = originalUri.Scheme == Uri.UriSchemeHttps | ||
| || originalUri.Scheme == Uri.UriSchemeWss | ||
| || originalUri.Scheme == Uri.UriSchemeFtps; | ||
|
|
||
| if (isSecurity == true) | ||
| { | ||
| // 修改Scheme之前,记录原始的Host | ||
| if (request.Headers.Host == null) | ||
| { | ||
| request.Headers.Host = originalUri.Authority; | ||
| } | ||
|
|
||
| // 修改协议非安全Scheme防止自动ssl连接 | ||
| if (originalUri.Scheme == Uri.UriSchemeHttps) | ||
| { | ||
| request.RequestUri = new UriBuilder(originalUri) { Scheme = Uri.UriSchemeHttp }.Uri; | ||
| } | ||
| else if (originalUri.Scheme == Uri.UriSchemeWss) | ||
| { | ||
| request.RequestUri = new UriBuilder(originalUri) { Scheme = Uri.UriSchemeWs }.Uri; | ||
| } | ||
| else if (originalUri.Scheme == Uri.UriSchemeFtps) | ||
| { | ||
| request.RequestUri = new UriBuilder(originalUri) { Scheme = Uri.UriSchemeFtp }.Uri; | ||
| } | ||
| } | ||
|
|
||
| var options = new ConnectionOptions(isSecurity, originalUri); | ||
| request.Options.Set(key, options); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// 移除使用自定义连接 | ||
| /// </summary> | ||
| /// <param name="request"></param> | ||
| public static void Remove(HttpRequestMessage request) | ||
| { | ||
| if (request.Options.Remove(key.Key, out var value) && | ||
| value is ConnectionOptions options) | ||
| { | ||
| request.RequestUri = options.OriginalUri; | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -78,13 +78,24 @@ namespace DotNetCampus.HttpClientOverOpenSsl; | |||||
| /// </remarks> | ||||||
| internal sealed class OpenSslAsyncStream : Stream | ||||||
| { | ||||||
| private readonly NetworkStream? _innerStream = null; | ||||||
| private readonly bool _leaveInnerStreamOpen; | ||||||
| private readonly Socket _socket; | ||||||
| private readonly bool _ownsSocket; | ||||||
| private SafeSslContextHandle? _sslContext; | ||||||
| private SafeSslHandle? _ssl; | ||||||
| private bool _isAuthenticated; | ||||||
| private bool _disposed; | ||||||
|
|
||||||
| public static bool IsSupported { get; } = OperatingSystem.IsWindows() && OpenSSLNative.IsOpenSslAvailable(); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个很棒,可以来做快速分支,只是我想着可以直接用计算的方式,就不用后备字段:
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 目前没有做过NativeLibrary.TryLoad的开销计算,IsSupported做为字段存储已计算过的值可以避开不确定的开销。如果不存储已计算过的值,我更倾向于直接设计成IsSupported()方法 |
||||||
|
|
||||||
| public OpenSslAsyncStream(NetworkStream innerStream, bool leaveInnerStreamOpen) | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 感觉这个构造函数的设计不妙,是否可以直接就绕过了?不要再走这里的构造函数进来
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 在代理环境下,需要stream套娃。此构造也是stream套娃,保持 NetworkStream 的引用和生命周期同步;而传入 Socket 的构造器反正不是很需要 |
||||||
| : this(innerStream.Socket, ownsSocket: false) | ||||||
| { | ||||||
| _innerStream = innerStream; | ||||||
| _leaveInnerStreamOpen = leaveInnerStreamOpen; | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// 使用指定的 Socket 创建 <see cref="OpenSslAsyncStream"/> 实例。 | ||||||
| /// </summary> | ||||||
|
|
@@ -482,6 +493,11 @@ protected override void Dispose(bool disposing) | |||||
| { | ||||||
| _socket.Dispose(); | ||||||
| } | ||||||
|
|
||||||
| if (!_leaveInnerStreamOpen && _innerStream is not null) | ||||||
| { | ||||||
| _innerStream.Dispose(); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| base.Dispose(disposing); | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ namespace DotNetCampus.HttpClientOverOpenSsl; | |
| /// <summary> | ||
| /// OpenSSL 客户端认证配置选项。 | ||
| /// </summary> | ||
| internal sealed class OpenSslClientAuthenticationOptions | ||
| public sealed class OpenSslClientAuthenticationOptions | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里是为什么要开放呢?我静态阅读代码没有看全哈
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个是要开放给httphandler做为ssl选项属性 |
||
| { | ||
| /// <summary> | ||
| /// 目标主机名,用于 TLS SNI(Server Name Indication)和证书验证。 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.