218 lines
8.0 KiB
C#
218 lines
8.0 KiB
C#
|
#if !UNITY_WEBGL || UNITY_EDITOR
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.IO;
|
||
|
|
||
|
using BestHTTP.Extensions;
|
||
|
|
||
|
#if !NETFX_CORE || UNITY_EDITOR
|
||
|
using System.Net.Security;
|
||
|
#endif
|
||
|
|
||
|
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Tls;
|
||
|
#endif
|
||
|
|
||
|
#if NETFX_CORE
|
||
|
using System.Threading.Tasks;
|
||
|
using Windows.Networking.Sockets;
|
||
|
|
||
|
using TcpClient = BestHTTP.PlatformSupport.TcpClient.WinRT.TcpClient;
|
||
|
|
||
|
//Disable CD4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
|
||
|
#pragma warning disable 4014
|
||
|
#else
|
||
|
using TcpClient = BestHTTP.PlatformSupport.TcpClient.General.TcpClient;
|
||
|
#endif
|
||
|
|
||
|
namespace BestHTTP.Connections
|
||
|
{
|
||
|
public sealed class TCPConnector : IDisposable
|
||
|
{
|
||
|
public bool IsConnected { get { return this.Client != null && this.Client.Connected; } }
|
||
|
|
||
|
public string NegotiatedProtocol { get; private set; }
|
||
|
|
||
|
public TcpClient Client { get; private set; }
|
||
|
|
||
|
public Stream TopmostStream { get; private set; }
|
||
|
|
||
|
public Stream Stream { get; private set; }
|
||
|
|
||
|
public bool LeaveOpen { get; set; }
|
||
|
|
||
|
public void Connect(HTTPRequest request)
|
||
|
{
|
||
|
string negotiatedProtocol = HTTPProtocolFactory.W3C_HTTP1;
|
||
|
|
||
|
Uri uri =
|
||
|
#if !BESTHTTP_DISABLE_PROXY
|
||
|
request.HasProxy ? request.Proxy.Address :
|
||
|
#endif
|
||
|
request.CurrentUri;
|
||
|
|
||
|
#region TCP Connection
|
||
|
|
||
|
if (Client == null)
|
||
|
Client = new TcpClient();
|
||
|
|
||
|
if (!Client.Connected)
|
||
|
{
|
||
|
Client.ConnectTimeout = request.ConnectTimeout;
|
||
|
|
||
|
#if NETFX_CORE
|
||
|
Client.UseHTTPSProtocol =
|
||
|
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||
|
!Request.UseAlternateSSL &&
|
||
|
#endif
|
||
|
HTTPProtocolFactory.IsSecureProtocol(uri);
|
||
|
#endif
|
||
|
|
||
|
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
|
||
|
HTTPManager.Logger.Verbose("TCPConnector", string.Format("'{0}' - Connecting to {1}:{2}", request.CurrentUri.ToString(), uri.Host, uri.Port.ToString()));
|
||
|
|
||
|
#if !NETFX_CORE && (!UNITY_WEBGL || UNITY_EDITOR)
|
||
|
Client.SendBufferSize = HTTPManager.SendBufferSize;
|
||
|
Client.ReceiveBufferSize = HTTPManager.ReceiveBufferSize;
|
||
|
|
||
|
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
|
||
|
HTTPManager.Logger.Verbose("TCPConnector", string.Format("'{0}' - Buffer sizes - Send: {1} Receive: {2} Blocking: {3}", request.CurrentUri.ToString(), Client.SendBufferSize.ToString(), Client.ReceiveBufferSize.ToString(), Client.Client.Blocking.ToString()));
|
||
|
#endif
|
||
|
|
||
|
Client.Connect(uri.Host, uri.Port);
|
||
|
|
||
|
if (HTTPManager.Logger.Level <= Logger.Loglevels.Information)
|
||
|
HTTPManager.Logger.Information("TCPConnector", "Connected to " + uri.Host + ":" + uri.Port.ToString());
|
||
|
}
|
||
|
else if (HTTPManager.Logger.Level <= Logger.Loglevels.Information)
|
||
|
HTTPManager.Logger.Information("TCPConnector", "Already connected to " + uri.Host + ":" + uri.Port.ToString());
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
if (Stream == null)
|
||
|
{
|
||
|
bool isSecure = HTTPProtocolFactory.IsSecureProtocol(request.CurrentUri);
|
||
|
|
||
|
this.Stream = this.TopmostStream = Client.GetStream();
|
||
|
|
||
|
/*if (Stream.CanTimeout)
|
||
|
Stream.ReadTimeout = Stream.WriteTimeout = (int)Request.Timeout.TotalMilliseconds;*/
|
||
|
|
||
|
|
||
|
#if !BESTHTTP_DISABLE_PROXY
|
||
|
if (request.Proxy != null)
|
||
|
request.Proxy.Connect(this.Stream, request);
|
||
|
#endif
|
||
|
|
||
|
// We have to use Request.CurrentUri here, because uri can be a proxy uri with a different protocol
|
||
|
if (isSecure)
|
||
|
{
|
||
|
#region SSL Upgrade
|
||
|
|
||
|
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||
|
if (request.UseAlternateSSL)
|
||
|
{
|
||
|
var handler = new TlsClientProtocol(Client.GetStream(), new BestHTTP.SecureProtocol.Org.BouncyCastle.Security.SecureRandom());
|
||
|
|
||
|
// http://tools.ietf.org/html/rfc3546#section-3.1
|
||
|
// -It is RECOMMENDED that clients include an extension of type "server_name" in the client hello whenever they locate a server by a supported name type.
|
||
|
// -Literal IPv4 and IPv6 addresses are not permitted in "HostName".
|
||
|
|
||
|
// User-defined list has a higher priority
|
||
|
List<string> hostNames = request.CustomTLSServerNameList;
|
||
|
|
||
|
// If there's no user defined one and the host isn't an IP address, add the default one
|
||
|
if ((hostNames == null || hostNames.Count == 0) && !request.CurrentUri.IsHostIsAnIPAddress())
|
||
|
{
|
||
|
hostNames = new List<string>(1);
|
||
|
hostNames.Add(request.CurrentUri.Host);
|
||
|
}
|
||
|
|
||
|
List<string> protocols = new List<string>();
|
||
|
#if !BESTHTTP_DISABLE_HTTP2
|
||
|
SupportedProtocols protocol = request.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(request.CurrentUri) : request.ProtocolHandler;
|
||
|
if (protocol == SupportedProtocols.HTTP)
|
||
|
{
|
||
|
// http/2 over tls (https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids)
|
||
|
protocols.Add(HTTPProtocolFactory.W3C_HTTP2);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
protocols.Add(HTTPProtocolFactory.W3C_HTTP1);
|
||
|
|
||
|
var tlsClient = new LegacyTlsClient(request.CurrentUri,
|
||
|
request.CustomCertificateVerifyer == null ? new AlwaysValidVerifyer() : request.CustomCertificateVerifyer,
|
||
|
request.CustomClientCredentialsProvider,
|
||
|
hostNames,
|
||
|
protocols);
|
||
|
handler.Connect(tlsClient);
|
||
|
|
||
|
if (!string.IsNullOrEmpty(tlsClient.ServerSupportedProtocol))
|
||
|
negotiatedProtocol = tlsClient.ServerSupportedProtocol;
|
||
|
|
||
|
Stream = handler.Stream;
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
#if !NETFX_CORE
|
||
|
SslStream sslStream = new SslStream(Client.GetStream(), false, (sender, cert, chain, errors) =>
|
||
|
{
|
||
|
return request.CallCustomCertificationValidator(cert, chain);
|
||
|
});
|
||
|
|
||
|
if (!sslStream.IsAuthenticated)
|
||
|
sslStream.AuthenticateAsClient(request.CurrentUri.Host);
|
||
|
Stream = sslStream;
|
||
|
#else
|
||
|
Stream = Client.GetStream();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.NegotiatedProtocol = negotiatedProtocol;
|
||
|
}
|
||
|
|
||
|
public void Close()
|
||
|
{
|
||
|
if (Client != null && !this.LeaveOpen)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
Client.Close();
|
||
|
}
|
||
|
catch
|
||
|
{
|
||
|
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
Stream = null;
|
||
|
Client = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Dispose()
|
||
|
{
|
||
|
Dispose(true);
|
||
|
GC.SuppressFinalize(this);
|
||
|
}
|
||
|
|
||
|
private void Dispose(bool disposing)
|
||
|
{
|
||
|
Close();
|
||
|
}
|
||
|
|
||
|
~TCPConnector()
|
||
|
{
|
||
|
Dispose(false);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
#endif
|