This repository has been archived on 2023-10-16. You can view files and clone it, but cannot push or open issues or pull requests.
hamakaze/Hamakaze/HttpConnection.cs

102 lines
3.2 KiB
C#
Raw Normal View History

2022-04-06 09:25:49 +00:00
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
2022-04-09 22:34:05 +00:00
using Hamakaze.WebSocket;
2022-04-06 09:25:49 +00:00
namespace Hamakaze {
public class HttpConnection : IDisposable {
public IPEndPoint EndPoint { get; }
public Stream Stream { get; }
2022-04-09 22:34:05 +00:00
private Socket Socket { get; }
2022-04-06 09:25:49 +00:00
2022-04-10 16:49:40 +00:00
private NetworkStream NetworkStream { get; }
private SslStream SslStream { get; }
2022-04-06 09:25:49 +00:00
public string Host { get; }
public bool IsSecure { get; }
2022-04-07 18:59:52 +00:00
public bool HasTimedOut => MaxRequests < 1 || (DateTimeOffset.Now - LastOperation) > MaxIdle;
2022-04-06 09:25:49 +00:00
2022-04-07 18:59:52 +00:00
public int? MaxRequests { get; set; } = null;
2022-04-06 09:25:49 +00:00
public TimeSpan MaxIdle { get; set; } = TimeSpan.MaxValue;
public DateTimeOffset LastOperation { get; private set; } = DateTimeOffset.Now;
public bool InUse { get; private set; }
2022-04-09 22:34:05 +00:00
public bool HasUpgraded { get; private set; }
2022-04-06 09:25:49 +00:00
public HttpConnection(string host, IPEndPoint endPoint, bool secure) {
Host = host ?? throw new ArgumentNullException(nameof(host));
EndPoint = endPoint ?? throw new ArgumentNullException(nameof(endPoint));
IsSecure = secure;
if(endPoint.AddressFamily is not AddressFamily.InterNetwork and not AddressFamily.InterNetworkV6)
throw new ArgumentException(@"Address must be an IPv4 or IPv6 address.", nameof(endPoint));
Socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) {
NoDelay = true,
Blocking = true,
};
Socket.Connect(endPoint);
2022-04-10 16:49:40 +00:00
NetworkStream = new NetworkStream(Socket, true);
2022-04-06 09:25:49 +00:00
if(IsSecure) {
2022-04-10 16:49:40 +00:00
SslStream = new SslStream(NetworkStream, false, (s, ce, ch, e) => e == SslPolicyErrors.None, null);
Stream = SslStream;
SslStream.AuthenticateAsClient(
2022-04-07 18:59:52 +00:00
Host,
null,
SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13,
true
);
2022-04-06 09:25:49 +00:00
} else
2022-04-10 16:49:40 +00:00
Stream = NetworkStream;
2022-04-06 09:25:49 +00:00
}
public void MarkUsed() {
LastOperation = DateTimeOffset.Now;
2022-04-07 18:59:52 +00:00
if(MaxRequests != null)
2022-04-06 09:25:49 +00:00
--MaxRequests;
}
public bool Acquire() {
2022-04-09 22:34:05 +00:00
return !HasUpgraded && !InUse && (InUse = true);
2022-04-06 09:25:49 +00:00
}
public void Release() {
InUse = false;
}
2022-04-09 22:34:05 +00:00
public WsConnection ToWebSocket() {
if(HasUpgraded)
throw new HttpConnectionAlreadyUpgradedException();
HasUpgraded = true;
2022-04-10 16:49:40 +00:00
NetworkStream.ReadTimeout = -1;
SslStream.ReadTimeout = -1;
2022-04-09 22:34:05 +00:00
return new WsConnection(Stream);
}
2022-04-06 09:25:49 +00:00
private bool IsDisposed;
~HttpConnection()
=> DoDispose();
public void Dispose() {
DoDispose();
GC.SuppressFinalize(this);
}
private void DoDispose() {
if(IsDisposed)
return;
IsDisposed = true;
2022-04-09 22:34:05 +00:00
if(!HasUpgraded)
Stream.Dispose();
2022-04-06 09:25:49 +00:00
}
}
}