diff --git a/RestSharp.sln b/RestSharp.sln index 83b6d020b..2f893b3ac 100644 --- a/RestSharp.sln +++ b/RestSharp.sln @@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharp.InteractiveTests" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharp.Serializers.Xml.Tests", "test\RestSharp.Serializers.Xml.Tests\RestSharp.Serializers.Xml.Tests.csproj", "{E6D94C12-9AD7-46E6-AB62-3676F25FDE51}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharp.Serializers.Xml", "src\RestSharp.Serializers.Xml\RestSharp.Serializers.Xml.csproj", "{4A35B1C5-520D-4267-BA70-2DCEAC0A5662}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.Appveyor|Any CPU = Debug.Appveyor|Any CPU @@ -316,6 +318,36 @@ Global {E6D94C12-9AD7-46E6-AB62-3676F25FDE51}.Release|x64.Build.0 = Release|Any CPU {E6D94C12-9AD7-46E6-AB62-3676F25FDE51}.Release|x86.ActiveCfg = Release|Any CPU {E6D94C12-9AD7-46E6-AB62-3676F25FDE51}.Release|x86.Build.0 = Release|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug.Appveyor|Any CPU.ActiveCfg = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug.Appveyor|Any CPU.Build.0 = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug.Appveyor|ARM.ActiveCfg = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug.Appveyor|ARM.Build.0 = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug.Appveyor|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug.Appveyor|Mixed Platforms.Build.0 = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug.Appveyor|x64.ActiveCfg = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug.Appveyor|x64.Build.0 = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug.Appveyor|x86.ActiveCfg = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug.Appveyor|x86.Build.0 = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug|ARM.ActiveCfg = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug|ARM.Build.0 = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug|x64.ActiveCfg = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug|x64.Build.0 = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug|x86.ActiveCfg = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Debug|x86.Build.0 = Debug|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Release|Any CPU.Build.0 = Release|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Release|ARM.ActiveCfg = Release|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Release|ARM.Build.0 = Release|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Release|x64.ActiveCfg = Release|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Release|x64.Build.0 = Release|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Release|x86.ActiveCfg = Release|Any CPU + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -332,5 +364,6 @@ Global {997AEFE5-D7D4-4033-A31A-07F476D6FE5D} = {1C42C435-8826-4044-8775-A1DA40EF4866} {6D7D1D60-4473-4C52-800C-9B892C6640A5} = {9051DDA0-E563-45D5-9504-085EBAACF469} {E6D94C12-9AD7-46E6-AB62-3676F25FDE51} = {9051DDA0-E563-45D5-9504-085EBAACF469} + {4A35B1C5-520D-4267-BA70-2DCEAC0A5662} = {8C7B43EB-2F93-483C-B433-E28F9386AD67} EndGlobalSection EndGlobal diff --git a/src/RestSharp.Serializers.NewtonsoftJson/JsonNetSerializer.cs b/src/RestSharp.Serializers.NewtonsoftJson/JsonNetSerializer.cs index ce9ca6a34..067db9377 100644 --- a/src/RestSharp.Serializers.NewtonsoftJson/JsonNetSerializer.cs +++ b/src/RestSharp.Serializers.NewtonsoftJson/JsonNetSerializer.cs @@ -48,6 +48,8 @@ public class JsonNetSerializer : IRestSerializer { public string? Serialize(Parameter bodyParameter) => Serialize(bodyParameter.Value); public T? Deserialize(RestResponse response) { + if (response.Content == null) + throw new DeserializationException(response, new InvalidOperationException("Response content is null")); using var reader = new JsonTextReader(new StringReader(response.Content)) { CloseInput = true }; return _serializer.Deserialize(reader); diff --git a/src/RestSharp.Serializers.NewtonsoftJson/RestSharp.Serializers.NewtonsoftJson.csproj b/src/RestSharp.Serializers.NewtonsoftJson/RestSharp.Serializers.NewtonsoftJson.csproj index 2e19faf9d..c0714836d 100644 --- a/src/RestSharp.Serializers.NewtonsoftJson/RestSharp.Serializers.NewtonsoftJson.csproj +++ b/src/RestSharp.Serializers.NewtonsoftJson/RestSharp.Serializers.NewtonsoftJson.csproj @@ -1,6 +1,6 @@ - + diff --git a/src/RestSharp/Serializers/DeserializeAsAttribute.cs b/src/RestSharp.Serializers.Xml/DeserializeAsAttribute.cs similarity index 85% rename from src/RestSharp/Serializers/DeserializeAsAttribute.cs rename to src/RestSharp.Serializers.Xml/DeserializeAsAttribute.cs index 6752e7d7f..29d2d486b 100644 --- a/src/RestSharp/Serializers/DeserializeAsAttribute.cs +++ b/src/RestSharp.Serializers.Xml/DeserializeAsAttribute.cs @@ -1,4 +1,5 @@ -namespace RestSharp.Serializers; +// ReSharper disable once CheckNamespace +namespace RestSharp.Serializers; /// /// Allows control how class and property names and values are deserialized by XmlAttributeDeserializer @@ -8,7 +9,7 @@ public sealed class DeserializeAsAttribute : Attribute { /// /// The name to use for the serialized element /// - public string Name { get; set; } + public string? Name { get; set; } /// /// Sets if the property to Deserialize is an Attribute or Element (Default: false) diff --git a/src/RestSharp.Serializers.Xml/RestSharp.Serializers.Xml.csproj b/src/RestSharp.Serializers.Xml/RestSharp.Serializers.Xml.csproj new file mode 100644 index 000000000..f0455ce76 --- /dev/null +++ b/src/RestSharp.Serializers.Xml/RestSharp.Serializers.Xml.csproj @@ -0,0 +1,11 @@ + + + + RestSharp.Serializers.Xml + + + + + + + diff --git a/src/RestSharp/Serializers/SerializeAsAttribute.cs b/src/RestSharp.Serializers.Xml/SerializeAsAttribute.cs similarity index 98% rename from src/RestSharp/Serializers/SerializeAsAttribute.cs rename to src/RestSharp.Serializers.Xml/SerializeAsAttribute.cs index 7e43a6ec8..28b330784 100644 --- a/src/RestSharp/Serializers/SerializeAsAttribute.cs +++ b/src/RestSharp.Serializers.Xml/SerializeAsAttribute.cs @@ -1,6 +1,7 @@ using System.Globalization; using RestSharp.Extensions; +// ReSharper disable once CheckNamespace namespace RestSharp.Serializers; /// diff --git a/src/RestSharp/Serializers/Xml/XmlAttributeDeserializer.cs b/src/RestSharp.Serializers.Xml/XmlAttributeDeserializer.cs similarity index 77% rename from src/RestSharp/Serializers/Xml/XmlAttributeDeserializer.cs rename to src/RestSharp.Serializers.Xml/XmlAttributeDeserializer.cs index 6cec90f48..a314bdb25 100644 --- a/src/RestSharp/Serializers/Xml/XmlAttributeDeserializer.cs +++ b/src/RestSharp.Serializers.Xml/XmlAttributeDeserializer.cs @@ -5,7 +5,7 @@ namespace RestSharp.Serializers.Xml; public class XmlAttributeDeserializer : XmlDeserializer { - protected override object? GetValueFromXml(XElement root, XName name, PropertyInfo prop, bool useExactName) { + protected override object? GetValueFromXml(XElement? root, XName? name, PropertyInfo prop, bool useExactName) { var isAttribute = false; //Check for the DeserializeAs attribute on the property @@ -18,7 +18,7 @@ public class XmlAttributeDeserializer : XmlDeserializer { if (!isAttribute) return base.GetValueFromXml(root, name, prop, useExactName); - var attributeVal = GetAttributeByName(root, name, useExactName); + var attributeVal = GetAttributeByName(root!, name!, useExactName); return attributeVal?.Value ?? base.GetValueFromXml(root, name, prop, useExactName); } diff --git a/src/RestSharp/Serializers/Xml/XmlDeserializer.cs b/src/RestSharp.Serializers.Xml/XmlDeserializer.cs similarity index 83% rename from src/RestSharp/Serializers/Xml/XmlDeserializer.cs rename to src/RestSharp.Serializers.Xml/XmlDeserializer.cs index 3fdc767bb..b59ff4b91 100644 --- a/src/RestSharp/Serializers/Xml/XmlDeserializer.cs +++ b/src/RestSharp.Serializers.Xml/XmlDeserializer.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using System.ComponentModel; using System.Globalization; using System.Reflection; @@ -15,9 +15,9 @@ public class XmlDeserializer : IXmlDeserializer { public string? RootElement { get; set; } - public string Namespace { get; set; } + public string? Namespace { get; set; } - public string DateFormat { get; set; } + public string? DateFormat { get; set; } public virtual T? Deserialize(RestResponse response) { if (string.IsNullOrEmpty(response.Content)) @@ -30,7 +30,7 @@ public class XmlDeserializer : IXmlDeserializer { root = doc.Root.DescendantsAndSelf(RootElement.AsNamespaced(Namespace)).SingleOrDefault(); // autodetect xml namespace - if (!Namespace.HasValue()) + if (Namespace.IsEmpty()) RemoveNamespace(doc); var x = Activator.CreateInstance(); @@ -93,10 +93,7 @@ protected virtual object Map(object x, XElement? root) { deserializeFromContent = attribute.Content; if (deserializeFromContentAttributeAlreadyUsed && deserializeFromContent) - throw new ArgumentException( - "Class cannot have two properties marked with " + - "SerializeAs(Content = true) attribute." - ); + throw new ArgumentException("Class cannot have two properties marked with SerializeAs(Content = true) attribute."); deserializeFromContentAttributeAlreadyUsed |= deserializeFromContent; } @@ -108,7 +105,7 @@ protected virtual object Map(object x, XElement? root) { if (value == null) { // special case for text content node if (deserializeFromContent) { - var textNode = root.Nodes().FirstOrDefault(n => n is XText); + var textNode = root!.Nodes().FirstOrDefault(n => n is XText); if (textNode != null) { value = ((XText)textNode).Value; @@ -122,7 +119,7 @@ protected virtual object Map(object x, XElement? root) { if (type.IsGenericType) { var genericType = type.GetGenericArguments()[0]; var first = GetElementByName(root, genericType.Name); - var list = (IList)Activator.CreateInstance(type.AsType()); + var list = (IList)Activator.CreateInstance(type.AsType())!; if (first != null && root != null) { var elements = root.Elements(first.Name); @@ -149,7 +146,7 @@ protected virtual object Map(object x, XElement? root) { var asType = type.AsType(); if (asType == typeof(bool)) { - var toConvert = value.ToString() + var toConvert = value.ToString()! .ToLower(Culture); prop.SetValue(x, XmlConvert.ToBoolean(toConvert), null); @@ -167,12 +164,12 @@ protected virtual object Map(object x, XElement? root) { } } else if (type.IsEnum) { - var converted = type.AsType().FindEnumValue(value.ToString(), Culture); + var converted = type.AsType().FindEnumValue(value.ToString()!, Culture); prop.SetValue(x, converted, null); } else if (asType == typeof(Uri)) { - var uri = new Uri(value.ToString(), UriKind.RelativeOrAbsolute); + var uri = new Uri(value.ToString()!, UriKind.RelativeOrAbsolute); prop.SetValue(x, uri, null); } @@ -180,9 +177,9 @@ protected virtual object Map(object x, XElement? root) { prop.SetValue(x, value, null); } else if (asType == typeof(DateTime)) { - value = DateFormat.HasValue() - ? DateTime.ParseExact(value.ToString(), DateFormat, Culture) - : DateTime.Parse(value.ToString(), Culture); + value = DateFormat.IsNotEmpty() + ? DateTime.ParseExact(value.ToString()!, DateFormat!, Culture) + : DateTime.Parse(value.ToString()!, Culture); prop.SetValue(x, value, null); } @@ -209,7 +206,7 @@ protected virtual object Map(object x, XElement? root) { } } else if (asType == typeof(decimal)) { - value = decimal.Parse(value.ToString(), Culture); + value = decimal.Parse(value.ToString()!, Culture); prop.SetValue(x, value, null); } else if (asType == typeof(Guid)) { @@ -217,20 +214,20 @@ protected virtual object Map(object x, XElement? root) { value = string.IsNullOrEmpty(raw) ? Guid.Empty - : new Guid(value.ToString()); + : new Guid(value.ToString()!); prop.SetValue(x, value, null); } else if (asType == typeof(TimeSpan)) { - var timeSpan = XmlConvert.ToTimeSpan(value.ToString()); + var timeSpan = XmlConvert.ToTimeSpan(value.ToString()!); prop.SetValue(x, timeSpan, null); } else if (type.IsGenericType) { - var list = (IList)Activator.CreateInstance(asType); + var list = (IList)Activator.CreateInstance(asType)!; var container = GetElementByName(root, name); - if (container.HasElements) { + if (container?.HasElements == true) { var first = container.Elements().FirstOrDefault(); if (first != null) { @@ -246,14 +243,14 @@ protected virtual object Map(object x, XElement? root) { else if (asType.IsSubclassOfRawGeneric(typeof(List<>))) { // handles classes that derive from List // e.g. a collection that also has attributes - var list = HandleListDerivative(root, prop.Name, asType); + var list = HandleListDerivative(root!, prop.Name, asType); prop.SetValue(x, list, null); } else { //fallback to type converters if possible - if (TryGetFromString(value.ToString(), out var result, asType)) { + if (TryGetFromString(value.ToString()!, out var result, asType)) { prop.SetValue(x, result, null); } else { @@ -294,9 +291,9 @@ void PopulateListFromElements(Type t, IEnumerable elements, IList list object HandleListDerivative(XElement root, string propName, Type type) { var t = type.IsGenericType ? type.GetGenericArguments()[0] - : type.BaseType.GetGenericArguments()[0]; + : type.BaseType!.GetGenericArguments()[0]; - var list = (IList)Activator.CreateInstance(type); + var list = (IList)Activator.CreateInstance(type)!; IList elements = root.Descendants(t.Name.AsNamespaced(Namespace)).ToList(); @@ -307,13 +304,13 @@ object HandleListDerivative(XElement root, string propName, Type type) { name = attribute.Name; if (!elements.Any()) { - var lowerName = name.ToLower(Culture).AsNamespaced(Namespace); + var lowerName = name?.ToLower(Culture).AsNamespaced(Namespace); elements = root.Descendants(lowerName).ToList(); } if (!elements.Any()) { - var camelName = name.ToCamelCase(Culture).AsNamespaced(Namespace); + var camelName = name?.ToCamelCase(Culture).AsNamespaced(Namespace); elements = root.Descendants(camelName).ToList(); } @@ -324,7 +321,7 @@ object HandleListDerivative(XElement root, string propName, Type type) { .ToList(); if (!elements.Any()) { - var lowerName = name.ToLower(Culture).AsNamespaced(Namespace); + var lowerName = name?.ToLower(Culture).AsNamespaced(Namespace); elements = root.Descendants() .Where(e => e.Name.LocalName.RemoveUnderscoresAndDashes() == lowerName) @@ -336,7 +333,7 @@ object HandleListDerivative(XElement root, string propName, Type type) { // get properties too, not just list items // only if this isn't a generic type if (!type.IsGenericType) - Map(list, root.Element(propName.AsNamespaced(Namespace)) ?? root); + Map(list, root.Element(propName.AsNamespaced(Namespace)!) ?? root); return list; } @@ -351,21 +348,21 @@ object HandleListDerivative(XElement root, string propName, Type type) { item = element.Value.ChangeType(t); } else { - item = Activator.CreateInstance(t); + item = Activator.CreateInstance(t)!; Map(item, element); } return item; } - protected virtual object? GetValueFromXml(XElement? root, XName name, PropertyInfo prop, bool useExactName) { + protected virtual object? GetValueFromXml(XElement? root, XName? name, PropertyInfo prop, bool useExactName) { object? val = null; if (root == null) return val; var element = GetElementByName(root, name); if (element == null) { - var attribute = GetAttributeByName(root, name, useExactName); + var attribute = GetAttributeByName(root, name!, useExactName); if (attribute != null) val = attribute.Value; @@ -378,37 +375,37 @@ object HandleListDerivative(XElement root, string propName, Type type) { return val; } - protected virtual XElement? GetElementByName(XElement root, XName name) { - var lowerName = name.LocalName.ToLower(Culture).AsNamespaced(name.NamespaceName); - var camelName = name.LocalName.ToCamelCase(Culture).AsNamespaced(name.NamespaceName); + protected virtual XElement? GetElementByName(XElement? root, XName? name) { + var lowerName = name?.LocalName.ToLower(Culture).AsNamespaced(name.NamespaceName); + var camelName = name?.LocalName.ToCamelCase(Culture).AsNamespaced(name.NamespaceName); - if (root.Element(name) != null) - return root.Element(name); + if (root?.Element(name!) != null) + return root.Element(name!); - if (root.Element(lowerName) != null) - return root.Element(lowerName); + if (root?.Element(lowerName!) != null) + return root.Element(lowerName!); - if (root.Element(camelName) != null) - return root.Element(camelName); + if (root?.Element(camelName!) != null) + return root.Element(camelName!); // try looking for element that matches sanitized property name (Order by depth) - var orderedDescendants = root.Descendants() + var orderedDescendants = root!.Descendants() .OrderBy(d => d.Ancestors().Count()) .ToList(); var element = orderedDescendants - .FirstOrDefault(d => d.Name.LocalName.RemoveUnderscoresAndDashes() == name.LocalName) ?? + .FirstOrDefault(d => d.Name.LocalName.RemoveUnderscoresAndDashes() == name?.LocalName) ?? orderedDescendants .FirstOrDefault( d => string.Equals( d.Name.LocalName.RemoveUnderscoresAndDashes(), - name.LocalName, + name?.LocalName, StringComparison.OrdinalIgnoreCase ) ); return element == null && - name == "Value".AsNamespaced(name.NamespaceName) && + name == "Value".AsNamespaced(name?.NamespaceName) && (!root.HasAttributes || root.Attributes().All(x => x.Name != name)) ? root : element; @@ -419,10 +416,8 @@ object HandleListDerivative(XElement root, string propName, Type type) { ? null : new List { name.LocalName, - name.LocalName.ToLower(Culture) - .AsNamespaced(name.NamespaceName), - name.LocalName.ToCamelCase(Culture) - .AsNamespaced(name.NamespaceName) + name.LocalName.ToLower(Culture).AsNamespaced(name.NamespaceName)!, + name.LocalName.ToCamelCase(Culture).AsNamespaced(name.NamespaceName)! }; return root.DescendantsAndSelf() diff --git a/src/RestSharp/Serializers/Xml/XmlExtensions.cs b/src/RestSharp.Serializers.Xml/XmlExtensions.cs similarity index 69% rename from src/RestSharp/Serializers/Xml/XmlExtensions.cs rename to src/RestSharp.Serializers.Xml/XmlExtensions.cs index fb8578f36..1834a2165 100644 --- a/src/RestSharp/Serializers/Xml/XmlExtensions.cs +++ b/src/RestSharp.Serializers.Xml/XmlExtensions.cs @@ -13,10 +13,10 @@ public static class XmlExtensions { /// Element name /// XML Namespace /// - public static XName AsNamespaced(this string name, string @namespace) { - XName xName = name; + public static XName? AsNamespaced(this string? name, string? @namespace) { + XName? xName = name; - if (@namespace.HasValue()) xName = XName.Get(name, @namespace); + if (name != null && @namespace.IsNotEmpty()) xName = XName.Get(name, @namespace!); return xName; } diff --git a/src/RestSharp/Serializers/Xml/XmlSerializer.cs b/src/RestSharp.Serializers.Xml/XmlSerializer.cs similarity index 90% rename from src/RestSharp/Serializers/Xml/XmlSerializer.cs rename to src/RestSharp.Serializers.Xml/XmlSerializer.cs index 3ce4aafc2..fdf27ea2f 100644 --- a/src/RestSharp/Serializers/Xml/XmlSerializer.cs +++ b/src/RestSharp.Serializers.Xml/XmlSerializer.cs @@ -35,7 +35,7 @@ public string Serialize(object obj) { if (options != null) name = options.TransformName(options.Name ?? name); - var root = new XElement(name.AsNamespaced(Namespace)); + var root = new XElement(name.AsNamespaced(Namespace)!); if (obj is IList list) { var itemTypeName = ""; @@ -50,7 +50,7 @@ public string Serialize(object obj) { if (itemTypeName == "") itemTypeName = type.Name; - var instance = new XElement(itemTypeName.AsNamespaced(Namespace)); + var instance = new XElement(itemTypeName.AsNamespaced(Namespace)!); Map(instance, item); root.Add(instance); @@ -61,7 +61,7 @@ public string Serialize(object obj) { } if (RootElement != null) { - var wrapper = new XElement(RootElement.AsNamespaced(Namespace), root); + var wrapper = new XElement(RootElement.AsNamespaced(Namespace)!, root); doc.Add(wrapper); } else { @@ -79,12 +79,12 @@ public string Serialize(object obj) { /// /// XML namespace to use when serializing /// - public string Namespace { get; set; } + public string? Namespace { get; set; } /// /// Format string to use when serializing dates /// - public string DateFormat { get; set; } + public string? DateFormat { get; set; } /// /// Content type for serialized content @@ -115,11 +115,11 @@ void Map(XContainer root, object obj) { var options = prop.GetAttribute(); if (options != null) { - name = options.Name.HasValue() + name = options.Name.IsNotEmpty() ? options.Name : name; - name = options.TransformName(name); + name = options.TransformName(name!); useAttribute = options.Attribute; @@ -135,7 +135,7 @@ void Map(XContainer root, object obj) { } var nsName = name.AsNamespaced(Namespace); - var element = new XElement(nsName); + var element = new XElement(nsName!); if (propType.GetTypeInfo().IsPrimitive || propType.GetTypeInfo().IsValueType || @@ -159,11 +159,11 @@ void Map(XContainer root, object obj) { var type = item.GetType(); var setting = type.GetAttribute(); - var itemTypeName = setting != null && setting.Name.HasValue() + var itemTypeName = setting != null && setting.Name.IsNotEmpty() ? setting.Name : type.Name; - var instance = new XElement(itemTypeName.AsNamespaced(Namespace)); + var instance = new XElement(itemTypeName!.AsNamespaced(Namespace)!); Map(instance, item); @@ -189,12 +189,12 @@ void Map(XContainer root, object obj) { string GetSerializedValue(object obj) { var output = obj switch { - DateTime time when DateFormat.HasValue() => time.ToString(DateFormat, CultureInfo.InvariantCulture), - bool b => b.ToString().ToLowerInvariant(), - _ => obj + DateTime time when DateFormat.IsNotEmpty() => time.ToString(DateFormat, CultureInfo.InvariantCulture), + bool b => b.ToString().ToLowerInvariant(), + _ => obj }; - return IsNumeric(obj) ? SerializeNumber(obj) : output.ToString(); + return IsNumeric(obj) ? SerializeNumber(obj) : output.ToString()!; } static string SerializeNumber(object number) diff --git a/src/RestSharp.Serializers.Xml/XmlSerializerClientExtensions.cs b/src/RestSharp.Serializers.Xml/XmlSerializerClientExtensions.cs new file mode 100644 index 000000000..c0ec75bcf --- /dev/null +++ b/src/RestSharp.Serializers.Xml/XmlSerializerClientExtensions.cs @@ -0,0 +1,38 @@ +// Copyright © 2009-2020 John Sheehan, Andrew Young, Alexey Zimarev and RestSharp community +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace RestSharp.Serializers.Xml; + +[PublicAPI] +public static class XmlSerializerClientExtensions { + public static RestClient UseXmlSerializer( + this RestClient restClient, + string? xmlNamespace = null, + string? rootElement = null, + bool useAttributeDeserializer = false + ) { + var xmlSerializer = new XmlSerializer { + Namespace = xmlNamespace, + RootElement = rootElement + }; + + var xmlDeserializer = useAttributeDeserializer ? new XmlAttributeDeserializer() : new XmlDeserializer(); + + var serializer = new XmlRestSerializer() + .WithXmlSerializer(xmlSerializer) + .WithXmlDeserializer(xmlDeserializer); + + return restClient.UseSerializer(() => serializer); + } +} \ No newline at end of file diff --git a/src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs b/src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs index 921111c2a..09eb301bb 100644 --- a/src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs +++ b/src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs @@ -4,46 +4,31 @@ // ReSharper disable CheckNamespace -namespace RestSharp.Authenticators; +namespace RestSharp.Authenticators; /// RFC: The OAuth 1.0 Protocol -[PublicAPI] public class OAuth1Authenticator : IAuthenticator { - public virtual string Realm { get; set; } - - public virtual OAuthParameterHandling ParameterHandling { get; set; } - - public virtual OAuthSignatureMethod SignatureMethod { get; set; } - + public virtual string? Realm { get; set; } + public virtual OAuthParameterHandling ParameterHandling { get; set; } + public virtual OAuthSignatureMethod SignatureMethod { get; set; } public virtual OAuthSignatureTreatment SignatureTreatment { get; set; } - - internal virtual OAuthType Type { get; set; } - - internal virtual string ConsumerKey { get; set; } - - internal virtual string? ConsumerSecret { get; set; } - - internal virtual string Token { get; set; } - - internal virtual string TokenSecret { get; set; } - - internal virtual string Verifier { get; set; } - - internal virtual string Version { get; set; } - - internal virtual string CallbackUrl { get; set; } - - internal virtual string SessionHandle { get; set; } - - internal virtual string ClientUsername { get; set; } - - internal virtual string ClientPassword { get; set; } + public virtual OAuthType Type { get; set; } + public virtual string? ConsumerKey { get; set; } + public virtual string? ConsumerSecret { get; set; } + public virtual string? Token { get; set; } + public virtual string? TokenSecret { get; set; } + public virtual string? Verifier { get; set; } + public virtual string? Version { get; set; } + public virtual string? CallbackUrl { get; set; } + public virtual string? SessionHandle { get; set; } + public virtual string? ClientUsername { get; set; } + public virtual string? ClientPassword { get; set; } public ValueTask Authenticate(RestClient client, RestRequest request) { var workflow = new OAuthWorkflow { - ConsumerKey = ConsumerKey, - ConsumerSecret = ConsumerSecret, - ParameterHandling = ParameterHandling, + ConsumerKey = ConsumerKey, + ConsumerSecret = ConsumerSecret, + // ParameterHandling = ParameterHandling, SignatureMethod = SignatureMethod, SignatureTreatment = SignatureTreatment, Verifier = Verifier, @@ -60,6 +45,7 @@ public ValueTask Authenticate(RestClient client, RestRequest request) { return default; } + [PublicAPI] public static OAuth1Authenticator ForRequestToken( string consumerKey, string? consumerSecret, @@ -77,6 +63,7 @@ public static OAuth1Authenticator ForRequestToken( return authenticator; } + [PublicAPI] public static OAuth1Authenticator ForRequestToken(string consumerKey, string? consumerSecret, string callbackUrl) { var authenticator = ForRequestToken(consumerKey, consumerSecret); @@ -85,6 +72,7 @@ public static OAuth1Authenticator ForRequestToken(string consumerKey, string? co return authenticator; } + [PublicAPI] public static OAuth1Authenticator ForAccessToken( string consumerKey, string? consumerSecret, @@ -103,6 +91,7 @@ public static OAuth1Authenticator ForAccessToken( Type = OAuthType.AccessToken }; + [PublicAPI] public static OAuth1Authenticator ForAccessToken( string consumerKey, string? consumerSecret, @@ -117,15 +106,7 @@ string verifier return authenticator; } - /// - /// - /// - /// - /// - /// - /// - /// - /// + [PublicAPI] public static OAuth1Authenticator ForAccessTokenRefresh( string consumerKey, string? consumerSecret, @@ -140,16 +121,7 @@ string sessionHandle return authenticator; } - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// + [PublicAPI] public static OAuth1Authenticator ForAccessTokenRefresh( string consumerKey, string? consumerSecret, @@ -166,15 +138,7 @@ string sessionHandle return authenticator; } - /// - /// - /// - /// - /// - /// - /// - /// - /// + [PublicAPI] public static OAuth1Authenticator ForClientAuthentication( string consumerKey, string? consumerSecret, @@ -193,15 +157,7 @@ public static OAuth1Authenticator ForClientAuthentication( Type = OAuthType.ClientAuthentication }; - /// - /// - /// - /// - /// - /// - /// - /// - /// + [PublicAPI] public static OAuth1Authenticator ForProtectedResource( string consumerKey, string? consumerSecret, @@ -247,7 +203,7 @@ void AddOAuthData(RestClient client, RestRequest request, OAuthWorkflow workflow var query = request.AlwaysMultipartFormData || request.Files.Count > 0 - ? x => BaseQuery(x) && x.Name.StartsWith("oauth_") + ? x => BaseQuery(x) && x.Name != null && x.Name.StartsWith("oauth_") : (Func)BaseQuery; parameters.AddRange(client.DefaultParameters.Where(query).ToWebParameters()); @@ -291,7 +247,7 @@ string GetAuthorizationHeader() { .ToList(); if (!Realm.IsEmpty()) - oathParameters.Insert(0, $"realm=\"{OAuthTools.UrlEncodeRelaxed(Realm)}\""); + oathParameters.Insert(0, $"realm=\"{OAuthTools.UrlEncodeRelaxed(Realm!)}\""); return "OAuth " + string.Join(",", oathParameters); } @@ -299,5 +255,6 @@ string GetAuthorizationHeader() { } static class ParametersExtensions { - internal static IEnumerable ToWebParameters(this IEnumerable p) => p.Select(x => new WebPair(x.Name, x.Value.ToString())); + internal static IEnumerable ToWebParameters(this IEnumerable p) + => p.Select(x => new WebPair(Ensure.NotNull(x.Name, "Parameter name"), Ensure.NotNull(x.Value, "Parameter value").ToString()!)); } \ No newline at end of file diff --git a/src/RestSharp/Authenticators/OAuth/OAuthWorkflow.cs b/src/RestSharp/Authenticators/OAuth/OAuthWorkflow.cs index e4d60f857..420709c4d 100644 --- a/src/RestSharp/Authenticators/OAuth/OAuthWorkflow.cs +++ b/src/RestSharp/Authenticators/OAuth/OAuthWorkflow.cs @@ -2,41 +2,27 @@ using RestSharp.Authenticators.OAuth.Extensions; using RestSharp.Extensions; -namespace RestSharp.Authenticators.OAuth; +namespace RestSharp.Authenticators.OAuth; /// /// A class to encapsulate OAuth authentication flow. /// sealed class OAuthWorkflow { - public string Version { get; set; } - - public string ConsumerKey { get; set; } - - public string? ConsumerSecret { get; set; } - - public string Token { get; set; } - - public string TokenSecret { get; set; } - - public string CallbackUrl { get; set; } - - public string Verifier { get; set; } - - public string SessionHandle { get; set; } - - public OAuthSignatureMethod SignatureMethod { get; set; } - + public string? Version { get; set; } + public string? ConsumerKey { get; set; } + public string? ConsumerSecret { get; set; } + public string? Token { get; set; } + public string? TokenSecret { get; set; } + public string? CallbackUrl { get; set; } + public string? Verifier { get; set; } + public string? SessionHandle { get; set; } + public OAuthSignatureMethod SignatureMethod { get; set; } public OAuthSignatureTreatment SignatureTreatment { get; set; } - - public OAuthParameterHandling ParameterHandling { get; set; } - - public string ClientUsername { get; set; } - - public string ClientPassword { get; set; } - - public string RequestTokenUrl { get; set; } - - public string AccessTokenUrl { get; set; } + // public OAuthParameterHandling ParameterHandling { get; set; } + public string? ClientUsername { get; set; } + public string? ClientPassword { get; set; } + public string? RequestTokenUrl { get; set; } + public string? AccessTokenUrl { get; set; } /// /// Generates an OAuth signature to pass to an @@ -58,7 +44,7 @@ public OAuthParameters BuildRequestTokenInfo(string method, WebPairCollection pa var authParameters = GenerateAuthParameters(timestamp, nonce); allParameters.AddRange(authParameters); - var signatureBase = OAuthTools.ConcatenateRequestElements(method, RequestTokenUrl, allParameters); + var signatureBase = OAuthTools.ConcatenateRequestElements(method, Ensure.NotNull(RequestTokenUrl, nameof(RequestTokenUrl)), allParameters); return new OAuthParameters { Signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret), @@ -79,7 +65,7 @@ public OAuthParameters BuildAccessTokenSignature(string method, WebPairCollectio var allParameters = new WebPairCollection(); allParameters.AddRange(parameters); - var uri = new Uri(AccessTokenUrl); + var uri = new Uri(Ensure.NotEmptyString(AccessTokenUrl, nameof(AccessTokenUrl))); var timestamp = OAuthTools.GetTimestamp(); var nonce = OAuthTools.GetNonce(); @@ -107,7 +93,7 @@ public OAuthParameters BuildClientAuthAccessTokenSignature(string method, WebPai var allParameters = new WebPairCollection(); allParameters.AddRange(parameters); - var uri = new Uri(AccessTokenUrl); + var uri = new Uri(Ensure.NotNull(AccessTokenUrl, nameof(AccessTokenUrl))); var timestamp = OAuthTools.GetTimestamp(); var nonce = OAuthTools.GetNonce(); @@ -132,7 +118,7 @@ public OAuthParameters BuildProtectedResourceSignature(string method, WebPairCol var uri = new Uri(url); var urlParameters = HttpUtility.ParseQueryString(uri.Query); - allParameters.AddRange(urlParameters.AllKeys.Select(x => new WebPair(x, urlParameters[x]))); + allParameters.AddRange(urlParameters.AllKeys.Select(x => new WebPair(x!, urlParameters[x]!))); var timestamp = OAuthTools.GetTimestamp(); var nonce = OAuthTools.GetNonce(); @@ -171,38 +157,38 @@ void ValidateProtectedResourceState() { WebPairCollection GenerateAuthParameters(string timestamp, string nonce) { var authParameters = new WebPairCollection { - new WebPair("oauth_consumer_key", ConsumerKey), - new WebPair("oauth_nonce", nonce), - new WebPair("oauth_signature_method", SignatureMethod.ToRequestValue()), - new WebPair("oauth_timestamp", timestamp), - new WebPair("oauth_version", Version ?? "1.0") + new("oauth_consumer_key", Ensure.NotNull(ConsumerKey, nameof(ConsumerKey))), + new("oauth_nonce", nonce), + new("oauth_signature_method", SignatureMethod.ToRequestValue()), + new("oauth_timestamp", timestamp), + new("oauth_version", Version ?? "1.0") }; - if (!Token.IsEmpty()) authParameters.Add(new WebPair("oauth_token", Token, true)); + if (!Token.IsEmpty()) authParameters.Add(new WebPair("oauth_token", Token!, true)); - if (!CallbackUrl.IsEmpty()) authParameters.Add(new WebPair("oauth_callback", CallbackUrl, true)); + if (!CallbackUrl.IsEmpty()) authParameters.Add(new WebPair("oauth_callback", CallbackUrl!, true)); - if (!Verifier.IsEmpty()) authParameters.Add(new WebPair("oauth_verifier", Verifier)); + if (!Verifier.IsEmpty()) authParameters.Add(new WebPair("oauth_verifier", Verifier!)); - if (!SessionHandle.IsEmpty()) authParameters.Add(new WebPair("oauth_session_handle", SessionHandle)); + if (!SessionHandle.IsEmpty()) authParameters.Add(new WebPair("oauth_session_handle", SessionHandle!)); return authParameters; } WebPairCollection GenerateXAuthParameters(string timestamp, string nonce) - => new WebPairCollection { - new WebPair("x_auth_username", ClientUsername), - new WebPair("x_auth_password", ClientPassword), - new WebPair("x_auth_mode", "client_auth"), - new WebPair("oauth_consumer_key", ConsumerKey), - new WebPair("oauth_signature_method", SignatureMethod.ToRequestValue()), - new WebPair("oauth_timestamp", timestamp), - new WebPair("oauth_nonce", nonce), - new WebPair("oauth_version", Version ?? "1.0") + => new() { + new("x_auth_username", Ensure.NotNull(ClientUsername, nameof(ClientUsername))), + new("x_auth_password", Ensure.NotNull(ClientPassword, nameof(ClientPassword))), + new("x_auth_mode", "client_auth"), + new("oauth_consumer_key", Ensure.NotNull(ConsumerKey, nameof(ConsumerKey))), + new("oauth_signature_method", SignatureMethod.ToRequestValue()), + new("oauth_timestamp", timestamp), + new("oauth_nonce", nonce), + new("oauth_version", Version ?? "1.0") }; internal class OAuthParameters { - public WebPairCollection Parameters { get; set; } - public string Signature { get; set; } + public WebPairCollection Parameters { get; init; } = null!; + public string Signature { get; init; } = null!; } } \ No newline at end of file diff --git a/src/RestSharp/Authenticators/OAuth/WebPair.cs b/src/RestSharp/Authenticators/OAuth/WebPair.cs index ffa856b8d..a71155860 100644 --- a/src/RestSharp/Authenticators/OAuth/WebPair.cs +++ b/src/RestSharp/Authenticators/OAuth/WebPair.cs @@ -14,7 +14,7 @@ using System.Collections.Generic; -namespace RestSharp.Authenticators.OAuth; +namespace RestSharp.Authenticators.OAuth; class WebPair { public WebPair(string name, string value, bool encode = false) { @@ -32,10 +32,10 @@ public WebPair(string name, string value, bool encode = false) { internal static WebPairComparer Comparer { get; } = new(); internal class WebPairComparer : IComparer { - public int Compare(WebPair x, WebPair y) { - var compareName = string.CompareOrdinal(x.Name, y.Name); + public int Compare(WebPair? x, WebPair? y) { + var compareName = string.CompareOrdinal(x?.Name, y?.Name); - return compareName != 0 ? compareName : string.CompareOrdinal(x.Value, y.Value); + return compareName != 0 ? compareName : string.CompareOrdinal(x?.Value, y?.Value); } } } \ No newline at end of file diff --git a/src/RestSharp/Extensions/ReflectionExtensions.cs b/src/RestSharp/Extensions/ReflectionExtensions.cs index 671b17cba..6d71f898c 100644 --- a/src/RestSharp/Extensions/ReflectionExtensions.cs +++ b/src/RestSharp/Extensions/ReflectionExtensions.cs @@ -29,7 +29,7 @@ public static class ReflectionExtensions { /// /// /// - public static bool IsSubclassOfRawGeneric(this Type toCheck, Type generic) { + public static bool IsSubclassOfRawGeneric(this Type? toCheck, Type generic) { while (toCheck != null && toCheck != typeof(object)) { var cur = toCheck.GetTypeInfo().IsGenericType ? toCheck.GetGenericTypeDefinition() @@ -70,7 +70,7 @@ public static bool IsSubclassOfRawGeneric(this Type toCheck, Type generic) { var enumValueAsUnderlyingType = Convert.ChangeType(value, Enum.GetUnderlyingType(type), culture); - if (enumValueAsUnderlyingType != null && Enum.IsDefined(type, enumValueAsUnderlyingType)) + if (Enum.IsDefined(type, enumValueAsUnderlyingType)) ret = (Enum)Enum.ToObject(type, enumValueAsUnderlyingType); return ret ?? Activator.CreateInstance(type); diff --git a/src/RestSharp/Extensions/StringEncodingExtensions.cs b/src/RestSharp/Extensions/StringEncodingExtensions.cs deleted file mode 100644 index 1630cd284..000000000 --- a/src/RestSharp/Extensions/StringEncodingExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Text; - -namespace RestSharp.Extensions; - -public static class StringEncodingExtensions { - /// - /// Converts a byte array to a string, using its byte order mark to convert it to the right encoding. - /// http://www.shrinkrays.net/code-snippets/csharp/an-extension-method-for-converting-a-byte-array-to-a-string.aspx - /// - /// An array of bytes to convert - /// Content encoding. Will fallback to UTF8 if not a valid encoding. - /// The byte as a string. - [Obsolete("This method will be removed soon. If you use it, please copy the code to your project.")] - public static string AsString(this byte[] buffer, string? encoding) { - var enc = encoding.IsEmpty() ? Encoding.UTF8 : TryParseEncoding(); - - return AsString(buffer, enc); - - Encoding TryParseEncoding() { - try { - return encoding != null ? Encoding.GetEncoding(encoding) : Encoding.UTF8; - } - catch (ArgumentException) { - return Encoding.UTF8; - } - } - } - - static string AsString(byte[]? buffer, Encoding encoding) => buffer == null ? "" : encoding.GetString(buffer, 0, buffer.Length); -} \ No newline at end of file diff --git a/src/RestSharp/Extensions/StringExtensions.cs b/src/RestSharp/Extensions/StringExtensions.cs index ce35cdff1..8eec2a512 100644 --- a/src/RestSharp/Extensions/StringExtensions.cs +++ b/src/RestSharp/Extensions/StringExtensions.cs @@ -5,7 +5,7 @@ namespace RestSharp.Extensions; -public static class StringExtensions { +static class StringExtensions { static readonly Regex IsUpperCaseRegex = new(@"^[A-Z]+$"); static readonly Regex AddUnderscoresRegex1 = new(@"[-\s]"); @@ -58,13 +58,6 @@ public static string UrlEncode(this string input) { return encoded?.Replace("+", "%20"); } - /// - /// Check that a string is not null or empty - /// - /// String to check - /// bool - public static bool HasValue(this string input) => !string.IsNullOrEmpty(input); - /// /// Remove underscores from a string /// diff --git a/src/RestSharp/Parameters/Parameter.cs b/src/RestSharp/Parameters/Parameter.cs index 9d5241c22..d11403830 100644 --- a/src/RestSharp/Parameters/Parameter.cs +++ b/src/RestSharp/Parameters/Parameter.cs @@ -26,7 +26,7 @@ public Parameter(string? name, object? value, ParameterType type, bool encode = } Name = name; - Value = type != ParameterType.UrlSegment ? value : value?.ToString().Replace("%2F", "/").Replace("%2f", "/"); + Value = type != ParameterType.UrlSegment ? value : value?.ToString()?.Replace("%2F", "/").Replace("%2f", "/"); Type = type == ParameterType.QueryStringWithoutEncode ? ParameterType.QueryString : type; Encode = type != ParameterType.QueryStringWithoutEncode && encode; } diff --git a/src/RestSharp/Properties/AssemblyInfo.cs b/src/RestSharp/Properties/AssemblyInfo.cs index 44d73bf55..cf7b969d5 100644 --- a/src/RestSharp/Properties/AssemblyInfo.cs +++ b/src/RestSharp/Properties/AssemblyInfo.cs @@ -6,5 +6,8 @@ ), InternalsVisibleTo( "RestSharp.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fda57af14a288d46e3efea89617037585c4de57159cd536ca6dff792ea1d6addc665f2fccb4285413d9d44db5a1be87cb82686db200d16325ed9c42c89cd4824d8cc447f7cee2ac000924c3bceeb1b7fcb5cc1a3901785964d48ce14172001084134f4dcd9973c3776713b595443b1064bb53e2eeb924969244d354e46495e9d" + ), + InternalsVisibleTo( + "RestSharp.Serializers.Xml, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fda57af14a288d46e3efea89617037585c4de57159cd536ca6dff792ea1d6addc665f2fccb4285413d9d44db5a1be87cb82686db200d16325ed9c42c89cd4824d8cc447f7cee2ac000924c3bceeb1b7fcb5cc1a3901785964d48ce14172001084134f4dcd9973c3776713b595443b1064bb53e2eeb924969244d354e46495e9d" )] [assembly: CLSCompliant(true)] \ No newline at end of file diff --git a/src/RestSharp/Request/HttpContentExtensions.cs b/src/RestSharp/Request/HttpContentExtensions.cs index f833d25ed..a12edf8c6 100644 --- a/src/RestSharp/Request/HttpContentExtensions.cs +++ b/src/RestSharp/Request/HttpContentExtensions.cs @@ -17,9 +17,9 @@ namespace RestSharp; public static class HttpContentExtensions { public static string GetFormBoundary(this HttpContent content) { - var contentType = content.Headers.ContentType.ToString(); - var index = contentType.IndexOf("boundary=", StringComparison.Ordinal); - return index > 0 ? GetFormBoundary(contentType, index) : ""; + var contentType = content.Headers.ContentType?.ToString(); + var index = contentType?.IndexOf("boundary=", StringComparison.Ordinal) ?? 0; + return index > 0 ? GetFormBoundary(contentType!, index) : ""; } static string GetFormBoundary(string headerValue, int index) { diff --git a/src/RestSharp/Request/HttpRequestMessageExtensions.cs b/src/RestSharp/Request/HttpRequestMessageExtensions.cs index 1d452b59f..a2cce28f4 100644 --- a/src/RestSharp/Request/HttpRequestMessageExtensions.cs +++ b/src/RestSharp/Request/HttpRequestMessageExtensions.cs @@ -26,8 +26,8 @@ public static void AddHeaders(this HttpRequestMessage message, IEnumerable Request = request; + + public RestRequest? Request { get; } +} \ No newline at end of file diff --git a/src/RestSharp/Request/RequestContent.cs b/src/RestSharp/Request/RequestContent.cs index 8add037c5..02f709575 100644 --- a/src/RestSharp/Request/RequestContent.cs +++ b/src/RestSharp/Request/RequestContent.cs @@ -61,21 +61,28 @@ void AddFiles() { } HttpContent Serialize(Parameter body) { - if (body.DataFormat == DataFormat.None) { - var stringContent = new StringContent(body.Value!.ToString(), _client.Options.Encoding, body.ContentType); - return stringContent; - } + return body.DataFormat switch { + DataFormat.None => new StringContent(body.Value!.ToString()!, _client.Options.Encoding, body.ContentType), + _ => GetSerialized() + }; + + HttpContent GetSerialized() { + if (!_client.Serializers.TryGetValue(body.DataFormat, out var serializer)) + throw new InvalidDataContractException( + $"Can't find serializer for content type {body.DataFormat}" + ); - if (!_client.Serializers.TryGetValue(body.DataFormat, out var serializer)) - throw new InvalidDataContractException( - $"Can't find serializer for content type {body.DataFormat}" - ); + var content = serializer.Serialize(body); + + if (content == null) + throw new SerializationException("Request body serialized to null"); - return new StringContent( - serializer.Serialize(body), - _client.Options.Encoding, - body.ContentType ?? serializer.ContentType - ); + return new StringContent( + content, + _client.Options.Encoding, + body.ContentType ?? serializer.ContentType + ); + } } static bool BodyShouldBeMultipartForm(Parameter bodyParameter) { @@ -96,7 +103,7 @@ void AddBody() { if (bodyParameter!.Name.IsEmpty()) mpContent.Add(bodyContent); else - mpContent.Add(bodyContent, bodyParameter.Name); + mpContent.Add(bodyContent, bodyParameter.Name!); Content = mpContent; } else { @@ -114,8 +121,8 @@ void AddPostParameters() { // we got the multipart form already instantiated, just add parameters to it foreach (var postParameter in postParameters) { mpContent.Add( - new StringContent(postParameter.Value!.ToString(), _client.Options.Encoding, postParameter.ContentType), - postParameter.Name + new StringContent(postParameter.Value!.ToString()!, _client.Options.Encoding, postParameter.ContentType), + postParameter.Name! ); } } @@ -124,7 +131,7 @@ void AddPostParameters() { var formContent = new FormUrlEncodedContent( _request.Parameters .Where(x => x.Type == ParameterType.GetOrPost) - .Select(x => new KeyValuePair(x.Name!, x.Value!.ToString())) + .Select(x => new KeyValuePair(x.Name!, x.Value!.ToString()!))! ); Content = formContent; } @@ -139,16 +146,20 @@ void AddHeader(Parameter parameter) { var parameterStringValue = parameter.Value!.ToString(); var value = parameter.Name switch { - ContentType => GetContentTypeHeader(parameterStringValue), + ContentType => GetContentTypeHeader(Ensure.NotNull(parameterStringValue, nameof(parameter))), _ => parameterStringValue }; - Content!.Headers.Remove(parameter.Name); - Content!.Headers.TryAddWithoutValidation(parameter.Name, value); + var pName = Ensure.NotNull(parameter.Name, nameof(parameter.Name)); + Content!.Headers.Remove(pName); + Content!.Headers.TryAddWithoutValidation(pName, value); } } string GetContentTypeHeader(string contentType) { - var boundary = Content!.GetFormBoundary(); + if (Content == null) + throw new InvalidRequestException("Content type headers should not be used when there's no body in the request"); + + var boundary = Content.GetFormBoundary(); return boundary.IsEmpty() ? contentType : $"{contentType}; boundary=\"{boundary}\""; } diff --git a/src/RestSharp/Request/RestRequestExtensions.cs b/src/RestSharp/Request/RestRequestExtensions.cs index 711c28ed4..fa2ab1fb1 100644 --- a/src/RestSharp/Request/RestRequestExtensions.cs +++ b/src/RestSharp/Request/RestRequestExtensions.cs @@ -3,6 +3,7 @@ namespace RestSharp; +[PublicAPI] public static class RestRequestExtensions { static readonly Regex PortSplitRegex = new(@":\d+"); @@ -132,7 +133,7 @@ public static RestRequest AddBody(this RestRequest request, object obj) => request.RequestFormat switch { DataFormat.Json => request.AddJsonBody(obj), DataFormat.Xml => request.AddXmlBody(obj), - _ => request.AddParameter("", obj.ToString()) + _ => request.AddParameter("", obj.ToString()!) }; public static RestRequest AddJsonBody(this RestRequest request, object obj) { @@ -190,7 +191,9 @@ bool IsAllowedProperty(string propertyName) => includedProperties.Length == 0 || includedProperties.Length > 0 && includedProperties.Contains(propertyName); } - public static RestRequest AddObject(this RestRequest request, object obj) => request.With(x => x.AddObject(obj, new string[] { })); + public static RestRequest AddObject(this RestRequest request, object obj) { + return request.With(x => x.AddObject(obj, new string[] { })); + } static void CheckAndThrowsForInvalidHost(string name, string value) { static bool InvalidHost(string host) => Uri.CheckHostName(PortSplitRegex.Split(host)[0]) == UriHostNameType.Unknown; diff --git a/src/RestSharp/Response/RestResponseBase.cs b/src/RestSharp/Response/RestResponseBase.cs index e165c1386..8526334fa 100644 --- a/src/RestSharp/Response/RestResponseBase.cs +++ b/src/RestSharp/Response/RestResponseBase.cs @@ -49,7 +49,7 @@ public abstract class RestResponseBase { /// /// Encoding of the response content /// - public ICollection ContentEncoding { get; init; } + public ICollection ContentEncoding { get; init; } = new List(); /// /// String representation of response content @@ -84,17 +84,17 @@ public abstract class RestResponseBase { /// /// HttpWebResponse.Server /// - public string Server { get; init; } + public string? Server { get; init; } /// /// Cookies returned by server with the response /// - public CookieCollection Cookies { get; protected internal set; } + public CookieCollection? Cookies { get; protected internal set; } /// /// Headers returned by server with the response /// - public IList Headers { get; protected internal set; } + public IList? Headers { get; protected internal set; } /// /// Status of the request. Will return Error for transport errors. @@ -105,7 +105,7 @@ public abstract class RestResponseBase { /// /// Transport or other non-HTTP error generated while attempting request /// - public string ErrorMessage { get; set; } + public string? ErrorMessage { get; set; } /// /// The exception thrown during the request, if any diff --git a/src/RestSharp/RestClient.cs b/src/RestSharp/RestClient.cs index 8ae88be53..37012bc55 100644 --- a/src/RestSharp/RestClient.cs +++ b/src/RestSharp/RestClient.cs @@ -96,7 +96,7 @@ public RestClient(string baseUrl) : this(new Uri(Ensure.NotEmptyString(baseUrl, Func Encode { get; set; } = s => s.UrlEncode(); - Func EncodeQuery { get; set; } = (s, encoding) => s.UrlEncode(encoding); + Func EncodeQuery { get; set; } = (s, encoding) => s.UrlEncode(encoding)!; /// /// Allows to use a custom way to encode URL parameters @@ -136,7 +136,7 @@ public RestClient AddDefaultParameter(Parameter p) { ); case ParameterType.Cookie: { lock (_cookieContainer) { - _cookieContainer.Add(new Cookie(p.Name, p.Value!.ToString())); + _cookieContainer.Add(new Cookie(p.Name!, p.Value!.ToString())); } break; } @@ -164,7 +164,7 @@ public Uri BuildUri(RestRequest request) { return new Uri(finalUri!); } - internal string? BuildUriWithoutQueryParameters(RestRequest request) { + internal string BuildUriWithoutQueryParameters(RestRequest request) { DoBuildUriValidations(request); var applied = GetUrlSegmentParamsValues(request); @@ -231,7 +231,7 @@ UrlSegmentParamsValues GetUrlSegmentParamsValues(RestRequest request) { foreach (var parameter in parameters) { var paramPlaceHolder = $"{{{parameter.Name}}}"; - var paramValue = parameter.Encode ? Encode(parameter.Value!.ToString()) : parameter.Value!.ToString(); + var paramValue = parameter.Encode ? Encode(parameter.Value!.ToString()!) : parameter.Value!.ToString(); if (hasResource) assembled = assembled.Replace(paramPlaceHolder, paramValue); @@ -241,12 +241,12 @@ UrlSegmentParamsValues GetUrlSegmentParamsValues(RestRequest request) { return new UrlSegmentParamsValues(builder.Uri, assembled); } - static string? MergeBaseUrlAndResource(Uri? baseUrl, string? resource) { + static string MergeBaseUrlAndResource(Uri? baseUrl, string? resource) { var assembled = resource; if (!IsNullOrEmpty(assembled) && assembled!.StartsWith("/")) assembled = assembled.Substring(1); - if (baseUrl == null || IsNullOrEmpty(baseUrl.AbsoluteUri)) return assembled; + if (baseUrl == null || IsNullOrEmpty(baseUrl.AbsoluteUri)) return assembled ?? ""; var usingBaseUri = baseUrl.AbsoluteUri.EndsWith("/") || IsNullOrEmpty(assembled) ? baseUrl : new Uri(baseUrl.AbsoluteUri + "/"); @@ -295,7 +295,7 @@ string EncodeParameter(Parameter parameter, Encoding encoding) { ? $"{parameter.Name}={StringOrEmpty(parameter.Value)}" : $"{EncodeQuery(parameter.Name!, encoding)}={EncodeQuery(StringOrEmpty(parameter.Value), encoding)}"; - static string StringOrEmpty(object? value) => value == null ? "" : value.ToString(); + static string StringOrEmpty(object? value) => value == null ? "" : value.ToString()!; } internal RestResponse Deserialize(RestRequest request, RestResponse raw) { diff --git a/src/RestSharp/RestClientOptions.cs b/src/RestSharp/RestClientOptions.cs index 61a32df85..1f4283b02 100644 --- a/src/RestSharp/RestClientOptions.cs +++ b/src/RestSharp/RestClientOptions.cs @@ -23,7 +23,7 @@ namespace RestSharp; public class RestClientOptions { - static readonly Version Version = new AssemblyName(typeof(RestClientOptions).Assembly.FullName).Version; + static readonly Version Version = new AssemblyName(typeof(RestClientOptions).Assembly.FullName!).Version!; static readonly string DefaultUserAgent = $"RestSharp/{Version}"; diff --git a/src/RestSharp/Serializers/ContentType.cs b/src/RestSharp/Serializers/ContentType.cs index 45629b0a7..5678f8dd2 100644 --- a/src/RestSharp/Serializers/ContentType.cs +++ b/src/RestSharp/Serializers/ContentType.cs @@ -5,6 +5,8 @@ public static class ContentType { public const string Xml = "application/xml"; + public const string Plain = "text/plain"; + public static readonly Dictionary FromDataFormat = new() { { DataFormat.Xml, Xml }, diff --git a/src/RestSharp/Serializers/Json/SystemTextJsonSerializer.cs b/src/RestSharp/Serializers/Json/SystemTextJsonSerializer.cs index b25be8420..ef57e2d6c 100644 --- a/src/RestSharp/Serializers/Json/SystemTextJsonSerializer.cs +++ b/src/RestSharp/Serializers/Json/SystemTextJsonSerializer.cs @@ -20,7 +20,7 @@ public class SystemTextJsonSerializer : IRestSerializer { public string? Serialize(Parameter bodyParameter) => Serialize(bodyParameter.Value); - public T? Deserialize(RestResponse response) => JsonSerializer.Deserialize(response.Content, _options); + public T? Deserialize(RestResponse response) => JsonSerializer.Deserialize(response.Content!, _options); public string[] SupportedContentTypes { get; } = { "application/json", "text/json", "text/x-json", "text/javascript", "*+json" diff --git a/src/RestSharp/Serializers/Xml/DotNetXmlDeserializer.cs b/src/RestSharp/Serializers/Xml/DotNetXmlDeserializer.cs index 230f9312e..aade10166 100644 --- a/src/RestSharp/Serializers/Xml/DotNetXmlDeserializer.cs +++ b/src/RestSharp/Serializers/Xml/DotNetXmlDeserializer.cs @@ -14,22 +14,22 @@ public class DotNetXmlDeserializer : IXmlDeserializer { /// /// Name of the root element to use when serializing /// - public string RootElement { get; set; } + public string? RootElement { get; set; } /// /// XML namespace to use when serializing /// - public string Namespace { get; set; } + public string? Namespace { get; set; } - public string DateFormat { get; set; } + public string? DateFormat { get; set; } public T? Deserialize(RestResponse response) { if (string.IsNullOrEmpty(response.Content)) return default; - using var stream = new MemoryStream(Encoding.GetBytes(response.Content)); + using var stream = new MemoryStream(Encoding.GetBytes(response.Content!)); var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T)); - return (T)serializer.Deserialize(stream); + return (T?)serializer.Deserialize(stream); } } \ No newline at end of file diff --git a/src/RestSharp/Serializers/Xml/DotNetXmlSerializer.cs b/src/RestSharp/Serializers/Xml/DotNetXmlSerializer.cs index 58c884b6f..c2aa9d768 100644 --- a/src/RestSharp/Serializers/Xml/DotNetXmlSerializer.cs +++ b/src/RestSharp/Serializers/Xml/DotNetXmlSerializer.cs @@ -53,12 +53,12 @@ public string Serialize(object obj) { /// /// XML namespace to use when serializing /// - public string Namespace { get; set; } + public string? Namespace { get; set; } /// /// Format string to use when serializing dates /// - public string DateFormat { get; set; } + public string? DateFormat { get; set; } /// /// Content type for serialized content diff --git a/src/RestSharp/Serializers/Xml/IXmlDeserializer.cs b/src/RestSharp/Serializers/Xml/IXmlDeserializer.cs index ccb77b01b..a85ea9970 100644 --- a/src/RestSharp/Serializers/Xml/IXmlDeserializer.cs +++ b/src/RestSharp/Serializers/Xml/IXmlDeserializer.cs @@ -1,7 +1,7 @@ namespace RestSharp.Serializers.Xml; public interface IXmlDeserializer : IDeserializer, IWithRootElement { - string Namespace { get; set; } + string? Namespace { get; set; } - string DateFormat { get; set; } + string? DateFormat { get; set; } } \ No newline at end of file diff --git a/src/RestSharp/Serializers/Xml/IXmlSerializer.cs b/src/RestSharp/Serializers/Xml/IXmlSerializer.cs index 4b29d3dff..27d845c52 100644 --- a/src/RestSharp/Serializers/Xml/IXmlSerializer.cs +++ b/src/RestSharp/Serializers/Xml/IXmlSerializer.cs @@ -1,7 +1,7 @@ namespace RestSharp.Serializers.Xml; public interface IXmlSerializer : ISerializer, IWithRootElement { - string Namespace { get; set; } + string? Namespace { get; set; } - string DateFormat { get; set; } + string? DateFormat { get; set; } } \ No newline at end of file diff --git a/src/RestSharp/Serializers/Xml/XmlRestSerializer.cs b/src/RestSharp/Serializers/Xml/XmlRestSerializer.cs index 129e77b55..9bc55c6fa 100644 --- a/src/RestSharp/Serializers/Xml/XmlRestSerializer.cs +++ b/src/RestSharp/Serializers/Xml/XmlRestSerializer.cs @@ -17,11 +17,11 @@ namespace RestSharp.Serializers.Xml; public class XmlRestSerializer : IRestSerializer, IXmlSerializer, IXmlDeserializer { - XmlSerilizationOptions _options = XmlSerilizationOptions.Default; - IXmlDeserializer _xmlDeserializer; - IXmlSerializer _xmlSerializer; + XmlSerializationOptions _options = XmlSerializationOptions.Default; + IXmlDeserializer _xmlDeserializer; + IXmlSerializer _xmlSerializer; - public XmlRestSerializer() : this(new XmlSerializer(), new XmlDeserializer()) { } + public XmlRestSerializer() : this(new DotNetXmlSerializer(), new DotNetXmlDeserializer()) { } public XmlRestSerializer(IXmlSerializer xmlSerializer, IXmlDeserializer xmlDeserializer) { _xmlDeserializer = xmlDeserializer; @@ -34,13 +34,16 @@ public XmlRestSerializer(IXmlSerializer xmlSerializer, IXmlDeserializer xmlDeser public string ContentType { get; set; } = Serializers.ContentType.Xml; - public string? Serialize(object? obj) => _xmlSerializer.Serialize(obj); + public string? Serialize(object? obj) => _xmlSerializer.Serialize(Ensure.NotNull(obj, nameof(obj))); public T? Deserialize(RestResponse response) => _xmlDeserializer.Deserialize(response); public string? Serialize(Parameter parameter) { if (parameter is not XmlParameter xmlParameter) - throw new InvalidOperationException("Supplied parameter is not an XML parameter"); + throw new ArgumentException("Supplied parameter is not an XML parameter", nameof(parameter)); + + if (parameter.Value == null) + throw new ArgumentNullException(nameof(parameter), "Parameter value is null"); var savedNamespace = _xmlSerializer.Namespace; _xmlSerializer.Namespace = xmlParameter.XmlNamespace ?? savedNamespace; @@ -61,30 +64,32 @@ public string? RootElement { } } - public string Namespace { + public string? Namespace { get => _options.Namespace; set { - _options.Namespace = value; - _xmlSerializer.Namespace = value; - _xmlDeserializer.Namespace = value; + var ns = Ensure.NotEmptyString(value, nameof(Namespace)); + _options.Namespace = ns; + _xmlSerializer.Namespace = ns; + _xmlDeserializer.Namespace = ns; } } - public string DateFormat { + public string? DateFormat { get => _options.DateFormat; set { - _options.DateFormat = value; - _xmlSerializer.DateFormat = value; - _xmlDeserializer.DateFormat = value; + var dateFormat = Ensure.NotEmptyString(value, nameof(DataFormat)); + _options.DateFormat = dateFormat; + _xmlSerializer.DateFormat = dateFormat; + _xmlDeserializer.DateFormat = dateFormat; } } - public XmlRestSerializer WithOptions(XmlSerilizationOptions options) { + public XmlRestSerializer WithOptions(XmlSerializationOptions options) { _options = options; return this; } - public XmlRestSerializer WithXmlSerializer(XmlSerilizationOptions? options = null) where T : IXmlSerializer, new() { + public XmlRestSerializer WithXmlSerializer(XmlSerializationOptions? options = null) where T : IXmlSerializer, new() { if (options != null) _options = options; return WithXmlSerializer( @@ -101,7 +106,7 @@ public XmlRestSerializer WithXmlSerializer(IXmlSerializer xmlSerializer) { return this; } - public XmlRestSerializer WithXmlDeserializer(XmlSerilizationOptions? options = null) where T : IXmlDeserializer, new() { + public XmlRestSerializer WithXmlDeserializer(XmlSerializationOptions? options = null) where T : IXmlDeserializer, new() { if (options != null) _options = options; return WithXmlDeserializer( @@ -119,23 +124,23 @@ public XmlRestSerializer WithXmlDeserializer(IXmlDeserializer xmlDeserializer) { } } -public class XmlSerilizationOptions { +public class XmlSerializationOptions { /// /// Name of the root element to use when serializing /// - public string RootElement { get; set; } + public string? RootElement { get; set; } /// /// XML namespace to use when serializing /// - public string Namespace { get; set; } + public string? Namespace { get; set; } /// /// Format string to use when serializing dates /// - public string DateFormat { get; set; } + public string? DateFormat { get; set; } - public CultureInfo Culture { get; set; } + public CultureInfo? Culture { get; set; } - public static XmlSerilizationOptions Default => new() { Culture = CultureInfo.InvariantCulture }; + public static XmlSerializationOptions Default => new() { Culture = CultureInfo.InvariantCulture }; } \ No newline at end of file diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 336a5e32a..cdb2a6932 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -4,6 +4,7 @@ true false net6.0 + disable diff --git a/test/RestSharp.IntegrationTests/AsyncTests.cs b/test/RestSharp.IntegrationTests/AsyncTests.cs index ae70b03dc..fb5c8c839 100644 --- a/test/RestSharp.IntegrationTests/AsyncTests.cs +++ b/test/RestSharp.IntegrationTests/AsyncTests.cs @@ -6,11 +6,13 @@ namespace RestSharp.IntegrationTests; public class AsyncTests : IAsyncLifetime { - readonly HttpServer _server; + readonly ITestOutputHelper _output; + readonly HttpServer _server; - public AsyncTests(ITestOutputHelper output) => _server = new HttpServer(output); - - static void UrlToStatusCodeHandler(HttpListenerContext obj) => obj.Response.StatusCode = int.Parse(obj.Request.Url.Segments.Last()); + public AsyncTests(ITestOutputHelper output) { + _output = output; + _server = new HttpServer(output); + } class ResponseHandler { void error(HttpListenerContext context) { diff --git a/test/RestSharp.IntegrationTests/Authentication/AuthenticationTests.cs b/test/RestSharp.IntegrationTests/Authentication/AuthenticationTests.cs index aec34f255..40ccfeeed 100644 --- a/test/RestSharp.IntegrationTests/Authentication/AuthenticationTests.cs +++ b/test/RestSharp.IntegrationTests/Authentication/AuthenticationTests.cs @@ -7,10 +7,14 @@ namespace RestSharp.IntegrationTests.Authentication; public class AuthenticationTests { + readonly ITestOutputHelper _output; + + public AuthenticationTests(ITestOutputHelper output) => _output = output; + static void UsernamePasswordEchoHandler(HttpListenerContext context) { - var header = context.Request.Headers["Authorization"]; + var header = context.Request.Headers["Authorization"]!; - var parts = Encoding.ASCII.GetString(Convert.FromBase64String(header.Substring("Basic ".Length))) + var parts = Encoding.ASCII.GetString(Convert.FromBase64String(header["Basic ".Length..])) .Split(':'); context.Response.OutputStream.WriteStringUtf8(string.Join("|", parts)); diff --git a/test/RestSharp.IntegrationTests/CompressionTests.cs b/test/RestSharp.IntegrationTests/CompressionTests.cs index 9bf01af03..8ef9cf1eb 100644 --- a/test/RestSharp.IntegrationTests/CompressionTests.cs +++ b/test/RestSharp.IntegrationTests/CompressionTests.cs @@ -6,6 +6,8 @@ namespace RestSharp.IntegrationTests; public class CompressionTests { + readonly ITestOutputHelper _output; + static Action GzipEchoValue(string value) => context => { context.Response.Headers.Add("Content-encoding", "gzip"); @@ -23,6 +25,8 @@ static Action DeflateEchoValue(string value) gzip.WriteStringUtf8(value); }; + + public CompressionTests(ITestOutputHelper output) => _output = output; [Fact] public async Task Can_Handle_Deflate_Compressed_Content() { diff --git a/test/RestSharp.IntegrationTests/Fixtures/RequestBodyFixture.cs b/test/RestSharp.IntegrationTests/Fixtures/RequestBodyFixture.cs index 60c683e25..82a5d874f 100644 --- a/test/RestSharp.IntegrationTests/Fixtures/RequestBodyFixture.cs +++ b/test/RestSharp.IntegrationTests/Fixtures/RequestBodyFixture.cs @@ -2,7 +2,7 @@ namespace RestSharp.IntegrationTests.Fixtures; -public class RequestBodyFixture : IDisposable { +public sealed class RequestBodyFixture : IDisposable { public SimpleServer Server { get; } public RequestBodyFixture() => Server = SimpleServer.Create(Handlers.Generic()); diff --git a/test/RestSharp.IntegrationTests/Fixtures/TestServer.cs b/test/RestSharp.IntegrationTests/Fixtures/TestServer.cs index 49b834c9a..0a4a74063 100644 --- a/test/RestSharp.IntegrationTests/Fixtures/TestServer.cs +++ b/test/RestSharp.IntegrationTests/Fixtures/TestServer.cs @@ -10,7 +10,7 @@ public class HttpServer { const string Address = "http://localhost:5151"; - public HttpServer(ITestOutputHelper? output = null) { + public HttpServer(ITestOutputHelper output = null) { var builder = WebApplication.CreateBuilder(); if (output != null) @@ -21,6 +21,7 @@ public HttpServer(ITestOutputHelper? output = null) { _app.MapGet("success", () => new TestResponse { Message = "Works!" }); _app.MapGet("echo", (string msg) => msg); _app.MapGet("timeout", async () => await Task.Delay(2000)); + // ReSharper disable once ConvertClosureToMethodGroup _app.MapGet("status", (int code) => Results.StatusCode(code)); } diff --git a/test/RestSharp.IntegrationTests/HttpHeadersTests.cs b/test/RestSharp.IntegrationTests/HttpHeadersTests.cs index aa925d9cc..4311ad2e3 100644 --- a/test/RestSharp.IntegrationTests/HttpHeadersTests.cs +++ b/test/RestSharp.IntegrationTests/HttpHeadersTests.cs @@ -4,6 +4,10 @@ namespace RestSharp.IntegrationTests; public class HttpHeadersTests : CaptureFixture { + readonly ITestOutputHelper _output; + + public HttpHeadersTests(ITestOutputHelper output) => _output = output; + [Fact] public async Task Ensure_headers_correctly_set_in_the_hook() { const string headerName = "HeaderName"; diff --git a/test/RestSharp.IntegrationTests/MultipartFormDataTests.cs b/test/RestSharp.IntegrationTests/MultipartFormDataTests.cs index 39dc5a512..6a5c79b0d 100644 --- a/test/RestSharp.IntegrationTests/MultipartFormDataTests.cs +++ b/test/RestSharp.IntegrationTests/MultipartFormDataTests.cs @@ -79,7 +79,7 @@ public async Task MultipartFormData() { AddParameters(request); - string? boundary = null; + string boundary = null; request.OnBeforeRequest += http => boundary = http.Content!.GetFormBoundary(); var response = await _client.ExecuteAsync(request); @@ -101,7 +101,7 @@ public async Task MultipartFormData_HasDefaultContentType() { request.AddParameter("controlName", "test", "application/json", ParameterType.RequestBody); - string? boundary = null; + string boundary = null; request.OnBeforeRequest = http => boundary = http.Content!.GetFormBoundary(); var response = await _client.ExecuteAsync(request); @@ -127,7 +127,7 @@ public async Task MultipartFormData_WithCustomContentType() { request.AddFile("fileName", path); request.AddParameter("controlName", "test", "application/json", ParameterType.RequestBody); - string? boundary = null; + string boundary = null; request.OnBeforeRequest = http => boundary = http.Content!.GetFormBoundary(); var response = await _client.ExecuteAsync(request); @@ -150,7 +150,7 @@ public async Task MultipartFormData_WithParameterAndFile_Async() { request.AddParameter("controlName", "test", "application/json", ParameterType.RequestBody); - string? boundary = null; + string boundary = null; request.OnBeforeRequest = http => boundary = http.Content!.GetFormBoundary(); var response = await _client.ExecuteAsync(request); @@ -166,7 +166,7 @@ public async Task MultipartFormDataAsync() { AddParameters(request); - string? boundary = null; + string boundary = null; request.OnBeforeRequest = http => boundary = http.Content!.GetFormBoundary(); diff --git a/test/RestSharp.IntegrationTests/NonProtocolExceptionHandlingTests.cs b/test/RestSharp.IntegrationTests/NonProtocolExceptionHandlingTests.cs index 05adb8e58..5c37dd06c 100644 --- a/test/RestSharp.IntegrationTests/NonProtocolExceptionHandlingTests.cs +++ b/test/RestSharp.IntegrationTests/NonProtocolExceptionHandlingTests.cs @@ -80,7 +80,7 @@ public async Task Task_Handles_Non_Existent_Domain() { var response = await client.ExecuteAsync(request); response.ErrorException.Should().BeOfType(); - response.ErrorException!.Message.Should().Contain("nodename nor servname provided, or not known"); + response.ErrorException!.Message.Should().Contain("known"); response.ResponseStatus.Should().Be(ResponseStatus.Error); } } \ No newline at end of file diff --git a/test/RestSharp.IntegrationTests/OAuth1Tests.cs b/test/RestSharp.IntegrationTests/OAuth1Tests.cs index 194660d40..f27dbeab5 100644 --- a/test/RestSharp.IntegrationTests/OAuth1Tests.cs +++ b/test/RestSharp.IntegrationTests/OAuth1Tests.cs @@ -319,8 +319,8 @@ public async Task Can_Query_Vimeo() { Assert.NotNull(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.NotNull(response.Content); - Assert.False(response.Content.Contains("\"stat\":\"fail\"")); - Assert.True(response.Content.Contains("\"stat\":\"ok\"")); + response.Content.Should().NotContain("\"stat\":\"fail\""); + response.Content.Should().Contain("\"stat\":\"ok\""); } [Fact(Skip = diff --git a/test/RestSharp.IntegrationTests/RequestBodyTests.cs b/test/RestSharp.IntegrationTests/RequestBodyTests.cs index d42ad7c73..4c408c461 100644 --- a/test/RestSharp.IntegrationTests/RequestBodyTests.cs +++ b/test/RestSharp.IntegrationTests/RequestBodyTests.cs @@ -4,7 +4,7 @@ namespace RestSharp.IntegrationTests; public class RequestBodyTests : IClassFixture { - readonly SimpleServer _server; + readonly SimpleServer _server; const string NewLine = "\r\n"; diff --git a/test/RestSharp.IntegrationTests/ResourceStringParametersTests.cs b/test/RestSharp.IntegrationTests/ResourceStringParametersTests.cs index 6208e4fca..eecbf6ccd 100644 --- a/test/RestSharp.IntegrationTests/ResourceStringParametersTests.cs +++ b/test/RestSharp.IntegrationTests/ResourceStringParametersTests.cs @@ -3,10 +3,10 @@ namespace RestSharp.IntegrationTests; -public class ResourcestringParametersTests : IDisposable { +public sealed class ResourceStringParametersTests : IDisposable { readonly SimpleServer _server; - public ResourcestringParametersTests() => _server = SimpleServer.Create(RequestHandler.Handle); + public ResourceStringParametersTests() => _server = SimpleServer.Create(RequestHandler.Handle); public void Dispose() => _server.Dispose(); @@ -24,7 +24,7 @@ public async Task Should_keep_to_parameters_with_the_same_name() { } static class RequestHandler { - public static Uri? Url { get; private set; } + public static Uri Url { get; private set; } public static void Handle(HttpListenerContext context) { Url = context.Request.Url; diff --git a/test/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj b/test/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj index 6193cbc89..cffe9a94f 100644 --- a/test/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj +++ b/test/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj @@ -1,5 +1,9 @@  + + disable + + @@ -11,4 +15,7 @@ + + + \ No newline at end of file diff --git a/test/RestSharp.IntegrationTests/StatusCodeTests.cs b/test/RestSharp.IntegrationTests/StatusCodeTests.cs index ce62bdc7d..22d32fa57 100644 --- a/test/RestSharp.IntegrationTests/StatusCodeTests.cs +++ b/test/RestSharp.IntegrationTests/StatusCodeTests.cs @@ -1,5 +1,6 @@ using System.Net; using RestSharp.Serializers; +using RestSharp.Serializers.Xml; using RestSharp.Tests.Shared.Extensions; using RestSharp.Tests.Shared.Fixtures; @@ -9,6 +10,7 @@ public class StatusCodeTests : IDisposable { public StatusCodeTests() { _server = SimpleServer.Create(UrlToStatusCodeHandler); _client = new RestClient(_server.Url); + _client.UseXmlSerializer(); } public void Dispose() => _server.Dispose(); @@ -81,7 +83,7 @@ public async Task Handles_GET_Request_404_Error() { Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } - [Fact(Skip = "Not sure why this hangs")] + [Fact] public async Task Reports_1xx_Status_Code_Success_Accurately() { var request = new RestRequest("100"); var response = await _client.ExecuteAsync(request); diff --git a/test/RestSharp.IntegrationTests/StructuredSyntaxSuffixTests.cs b/test/RestSharp.IntegrationTests/StructuredSyntaxSuffixTests.cs index 342c75cb7..6a0be495b 100644 --- a/test/RestSharp.IntegrationTests/StructuredSyntaxSuffixTests.cs +++ b/test/RestSharp.IntegrationTests/StructuredSyntaxSuffixTests.cs @@ -1,4 +1,5 @@ using System.Net; +using RestSharp.Serializers.Xml; using RestSharp.Tests.Shared.Extensions; using RestSharp.Tests.Shared.Fixtures; @@ -60,7 +61,7 @@ public async Task By_default_content_types_with_JSON_structured_syntax_suffix_sh [Fact] public async Task By_default_content_types_with_XML_structured_syntax_suffix_should_deserialize_as_XML() { - var client = new RestClient(_url); + var client = new RestClient(_url).UseXmlSerializer(); var request = new RestRequest() .AddParameter("ct", "application/vnd.somebody.something+xml") @@ -74,7 +75,7 @@ public async Task By_default_content_types_with_XML_structured_syntax_suffix_sho [Fact] public async Task By_default_text_xml_content_type_should_deserialize_as_XML() { - var client = new RestClient(_url); + var client = new RestClient(_url).UseXmlSerializer(); var request = new RestRequest() .AddParameter("ct", "text/xml") @@ -85,21 +86,4 @@ public async Task By_default_text_xml_content_type_should_deserialize_as_XML() { Assert.Equal("Bob", response.Data.Name); Assert.Equal(50, response.Data.Age); } - - // [Fact] - // public void Should_allow_wildcard_content_types_to_be_defined() { - // var client = new RestClient(_url); - // - // // In spite of the content type, handle ALL structured syntax suffixes of "+xml" as JSON - // client.AddHandler("*+xml", new JsonSerializer()); - // - // var request = new RestRequest() - // .AddParameter("ct", "application/vnd.somebody.something+xml") - // .AddParameter("c", JsonContent); - // - // var response = client.Execute(request); - // - // Assert.Equal("Bob", response.Data.Name); - // Assert.Equal(50, response.Data.Age); - // } } \ No newline at end of file diff --git a/test/RestSharp.IntegrationTests/xunit.runner.json b/test/RestSharp.IntegrationTests/xunit.runner.json new file mode 100644 index 000000000..3ad9c00e1 --- /dev/null +++ b/test/RestSharp.IntegrationTests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "parallelizeAssembly": false, + "parallelizeTestCollections": false +} \ No newline at end of file diff --git a/test/RestSharp.InteractiveTests/RestSharp.InteractiveTests.csproj b/test/RestSharp.InteractiveTests/RestSharp.InteractiveTests.csproj index 0055c78bc..8b96e9ad2 100644 --- a/test/RestSharp.InteractiveTests/RestSharp.InteractiveTests.csproj +++ b/test/RestSharp.InteractiveTests/RestSharp.InteractiveTests.csproj @@ -1,7 +1,7 @@ Exe - net6.0 + false diff --git a/test/RestSharp.Serializers.Json.Tests/RestSharp.Serializers.Json.Tests.csproj b/test/RestSharp.Serializers.Json.Tests/RestSharp.Serializers.Json.Tests.csproj index a2ee248bb..eb6f293b6 100644 --- a/test/RestSharp.Serializers.Json.Tests/RestSharp.Serializers.Json.Tests.csproj +++ b/test/RestSharp.Serializers.Json.Tests/RestSharp.Serializers.Json.Tests.csproj @@ -1,7 +1,4 @@ - - net6.0 - diff --git a/test/RestSharp.Serializers.Json.Tests/SystemTextJsonTests.cs b/test/RestSharp.Serializers.Json.Tests/SystemTextJsonTests.cs index 3163d8fd3..b269d7fe3 100644 --- a/test/RestSharp.Serializers.Json.Tests/SystemTextJsonTests.cs +++ b/test/RestSharp.Serializers.Json.Tests/SystemTextJsonTests.cs @@ -9,7 +9,7 @@ namespace RestSharp.Serializers.Json.Tests; public class SystemTextJsonTests { static readonly Fixture Fixture = new(); - string? _body; + string _body; [Fact] public async Task Use_JsonNet_For_Requests() { diff --git a/test/RestSharp.Serializers.Xml.Tests/NamespacedXmlTests.cs b/test/RestSharp.Serializers.Xml.Tests/NamespacedXmlTests.cs index 386ed6405..148fc9d93 100644 --- a/test/RestSharp.Serializers.Xml.Tests/NamespacedXmlTests.cs +++ b/test/RestSharp.Serializers.Xml.Tests/NamespacedXmlTests.cs @@ -1,10 +1,8 @@ using System.Xml.Linq; -using RestSharp.Serializers.Xml; -using RestSharp.Tests.SampleClasses; -using RestSharp.Tests.SampleClasses.DeserializeAsTest; -using RestSharp.Tests.SampleClasses.Lastfm; +using RestSharp.Serializers.Xml.Tests.SampleClasses; +using RestSharp.Serializers.Xml.Tests.SampleClasses.DeserializeAsTest; -namespace RestSharp.Tests; +namespace RestSharp.Serializers.Xml.Tests; public class NamespacedXmlTests { const string GuidString = "AC1FC4BC-087A-4242-B8EE-C53EBE9887A5"; diff --git a/test/RestSharp.Serializers.Xml.Tests/RestSharp.Serializers.Xml.Tests.csproj b/test/RestSharp.Serializers.Xml.Tests/RestSharp.Serializers.Xml.Tests.csproj index 615758042..a121cfbc1 100644 --- a/test/RestSharp.Serializers.Xml.Tests/RestSharp.Serializers.Xml.Tests.csproj +++ b/test/RestSharp.Serializers.Xml.Tests/RestSharp.Serializers.Xml.Tests.csproj @@ -1,31 +1,27 @@ - - net6.0 - enable - enable + disable - - + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/BearerToken.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/BearerToken.cs index 02a03d1d6..0c858ec19 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/BearerToken.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/BearerToken.cs @@ -1,6 +1,4 @@ -using RestSharp.Serializers; - -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class BearerToken { public string AccessToken { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/BooleanTest.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/BooleanTest.cs index 9944aa4df..8e8bec8c8 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/BooleanTest.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/BooleanTest.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class BooleanTest { public bool Value { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/ColorWithValue.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/ColorWithValue.cs index d4c4bc245..c7411c903 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/ColorWithValue.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/ColorWithValue.cs @@ -1,6 +1,4 @@ -using RestSharp.Serializers; - -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; [DeserializeAs(Name = "Color")] public class ColorWithValue { diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/DeserializeAsTest/misc.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/DeserializeAsTest/misc.cs index 0bf8ce704..60cbaa244 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/DeserializeAsTest/misc.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/DeserializeAsTest/misc.cs @@ -1,6 +1,4 @@ -using RestSharp.Serializers; - -namespace RestSharp.Tests.SampleClasses.DeserializeAsTest; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses.DeserializeAsTest; public class NodeWithAttributeAndValue { [DeserializeAs(Name = "attribute-value", Attribute = true)] diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/EmployeeTracker.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/EmployeeTracker.cs index 9d6fdb5dc..ae33edc94 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/EmployeeTracker.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/EmployeeTracker.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class EmployeeTracker { /// diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/EnumTest.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/EnumTest.cs index ea5d85d39..12439e94c 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/EnumTest.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/EnumTest.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public enum ByteEnum : byte { EnumMin = 0, EnumMax = 255 } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Goodreads.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Goodreads.cs index dcb257005..1f06ffcc2 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Goodreads.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Goodreads.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class GoodReadsReviewCollection { public int Start { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/GoogleWeatherWithAttributes.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/GoogleWeatherWithAttributes.cs index 2045a7da7..2b702cbdd 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/GoogleWeatherWithAttributes.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/GoogleWeatherWithAttributes.cs @@ -1,6 +1,4 @@ -using RestSharp.Serializers; - -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class GoogleWeatherApi { public string Version { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/HeaderAndRows.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/HeaderAndRows.cs index 80afef944..1d3339f49 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/HeaderAndRows.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/HeaderAndRows.cs @@ -1,6 +1,4 @@ -using RestSharp.Serializers; - -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class Header { public string Title { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/JsonLists.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/JsonLists.cs index 0c5437871..d1e016882 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/JsonLists.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/JsonLists.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class JsonLists { public List Names { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Lastfm.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Lastfm.cs index f8778d119..75fb02730 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Lastfm.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Lastfm.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses.Lastfm; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class Event : LastfmBase { public string id { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/ListSamples.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/ListSamples.cs index c1d7cef91..c214b7008 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/ListSamples.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/ListSamples.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class SimpleTypesListSample { public List Names { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Oddball.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Oddball.cs index 63a56ad6f..3eef8c02a 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Oddball.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Oddball.cs @@ -1,6 +1,4 @@ -using RestSharp.Serializers; - -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; [DeserializeAs(Name = "oddballRootName")] public class Oddball { diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/SOUser.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/SOUser.cs index af2afa67e..90901f617 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/SOUser.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/SOUser.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class SoUser { public int Id { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Struct.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Struct.cs index 510cc4e36..3965c5a5d 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Struct.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/Struct.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public struct SimpleStruct { public string One { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/TwilioCallList.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/TwilioCallList.cs index d3cc9c6df..f3e3c3d1b 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/TwilioCallList.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/TwilioCallList.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class TwilioCallList : List { public int Page { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/eventful.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/eventful.cs index 3489c313e..820d514bf 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/eventful.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/eventful.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class VenueSearch { public string total_items { get; set; } @@ -106,22 +106,4 @@ public class ServiceImage1 { public string width { get; set; } public string height { get; set; } -} - -public class Event { - public string id { get; set; } - - public string url { get; set; } - - public string title { get; set; } - - public string description { get; set; } - - public string start_time { get; set; } - - public string venue_name { get; set; } - - public string venue_id { get; set; } - - public List performers { get; set; } } \ No newline at end of file diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/foursq.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/foursq.cs index ac6ecb80b..a362c0634 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/foursq.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/foursq.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class VenuesResponse { public List Groups { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/googleweather.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/googleweather.cs index 7b0b893ad..25c8508ea 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/googleweather.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/googleweather.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class xml_api_reply { public string version { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/misc.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/misc.cs index 7ebf5de96..3ab733479 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/misc.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/misc.cs @@ -1,6 +1,4 @@ -using RestSharp.Serializers; - -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class PersonForXml { public string Name { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/nullables.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/nullables.cs index a6b9c97f8..0b2b1ed14 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/nullables.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/nullables.cs @@ -1,4 +1,4 @@ -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class NullableValues { public int? Id { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/twitter.cs b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/twitter.cs index 050c0dbf2..75d6c6051 100644 --- a/test/RestSharp.Serializers.Xml.Tests/SampleClasses/twitter.cs +++ b/test/RestSharp.Serializers.Xml.Tests/SampleClasses/twitter.cs @@ -1,6 +1,4 @@ -using RestSharp.Serializers; - -namespace RestSharp.Tests.SampleClasses; +namespace RestSharp.Serializers.Xml.Tests.SampleClasses; public class status { public bool truncated { get; set; } diff --git a/test/RestSharp.Serializers.Xml.Tests/XmlAttributeDeserializerTests.cs b/test/RestSharp.Serializers.Xml.Tests/XmlAttributeDeserializerTests.cs index 2e63a0c72..99ae2a7ab 100644 --- a/test/RestSharp.Serializers.Xml.Tests/XmlAttributeDeserializerTests.cs +++ b/test/RestSharp.Serializers.Xml.Tests/XmlAttributeDeserializerTests.cs @@ -1,10 +1,8 @@ using System.Globalization; using System.Xml.Linq; -using RestSharp.Serializers.Xml; -using RestSharp.Tests.SampleClasses; -using Event = RestSharp.Tests.SampleClasses.Lastfm.Event; +using RestSharp.Serializers.Xml.Tests.SampleClasses; -namespace RestSharp.Tests; +namespace RestSharp.Serializers.Xml.Tests; public class XmlAttributeDeserializerTests { const string GuidString = "AC1FC4BC-087A-4242-B8EE-C53EBE9887A5"; diff --git a/test/RestSharp.Serializers.Xml.Tests/XmlDeserializerTests.cs b/test/RestSharp.Serializers.Xml.Tests/XmlDeserializerTests.cs index 69d696c6b..adb2cb32c 100644 --- a/test/RestSharp.Serializers.Xml.Tests/XmlDeserializerTests.cs +++ b/test/RestSharp.Serializers.Xml.Tests/XmlDeserializerTests.cs @@ -1,11 +1,9 @@ using System.Globalization; using System.Xml.Linq; -using RestSharp.Serializers.Xml; -using RestSharp.Tests.SampleClasses; -using RestSharp.Tests.SampleClasses.DeserializeAsTest; -using Event = RestSharp.Tests.SampleClasses.Lastfm.Event; +using RestSharp.Serializers.Xml.Tests.SampleClasses; +using RestSharp.Serializers.Xml.Tests.SampleClasses.DeserializeAsTest; -namespace RestSharp.Tests; +namespace RestSharp.Serializers.Xml.Tests; public class XmlDeserializerTests { const string GuidString = "AC1FC4BC-087A-4242-B8EE-C53EBE9887A5"; @@ -312,14 +310,14 @@ static string CreateXmlWithEmptyInlineList() { static string CreateXmlWithAttributesAndNullValues() { var doc = new XDocument(); var root = new XElement("NullableValues"); - var idElement = new XElement("Id", null); + var idElement = new XElement("Id", null!); idElement.SetAttributeValue("SomeAttribute", "SomeAttribute_Value"); root.Add( idElement, - new XElement("StartDate", null), - new XElement("UniqueId", null) + new XElement("StartDate", null!), + new XElement("UniqueId", null!) ); doc.Add(root); diff --git a/test/RestSharp.Serializers.Xml.Tests/XmlSerializerTests.cs b/test/RestSharp.Serializers.Xml.Tests/XmlSerializerTests.cs index 9448800da..c3db38ae7 100644 --- a/test/RestSharp.Serializers.Xml.Tests/XmlSerializerTests.cs +++ b/test/RestSharp.Serializers.Xml.Tests/XmlSerializerTests.cs @@ -1,10 +1,8 @@ using System.Globalization; using System.Xml.Linq; -using RestSharp.Serializers; -using RestSharp.Serializers.Xml; -using RestSharp.Tests.SampleClasses; +using RestSharp.Serializers.Xml.Tests.SampleClasses; -namespace RestSharp.Tests; +namespace RestSharp.Serializers.Xml.Tests; public class XmlSerializerTests { public XmlSerializerTests() { diff --git a/test/RestSharp.Tests.Shared/Extensions/StreamExtensions.cs b/test/RestSharp.Tests.Shared/Extensions/StreamExtensions.cs index 33183088b..8adce5aee 100644 --- a/test/RestSharp.Tests.Shared/Extensions/StreamExtensions.cs +++ b/test/RestSharp.Tests.Shared/Extensions/StreamExtensions.cs @@ -1,6 +1,6 @@ using System.Text; -namespace RestSharp.Tests.Shared.Extensions; +namespace RestSharp.Tests.Shared.Extensions; public static class StreamExtensions { public static void WriteStringUtf8(this Stream target, string value) { @@ -14,6 +14,4 @@ public static string StreamToString(this Stream stream) { return streamReader.ReadToEnd(); } - - public static byte[] StreamToBytes(this Stream stream) => Encoding.UTF8.GetBytes(stream.StreamToString()); } \ No newline at end of file diff --git a/test/RestSharp.Tests.Shared/Fixtures/Handlers.cs b/test/RestSharp.Tests.Shared/Fixtures/Handlers.cs index 4a3a10c28..5d6351541 100644 --- a/test/RestSharp.Tests.Shared/Fixtures/Handlers.cs +++ b/test/RestSharp.Tests.Shared/Fixtures/Handlers.cs @@ -1,73 +1,66 @@ -using System; -using System.IO; -using System.Linq; -using System.Net; +using System.Net; using System.Reflection; using RestSharp.Tests.Shared.Extensions; -namespace RestSharp.Tests.Shared.Fixtures -{ - public static class Handlers - { - /// - /// Echoes the request input back to the output. - /// - public static void Echo(HttpListenerContext context) => context.Request.InputStream.CopyTo(context.Response.OutputStream); +namespace RestSharp.Tests.Shared.Fixtures; - /// - /// Echoes the given value back to the output. - /// - public static Action EchoValue(string value) => ctx => ctx.Response.OutputStream.WriteStringUtf8(value); +public static class Handlers { + /// + /// Echoes the request input back to the output. + /// + public static void Echo(HttpListenerContext context) => context.Request.InputStream.CopyTo(context.Response.OutputStream); - /// - /// Response to a request like this: http://localhost:8888/assets/koala.jpg - /// by streaming the file located at "assets\koala.jpg" back to the client. - /// - public static void FileHandler(HttpListenerContext context, string path) - { - var pathToFile = Path.Combine( - path, - Path.Combine( - context.Request.Url.Segments.Select(s => s.Replace("/", "")).ToArray() - ) - ); + /// + /// Echoes the given value back to the output. + /// + public static Action EchoValue(string value) => ctx => ctx.Response.OutputStream.WriteStringUtf8(value); - using var reader = new StreamReader(pathToFile); + /// + /// Response to a request like this: http://localhost:8888/assets/koala.jpg + /// by streaming the file located at "assets\koala.jpg" back to the client. + /// + public static void FileHandler(HttpListenerContext context, string path) { + var pathToFile = Path.Combine( + path, + Path.Combine( + context.Request.Url.Segments.Select(s => s.Replace("/", "")).ToArray() + ) + ); - reader.BaseStream.CopyTo(context.Response.OutputStream); - } + using var reader = new StreamReader(pathToFile); - /// - /// T should be a class that implements methods whose names match the urls being called, and take one parameter, an - /// HttpListenerContext. - /// e.g. - /// urls exercised: "http://localhost:8888/error" and "http://localhost:8888/get_list" - /// class MyHandler - /// { - /// void error(HttpListenerContext ctx) - /// { - /// // do something interesting here - /// } - /// void get_list(HttpListenerContext ctx) - /// { - /// // do something interesting here - /// } - /// } - /// - public static Action Generic() where T : new() - => ctx => - { - var methodName = ctx.Request.Url.Segments.Last(); + reader.BaseStream.CopyTo(context.Response.OutputStream); + } - var method = typeof(T).GetMethod( - methodName, - BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static - ); + /// + /// T should be a class that implements methods whose names match the urls being called, and take one parameter, an + /// HttpListenerContext. + /// e.g. + /// urls exercised: "http://localhost:8888/error" and "http://localhost:8888/get_list" + /// class MyHandler + /// { + /// void error(HttpListenerContext ctx) + /// { + /// // do something interesting here + /// } + /// void get_list(HttpListenerContext ctx) + /// { + /// // do something interesting here + /// } + /// } + /// + public static Action Generic() where T : new() + => ctx => { + var methodName = ctx.Request.Url.Segments.Last(); - if (method.IsStatic) - method.Invoke(null, new object[] {ctx}); - else - method.Invoke(new T(), new object[] {ctx}); - }; - } + var method = typeof(T).GetMethod( + methodName, + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static + ); + + if (method.IsStatic) + method.Invoke(null, new object[] { ctx }); + else + method.Invoke(new T(), new object[] { ctx }); + }; } \ No newline at end of file diff --git a/test/RestSharp.Tests.Shared/Fixtures/HttpServerFixture.cs b/test/RestSharp.Tests.Shared/Fixtures/HttpServerFixture.cs index 56996400f..d94595845 100644 --- a/test/RestSharp.Tests.Shared/Fixtures/HttpServerFixture.cs +++ b/test/RestSharp.Tests.Shared/Fixtures/HttpServerFixture.cs @@ -1,27 +1,23 @@ -using System; using System.Net; -namespace RestSharp.Tests.Shared.Fixtures -{ - public class HttpServerFixture : IDisposable - { - public static HttpServerFixture StartServer(string url, Action handle) - { - var server = new TestHttpServer(0, url, (request, response, _) => handle(request, response)); - return new HttpServerFixture(server); - } +namespace RestSharp.Tests.Shared.Fixtures; - public static HttpServerFixture StartServer(Action handle) => StartServer("", handle); - - HttpServerFixture(TestHttpServer server) - { - Url = $"http://localhost:{server.Port}"; - _server = server; - } +public sealed class HttpServerFixture : IDisposable { + public static HttpServerFixture StartServer(string url, Action handle) { + var server = new TestHttpServer(0, url, (request, response, _) => handle(request, response)); + return new HttpServerFixture(server); + } - public string Url { get; } + public static HttpServerFixture StartServer(Action handle) => StartServer("", handle); - readonly TestHttpServer _server; - public void Dispose() => _server.Dispose(); + HttpServerFixture(TestHttpServer server) { + Url = $"http://localhost:{server.Port}"; + _server = server; } + + public string Url { get; } + + readonly TestHttpServer _server; + + public void Dispose() => _server.Dispose(); } \ No newline at end of file diff --git a/test/RestSharp.Tests.Shared/Fixtures/RequestBodyCapturer.cs b/test/RestSharp.Tests.Shared/Fixtures/RequestBodyCapturer.cs index 6c47480ac..3adc07fec 100644 --- a/test/RestSharp.Tests.Shared/Fixtures/RequestBodyCapturer.cs +++ b/test/RestSharp.Tests.Shared/Fixtures/RequestBodyCapturer.cs @@ -1,30 +1,22 @@ -using System; -using System.IO; using System.Net; using RestSharp.Tests.Shared.Extensions; -namespace RestSharp.Tests.Shared.Fixtures -{ - public class RequestBodyCapturer - { - public const string Resource = "Capture"; +namespace RestSharp.Tests.Shared.Fixtures; - public static string CapturedContentType { get; set; } +public class RequestBodyCapturer { + public const string Resource = "Capture"; - public static bool CapturedHasEntityBody { get; set; } + public static string CapturedContentType { get; set; } + public static bool CapturedHasEntityBody { get; set; } + public static string CapturedEntityBody { get; set; } + public static Uri CapturedUrl { get; set; } - public static string CapturedEntityBody { get; set; } + public static void Capture(HttpListenerContext context) { + var request = context.Request; - public static Uri CapturedUrl { get; set; } - - public static void Capture(HttpListenerContext context) - { - var request = context.Request; - - CapturedContentType = request.ContentType; - CapturedHasEntityBody = request.HasEntityBody; - CapturedEntityBody = request.InputStream.StreamToString(); - CapturedUrl = request.Url; - } + CapturedContentType = request.ContentType; + CapturedHasEntityBody = request.HasEntityBody; + CapturedEntityBody = request.InputStream.StreamToString(); + CapturedUrl = request.Url; } } \ No newline at end of file diff --git a/test/RestSharp.Tests.Shared/Fixtures/SimpleServer.cs b/test/RestSharp.Tests.Shared/Fixtures/SimpleServer.cs index 216533e81..d53863c2f 100644 --- a/test/RestSharp.Tests.Shared/Fixtures/SimpleServer.cs +++ b/test/RestSharp.Tests.Shared/Fixtures/SimpleServer.cs @@ -1,28 +1,32 @@ using System.Net; -namespace RestSharp.Tests.Shared.Fixtures; +namespace RestSharp.Tests.Shared.Fixtures; -public class SimpleServer : IDisposable { +public sealed class SimpleServer : IDisposable { static readonly Random Random = new(DateTimeOffset.Now.Millisecond); - readonly WebServer _server; + readonly WebServer _server; + readonly CancellationTokenSource _cts = new(); - public string Url { get; } + public string Url { get; } public string ServerUrl { get; } SimpleServer( - int port, - Action? handler = null, - AuthenticationSchemes authenticationSchemes = AuthenticationSchemes.Anonymous + int port, + Action handler = null, + AuthenticationSchemes authenticationSchemes = AuthenticationSchemes.Anonymous ) { - Url = $"http://localhost:{port}/"; - ; + Url = $"http://localhost:{port}/"; ServerUrl = $"http://{Environment.MachineName}:{port}/"; _server = new WebServer(Url, handler, authenticationSchemes); - _server.Run(); + Task.Run(() => _server.Run(_cts.Token)); } - public void Dispose() => _server.Stop(); + public void Dispose() { + _cts.Cancel(); + _server.Stop(); + _cts.Dispose(); + } public static SimpleServer Create( Action handler = null, diff --git a/test/RestSharp.Tests.Shared/Fixtures/TestHttpServer.cs b/test/RestSharp.Tests.Shared/Fixtures/TestHttpServer.cs index 5a5cd637c..8fb323d53 100644 --- a/test/RestSharp.Tests.Shared/Fixtures/TestHttpServer.cs +++ b/test/RestSharp.Tests.Shared/Fixtures/TestHttpServer.cs @@ -1,142 +1,117 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; -using System.Threading.Tasks; - -namespace RestSharp.Tests.Shared.Fixtures -{ - public class TestHttpServer : IDisposable - { - readonly HttpListener _listener; - readonly List _requestHandlers; - readonly object _requestHandlersLock = new object(); - - public int Port { get; } - - public TestHttpServer( - int port, - string url, - Action> handlerAction, - string hostName = "localhost" - ) - : this(port, new List {new TestRequestHandler(url, handlerAction)}, hostName) { } - - public TestHttpServer( - int port, - List handlers, - string hostName = "localhost" - ) - { - _requestHandlers = handlers; - - Port = port > 0 ? port : GetRandomUnusedPort(); - - //create and start listener - _listener = new HttpListener(); - _listener.Prefixes.Add($"http://{hostName}:{Port}/"); - _listener.Start(); - -// Cannot await Async Call in a Constructor -#pragma warning disable 4014 - HandleRequests(); -#pragma warning restore 4014 - } - static int GetRandomUnusedPort() - { - var listener = new TcpListener(IPAddress.Any, 0); - listener.Start(); - var port = ((IPEndPoint) listener.LocalEndpoint).Port; - listener.Stop(); - return port; - } +namespace RestSharp.Tests.Shared.Fixtures; - async Task HandleRequests() - { - try - { - //listen for all requests - while (_listener.IsListening) - { - //get the request - var context = await _listener.GetContextAsync(); - - try - { - Dictionary parameters = null; - TestRequestHandler handler; - - lock (_requestHandlersLock) - { - handler = _requestHandlers.FirstOrDefault( - h => h.TryMatchUrl(context.Request.RawUrl, context.Request.HttpMethod, out parameters) - ); - } +public class TestHttpServer : IDisposable { + readonly HttpListener _listener; + readonly List _requestHandlers; + readonly object _requestHandlersLock = new(); + readonly CancellationTokenSource _cts = new(); - string responseString = null; - - if (handler != null) - { - //add the query string parameters to the pre-defined url parameters that were set from MatchesUrl() - foreach (var qsParamName in context.Request.QueryString.AllKeys) - parameters[qsParamName] = context.Request.QueryString[qsParamName]; - - try - { - handler.HandlerAction(context.Request, context.Response, parameters); - } - catch (Exception ex) - { - responseString = $"Exception in handler: {ex.Message}"; - context.Response.StatusCode = (int) HttpStatusCode.InternalServerError; - } - } - else - { - context.Response.ContentType("text/plain").StatusCode(404); - responseString = "No handler provided for URL: " + context.Request.RawUrl; - } + public int Port { get; } + + public TestHttpServer( + int port, + string url, + Action> handlerAction, + string hostName = "localhost" + ) + : this(port, new List { new(url, handlerAction) }, hostName) { } + + public TestHttpServer( + int port, + List handlers, + string hostName = "localhost" + ) { + _requestHandlers = handlers; - context.Request.ClearContent(); + Port = port > 0 ? port : GetRandomUnusedPort(); + + //create and start listener + _listener = new HttpListener(); + _listener.Prefixes.Add($"http://{hostName}:{Port}/"); + _listener.Start(); + + Task.Run(() => HandleRequests(_cts.Token)); + } + + static int GetRandomUnusedPort() { + var listener = new TcpListener(IPAddress.Any, 0); + listener.Start(); + var port = ((IPEndPoint)listener.LocalEndpoint).Port; + listener.Stop(); + return port; + } + + async Task HandleRequests(CancellationToken cancellationToken) { + try { + //listen for all requests + while (_listener.IsListening && !cancellationToken.IsCancellationRequested) { + //get the request + var context = await _listener.GetContextAsync(); + + try { + Dictionary parameters = null; + TestRequestHandler handler; + + lock (_requestHandlersLock) { + handler = _requestHandlers.FirstOrDefault( + h => h.TryMatchUrl(context.Request.RawUrl, context.Request.HttpMethod, out parameters) + ); + } - //send the response, if there is not (if responseString is null, then the handler method should have manually set the output stream) - if (responseString != null) - { - var buffer = Encoding.UTF8.GetBytes(responseString); - context.Response.ContentLength64 += buffer.Length; - context.Response.OutputStream.Write(buffer, 0, buffer.Length); + string responseString = null; + + if (handler != null) { + //add the query string parameters to the pre-defined url parameters that were set from MatchesUrl() + foreach (var qsParamName in context.Request.QueryString.AllKeys) + parameters[qsParamName] = context.Request.QueryString[qsParamName]; + + try { + handler.HandlerAction(context.Request, context.Response, parameters); + } + catch (Exception ex) { + responseString = $"Exception in handler: {ex.Message}"; + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } } - finally - { - context.Response.OutputStream.Close(); - context.Response.Close(); + else { + context.Response.ContentType("text/plain").StatusCode(404); + responseString = "No handler provided for URL: " + context.Request.RawUrl; + } + + context.Request.ClearContent(); + + //send the response, if there is not (if responseString is null, then the handler method should have manually set the output stream) + if (responseString != null) { + var buffer = Encoding.UTF8.GetBytes(responseString); + context.Response.ContentLength64 += buffer.Length; + context.Response.OutputStream.Write(buffer, 0, buffer.Length); } } - } - catch (HttpListenerException ex) - { - //when the listener is stopped, it will throw an exception for being cancelled, so just ignore it - if (ex.Message != "The I/O operation has been aborted because of either a thread exit or an application request") - throw; + finally { + context.Response.OutputStream.Close(); + context.Response.Close(); + } } } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); + catch (HttpListenerException ex) { + //when the listener is stopped, it will throw an exception for being cancelled, so just ignore it + if (ex.Message != "The I/O operation has been aborted because of either a thread exit or an application request") + throw; } + } - protected virtual void Dispose(bool disposing) - { - if (disposing && (_listener?.IsListening ?? false)) - { - _listener.Stop(); - } + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { + if (disposing && _listener.IsListening) { + _listener.Stop(); } } } \ No newline at end of file diff --git a/test/RestSharp.Tests.Shared/Fixtures/TestHttpServerExtensions.cs b/test/RestSharp.Tests.Shared/Fixtures/TestHttpServerExtensions.cs index 65058ccf8..53d05abf1 100644 --- a/test/RestSharp.Tests.Shared/Fixtures/TestHttpServerExtensions.cs +++ b/test/RestSharp.Tests.Shared/Fixtures/TestHttpServerExtensions.cs @@ -1,28 +1,22 @@ -using System.Collections.Generic; using System.Net; -namespace RestSharp.Tests.Shared.Fixtures -{ - public static class TestHttpServerExtensions - { - static readonly Dictionary RequestContent = new Dictionary(); +namespace RestSharp.Tests.Shared.Fixtures; - internal static void ClearContent(this HttpListenerRequest request) - { - if (RequestContent.ContainsKey(request)) - RequestContent.Remove(request); - } +public static class TestHttpServerExtensions { + static readonly Dictionary RequestContent = new(); - public static HttpListenerResponse ContentType(this HttpListenerResponse response, string contentType) - { - response.ContentType = contentType; - return response; - } + internal static void ClearContent(this HttpListenerRequest request) { + if (RequestContent.ContainsKey(request)) + RequestContent.Remove(request); + } + + public static HttpListenerResponse ContentType(this HttpListenerResponse response, string contentType) { + response.ContentType = contentType; + return response; + } - public static HttpListenerResponse StatusCode(this HttpListenerResponse response, int statusCode) - { - response.StatusCode = statusCode; - return response; - } + public static HttpListenerResponse StatusCode(this HttpListenerResponse response, int statusCode) { + response.StatusCode = statusCode; + return response; } } \ No newline at end of file diff --git a/test/RestSharp.Tests.Shared/Fixtures/TestRequestHandler.cs b/test/RestSharp.Tests.Shared/Fixtures/TestRequestHandler.cs index e1640b20f..e83a8ba01 100644 --- a/test/RestSharp.Tests.Shared/Fixtures/TestRequestHandler.cs +++ b/test/RestSharp.Tests.Shared/Fixtures/TestRequestHandler.cs @@ -1,74 +1,64 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; using System.Text.RegularExpressions; -namespace RestSharp.Tests.Shared.Fixtures -{ - public class TestRequestHandler - { - readonly Regex _comparisonRegex; - - readonly List _urlParameterNames = new List(); - - public TestRequestHandler( - string url, - string httpMethod, - Action> handlerAction - ) - { - Url = url; - HttpMethod = httpMethod; - HandlerAction = handlerAction; - - _comparisonRegex = CreateComparisonRegex(url); - } +namespace RestSharp.Tests.Shared.Fixtures; + +public class TestRequestHandler { + readonly Regex _comparisonRegex; - public TestRequestHandler(string url, Action> handlerAction) - : this(url, null, handlerAction) { } + readonly List _urlParameterNames = new(); - string Url { get; } - string HttpMethod { get; } - internal Action> HandlerAction { get; } + public TestRequestHandler( + string url, + string httpMethod, + Action> handlerAction + ) { + Url = url; + HttpMethod = httpMethod; + HandlerAction = handlerAction; + + _comparisonRegex = CreateComparisonRegex(url); + } - Regex CreateComparisonRegex(string url) - { - var regexString = Regex.Escape(url).Replace(@"\{", "{"); + public TestRequestHandler(string url, Action> handlerAction) + : this(url, null, handlerAction) { } - regexString += regexString.EndsWith("/") ? "?" : "/?"; - regexString = (regexString.StartsWith("/") ? "^" : "^/") + regexString; + string Url { get; } + string HttpMethod { get; } + internal Action> HandlerAction { get; } - var regex = new Regex(@"{(.*?)}"); + Regex CreateComparisonRegex(string url) { + var regexString = Regex.Escape(url).Replace(@"\{", "{"); - foreach (Match match in regex.Matches(regexString)) - { - regexString = regexString.Replace(match.Value, @"(.*?)"); - _urlParameterNames.Add(match.Groups[1].Value); - } + regexString += regexString.EndsWith("/") ? "?" : "/?"; + regexString = (regexString.StartsWith("/") ? "^" : "^/") + regexString; - regexString += !regexString.Contains(@"\?") ? @"(\?.*)?$" : "$"; + var regex = new Regex(@"{(.*?)}"); - return new Regex(regexString); + foreach (Match match in regex.Matches(regexString)) { + regexString = regexString.Replace(match.Value, @"(.*?)"); + _urlParameterNames.Add(match.Groups[1].Value); } - public bool TryMatchUrl(string rawUrl, string httpMethod, out Dictionary parameters) - { - var match = _comparisonRegex.Match(rawUrl); + regexString += !regexString.Contains(@"\?") ? @"(\?.*)?$" : "$"; - var isMethodMatched = HttpMethod == null || HttpMethod.Split(',').Contains(httpMethod); + return new Regex(regexString); + } - if (!match.Success || !isMethodMatched) - { - parameters = null; - return false; - } + public bool TryMatchUrl(string rawUrl, string httpMethod, out Dictionary parameters) { + var match = _comparisonRegex.Match(rawUrl); - parameters = new Dictionary(); + var isMethodMatched = HttpMethod == null || HttpMethod.Split(',').Contains(httpMethod); - for (var i = 0; i < _urlParameterNames.Count; i++) - parameters[_urlParameterNames[i]] = match.Groups[i + 1].Value; - return true; + if (!match.Success || !isMethodMatched) { + parameters = null; + return false; } + + parameters = new Dictionary(); + + for (var i = 0; i < _urlParameterNames.Count; i++) + parameters[_urlParameterNames[i]] = match.Groups[i + 1].Value; + return true; } } \ No newline at end of file diff --git a/test/RestSharp.Tests.Shared/Fixtures/WebServer.cs b/test/RestSharp.Tests.Shared/Fixtures/WebServer.cs index 7dc773d34..35c311bc4 100644 --- a/test/RestSharp.Tests.Shared/Fixtures/WebServer.cs +++ b/test/RestSharp.Tests.Shared/Fixtures/WebServer.cs @@ -1,74 +1,64 @@ -using System; -using System.Net; -using System.Threading; +using System.Net; -namespace RestSharp.Tests.Shared.Fixtures -{ - public class WebServer - { - readonly HttpListener _listener = new HttpListener(); - CancellationTokenSource _cts; - Action _responderMethod; +namespace RestSharp.Tests.Shared.Fixtures; - public WebServer(string prefix, Action method, AuthenticationSchemes authenticationSchemes) - { - if (string.IsNullOrEmpty(prefix)) - throw new ArgumentException("URI prefix is required"); +public class WebServer { + readonly HttpListener _listener = new(); + Action _responderMethod; - _listener.Prefixes.Add(prefix); - _listener.AuthenticationSchemes = authenticationSchemes; + public WebServer(string prefix, Action method, AuthenticationSchemes authenticationSchemes) { + if (string.IsNullOrEmpty(prefix)) + throw new ArgumentException("URI prefix is required"); - _responderMethod = method; - _listener.Start(); - } + _listener.Prefixes.Add(prefix); + _listener.AuthenticationSchemes = authenticationSchemes; + + _responderMethod = method; + } - public void Run() - { - _cts = new CancellationTokenSource(); + public async Task Run(CancellationToken token) { + var taskFactory = new TaskFactory(token); + _listener.Start(); - ThreadPool.QueueUserWorkItem( - o => - { - var token = (CancellationToken) o; + try { + while (!token.IsCancellationRequested && _listener.IsListening) { + try { + var ctx = await GetContextAsync(); + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (ctx == null) continue; - try - { - while (!token.IsCancellationRequested && _listener.IsListening) - { - ThreadPool.QueueUserWorkItem( - c => - { - try - { - if (!(c is HttpListenerContext ctx)) return; + _responderMethod?.Invoke(ctx); + ctx.Response.OutputStream.Close(); + } + catch (Exception e) { + Console.WriteLine(e.ToString()); + } + } + } + catch (Exception e) { + Console.WriteLine(e.ToString()); + } - _responderMethod?.Invoke(ctx); - ctx.Response.OutputStream?.Close(); - } - catch (Exception e) - { - Console.WriteLine(e); - } - }, _listener.IsListening ? _listener.GetContext() : null - ); - } + Task GetContextAsync() + => taskFactory.FromAsync( + (callback, state) => ((HttpListener)state!).BeginGetContext(callback, state), + iar => { + try { + return ((HttpListener)iar.AsyncState!).EndGetContext(iar); } - catch (Exception e) - { - Console.WriteLine(e); + catch (HttpListenerException) { + // it's ok + return null; } - }, _cts.Token + }, + _listener ); - } - - public void Stop() - { - _cts.Cancel(); - _listener.Stop(); - _listener.Close(); - _cts.Dispose(); - } + } - public void ChangeHandler(Action handler) => _responderMethod = handler; + public void Stop() { + _listener.Stop(); + _listener.Close(); } + + public void ChangeHandler(Action handler) => _responderMethod = handler; } \ No newline at end of file diff --git a/test/RestSharp.Tests/Fixtures/CultureChange.cs b/test/RestSharp.Tests/Fixtures/CultureChange.cs deleted file mode 100644 index e6abe3d29..000000000 --- a/test/RestSharp.Tests/Fixtures/CultureChange.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Globalization; - -namespace RestSharp.Tests.Fixtures; - -public class CultureChange : IDisposable { - public CultureChange(string culture) { - Ensure.NotEmpty(culture, nameof(culture)); - - PreviousCulture = Thread.CurrentThread.CurrentCulture; - - Thread.CurrentThread.CurrentCulture = new CultureInfo(culture); - } - - public CultureInfo? PreviousCulture { get; private set; } - - public void Dispose() { - if (PreviousCulture == null) return; - - Thread.CurrentThread.CurrentCulture = PreviousCulture; - - PreviousCulture = null; - } -} \ No newline at end of file diff --git a/test/RestSharp.Tests/JwtAuthTests.cs b/test/RestSharp.Tests/JwtAuthTests.cs index c771fe03a..f7133b353 100644 --- a/test/RestSharp.Tests/JwtAuthTests.cs +++ b/test/RestSharp.Tests/JwtAuthTests.cs @@ -71,7 +71,7 @@ public void Set_Auth_Header_Only_Once() { var paramList = request.Parameters.Where(p => p.Name.Equals("Authorization")).ToList(); - Assert.Equal(1, paramList.Count); + paramList.Should().HaveCount(1); var authParam = paramList[0]; diff --git a/test/RestSharp.Tests/OAuth1AuthenticatorTests.cs b/test/RestSharp.Tests/OAuth1AuthenticatorTests.cs index 83d3601e1..a4b6191c9 100644 --- a/test/RestSharp.Tests/OAuth1AuthenticatorTests.cs +++ b/test/RestSharp.Tests/OAuth1AuthenticatorTests.cs @@ -41,17 +41,17 @@ public void Authenticate_ShouldAddAuthorizationAsTextValueToRequest_OnHttpAuthor var authParameter = request.Parameters.Single(x => x.Name == "Authorization"); var value = (string)authParameter.Value; - Assert.True(value.Contains("OAuth")); - Assert.True(value.Contains("realm=\"Realm\"")); - Assert.True(value.Contains("oauth_timestamp=")); - Assert.True(value.Contains("oauth_signature=\"ConsumerSecret")); - Assert.True(value.Contains("oauth_nonce=")); - Assert.True(value.Contains("oauth_consumer_key=\"ConsumerKey\"")); - Assert.True(value.Contains("oauth_signature_method=\"PLAINTEXT\"")); - Assert.True(value.Contains("oauth_version=\"Version\"")); - Assert.True(value.Contains("x_auth_mode=\"client_auth\"")); - Assert.True(value.Contains("x_auth_username=\"ClientUsername\"")); - Assert.True(value.Contains("x_auth_password=\"ClientPassword\"")); + Assert.Contains("OAuth", value); + Assert.Contains("realm=\"Realm\"", value); + Assert.Contains("oauth_timestamp=", value); + Assert.Contains("oauth_signature=\"ConsumerSecret", value); + Assert.Contains("oauth_nonce=", value); + Assert.Contains("oauth_consumer_key=\"ConsumerKey\"", value); + Assert.Contains("oauth_signature_method=\"PLAINTEXT\"", value); + Assert.Contains("oauth_version=\"Version\"", value); + Assert.Contains("x_auth_mode=\"client_auth\"", value); + Assert.Contains("x_auth_username=\"ClientUsername\"", value); + Assert.Contains("x_auth_password=\"ClientPassword\"", value); } [Fact] @@ -179,8 +179,6 @@ public void Authenticate_ShouldEncodeOAuthTokenParameter(OAuthType type, string [Theory] [InlineData(OAuthType.AccessToken)] [InlineData(OAuthType.ProtectedResource)] - [InlineData(OAuthType.AccessToken)] - [InlineData(OAuthType.ProtectedResource)] public void Authenticate_ShouldAllowEmptyConsumerSecret_OnHttpAuthorizationHeaderHandling(OAuthType type) { // Arrange const string url = "https://no-query.string"; @@ -197,6 +195,7 @@ public void Authenticate_ShouldAllowEmptyConsumerSecret_OnHttpAuthorizationHeade var authParameter = request.Parameters.Single(x => x.Name == "Authorization"); var value = (string)authParameter.Value; + Assert.NotNull(value); Assert.NotEmpty(value); Assert.Contains("OAuth", value!); Assert.Contains("oauth_signature=\"" + OAuthTools.UrlEncodeStrict("&"), value); diff --git a/test/RestSharp.Tests/OAuthTests.cs b/test/RestSharp.Tests/OAuthTests.cs index 867ab74c7..4f2f94051 100644 --- a/test/RestSharp.Tests/OAuthTests.cs +++ b/test/RestSharp.Tests/OAuthTests.cs @@ -57,7 +57,7 @@ public void PercentEncode_Encodes_Correctly(string value, string expected) { [InlineData("", 2048)] [InlineData(" !\"#$%&'()*+,", 2048)] public void RsaSha1_Signs_Correctly(string value, int keySize) { - var hasher = new SHA1Managed(); + var hasher = SHA1.Create(); var hash = hasher.ComputeHash(value.GetBytes()); using var crypto = new RSACryptoServiceProvider(keySize) { PersistKeyInCsp = false }; diff --git a/test/RestSharp.Tests/RestSharp.Tests.csproj b/test/RestSharp.Tests/RestSharp.Tests.csproj index 72cc9fd3d..9db982d04 100644 --- a/test/RestSharp.Tests/RestSharp.Tests.csproj +++ b/test/RestSharp.Tests/RestSharp.Tests.csproj @@ -1,7 +1,4 @@  - - net6.0 - @@ -28,4 +25,7 @@ + + + \ No newline at end of file diff --git a/test/RestSharp.Tests/StringExtensionsTests.cs b/test/RestSharp.Tests/StringExtensionsTests.cs index d86be1886..045be6d9f 100644 --- a/test/RestSharp.Tests/StringExtensionsTests.cs +++ b/test/RestSharp.Tests/StringExtensionsTests.cs @@ -7,8 +7,8 @@ namespace RestSharp.Tests; public class StringExtensionsTests { [Fact] public void UrlEncode_Throws_ArgumentNullException_For_Null_Input() { - string? nullString = null; - + string nullString = null; + // ReSharper disable once ExpressionIsAlwaysNull Assert.Throws(() => nullString!.UrlEncode()); } @@ -70,24 +70,4 @@ public void ToCamelCase(string start, string finish) { Assert.Equal(finish, result); } - - [Fact] - public void Does_not_throw_on_invalid_encoding() { - const string value = "SomeValue"; - - var bytes = Encoding.UTF8.GetBytes(value); - - var decoded = bytes.AsString("blah"); - decoded.Should().Be(value); - } - - [Fact] - public void Does_not_throw_on_missing_encoding() { - const string value = "SomeValue"; - - var bytes = Encoding.UTF8.GetBytes(value); - - var decoded = bytes.AsString(null); - decoded.Should().Be(value); - } } \ No newline at end of file