From 6dea876e392dd9a80290762ee7f35d8e438c0ee1 Mon Sep 17 00:00:00 2001 From: Grant Birchmeier Date: Wed, 31 Jan 2024 17:25:49 -0600 Subject: [PATCH 1/3] small fix to SessionFactory --- QuickFIXn/SessionFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QuickFIXn/SessionFactory.cs b/QuickFIXn/SessionFactory.cs index eb433a8a1..7ceca116d 100755 --- a/QuickFIXn/SessionFactory.cs +++ b/QuickFIXn/SessionFactory.cs @@ -23,7 +23,7 @@ public SessionFactory( IMessageFactory? messageFactory = null) { // TODO: for V2, consider ONLY instantiating MessageFactory in the Create() method, - // and removing instance var messageFactory_ altogether. + // and removing instance var _messageFactory altogether. // This makes sense because we can't distinguish FIX50 versions here in this constructor, // and thus can't create the right FIX-Version factory because we don't know what // session to use to look up the BeginString and DefaultApplVerID. From d6afffd50544033e32c044a0e2cdc714d6ec3b6d Mon Sep 17 00:00:00 2001 From: Grant Birchmeier Date: Wed, 31 Jan 2024 17:26:18 -0600 Subject: [PATCH 2/3] cleanup/nullableize StreamFactory --- QuickFIXn/Transport/StreamFactory.cs | 273 +++++++++++++++------------ 1 file changed, 155 insertions(+), 118 deletions(-) diff --git a/QuickFIXn/Transport/StreamFactory.cs b/QuickFIXn/Transport/StreamFactory.cs index a420ed585..8b637fea9 100644 --- a/QuickFIXn/Transport/StreamFactory.cs +++ b/QuickFIXn/Transport/StreamFactory.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -17,9 +18,9 @@ namespace QuickFix.Transport /// public static class StreamFactory { - private static Socket CreateTunnelThruProxy(string destIP, int destPort) + private static Socket? CreateTunnelThruProxy(string destIp, int destPort) { - string destUriWithPort = $"{destIP}:{destPort}"; + string destUriWithPort = $"{destIp}:{destPort}"; UriBuilder uriBuilder = new UriBuilder(destUriWithPort); Uri destUri = uriBuilder.Uri; IWebProxy webProxy = WebRequest.GetSystemWebProxy(); @@ -36,8 +37,8 @@ private static Socket CreateTunnelThruProxy(string destIP, int destPort) return null; } - Uri proxyUri = webProxy.GetProxy(destUri); - if (proxyUri == null) + Uri? proxyUri = webProxy.GetProxy(destUri); + if (proxyUri is null) return null; IPAddress[] proxyEntry = Dns.GetHostAddresses(proxyUri.Host); @@ -47,18 +48,17 @@ private static Socket CreateTunnelThruProxy(string destIP, int destPort) Socket socketThruProxy = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socketThruProxy.Connect(proxyEndPoint); - string proxyMsg = $"CONNECT {destIP}:{destPort} HTTP/1.1 \n\n"; + string proxyMsg = $"CONNECT {destIp}:{destPort} HTTP/1.1 \n\n"; byte[] buffer = Encoding.ASCII.GetBytes(proxyMsg); byte[] buffer12 = new byte[500]; socketThruProxy.Send(buffer, buffer.Length, 0); - int msg = socketThruProxy.Receive(buffer12, 500, 0); - string data; - data = Encoding.ASCII.GetString(buffer12); + socketThruProxy.Receive(buffer12, 500, 0); + string data = Encoding.ASCII.GetString(buffer12); int index = data.IndexOf("200", StringComparison.Ordinal); if (index < 0) throw new ApplicationException( - $"Connection failed to {destUriWithPort} through proxy server {proxyUri.ToString()}."); + $"Connection failed to {destUriWithPort} through proxy server {proxyUri}."); return socketThruProxy; } @@ -73,10 +73,10 @@ private static Socket CreateTunnelThruProxy(string destIP, int destPort) public static Stream CreateClientStream(IPEndPoint endpoint, SocketSettings settings, ILog logger) { // If system has configured a proxy for this config, use it. - Socket socket = CreateTunnelThruProxy(endpoint.Address.ToString(), endpoint.Port); + Socket? socket = CreateTunnelThruProxy(endpoint.Address.ToString(), endpoint.Port); // No proxy. Set up a regular socket. - if (socket == null) + if (socket is null) { socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.NoDelay = settings.SocketNodelay; @@ -102,10 +102,7 @@ public static Stream CreateClientStream(IPEndPoint endpoint, SocketSettings sett Stream stream = new NetworkStream(socket, true); if (settings.UseSSL) - { - stream = new SSLStreamFactory(logger, settings) - .CreateClientStreamAndAuthenticate(stream); - } + stream = new SslStreamFactory(logger, settings).CreateClientStreamAndAuthenticate(stream); return stream; } @@ -121,13 +118,12 @@ public static Stream CreateClientStream(IPEndPoint endpoint, SocketSettings sett public static Stream CreateServerStream(TcpClient tcpClient, SocketSettings settings, ILog logger) { if (tcpClient.Connected == false) - throw new ArgumentException("tcp client must be connected in order to get stream", "tcpClient"); + throw new ArgumentException("tcp client must be connected in order to get stream", nameof(tcpClient)); Stream stream = tcpClient.GetStream(); if (settings.UseSSL) { - stream = new SSLStreamFactory(logger, settings) - .CreateServerStreamAndAuthenticate(stream); + stream = new SslStreamFactory(logger, settings).CreateServerStreamAndAuthenticate(stream); } return stream; @@ -136,7 +132,7 @@ public static Stream CreateServerStream(TcpClient tcpClient, SocketSettings sett /// /// Cache loaded certificates since loading a certificate can be a costly operation /// - private static Dictionary _certificateCache = new Dictionary(); + private static readonly Dictionary CertificateCache = new (); /// /// Loads the specified certificate given a path, DistinguishedName or subject name @@ -144,22 +140,20 @@ public static Stream CreateServerStream(TcpClient tcpClient, SocketSettings sett /// The certificate path or DistinguishedName/subjectname if it should be loaded from the personal certificate store. /// The certificate password. /// The specified certificate, or null if no certificate is found - internal static X509Certificate2 LoadCertificate(string name, string password) + private static X509Certificate2? LoadCertificate(string name, string? password) { - X509Certificate2 certificate; - // TODO: Change _certificateCache's type to ConcurrentDictionary once we start targeting .NET 4, // then remove this lock and use GetOrAdd function of concurrent dictionary // e.g.: certificate = _certificateCache.GetOrAdd(name, (key) => LoadCertificateInner(name, password)); - lock (_certificateCache) + lock (CertificateCache) { - if (_certificateCache.TryGetValue(name, out certificate)) + if (CertificateCache.TryGetValue(name, out X509Certificate2? certificate)) return certificate; certificate = LoadCertificateInner(name, password); - if (certificate != null) - _certificateCache.Add(name, certificate); + if (certificate is not null) + CertificateCache.Add(name, certificate); return certificate; } @@ -171,21 +165,19 @@ internal static X509Certificate2 LoadCertificate(string name, string password) /// The certificate path or DistinguishedName/subjectname if it should be loaded from the personal certificate store. /// The certificate password. /// The specified certificate, or null if no certificate is found - private static X509Certificate2 LoadCertificateInner(string name, string password) + private static X509Certificate2? LoadCertificateInner(string name, string? password) { - X509Certificate2 certificate; + X509Certificate2? certificate; // If no extension is found try to get from certificate store if (!File.Exists(name)) { certificate = GetCertificateFromStore(name); } - else - { - if (password != null) - certificate = new X509Certificate2(name, password); - else - certificate = new X509Certificate2(name); + else { + certificate = password is not null + ? new X509Certificate2(name, password) + : new X509Certificate2(name); } return certificate; } @@ -195,14 +187,14 @@ private static X509Certificate2 LoadCertificateInner(string name, string passwor /// /// See http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx for complete example /// Name of the cert. - /// - private static X509Certificate2 GetCertificateFromStore(string certName) + /// The cert, or null if not found + private static X509Certificate2? GetCertificateFromStore(string certName) { return GetCertificateFromStoreHelper(certName, new X509Store(StoreLocation.LocalMachine)) ?? GetCertificateFromStoreHelper(certName, new X509Store(StoreLocation.CurrentUser)); } - private static X509Certificate2 GetCertificateFromStoreHelper(string certName, X509Store store) + private static X509Certificate2? GetCertificateFromStoreHelper(string certName, X509Store store) { try { @@ -214,10 +206,9 @@ private static X509Certificate2 GetCertificateFromStoreHelper(string certName, X // currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, true); X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false); - if (certName.Contains("CN=")) - currentCerts = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, false); - else - currentCerts = currentCerts.Find(X509FindType.FindBySubjectName, certName, false); + currentCerts = currentCerts.Find(certName.Contains("CN=") + ? X509FindType.FindBySubjectDistinguishedName + : X509FindType.FindBySubjectName, certName, false); if (currentCerts.Count == 0) return null; @@ -234,17 +225,17 @@ private static X509Certificate2 GetCertificateFromStoreHelper(string certName, X /// /// The SSLClientStreamFactory is responsible for setting up a SSLStream in either client or server mode /// - private sealed class SSLStreamFactory + private sealed class SslStreamFactory { - ILog log_; - SocketSettings socketSettings_; - const string clientAuthenticationOid = "1.3.6.1.5.5.7.3.2"; - const string serverAuthenticationOid = "1.3.6.1.5.5.7.3.1"; + private readonly ILog _log; + private readonly SocketSettings _socketSettings; + private const string CLIENT_AUTHENTICATION_OID = "1.3.6.1.5.5.7.3.2"; + private const string SERVER_AUTHENTICATION_OID = "1.3.6.1.5.5.7.3.1"; - public SSLStreamFactory(ILog log, SocketSettings settings) + public SslStreamFactory(ILog log, SocketSettings settings) { - log_ = log; - socketSettings_ = settings; + _log = log; + _socketSettings = settings; } /// @@ -254,20 +245,27 @@ public SSLStreamFactory(ILog log, SocketSettings settings) /// a ssl enabled stream public Stream CreateClientStreamAndAuthenticate(Stream innerStream) { - SslStream sslStream = new SslStream(innerStream, false, ValidateServerCertificate, SelectLocalCertificate); + SslStream sslStream = new SslStream( + innerStream, + false, + ValidateServerCertificate, +#pragma warning disable CS8621 // Nullability of reference types in return type doesn't match the target delegate (possibly because of nullability attributes). + // Per MS docs, this delete /should/ have a nullable return type + SelectLocalCertificate); +#pragma warning restore CS8621 // Nullability of reference types in return type doesn't match the target delegate (possibly because of nullability attributes). try { // Setup secure SSL Communication X509CertificateCollection clientCertificates = GetClientCertificates(); - sslStream.AuthenticateAsClient(socketSettings_.ServerCommonName, + sslStream.AuthenticateAsClient(_socketSettings.ServerCommonName, clientCertificates, - socketSettings_.SslProtocol, - socketSettings_.CheckCertificateRevocation); + _socketSettings.SslProtocol, + _socketSettings.CheckCertificateRevocation); } catch (System.Security.Authentication.AuthenticationException ex) { - log_.OnEvent("Unable to perform authentication against server: " + ex.Message); + _log.OnEvent("Unable to perform authentication against server: " + ex.Message); throw; } @@ -281,23 +279,34 @@ public Stream CreateClientStreamAndAuthenticate(Stream innerStream) /// a ssl enabled stream public Stream CreateServerStreamAndAuthenticate(Stream innerStream) { - SslStream sslStream = new SslStream(innerStream, false, ValidateClientCertificate, SelectLocalCertificate); + SslStream sslStream = new SslStream( + innerStream, + false, + ValidateClientCertificate, +#pragma warning disable CS8621 // Nullability of reference types in return type doesn't match the target delegate (possibly because of nullability attributes). + // Per MS docs, this delete /should/ have a nullable return type + SelectLocalCertificate); +#pragma warning restore CS8621 // Nullability of reference types in return type doesn't match the target delegate (possibly because of nullability attributes). try { - if (string.IsNullOrEmpty(socketSettings_.CertificatePath)) - throw new Exception(string.Format("No server certificate specified, the {0} setting must be configured", SessionSettings.SSL_CERTIFICATE)); + if (string.IsNullOrEmpty(_socketSettings.CertificatePath)) + throw new Exception($"No server certificate specified, the {SessionSettings.SSL_CERTIFICATE} setting must be configured"); // Setup secure SSL Communication - X509Certificate2 serverCertificate = StreamFactory.LoadCertificate(socketSettings_.CertificatePath, socketSettings_.CertificatePassword); - sslStream.AuthenticateAsServer(serverCertificate, - socketSettings_.RequireClientCertificate, - socketSettings_.SslProtocol, - socketSettings_.CheckCertificateRevocation); + X509Certificate2? serverCertificate = LoadCertificate(_socketSettings.CertificatePath, _socketSettings.CertificatePassword); + sslStream.AuthenticateAsServer(new SslServerAuthenticationOptions + { + ServerCertificate = serverCertificate, + ClientCertificateRequired = _socketSettings.RequireClientCertificate, + EnabledSslProtocols = _socketSettings.SslProtocol, + CertificateRevocationCheckMode = _socketSettings.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck, + EncryptionPolicy = EncryptionPolicy.RequireEncryption + }); } catch (System.Security.Authentication.AuthenticationException ex) { - log_.OnEvent("Unable to perform authentication against server: " + ex.Message); + _log.OnEvent("Unable to perform authentication against server: " + ex.Message); throw; } @@ -306,44 +315,43 @@ public Stream CreateServerStreamAndAuthenticate(Stream innerStream) private X509CertificateCollection GetClientCertificates() { - if (!string.IsNullOrEmpty(socketSettings_.CertificatePath)) - { - X509CertificateCollection certificates = new X509Certificate2Collection(); - X509Certificate2 clientCert = StreamFactory.LoadCertificate(socketSettings_.CertificatePath, socketSettings_.CertificatePassword); - certificates.Add(clientCert); - return certificates; - } - else + var rv = new X509Certificate2Collection(); + if (!string.IsNullOrEmpty(_socketSettings.CertificatePath)) { - return new X509Certificate2Collection(); + X509Certificate2? clientCert = LoadCertificate(_socketSettings.CertificatePath, _socketSettings.CertificatePassword); + if (clientCert is not null) + rv.Add(clientCert); } - } + return rv; + } /// /// Perform validation of the servers certificate. (the initiator validates the server/acceptors certificate) + /// (Satisfies interface to delegate System.Net.Security.RemoteCertificateValidationCallback) /// /// The sender. /// The certificate. /// The chain. /// The SSL policy errors. /// true if the certificate should be treated as trusted; otherwise false - private bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + private bool ValidateServerCertificate(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { - return VerifyRemoteCertificate(certificate, sslPolicyErrors, serverAuthenticationOid); + return VerifyRemoteCertificate(certificate, sslPolicyErrors, SERVER_AUTHENTICATION_OID); } /// /// Perform validation of a a client certificate.(the acceptor validates the client/initiators certificate) + /// (Satisfies interface to delegate System.Net.Security.RemoteCertificateValidationCallback) /// /// The sender. /// The certificate. /// The chain. /// The SSL policy errors. /// true if the certificate should be treated as trusted; otherwise false - private bool ValidateClientCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + private bool ValidateClientCertificate(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { - return VerifyRemoteCertificate(certificate, sslPolicyErrors, clientAuthenticationOid); + return VerifyRemoteCertificate(certificate, sslPolicyErrors, CLIENT_AUTHENTICATION_OID); } /// @@ -353,46 +361,66 @@ private bool ValidateClientCertificate(object sender, X509Certificate certificat /// The SSL policy errors supplied by .Net. /// Enhanced key usage, which the remote computers certificate should contain. /// true if the certificate should be treated as trusted; otherwise false - private bool VerifyRemoteCertificate(X509Certificate certificate, SslPolicyErrors sslPolicyErrors, string enhancedKeyUsage) + private bool VerifyRemoteCertificate( + X509Certificate? certificate, + SslPolicyErrors sslPolicyErrors, + string enhancedKeyUsage) { // Accept without looking at if the certificate is valid if validation is disabled - if (socketSettings_.ValidateCertificates == false) + if (_socketSettings.ValidateCertificates == false) return true; + if (certificate is null) + return false; + // Validate enhanced key usage - if (!ContainsEnhancedKeyUsage(certificate, enhancedKeyUsage)) - { - if (enhancedKeyUsage == clientAuthenticationOid) - log_.OnEvent("Remote certificate is not intended for client authentication: It is missing enhanced key usage " + enhancedKeyUsage); + if (!ContainsEnhancedKeyUsage(certificate, enhancedKeyUsage)) { + if (enhancedKeyUsage == CLIENT_AUTHENTICATION_OID) + _log.OnEvent( + "Remote certificate is not intended for client authentication: It is missing enhanced key usage " + + enhancedKeyUsage); else - log_.OnEvent("Remote certificate is not intended for server authentication: It is missing enhanced key usage " + enhancedKeyUsage); + _log.OnEvent( + "Remote certificate is not intended for server authentication: It is missing enhanced key usage " + + enhancedKeyUsage); + + return false; + } + + if (string.IsNullOrEmpty(_socketSettings.CACertificatePath)) { + _log.OnEvent("CACertificatePath is not specified"); + return false; + } + // If CA Certficiate is specified then validate agains the CA certificate, otherwise it is validated against the installed certificates + X509Certificate2? cert = LoadCertificate(_socketSettings.CACertificatePath, null); + if (cert is null) { + _log.OnEvent("Remote certificate was not recognized as a valid certificate: " + sslPolicyErrors); return false; } - // If CA Certficiate is specifed then validate agains the CA certificate, otherwise it is validated against the installed certificates - if (!string.IsNullOrEmpty(socketSettings_.CACertificatePath)) + X509Chain chain0 = new X509Chain(); + chain0.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + // add all your extra certificate chain + + chain0.ChainPolicy.ExtraStore.Add(cert); + chain0.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; + bool isValid = chain0.Build((X509Certificate2)certificate); + + if (isValid) + { + // resets the sslPolicyErrors.RemoteCertificateChainErrors status + sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors; + } + else { - X509Chain chain0 = new X509Chain(); - chain0.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - // add all your extra certificate chain - - chain0.ChainPolicy.ExtraStore.Add(StreamFactory.LoadCertificate(socketSettings_.CACertificatePath, null)); - chain0.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; - bool isValid = chain0.Build((X509Certificate2)certificate); - - // If the certificate is valid then reset the sslPolicyErrors.RemoteCertificateChainErrors status - if (isValid) - sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors; - // If the certificate could not be validated against CA, then set the SslPolicyErrors.RemoteCertificateChainErrors - else //if (isValid == false) - sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; + sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; } // Any basic authentication check failed, do after checking CA if (sslPolicyErrors != SslPolicyErrors.None) { - log_.OnEvent("Remote certificate was not recognized as a valid certificate: " + sslPolicyErrors); + _log.OnEvent("Remote certificate was not recognized as a valid certificate: " + sslPolicyErrors); return false; } @@ -408,16 +436,12 @@ private bool VerifyRemoteCertificate(X509Certificate certificate, SslPolicyError /// true if the oid is specified as an enhanced key usage; otherwise false private static bool ContainsEnhancedKeyUsage(X509Certificate certificate, string enhancedKeyOid) { - X509Certificate2 cert2 = certificate as X509Certificate2; - - if (cert2 == null) - cert2 = new X509Certificate2(certificate); + X509Certificate2 cert2 = certificate as X509Certificate2 ?? new X509Certificate2(certificate); foreach (X509Extension extension in cert2.Extensions) { - if (extension is X509EnhancedKeyUsageExtension) + if (extension is X509EnhancedKeyUsageExtension keyUsage) { - X509EnhancedKeyUsageExtension keyUsage = (X509EnhancedKeyUsageExtension)extension; foreach (System.Security.Cryptography.Oid oid in keyUsage.EnhancedKeyUsages) { if (oid.Value == enhancedKeyOid) @@ -429,21 +453,31 @@ private static bool ContainsEnhancedKeyUsage(X509Certificate certificate, string return false; } - - public X509Certificate SelectLocalCertificate(object sender, string targetHost, - X509CertificateCollection localCertificates, - X509Certificate remoteCertificate, - string[] acceptableIssuers) + /// + /// (Satisfies interface to delegate System.Net.Security.LocalCertificateSelectionCallback) + /// + /// + /// + /// + /// + /// + /// + private static X509Certificate? SelectLocalCertificate( + object sender, + string targetHost, + X509CertificateCollection localCertificates, + X509Certificate? remoteCertificate, + string[] acceptableIssuers) { // No certificate can be selected if we have no local certificates at all - if (localCertificates == null || localCertificates.Count <= 0) + if (localCertificates.Count <= 0) return null; - Debug.Assert(localCertificates != null && localCertificates.Count > 0); + Debug.Assert(localCertificates is not null && localCertificates.Count > 0); //Otherwise we select the first availible certificate as per msdn documentation // http://msdn.microsoft.com/en-us/library/system.net.security.localcertificateselectioncallback.aspx - if (acceptableIssuers != null) + if (acceptableIssuers.Length > 0) { // Use the first certificate that is from an acceptable issuer. foreach (X509Certificate certificate in localCertificates) @@ -454,8 +488,11 @@ public X509Certificate SelectLocalCertificate(object sender, string targetHost, } } - // Just use any certificate - return localCertificates[0]; + // Just use any certificate (if there is one) + if (localCertificates.Count > 0) + return localCertificates[0]; + + return null; } } } From d3a7f840bd5daeddb5c1599ef67a2b45ba863c10 Mon Sep 17 00:00:00 2001 From: Grant Birchmeier Date: Thu, 1 Feb 2024 09:51:49 -0600 Subject: [PATCH 3/3] cleanup/nullable-ize HttpServer/Session/Settings --- QuickFIXn/HttpServer.cs | 15 ++- QuickFIXn/SessionID.cs | 131 ++++++-------------- QuickFIXn/SessionSchedule.cs | 194 ++++++++++++------------------ QuickFIXn/SessionSettings.cs | 103 ++++++++-------- QuickFIXn/Settings.cs | 20 +-- RELEASE_NOTES.md | 4 + UnitTests/SessionScheduleTests.cs | 95 --------------- 7 files changed, 193 insertions(+), 369 deletions(-) diff --git a/QuickFIXn/HttpServer.cs b/QuickFIXn/HttpServer.cs index 2de690779..d0c452e3a 100644 --- a/QuickFIXn/HttpServer.cs +++ b/QuickFIXn/HttpServer.cs @@ -153,7 +153,10 @@ private string EnableSessions(HttpListenerRequest request, StringBuilder pageBui private string RefreshSession(HttpListenerRequest request, StringBuilder pageBuilder) { - SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]); + SessionID sessionId = new SessionID( + request.QueryString["beginstring"] ?? "", + request.QueryString["sendercompid"] ?? "", + request.QueryString["targetcompid"] ?? ""); Session? sessionDetails = Session.LookupSession(sessionId); if (sessionDetails == null) throw new Exception("Session not found"); bool confirm = false; @@ -248,7 +251,10 @@ private string ResetSessions(HttpListenerRequest request, StringBuilder pageBuil private string ResetSession(HttpListenerRequest request, StringBuilder pageBuilder) { - SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]); + SessionID sessionId = new SessionID( + request.QueryString["beginstring"] ?? "", + request.QueryString["sendercompid"] ?? "", + request.QueryString["targetcompid"] ?? ""); Session? sessionDetails = Session.LookupSession(sessionId); if (sessionDetails == null) throw new Exception("Session not found"); @@ -326,7 +332,10 @@ private string ProcessRoot(HttpListenerRequest request, StringBuilder pageBuilde private string SessionDetails(HttpListenerRequest request, StringBuilder pageBuilder) { - SessionID sessionId = new SessionID(request.QueryString["beginstring"], request.QueryString["sendercompid"], request.QueryString["targetcompid"]); + SessionID sessionId = new SessionID( + request.QueryString["beginstring"] ?? "", + request.QueryString["sendercompid"] ?? "", + request.QueryString["targetcompid"] ?? ""); Session? sessionDetails = Session.LookupSession(sessionId); if (sessionDetails == null) throw new Exception("Session not found"); diff --git a/QuickFIXn/SessionID.cs b/QuickFIXn/SessionID.cs index cef7203c0..55fc2cb98 100755 --- a/QuickFIXn/SessionID.cs +++ b/QuickFIXn/SessionID.cs @@ -1,4 +1,4 @@ - +#nullable enable using System; namespace QuickFix @@ -8,64 +8,36 @@ namespace QuickFix /// and a session qualifier. Sessions are also identified by FIX version so /// that it's possible to have multiple sessions to the same counterparty /// but using different FIX versions (and/or session qualifiers). - /// /// public class SessionID { #region Properties - public string BeginString - { - get { return beginString_; } - } + public string BeginString { get; } - public string SenderCompID - { - get { return senderCompID_; } - } + public string SenderCompID { get; } - public string SenderSubID - { - get { return senderSubID_; } - } + public string SenderSubID { get; } - public string SenderLocationID - { - get { return senderLocationID_; } - } + public string SenderLocationID { get; } - public string TargetCompID - { - get { return targetCompID_; } - } + public string TargetCompID { get; } - public string TargetSubID - { - get { return targetSubID_; } - } + public string TargetSubID { get; } - public string TargetLocationID - { - get { return targetLocationID_; } - } + public string TargetLocationID { get; } /// /// Session qualifier can be used to identify different sessions /// for the same target company ID. Session qualifiers can only be used /// with initiated sessions. They cannot be used with accepted sessions. /// - public string SessionQualifier - { - get { return sessionQualifier_; } - } + public string? SessionQualifier { get; } /// /// Returns whether session version is FIXT 1.1 or newer /// - public bool IsFIXT - { - get { return isFIXT_; } - } + public bool IsFIXT { get; } #endregion @@ -74,87 +46,64 @@ public bool IsFIXT #endregion #region Private Members - private string id_; - private string beginString_; - private string senderCompID_; - private string senderSubID_; - private string senderLocationID_; - private string targetCompID_; - private string targetSubID_; - private string targetLocationID_; - private string sessionQualifier_; - private bool isFIXT_; + private readonly string _id; #endregion - public SessionID(string beginString, string senderCompID, string senderSubID, string senderLocationID, string targetCompID, string targetSubID, string targetLocationID, string sessionQualifier) + public SessionID(string beginString, string senderCompId, string senderSubId, string senderLocationId, string targetCompId, string targetSubId, string targetLocationId, string? sessionQualifier = NOT_SET) { - if (beginString == null) - throw new ArgumentNullException("beginString"); - if (senderCompID == null) - throw new ArgumentNullException("senderCompID"); - if (targetCompID == null) - throw new ArgumentNullException("targetCompID"); - beginString_ = beginString; - senderCompID_ = senderCompID; - senderSubID_ = senderSubID; - senderLocationID_ = senderLocationID; - targetCompID_ = targetCompID; - targetSubID_ = targetSubID; - targetLocationID_ = targetLocationID; - sessionQualifier_ = sessionQualifier; - isFIXT_ = beginString_.StartsWith("FIXT", StringComparison.Ordinal); - - id_ = beginString_ + BeginString = beginString ?? throw new ArgumentNullException(nameof(beginString)); + SenderCompID = senderCompId ?? throw new ArgumentNullException(nameof(senderCompId)); + SenderSubID = senderSubId; + SenderLocationID = senderLocationId; + TargetCompID = targetCompId ?? throw new ArgumentNullException(nameof(targetCompId)); + TargetSubID = targetSubId; + TargetLocationID = targetLocationId; + SessionQualifier = sessionQualifier; + IsFIXT = BeginString.StartsWith("FIXT", StringComparison.Ordinal); + + _id = BeginString + ":" - + senderCompID_ - + (IsSet(senderSubID_) ? "/" + senderSubID_ : "") - + (IsSet(senderLocationID_) ? "/" + senderLocationID_ : "") + + SenderCompID + + (IsSet(SenderSubID) ? "/" + SenderSubID : "") + + (IsSet(SenderLocationID) ? "/" + SenderLocationID : "") + "->" - + targetCompID_ - + (IsSet(targetSubID_) ? "/" + targetSubID_ : "") - + (IsSet(targetLocationID_) ? "/" + targetLocationID_ : ""); - if (null != sessionQualifier_ && sessionQualifier_.Length > 0) - id_ += ":" + sessionQualifier_; + + TargetCompID + + (IsSet(TargetSubID) ? "/" + TargetSubID : "") + + (IsSet(TargetLocationID) ? "/" + TargetLocationID : ""); + if (SessionQualifier is not null && SessionQualifier.Length > 0) + _id += ":" + SessionQualifier; } - public SessionID(string beginString, string senderCompID, string targetCompID) - : this(beginString, senderCompID, targetCompID, NOT_SET) - { } - - public SessionID(string beginString, string senderCompID, string senderSubID, string targetCompID, string targetSubID) - : this(beginString, senderCompID, senderSubID, NOT_SET, targetCompID, targetSubID, NOT_SET, NOT_SET) - { } - - public SessionID(string beginString, string senderCompID, string senderSubID, string senderLocationID, string targetCompID, string targetSubID, string targetLocationID) - : this(beginString, senderCompID, senderSubID, senderLocationID, targetCompID, targetSubID, targetLocationID, NOT_SET) + public SessionID(string beginString, string senderCompId, string senderSubId, string targetCompId, string targetSubId) + : this(beginString, senderCompId, senderSubId, senderLocationId: NOT_SET, targetCompId, targetSubId, targetLocationId: NOT_SET) { } - public SessionID(string beginString, string senderCompID, string targetCompID, string sessionQualifier) - : this(beginString, senderCompID, NOT_SET, NOT_SET, targetCompID, NOT_SET, NOT_SET, sessionQualifier) + public SessionID(string beginString, string senderCompId, string targetCompId, string sessionQualifier = NOT_SET) + : this(beginString, senderCompId, senderSubId: NOT_SET, senderLocationId: NOT_SET, targetCompId, targetSubId: NOT_SET, targetLocationId: NOT_SET, sessionQualifier) { } - public static bool IsSet(string value) + public static bool IsSet(string? value) { return value != null && value != NOT_SET; } public override string ToString() { - return id_; + return _id; } public override int GetHashCode() { - return id_.GetHashCode(); + return _id.GetHashCode(); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj == null || GetType() != obj.GetType()) return false; SessionID rhs = (SessionID)obj; - return id_.Equals(rhs.id_); + return _id.Equals(rhs._id); } } } diff --git a/QuickFIXn/SessionSchedule.cs b/QuickFIXn/SessionSchedule.cs index 7e4b54d61..ebb8390df 100755 --- a/QuickFIXn/SessionSchedule.cs +++ b/QuickFIXn/SessionSchedule.cs @@ -1,47 +1,48 @@ - +#nullable enable using System; + namespace QuickFix { public class SessionSchedule { - public System.TimeSpan StartTime { get; private set; } - public System.TimeSpan EndTime { get; private set; } + public TimeSpan? StartTime { get; } + public TimeSpan? EndTime { get; } - public bool WeeklySession { get; private set; } - public System.DayOfWeek StartDay { get; private set; } - public System.DayOfWeek EndDay { get; private set; } + public bool WeeklySession { get; } + public DayOfWeek? StartDay { get; } + public DayOfWeek? EndDay { get; } - public bool NonStopSession { get; private set; } + public bool NonStopSession { get; } - public bool UseLocalTime { get; private set; } - public System.TimeZoneInfo TimeZone { get; private set; } + public bool UseLocalTime { get; } + public TimeZoneInfo? TimeZone { get; } /// /// Returns true if testtime is in a different and newer session than old time /// (or more explicitly: oldtime <= some EndTime < testtime) /// - /// - /// + /// + /// /// - public bool IsNewSession(DateTime oldtime_utc, DateTime testtime_utc) + public bool IsNewSession(DateTime oldtimeUtc, DateTime testtimeUtc) { if (NonStopSession) { return false; } - if (oldtime_utc.Kind != System.DateTimeKind.Utc) - throw new System.ArgumentException("Only UTC time is supported", "oldtime"); - if (testtime_utc.Kind != System.DateTimeKind.Utc) - throw new System.ArgumentException("Only UTC time is supported", "testtime"); + if (oldtimeUtc.Kind != DateTimeKind.Utc) + throw new ArgumentException("Only UTC time is supported", nameof(oldtimeUtc)); + if (testtimeUtc.Kind != DateTimeKind.Utc) + throw new ArgumentException("Only UTC time is supported", nameof(testtimeUtc)); - DateTime old = AdjustUtcDateTime(oldtime_utc); - DateTime test = AdjustUtcDateTime(testtime_utc); + DateTime old = AdjustUtcDateTime(oldtimeUtc); + DateTime test = AdjustUtcDateTime(testtimeUtc); if (DateTime.Compare(old, test) < 0) // old is earlier than test { - DateTime nextend = NextEndTime(oldtime_utc); + DateTime nextend = NextEndTime(oldtimeUtc); return (DateTime.Compare(old, nextend) <= 0) && (DateTime.Compare(nextend, test) < 0); } @@ -55,28 +56,23 @@ public bool IsNewSession(DateTime oldtime_utc, DateTime testtime_utc) /// public DateTime AdjustUtcDateTime(DateTime utc) { - if (utc.Kind != System.DateTimeKind.Utc) - throw new System.ArgumentException("Only UTC time is supported", "time"); + if (utc.Kind != DateTimeKind.Utc) + throw new ArgumentException("Only UTC time is supported", nameof(utc)); if(UseLocalTime) return utc.ToLocalTime(); - else if (TimeZone==null) - return utc; - else - return System.TimeZoneInfo.ConvertTimeFromUtc(utc, TimeZone); + + return TimeZone==null ? utc : TimeZoneInfo.ConvertTimeFromUtc(utc, TimeZone); } - public bool IsSessionTime(System.DateTime utc) + public bool IsSessionTime(DateTime utc) { - if (utc.Kind != System.DateTimeKind.Utc) - throw new System.ArgumentException("Only UTC time is supported", "time"); + if (utc.Kind != DateTimeKind.Utc) + throw new ArgumentException("Only UTC time is supported", nameof(utc)); - System.DateTime adjusted = AdjustUtcDateTime(utc); + DateTime adjusted = AdjustUtcDateTime(utc); - if (WeeklySession) - return CheckDay(adjusted); - else - return CheckTime(adjusted.TimeOfDay); + return WeeklySession ? CheckDay(adjusted) : CheckTime(adjusted.TimeOfDay); } /// @@ -87,19 +83,19 @@ public bool IsSessionTime(System.DateTime utc) public DateTime NextEndTime(DateTime utc) { if (NonStopSession) - { throw new NotSupportedException("NonStopSession"); - } + + TimeSpan vEndTime = EndTime ?? throw new QuickFix.ConfigError("EndTime is null"); if (utc.Kind != DateTimeKind.Utc) - throw new ArgumentException("Only UTC time is supported", "time"); + throw new ArgumentException("Only UTC time is supported", nameof(utc)); DateTime d = AdjustUtcDateTime(utc); DateTime end = DateTime.MinValue; if (WeeklySession) { - end = new DateTime(d.Year, d.Month, d.Day, EndTime.Hours, EndTime.Minutes, EndTime.Seconds, d.Kind); + end = new DateTime(d.Year, d.Month, d.Day, vEndTime.Hours, vEndTime.Minutes, vEndTime.Seconds, d.Kind); while (end.DayOfWeek != EndDay) end = end.AddDays(1); if (DateTime.Compare(d, end) > 0) // d is later than end @@ -107,7 +103,7 @@ public DateTime NextEndTime(DateTime utc) } else { - end = new DateTime(d.Year, d.Month, d.Day, EndTime.Hours, EndTime.Minutes, EndTime.Seconds, d.Kind); + end = new DateTime(d.Year, d.Month, d.Day, vEndTime.Hours, vEndTime.Minutes, vEndTime.Seconds, d.Kind); if (DateTime.Compare(d, end) > 0) // d is later than end end = end.AddDays(1); } @@ -115,83 +111,48 @@ public DateTime NextEndTime(DateTime utc) return end; } - // TODO: consider removing this function in v2.0, as it's not used. - /// - /// Return the latest EndTime (in UTC) before time. - /// - /// - /// - public DateTime LastEndTime(DateTime utc) - { - if (NonStopSession) - { - throw new NotSupportedException("NonStopSession"); - } - - if (utc.Kind != DateTimeKind.Utc) - throw new ArgumentException("Only UTC time is supported", "time"); - - DateTime n = NextEndTime(utc); - if (WeeklySession) - n = n.AddDays(-7); - else - n = n.AddDays(-1); - - if (UseLocalTime) - return n.ToUniversalTime(); - if (TimeZone != null) - return TimeZoneInfo.ConvertTimeBySystemTimeZoneId(n, this.TimeZone.Id, "UTC"); - return n; - } - /// /// return true if time falls within StartTime/EndTime /// /// /// - private bool CheckDay(System.DateTime time) + private bool CheckDay(DateTime time) { if (NonStopSession) - { throw new InvalidOperationException("NonStopSession is set -- this should never be called."); - } - if (StartDay < EndDay) + + DayOfWeek vStartDay = StartDay ?? throw new QuickFix.ConfigError("StartDay is null"); + DayOfWeek vEndDay = EndDay ?? throw new QuickFix.ConfigError("EndDay is null"); + TimeSpan vStartTime = StartTime ?? throw new QuickFix.ConfigError("StartTime is null"); + TimeSpan vEndTime = EndTime ?? throw new QuickFix.ConfigError("EndTime is null"); + + if (vStartDay < vEndDay) { - if (time.DayOfWeek < StartDay || time.DayOfWeek > EndDay) - { + if (time.DayOfWeek < vStartDay || time.DayOfWeek > vEndDay) return false; - } - else if (time.DayOfWeek < EndDay) - { - return (StartDay < time.DayOfWeek) || (StartTime.CompareTo(time.TimeOfDay) <= 0); - } - else - { - return (time.DayOfWeek < EndDay) || (EndTime.CompareTo(time.TimeOfDay) >= 0); - } + + if (time.DayOfWeek < vEndDay) + return (vStartDay < time.DayOfWeek) || (vStartTime.CompareTo(time.TimeOfDay) <= 0); + + return (time.DayOfWeek < vEndDay) || (vEndTime.CompareTo(time.TimeOfDay) >= 0); } - if (EndDay < StartDay) + if (vEndDay < vStartDay) { - if (EndDay < time.DayOfWeek && time.DayOfWeek < StartDay) - { + if (vEndDay < time.DayOfWeek && time.DayOfWeek < vStartDay) return false; - } - else if (time.DayOfWeek < StartDay) - { - return (time.DayOfWeek < EndDay) || (EndTime.CompareTo(time.TimeOfDay) >= 0); - } - else - { - return (time.DayOfWeek > StartDay) || (StartTime.CompareTo(time.TimeOfDay) <= 0); - } + + if (time.DayOfWeek < vStartDay) + return (time.DayOfWeek < vEndDay) || (vEndTime.CompareTo(time.TimeOfDay) >= 0); + + return (time.DayOfWeek > vStartDay) || (vStartTime.CompareTo(time.TimeOfDay) <= 0); } //start day must be same as end day - if (StartTime >= EndTime) - return time.DayOfWeek != StartDay || CheckTime(time.TimeOfDay); - else - return time.DayOfWeek == StartDay && CheckTime(time.TimeOfDay); + if (vStartTime >= vEndTime) + return time.DayOfWeek != vStartDay || CheckTime(time.TimeOfDay); + + return time.DayOfWeek == vStartDay && CheckTime(time.TimeOfDay); } /// @@ -199,22 +160,24 @@ private bool CheckDay(System.DateTime time) /// /// /// - private bool CheckTime(System.TimeSpan time) + private bool CheckTime(TimeSpan time) { if (NonStopSession) - { return true; - } - if (StartTime.CompareTo(EndTime) < 0) + TimeSpan vStartTime = StartTime ?? throw new QuickFix.ConfigError("StartTime is null"); + TimeSpan vEndTime = EndTime ?? throw new QuickFix.ConfigError("EndTime is null"); + + if (vStartTime.CompareTo(vEndTime) < 0) { - return (time.CompareTo(StartTime) >= 0 && - time.CompareTo(EndTime) <= 0); + return (time.CompareTo(vStartTime) >= 0 && + time.CompareTo(vEndTime) <= 0); } - else if (StartTime.CompareTo(EndTime) > 0) + + if (vStartTime.CompareTo(vEndTime) > 0) { - return (time.CompareTo(StartTime) >= 0 || - time.CompareTo(EndTime) <= 0); + return time.CompareTo(vStartTime) >= 0 || + time.CompareTo(vEndTime) <= 0; } return true; @@ -226,23 +189,16 @@ private bool CheckTime(System.TimeSpan time) public SessionSchedule(QuickFix.Dictionary settings) { if (settings.Has(SessionSettings.NON_STOP_SESSION)) - { NonStopSession = settings.GetBool(SessionSettings.NON_STOP_SESSION); - } + if (NonStopSession) - { return; - } if (!settings.Has(SessionSettings.START_DAY) && settings.Has(SessionSettings.END_DAY)) - { throw new QuickFix.ConfigError("EndDay used without StartDay"); - } if (settings.Has(SessionSettings.START_DAY) && !settings.Has(SessionSettings.END_DAY)) - { throw new QuickFix.ConfigError("StartDay used without EndDay"); - } if (settings.Has(SessionSettings.START_DAY) && settings.Has(SessionSettings.END_DAY)) { @@ -264,18 +220,18 @@ public SessionSchedule(QuickFix.Dictionary settings) SessionSettings.TIME_ZONE + " conflicts with " + SessionSettings.USE_LOCAL_TIME); } string id = settings.GetString(SessionSettings.TIME_ZONE); - TimeZone = System.TimeZoneInfo.FindSystemTimeZoneById(id); + TimeZone = TimeZoneInfo.FindSystemTimeZoneById(id); } try { - this.StartTime = System.TimeSpan.Parse( + StartTime = TimeSpan.Parse( settings.GetString(SessionSettings.START_TIME)); - this.EndTime = System.TimeSpan.Parse( + EndTime = TimeSpan.Parse( settings.GetString(SessionSettings.END_TIME)); } - catch (System.FormatException e) + catch (FormatException e) { throw new ConfigError(e.Message); } diff --git a/QuickFIXn/SessionSettings.cs b/QuickFIXn/SessionSettings.cs index 7f87d8364..abefde561 100755 --- a/QuickFIXn/SessionSettings.cs +++ b/QuickFIXn/SessionSettings.cs @@ -84,8 +84,8 @@ public class SessionSettings #region Private Members - private QuickFix.Dictionary defaults_ = new QuickFix.Dictionary(); - private System.Collections.Generic.Dictionary settings_ = new Dictionary(); + private QuickFix.Dictionary _defaults = new(); + private readonly System.Collections.Generic.Dictionary _settings = new(); #endregion @@ -101,7 +101,7 @@ public SessionSettings(string file) } catch (System.Exception e) { - throw new ConfigError("File " + file + " not found (" + e.Message + ")"); + throw new ConfigError($"File {file} not found ({e.Message})"); } } @@ -121,9 +121,9 @@ protected void Load(TextReader conf) //---- load the DEFAULT section LinkedList section = settings.Get("DEFAULT"); - QuickFix.Dictionary def = new QuickFix.Dictionary(); + QuickFix.Dictionary def = new(); if (section.Count > 0) - def = section.First.Value; + def = section.First!.Value; Set(def); //---- load each SESSION section @@ -133,29 +133,29 @@ protected void Load(TextReader conf) dict.Merge(def); string sessionQualifier = SessionID.NOT_SET; - string senderSubID = SessionID.NOT_SET; - string senderLocID = SessionID.NOT_SET; - string targetSubID = SessionID.NOT_SET; - string targetLocID = SessionID.NOT_SET; + string senderSubId = SessionID.NOT_SET; + string senderLocId = SessionID.NOT_SET; + string targetSubId = SessionID.NOT_SET; + string targetLocId = SessionID.NOT_SET; if (dict.Has(SESSION_QUALIFIER)) sessionQualifier = dict.GetString(SESSION_QUALIFIER); if (dict.Has(SENDERSUBID)) - senderSubID = dict.GetString(SENDERSUBID); + senderSubId = dict.GetString(SENDERSUBID); if (dict.Has(SENDERLOCID)) - senderLocID = dict.GetString(SENDERLOCID); + senderLocId = dict.GetString(SENDERLOCID); if (dict.Has(TARGETSUBID)) - targetSubID = dict.GetString(TARGETSUBID); + targetSubId = dict.GetString(TARGETSUBID); if (dict.Has(TARGETLOCID)) - targetLocID = dict.GetString(TARGETLOCID); - SessionID sessionID = new SessionID(dict.GetString(BEGINSTRING), dict.GetString(SENDERCOMPID), senderSubID, senderLocID, dict.GetString(TARGETCOMPID), targetSubID, targetLocID, sessionQualifier); - Set(sessionID, dict); + targetLocId = dict.GetString(TARGETLOCID); + SessionID sessionId = new SessionID(dict.GetString(BEGINSTRING), dict.GetString(SENDERCOMPID), senderSubId, senderLocId, dict.GetString(TARGETCOMPID), targetSubId, targetLocId, sessionQualifier); + Set(sessionId, dict); } } - public bool Has(SessionID sessionID) + public bool Has(SessionID sessionId) { - return settings_.ContainsKey(sessionID); + return _settings.ContainsKey(sessionId); } /// @@ -164,68 +164,67 @@ public bool Has(SessionID sessionID) /// Dictionary of settings from the [DEFAULT] section public QuickFix.Dictionary Get() { - return defaults_; + return _defaults; } /// /// Get a dictionary for a session /// - /// the ID of the session + /// the ID of the session /// Dictionary of settings from the [SESSION] section for the given SessionID - public Dictionary Get(SessionID sessionID) + public Dictionary Get(SessionID sessionId) { - Dictionary dict; - if (!settings_.TryGetValue(sessionID, out dict)) - throw new ConfigError("Session '" + sessionID + "' not found"); + if (!_settings.TryGetValue(sessionId, out var dict)) + throw new ConfigError($"Session '{sessionId}' not found"); return dict; } public void Set(QuickFix.Dictionary defaults) { - defaults_ = defaults; - foreach (KeyValuePair entry in settings_) - entry.Value.Merge(defaults_); + _defaults = defaults; + foreach (KeyValuePair entry in _settings) + entry.Value.Merge(_defaults); } /// /// Remove existing session config from the settings /// - /// ID of session for which config is to be removed + /// ID of session for which config is to be removed /// true if removed, false if config for the session does not exist - public bool Remove(SessionID sessionID) + public bool Remove(SessionID sessionId) { - return settings_.Remove(sessionID); + return _settings.Remove(sessionId); } /// /// Add new session config /// - /// ID of session for which to add config + /// ID of session for which to add config /// session config - public void Set(SessionID sessionID, QuickFix.Dictionary settings) + public void Set(SessionID sessionId, QuickFix.Dictionary settings) { - if (Has(sessionID)) - throw new ConfigError("Duplicate Session " + sessionID.ToString()); - settings.SetString(BEGINSTRING, sessionID.BeginString); - settings.SetString(SENDERCOMPID, sessionID.SenderCompID); - if (SessionID.IsSet(sessionID.SenderSubID)) - settings.SetString(SENDERSUBID, sessionID.SenderSubID); - if (SessionID.IsSet(sessionID.SenderLocationID)) - settings.SetString(SENDERLOCID, sessionID.SenderLocationID); - settings.SetString(TARGETCOMPID, sessionID.TargetCompID); - if (SessionID.IsSet(sessionID.TargetSubID)) - settings.SetString(TARGETSUBID, sessionID.TargetSubID); - if (SessionID.IsSet(sessionID.TargetLocationID)) - settings.SetString(TARGETLOCID, sessionID.TargetLocationID); - settings.Merge(defaults_); + if (Has(sessionId)) + throw new ConfigError($"Duplicate Session {sessionId}"); + settings.SetString(BEGINSTRING, sessionId.BeginString); + settings.SetString(SENDERCOMPID, sessionId.SenderCompID); + if (SessionID.IsSet(sessionId.SenderSubID)) + settings.SetString(SENDERSUBID, sessionId.SenderSubID); + if (SessionID.IsSet(sessionId.SenderLocationID)) + settings.SetString(SENDERLOCID, sessionId.SenderLocationID); + settings.SetString(TARGETCOMPID, sessionId.TargetCompID); + if (SessionID.IsSet(sessionId.TargetSubID)) + settings.SetString(TARGETSUBID, sessionId.TargetSubID); + if (SessionID.IsSet(sessionId.TargetLocationID)) + settings.SetString(TARGETLOCID, sessionId.TargetLocationID); + settings.Merge(_defaults); Validate(settings); - settings_[sessionID] = settings; + _settings[sessionId] = settings; } public HashSet GetSessions() { HashSet result = new HashSet(); - foreach (KeyValuePair entry in settings_) + foreach (KeyValuePair entry in _settings) result.Add(entry.Key); return result; } @@ -235,15 +234,15 @@ public override string ToString() System.Text.StringBuilder s = new System.Text.StringBuilder(); s.AppendLine("[DEFAULT]"); - foreach (System.Collections.Generic.KeyValuePair entry in defaults_) + foreach (System.Collections.Generic.KeyValuePair entry in _defaults) s.Append(entry.Key).Append('=').AppendLine(entry.Value); - foreach (KeyValuePair entry in settings_) + foreach (KeyValuePair entry in _settings) { s.AppendLine().AppendLine("[SESSION]"); foreach (System.Collections.Generic.KeyValuePair kvp in entry.Value) { - if (defaults_.Has(kvp.Key) && defaults_.GetString(kvp.Key).Equals(kvp.Value)) + if (_defaults.Has(kvp.Key) && _defaults.GetString(kvp.Key).Equals(kvp.Value)) continue; s.Append(kvp.Key).Append('=').AppendLine(kvp.Value); } @@ -262,13 +261,13 @@ protected void Validate(QuickFix.Dictionary dictionary) beginString != Values.BeginString_FIX44 && beginString != Values.BeginString_FIXT11) { - throw new ConfigError(BEGINSTRING + " (" + beginString + ") must be FIX.4.0 to FIX.4.4 or FIXT.1.1"); + throw new ConfigError($"{BEGINSTRING} ({beginString}) must be FIX.4.0 to FIX.4.4 or FIXT.1.1"); } string connectionType = dictionary.GetString(CONNECTION_TYPE); if (!"initiator".Equals(connectionType) && !"acceptor".Equals(connectionType)) { - throw new ConfigError(CONNECTION_TYPE + " must be 'initiator' or 'acceptor'"); + throw new ConfigError($"{CONNECTION_TYPE} must be 'initiator' or 'acceptor'"); } } } diff --git a/QuickFIXn/Settings.cs b/QuickFIXn/Settings.cs index 1219dc222..1cd517ea1 100755 --- a/QuickFIXn/Settings.cs +++ b/QuickFIXn/Settings.cs @@ -1,16 +1,17 @@ -using System.Collections.Generic; +#nullable enable +using System.Collections.Generic; namespace QuickFix { public class Settings { - private LinkedList sections_ = new LinkedList(); + private readonly LinkedList _sections = new(); public Settings(System.IO.TextReader conf) { - QuickFix.Dictionary currentSection = null; + QuickFix.Dictionary? currentSection = null; - string line = null; + string? line; while ((line = conf.ReadLine()) != null) { line = line.Trim(); @@ -18,7 +19,8 @@ public Settings(System.IO.TextReader conf) { continue; } - else if (IsSection(line)) + + if (IsSection(line)) { currentSection = Add(new Dictionary(SplitSection(line))); } @@ -56,12 +58,12 @@ public static bool IsSection(string s) { if (s.Length < 2) return false; - return s[0] == '[' && s[s.Length - 1] == ']'; + return s[0] == '[' && s[^1] == ']'; } public QuickFix.Dictionary Add(QuickFix.Dictionary section) { - sections_.AddLast(section); + _sections.AddLast(section); return section; } @@ -73,8 +75,8 @@ public QuickFix.Dictionary Add(QuickFix.Dictionary section) /// public LinkedList Get(string sectionName) { - LinkedList result = new LinkedList(); - foreach (QuickFix.Dictionary dict in sections_) + LinkedList result = new(); + foreach (QuickFix.Dictionary dict in _sections) if (sectionName.ToUpperInvariant() == dict.Name.ToUpperInvariant()) result.AddLast(dict); return result; diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e4d27b1a9..86af9145d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -42,6 +42,10 @@ What's New * Session: Logon() and Logout() made internal and renamed. No one should be using these. * SessionState: ctor now requires a MessageStore. (Existing callers used an object initializer anyway) * Many protected functions were altered, but probably no user code is touching them +* #827 - cleanup/nullable-ize StreamFactory, SessionID, Settings, SessionSettings, SessionSchedule (gbirchmeier) + * StreamFactory: privatized a lot of members; I don't think users are inheriting from this + * SessionSchedule: remove unused LastEndTime() + **Non-breaking changes** * #400 - added DDTool, a C#-based codegen, and deleted Ruby-based generator (gbirchmeier) diff --git a/UnitTests/SessionScheduleTests.cs b/UnitTests/SessionScheduleTests.cs index 1cd70a4a1..b5854f4d0 100755 --- a/UnitTests/SessionScheduleTests.cs +++ b/UnitTests/SessionScheduleTests.cs @@ -353,101 +353,6 @@ public void testTimeZone() Assert.IsFalse(sched.IsSessionTime(new DateTime(2011, 10, 17, 20, 0, 1, DateTimeKind.Utc))); } - [Test] - public void testLastEndTime_takesUtcOnly() - { - QuickFix.Dictionary settings = new QuickFix.Dictionary(); - settings.SetString(QuickFix.SessionSettings.START_TIME, "09:30:00"); - settings.SetString(QuickFix.SessionSettings.END_TIME, "16:00:00"); - - QuickFix.SessionSchedule sched = new QuickFix.SessionSchedule(settings); - - Assert.Throws( - delegate { sched.LastEndTime(new DateTime(2012, 10, 18, 12, 00, 00, DateTimeKind.Local)); }); - Assert.Throws( - delegate { sched.LastEndTime(new DateTime(2012, 10, 18, 12, 00, 00, DateTimeKind.Unspecified)); }); - } - - [Test] - public void testLastEndTime_DailySessions() - { - QuickFix.Dictionary settings = new QuickFix.Dictionary(); - settings.SetString(QuickFix.SessionSettings.START_TIME, "09:30:00"); - settings.SetString(QuickFix.SessionSettings.END_TIME, "16:00:00"); - QuickFix.SessionSchedule sched = new QuickFix.SessionSchedule(settings); - - DateTime thisDayEnd = new DateTime(2013, 02, 05, 16, 00, 00, DateTimeKind.Utc); - DateTime prevDayEnd = new DateTime(2013, 02, 04, 16, 00, 00, DateTimeKind.Utc); - - // before starttime - Assert.AreEqual(prevDayEnd, sched.LastEndTime(new DateTime(2013, 02, 05, 08, 00, 00, DateTimeKind.Utc))); - // during session - Assert.AreEqual(prevDayEnd, sched.LastEndTime(new DateTime(2013, 02, 05, 10, 00, 00, DateTimeKind.Utc))); - // equals endtime - Assert.AreEqual(prevDayEnd, sched.LastEndTime(thisDayEnd)); - // after endtime - Assert.AreEqual(thisDayEnd, sched.LastEndTime(new DateTime(2013, 02, 05, 17, 00, 00, DateTimeKind.Utc))); - - // ========== - // Settings file is specified in a zone (est, -5) - settings = new QuickFix.Dictionary(); - settings.SetString(QuickFix.SessionSettings.START_TIME, "04:30:00"); // 09:30:00 utc - settings.SetString(QuickFix.SessionSettings.END_TIME, "11:00:00"); // 16:00:00 utc - settings.SetString(QuickFix.SessionSettings.TIME_ZONE, EASTERN_STANDARD_TIME_ZONE_ID); //-5 - sched = new QuickFix.SessionSchedule(settings); - - // before starttime - Assert.AreEqual(prevDayEnd, sched.LastEndTime(new DateTime(2013, 02, 05, 08, 00, 00, DateTimeKind.Utc))); - // during session - Assert.AreEqual(prevDayEnd, sched.LastEndTime(new DateTime(2013, 02, 05, 10, 00, 00, DateTimeKind.Utc))); - // equals endtime - Assert.AreEqual(prevDayEnd, sched.LastEndTime(thisDayEnd)); - // after endtime - Assert.AreEqual(thisDayEnd, sched.LastEndTime(new DateTime(2013, 02, 05, 17, 00, 00, DateTimeKind.Utc))); - } - - [Test] - public void testLastEndTime_WeeklySessions() - { - QuickFix.Dictionary settings = new QuickFix.Dictionary(); - settings.SetString(QuickFix.SessionSettings.START_TIME, "09:30:00"); - settings.SetString(QuickFix.SessionSettings.END_TIME, "16:00:00"); - settings.SetDay(QuickFix.SessionSettings.START_DAY, System.DayOfWeek.Monday); - settings.SetDay(QuickFix.SessionSettings.END_DAY, System.DayOfWeek.Friday); - QuickFix.SessionSchedule sched = new QuickFix.SessionSchedule(settings); - - DateTime thisWeekEnd = new DateTime(2013, 02, 08, 16, 00, 00, DateTimeKind.Utc); - DateTime prevWeekEnd = new DateTime(2013, 02, 01, 16, 00, 00, DateTimeKind.Utc); - - // before starttime - Assert.AreEqual(prevWeekEnd, sched.LastEndTime(new DateTime(2013, 02, 04, 08, 00, 00, DateTimeKind.Utc))); - // during session - Assert.AreEqual(prevWeekEnd, sched.LastEndTime(new DateTime(2013, 02, 05, 08, 00, 00, DateTimeKind.Utc))); - // equals endtime - Assert.AreEqual(prevWeekEnd, sched.LastEndTime(thisWeekEnd)); - // after endtime - Assert.AreEqual(thisWeekEnd, sched.LastEndTime(new DateTime(2013, 02, 08, 17, 00, 00, DateTimeKind.Utc))); - - // ========== - // Settings file is specified in a zone (est, -5) - settings = new QuickFix.Dictionary(); - settings.SetString(QuickFix.SessionSettings.START_TIME, "04:30:00"); // 09:30:00 utc - settings.SetString(QuickFix.SessionSettings.END_TIME, "11:00:00"); // 16:00:00 utc - settings.SetString(QuickFix.SessionSettings.TIME_ZONE, EASTERN_STANDARD_TIME_ZONE_ID); //-5 - settings.SetDay(QuickFix.SessionSettings.START_DAY, System.DayOfWeek.Monday); - settings.SetDay(QuickFix.SessionSettings.END_DAY, System.DayOfWeek.Friday); - sched = new QuickFix.SessionSchedule(settings); - - // before starttime - Assert.AreEqual(prevWeekEnd, sched.LastEndTime(new DateTime(2013, 02, 04, 08, 00, 00, DateTimeKind.Utc))); - // during session - Assert.AreEqual(prevWeekEnd, sched.LastEndTime(new DateTime(2013, 02, 05, 08, 00, 00, DateTimeKind.Utc))); - // equals endtime - Assert.AreEqual(prevWeekEnd, sched.LastEndTime(thisWeekEnd)); - // after endtime - Assert.AreEqual(thisWeekEnd, sched.LastEndTime(new DateTime(2013, 02, 08, 17, 00, 00, DateTimeKind.Utc))); - } - [Test] public void testNextEndTime_takesUtcOnly() {