#region Disclaimer / License // Copyright (C) 2015, The Duplicati Team // http://www.duplicati.com, info@duplicati.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.Text; using System.Linq; namespace Duplicati.Library.Modules.Builtin { public class HttpOptions : Duplicati.Library.Interface.IGenericModule, Duplicati.Library.Interface.IConnectionModule, IDisposable { private const string OPTION_DISABLE_EXPECT100 = "disable-expect100-continue"; private const string OPTION_DISABLE_NAGLING = "disable-nagling"; private const string OPTION_ACCEPT_SPECIFIED_CERTIFICATE = "accept-specified-ssl-hash"; private const string OPTION_ACCEPT_ANY_CERTIFICATE = "accept-any-ssl-certificate"; private const string OPTION_OAUTH_URL = "oauth-url"; private const string OPTION_SSL_VERSIONS = "allowed-ssl-versions"; private const string OPTION_BUFFER_REQUESTS = "http-enable-buffering"; private const string OPTION_OPERATION_TIMEOUT = "http-operation-timeout"; private const string OPTION_READWRITE_TIMEOUT = "http-readwrite-timeout"; private bool m_useNagle; private bool m_useExpect; private System.Net.SecurityProtocolType m_securityProtocol; private bool m_dispose; private bool m_resetNagle; private bool m_resetExpect; private bool m_resetSecurity; /// /// The handle to the call-context http settings /// private IDisposable m_httpsettings; /// /// The handle to the call-contet oauth setttings /// private IDisposable m_oauthsettings; // Copied from system reference [Flags] private enum CopySecurityProtocolType { Ssl3 = 48, Tls = 192, Tls11 = 768, Tls12 = 3072, } private static Dictionary SecurityProtocols { get { var res = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var val in Enum.GetNames(typeof(CopySecurityProtocolType)).Zip(Enum.GetValues(typeof(CopySecurityProtocolType)).Cast(), (x,y) => new KeyValuePair(x, y))) res[val.Key] = val.Value; foreach (var val in Enum.GetNames(typeof(System.Net.SecurityProtocolType)).Zip(Enum.GetValues(typeof(System.Net.SecurityProtocolType)).Cast(), (x,y) => new KeyValuePair(x, y))) res[val.Key] = val.Value; return res; } } private static System.Net.SecurityProtocolType ParseSSLProtocols(string names) { var ptr = SecurityProtocols; var res = 0; foreach (var s in names.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim())) if (ptr.ContainsKey(s)) res = res | ptr[s]; return (System.Net.SecurityProtocolType)res; } #region IGenericModule Members public string Key { get { return "http-options"; } } public string DisplayName { get { return Strings.HttpOptions.DisplayName; } } public string Description { get { return Strings.HttpOptions.Description; } } public bool LoadAsDefault { get { return true; } } public IList SupportedCommands { get { var sslnames = SecurityProtocols.Select(x => x.Key).ToArray(); var defaultssl = string.Join(",", Enum.GetValues(typeof(System.Net.SecurityProtocolType)).Cast().Where(x => System.Net.ServicePointManager.SecurityProtocol.HasFlag(x))); return new List( new Duplicati.Library.Interface.ICommandLineArgument[] { new Duplicati.Library.Interface.CommandLineArgument(OPTION_DISABLE_EXPECT100, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.HttpOptions.DisableExpect100Short, Strings.HttpOptions.DisableExpect100Long, "false"), new Duplicati.Library.Interface.CommandLineArgument(OPTION_DISABLE_NAGLING, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.HttpOptions.DisableNagleShort, Strings.HttpOptions.DisableNagleLong, "false"), new Duplicati.Library.Interface.CommandLineArgument(OPTION_ACCEPT_SPECIFIED_CERTIFICATE, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.String, Strings.HttpOptions.DescriptionAcceptHashShort, Strings.HttpOptions.DescriptionAcceptHashLong2), new Duplicati.Library.Interface.CommandLineArgument(OPTION_ACCEPT_ANY_CERTIFICATE, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.HttpOptions.DescriptionAcceptAnyCertificateShort, Strings.HttpOptions.DescriptionAcceptAnyCertificateLong), new Duplicati.Library.Interface.CommandLineArgument(OPTION_OAUTH_URL, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.String, Strings.HttpOptions.OauthurlShort, Strings.HttpOptions.OauthurlLong, OAuthHelper.DUPLICATI_OAUTH_SERVICE), new Duplicati.Library.Interface.CommandLineArgument(OPTION_SSL_VERSIONS, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Flags, Strings.HttpOptions.SslversionsShort, Strings.HttpOptions.SslversionsLong, defaultssl, null, sslnames), new Duplicati.Library.Interface.CommandLineArgument(OPTION_OPERATION_TIMEOUT, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Timespan, Strings.HttpOptions.OperationtimeoutShort, Strings.HttpOptions.OperationtimeoutLong), new Duplicati.Library.Interface.CommandLineArgument(OPTION_READWRITE_TIMEOUT, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Timespan, Strings.HttpOptions.ReadwritetimeoutShort, Strings.HttpOptions.ReadwritetimeoutLong), new Duplicati.Library.Interface.CommandLineArgument(OPTION_BUFFER_REQUESTS, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.HttpOptions.BufferrequestsShort, Strings.HttpOptions.BufferrequestsLong, "false"), }); } } public void Configure(IDictionary commandlineOptions) { m_dispose = true; TimeSpan operationTimeout = new TimeSpan(0); TimeSpan readwriteTimeout = new TimeSpan(0); string timetmp; commandlineOptions.TryGetValue(OPTION_OPERATION_TIMEOUT, out timetmp); if (!string.IsNullOrWhiteSpace(timetmp)) operationTimeout = Utility.Timeparser.ParseTimeSpan(timetmp); commandlineOptions.TryGetValue(OPTION_READWRITE_TIMEOUT, out timetmp); if (!string.IsNullOrWhiteSpace(timetmp)) readwriteTimeout = Utility.Timeparser.ParseTimeSpan(timetmp); bool accepAllCertificates = Utility.Utility.ParseBoolOption(commandlineOptions, OPTION_ACCEPT_ANY_CERTIFICATE); string certHash; commandlineOptions.TryGetValue(OPTION_ACCEPT_SPECIFIED_CERTIFICATE, out certHash); m_httpsettings = Duplicati.Library.Utility.HttpContextSettings.StartSession( operationTimeout, readwriteTimeout, Utility.Utility.ParseBoolOption(commandlineOptions, OPTION_BUFFER_REQUESTS), accepAllCertificates, certHash == null ? null : certHash.Split(new string[] { ",", ";" }, StringSplitOptions.RemoveEmptyEntries) ); bool disableNagle = Utility.Utility.ParseBoolOption(commandlineOptions, OPTION_DISABLE_NAGLING); bool disableExpect100 = Utility.Utility.ParseBoolOption(commandlineOptions, OPTION_DISABLE_EXPECT100); // TODO: This is done to avoid conflicting settings, // but ideally, we should run each operation in a seperate // app-domain to ensure that multiple invocations of this module // does not interfere, as the options are shared in the app-domain m_resetNagle = commandlineOptions.ContainsKey(OPTION_DISABLE_NAGLING); m_resetExpect = commandlineOptions.ContainsKey(OPTION_DISABLE_EXPECT100); m_resetSecurity = commandlineOptions.ContainsKey(OPTION_SSL_VERSIONS); m_useNagle = System.Net.ServicePointManager.UseNagleAlgorithm; m_useExpect = System.Net.ServicePointManager.Expect100Continue; m_securityProtocol = System.Net.ServicePointManager.SecurityProtocol; if (m_resetNagle) System.Net.ServicePointManager.UseNagleAlgorithm = !disableNagle; if (m_resetExpect) System.Net.ServicePointManager.Expect100Continue = !disableExpect100; string sslprotocol; commandlineOptions.TryGetValue(OPTION_SSL_VERSIONS, out sslprotocol); if (!string.IsNullOrWhiteSpace(sslprotocol) && m_resetSecurity) System.Net.ServicePointManager.SecurityProtocol = ParseSSLProtocols(sslprotocol); string url; commandlineOptions.TryGetValue(OPTION_OAUTH_URL, out url); if (!string.IsNullOrWhiteSpace(url)) m_oauthsettings = OAuthContextSettings.StartSession(url); } #endregion #region IDisposable Members public void Dispose() { if (m_dispose) { m_dispose = false; if (m_resetNagle) System.Net.ServicePointManager.UseNagleAlgorithm = m_useNagle; if (m_resetExpect) System.Net.ServicePointManager.Expect100Continue = m_useExpect; if (m_resetSecurity) System.Net.ServicePointManager.SecurityProtocol = m_securityProtocol; } if (m_httpsettings != null) { m_httpsettings.Dispose(); m_httpsettings = null; } if (m_oauthsettings != null) { m_oauthsettings.Dispose(); m_oauthsettings = null; } } #endregion } }