Enable more nullable annotations in WebCmdlets (#19359)

This commit is contained in:
CarloToso
2023-04-24 08:38:30 +05:00
committed by GitHub
parent 3547acaa1c
commit 543b20b4ed
14 changed files with 105 additions and 74 deletions
@@ -1,8 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Management.Automation;
using System.Net.Http;
@@ -33,7 +36,7 @@ namespace Microsoft.PowerShell.Commands
/// <param name="response">The response.</param>
/// <param name="contentStream">The content stream associated with the response.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public BasicHtmlWebResponseObject(HttpResponseMessage response, Stream contentStream, CancellationToken cancellationToken) : base(response, contentStream, cancellationToken)
public BasicHtmlWebResponseObject(HttpResponseMessage response, Stream? contentStream, CancellationToken cancellationToken) : base(response, contentStream, cancellationToken)
{
InitializeContent(cancellationToken);
InitializeRawContent(response);
@@ -60,9 +63,9 @@ namespace Microsoft.PowerShell.Commands
/// Encoding of the response body from the <c>Content-Type</c> header,
/// or <see langword="null"/> if the encoding could not be determined.
/// </value>
public Encoding Encoding { get; private set; }
public Encoding? Encoding { get; private set; }
private WebCmdletElementCollection _inputFields;
private WebCmdletElementCollection? _inputFields;
/// <summary>
/// Gets the HTML input field elements parsed from <see cref="Content"/>.
@@ -87,7 +90,7 @@ namespace Microsoft.PowerShell.Commands
}
}
private WebCmdletElementCollection _links;
private WebCmdletElementCollection? _links;
/// <summary>
/// Gets the HTML a link elements parsed from <see cref="Content"/>.
@@ -112,7 +115,7 @@ namespace Microsoft.PowerShell.Commands
}
}
private WebCmdletElementCollection _images;
private WebCmdletElementCollection? _images;
/// <summary>
/// Gets the HTML img elements parsed from <see cref="Content"/>.
@@ -145,13 +148,14 @@ namespace Microsoft.PowerShell.Commands
/// Reads the response content from the web response.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
[MemberNotNull(nameof(Content))]
protected void InitializeContent(CancellationToken cancellationToken)
{
string contentType = ContentHelper.GetContentType(BaseResponse);
string? contentType = ContentHelper.GetContentType(BaseResponse);
if (ContentHelper.IsText(contentType))
{
// Fill the Content buffer
string characterSet = WebResponseHelper.GetCharacterSet(BaseResponse);
string? characterSet = WebResponseHelper.GetCharacterSet(BaseResponse);
Content = StreamHelper.DecodeStream(RawContentStream, characterSet, out Encoding encoding, cancellationToken);
Encoding = encoding;
@@ -204,7 +208,7 @@ namespace Microsoft.PowerShell.Commands
string name = nvMatches.Groups[1].Value;
// The value (if any) is captured by group #2, #3, or #4, depending on quoting or lack thereof
string value = null;
string? value = null;
if (nvMatches.Groups[2].Success)
{
value = nvMatches.Groups[2].Value;
@@ -1,7 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System;
using System.Diagnostics.CodeAnalysis;
using System.Management.Automation;
using System.Net.Http;
using System.Net.Http.Headers;
@@ -16,7 +19,7 @@ namespace Microsoft.PowerShell.Commands
#region Internal Methods
// ContentType may not exist in response header. Return null if not.
internal static string GetContentType(HttpResponseMessage response) => response.Content.Headers.ContentType?.MediaType;
internal static string? GetContentType(HttpResponseMessage response) => response.Content.Headers.ContentType?.MediaType;
internal static Encoding GetDefaultEncoding() => Encoding.UTF8;
@@ -35,7 +38,7 @@ namespace Microsoft.PowerShell.Commands
HttpHeaders[] headerCollections =
{
response.Headers,
response.Content?.Headers
response.Content.Headers
};
foreach (var headerCollection in headerCollections)
@@ -59,7 +62,7 @@ namespace Microsoft.PowerShell.Commands
return raw;
}
internal static bool IsJson(string contentType)
internal static bool IsJson([NotNullWhen(true)] string? contentType)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -80,7 +83,7 @@ namespace Microsoft.PowerShell.Commands
return isJson;
}
internal static bool IsText(string contentType)
internal static bool IsText([NotNullWhen(true)] string? contentType)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -96,19 +99,18 @@ namespace Microsoft.PowerShell.Commands
if (Platform.IsWindows && !isText)
{
// Media types registered with Windows as having a perceived type of text, are text
using (RegistryKey contentTypeKey = Registry.ClassesRoot.OpenSubKey(@"MIME\Database\Content Type\" + contentType))
using (RegistryKey? contentTypeKey = Registry.ClassesRoot.OpenSubKey(@"MIME\Database\Content Type\" + contentType))
{
if (contentTypeKey != null)
{
string extension = contentTypeKey.GetValue("Extension") as string;
if (extension != null)
if (contentTypeKey.GetValue("Extension") is string extension)
{
using (RegistryKey extensionKey = Registry.ClassesRoot.OpenSubKey(extension))
using (RegistryKey? extensionKey = Registry.ClassesRoot.OpenSubKey(extension))
{
if (extensionKey != null)
{
string perceivedType = extensionKey.GetValue("PerceivedType") as string;
isText = (perceivedType == "text");
string? perceivedType = extensionKey.GetValue("PerceivedType") as string;
isText = perceivedType == "text";
}
}
}
@@ -119,7 +121,7 @@ namespace Microsoft.PowerShell.Commands
return isText;
}
internal static bool IsXml(string contentType)
internal static bool IsXml([NotNullWhen(true)] string? contentType)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -1,7 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Management.Automation;
using System.Net.Http;
@@ -56,13 +59,13 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
[Parameter]
[Alias("RHV")]
public string ResponseHeadersVariable { get; set; }
public string? ResponseHeadersVariable { get; set; }
/// <summary>
/// Gets or sets the variable name to use for storing the status code from the response.
/// </summary>
[Parameter]
public string StatusCodeVariable { get; set; }
public string? StatusCodeVariable { get; set; }
#endregion Parameters
@@ -95,12 +98,12 @@ namespace Microsoft.PowerShell.Commands
RestReturnType returnType = CheckReturnType(response);
// Try to get the response encoding from the ContentType header.
string charSet = WebResponseHelper.GetCharacterSet(response);
string? characterSet = WebResponseHelper.GetCharacterSet(response);
string str = StreamHelper.DecodeStream(responseStream, charSet, out Encoding encoding, _cancelToken.Token);
string str = StreamHelper.DecodeStream(responseStream, characterSet, out Encoding encoding, _cancelToken.Token);
object obj = null;
Exception ex = null;
object? obj = null;
Exception? ex = null;
string encodingVerboseName;
try
@@ -167,7 +170,7 @@ namespace Microsoft.PowerShell.Commands
ArgumentNullException.ThrowIfNull(response);
RestReturnType rt = RestReturnType.Detect;
string contentType = ContentHelper.GetContentType(response);
string? contentType = ContentHelper.GetContentType(response);
if (string.IsNullOrEmpty(contentType))
{
rt = RestReturnType.Detect;
@@ -223,7 +226,7 @@ namespace Microsoft.PowerShell.Commands
)
{
// This one will do reader.Read() internally
XmlNode result = workingDocument.ReadNode(reader);
XmlNode? result = workingDocument.ReadNode(reader);
WriteObject(result);
}
else
@@ -262,7 +265,7 @@ namespace Microsoft.PowerShell.Commands
return xrs;
}
private static bool TryConvertToXml(string xml, out object doc, ref Exception exRef)
private static bool TryConvertToXml(string xml, [NotNullWhen(true)] out object? doc, ref Exception? exRef)
{
try
{
@@ -284,13 +287,12 @@ namespace Microsoft.PowerShell.Commands
return doc != null;
}
private static bool TryConvertToJson(string json, out object obj, ref Exception exRef)
private static bool TryConvertToJson(string json, [NotNullWhen(true)] out object? obj, ref Exception? exRef)
{
bool converted = false;
try
{
ErrorRecord error;
obj = JsonObject.ConvertFromJson(json, out error);
obj = JsonObject.ConvertFromJson(json, out ErrorRecord error);
if (obj == null)
{
@@ -340,7 +342,7 @@ namespace Microsoft.PowerShell.Commands
/// <summary>
/// Json return type.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
Json,
/// <summary>
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -26,14 +28,14 @@ namespace Microsoft.PowerShell.Commands
/// <summary>
/// Gets or protected sets the response body content.
/// </summary>
public byte[] Content { get; protected set; }
public byte[]? Content { get; protected set; }
/// <summary>
/// Gets the Headers property.
/// </summary>
public Dictionary<string, IEnumerable<string>> Headers => _headers ??= WebResponseHelper.GetHeadersDictionary(BaseResponse);
private Dictionary<string, IEnumerable<string>> _headers = null;
private Dictionary<string, IEnumerable<string>>? _headers;
/// <summary>
/// Gets or protected sets the full response content.
@@ -41,7 +43,7 @@ namespace Microsoft.PowerShell.Commands
/// <value>
/// Full response content, including the HTTP status line, headers, and body.
/// </value>
public string RawContent { get; protected set; }
public string? RawContent { get; protected set; }
/// <summary>
/// Gets the length (in bytes) of <see cref="RawContentStream"/>.
@@ -56,7 +58,7 @@ namespace Microsoft.PowerShell.Commands
/// <summary>
/// Gets the RelationLink property.
/// </summary>
public Dictionary<string, string> RelationLink { get; internal set; }
public Dictionary<string, string>? RelationLink { get; internal set; }
/// <summary>
/// Gets the response status code.
@@ -77,8 +79,7 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
/// <param name="response">The Http response.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public WebResponseObject(HttpResponseMessage response, CancellationToken cancellationToken) : this(response, null, cancellationToken)
{ }
public WebResponseObject(HttpResponseMessage response, CancellationToken cancellationToken) : this(response, null, cancellationToken) { }
/// <summary>
/// Initializes a new instance of the <see cref="WebResponseObject"/> class
@@ -87,7 +88,7 @@ namespace Microsoft.PowerShell.Commands
/// <param name="response">Http response.</param>
/// <param name="contentStream">The http content stream.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public WebResponseObject(HttpResponseMessage response, Stream contentStream, CancellationToken cancellationToken)
public WebResponseObject(HttpResponseMessage response, Stream? contentStream, CancellationToken cancellationToken)
{
SetResponse(response, contentStream, cancellationToken);
InitializeContent();
@@ -111,9 +112,9 @@ namespace Microsoft.PowerShell.Commands
StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse);
// Use ASCII encoding for the RawContent visual view of the content.
if (Content.Length > 0)
if (Content?.Length > 0)
{
raw.Append(this.ToString());
raw.Append(ToString());
}
RawContent = raw.ToString();
@@ -125,26 +126,23 @@ namespace Microsoft.PowerShell.Commands
|| char.IsSymbol(c)
|| char.IsWhiteSpace(c);
private void SetResponse(HttpResponseMessage response, Stream contentStream, CancellationToken cancellationToken)
[MemberNotNull(nameof(RawContentStream))]
[MemberNotNull(nameof(BaseResponse))]
private void SetResponse(HttpResponseMessage response, Stream? contentStream, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(response);
BaseResponse = response;
MemoryStream ms = contentStream as MemoryStream;
if (ms is not null)
if (contentStream is MemoryStream ms)
{
RawContentStream = ms;
}
else
{
Stream st = contentStream;
if (contentStream is null)
{
st = StreamHelper.GetResponseStream(response, cancellationToken);
}
Stream st = contentStream ?? StreamHelper.GetResponseStream(response, cancellationToken);
long contentLength = response.Content.Headers.ContentLength.Value;
long contentLength = response.Content.Headers.ContentLength.GetValueOrDefault();
if (contentLength <= 0)
{
contentLength = StreamHelper.DefaultReadBuffer;
@@ -164,7 +162,12 @@ namespace Microsoft.PowerShell.Commands
/// <returns>The string representation of this web response.</returns>
public sealed override string ToString()
{
char[] stringContent = System.Text.Encoding.ASCII.GetChars(Content);
if (Content is null)
{
return string.Empty;
}
char[] stringContent = Encoding.ASCII.GetChars(Content);
for (int counter = 0; counter < stringContent.Length; counter++)
{
if (!IsPrintable(stringContent[counter]))
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System;
using System.Collections.Generic;
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System;
using System.IO;
using System.Management.Automation;
@@ -37,7 +39,7 @@ namespace Microsoft.PowerShell.Commands
Stream responseStream = StreamHelper.GetResponseStream(response, _cancelToken.Token);
if (ShouldWriteToPipeline)
{
// creating a MemoryStream wrapper to response stream here to support IsStopping.
// Creating a MemoryStream wrapper to response stream here to support IsStopping.
responseStream = new WebResponseContentMemoryStream(
responseStream,
StreamHelper.ChunkSize,
@@ -48,7 +50,7 @@ namespace Microsoft.PowerShell.Commands
ro.RelationLink = _relationLink;
WriteObject(ro);
// use the rawcontent stream from WebResponseObject for further
// Use the rawcontent stream from WebResponseObject for further
// processing of the stream. This is need because WebResponse's
// stream can be used only once.
responseStream = ro.RawContentStream;
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System;
using System.Net;
@@ -8,7 +10,7 @@ namespace Microsoft.PowerShell.Commands
{
internal class WebProxy : IWebProxy, IEquatable<WebProxy>
{
private ICredentials _credentials;
private ICredentials? _credentials;
private readonly Uri _proxyAddress;
internal WebProxy(Uri address)
@@ -18,11 +20,11 @@ namespace Microsoft.PowerShell.Commands
_proxyAddress = address;
}
public override bool Equals(object obj) => Equals(obj as WebProxy);
public override bool Equals(object? obj) => Equals(obj as WebProxy);
public override int GetHashCode() => HashCode.Combine(_proxyAddress, _credentials, BypassProxyOnLocal);
public bool Equals(WebProxy other)
public bool Equals(WebProxy? other)
{
if (other is null)
{
@@ -35,7 +37,7 @@ namespace Microsoft.PowerShell.Commands
&& BypassProxyOnLocal == other.BypassProxyOnLocal;
}
public ICredentials Credentials
public ICredentials? Credentials
{
get => _credentials;
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -11,7 +13,7 @@ namespace Microsoft.PowerShell.Commands
{
internal static class WebResponseHelper
{
internal static string GetCharacterSet(HttpResponseMessage response) => response.Content.Headers.ContentType?.CharSet;
internal static string? GetCharacterSet(HttpResponseMessage response) => response.Content.Headers.ContentType?.CharSet;
internal static Dictionary<string, IEnumerable<string>> GetHeadersDictionary(HttpResponseMessage response)
{
@@ -38,7 +40,7 @@ namespace Microsoft.PowerShell.Commands
internal static string GetOutFilePath(HttpResponseMessage response, string _qualifiedOutFile)
{
// Get file name from last segment of Uri
string lastUriSegment = System.Net.WebUtility.UrlDecode(response.RequestMessage.RequestUri.Segments[^1]);
string? lastUriSegment = System.Net.WebUtility.UrlDecode(response.RequestMessage?.RequestUri?.Segments[^1]);
return Directory.Exists(_qualifiedOutFile) ? Path.Join(_qualifiedOutFile, lastUriSegment) : _qualifiedOutFile;
}
@@ -52,7 +54,7 @@ namespace Microsoft.PowerShell.Commands
internal static bool IsText(HttpResponseMessage response)
{
// ContentType may not exist in response header.
string contentType = ContentHelper.GetContentType(response);
string? contentType = ContentHelper.GetContentType(response);
return ContentHelper.IsText(contentType);
}
}
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System.Collections.Generic;
namespace Microsoft.PowerShell.Commands
@@ -46,7 +48,7 @@ namespace Microsoft.PowerShell.Commands
internal void AddField(string key, string value)
{
if (key is not null && !Fields.TryGetValue(key, out string test))
if (key is not null && !Fields.TryGetValue(key, out string? _))
{
Fields[key] = value;
}
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System;
using System.Collections.ObjectModel;
@@ -16,11 +18,11 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public FormObject this[string key]
public FormObject? this[string key]
{
get
{
FormObject form = null;
FormObject? form = null;
foreach (FormObject f in this)
{
if (string.Equals(key, f.Id, StringComparison.OrdinalIgnoreCase))
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System;
using System.Globalization;
using System.Management.Automation;
@@ -14,7 +16,7 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
public static class PSUserAgent
{
private static string s_windowsUserAgent;
private static string? s_windowsUserAgent;
// Format the user-agent string from the various component parts
internal static string UserAgent => string.Create(CultureInfo.InvariantCulture, $"{Compatibility} ({PlatformName}; {OS}; {Culture}) {App}");
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System;
using System.IO;
using System.Management.Automation;
@@ -25,7 +27,7 @@ namespace Microsoft.PowerShell.Commands
private readonly long? _contentLength;
private readonly Stream _originalStreamToProxy;
private readonly Cmdlet _ownerCmdlet;
private readonly Cmdlet? _ownerCmdlet;
private readonly CancellationToken _cancellationToken;
private bool _isInitialized = false;
@@ -40,7 +42,7 @@ namespace Microsoft.PowerShell.Commands
/// <param name="cmdlet">Owner cmdlet if any.</param>
/// <param name="contentLength">Expected download size in Bytes.</param>
/// <param name="cancellationToken">Cancellation token.</param>
internal WebResponseContentMemoryStream(Stream stream, int initialCapacity, Cmdlet cmdlet, long? contentLength, CancellationToken cancellationToken) : base(initialCapacity)
internal WebResponseContentMemoryStream(Stream stream, int initialCapacity, Cmdlet? cmdlet, long? contentLength, CancellationToken cancellationToken) : base(initialCapacity)
{
this._contentLength = contentLength;
_originalStreamToProxy = stream;
@@ -361,7 +363,7 @@ namespace Microsoft.PowerShell.Commands
while (!completed)
{
// If this is the last input data, flush the decoder's internal buffer and state.
bool flush = bytesRead == 0;
bool flush = bytesRead is 0;
decoder.Convert(bytes, byteIndex, bytesRead - byteIndex, chars, 0, useBufferSize, flush, out int bytesUsed, out int charsUsed, out completed);
// The conversion produced the number of characters indicated by charsUsed. Write that number
@@ -386,7 +388,7 @@ namespace Microsoft.PowerShell.Commands
return result.ToString();
}
internal static string DecodeStream(Stream stream, string characterSet, out Encoding encoding, CancellationToken cancellationToken)
internal static string DecodeStream(Stream stream, string? characterSet, out Encoding encoding, CancellationToken cancellationToken)
{
bool isDefaultEncoding = !TryGetEncoding(characterSet, out encoding);
@@ -422,12 +424,12 @@ namespace Microsoft.PowerShell.Commands
return content;
}
internal static bool TryGetEncoding(string characterSet, out Encoding encoding)
internal static bool TryGetEncoding(string? characterSet, out Encoding encoding)
{
bool result = false;
try
{
encoding = Encoding.GetEncoding(characterSet);
encoding = Encoding.GetEncoding(characterSet!);
result = true;
}
catch (ArgumentException)
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
@@ -21,23 +23,23 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
/// <param name="nameOrId"></param>
/// <returns>Found element as PSObject.</returns>
public PSObject Find(string nameOrId) => FindById(nameOrId) ?? FindByName(nameOrId);
public PSObject? Find(string nameOrId) => FindById(nameOrId) ?? FindByName(nameOrId);
/// <summary>
/// Finds the element by id.
/// </summary>
/// <param name="id"></param>
/// <returns>Found element as PSObject.</returns>
public PSObject FindById(string id) => Find(id, findById: true);
public PSObject? FindById(string id) => Find(id, findById: true);
/// <summary>
/// Finds the element by name.
/// </summary>
/// <param name="name"></param>
/// <returns>Found element as PSObject.</returns>
public PSObject FindByName(string name) => Find(name, findById: false);
public PSObject? FindByName(string name) => Find(name, findById: false);
private PSObject Find(string nameOrId, bool findById)
private PSObject? Find(string nameOrId, bool findById)
{
foreach (PSObject candidate in this)
{
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#nullable enable
namespace Microsoft.PowerShell.Commands
{
/// <summary>