chore: Fix null reference complaints under .NET 8.0
Signed-off-by: Jon Skeet <jonskeet@google.com>
This commit is contained in:
parent
08264ad333
commit
ad3a03d68f
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Cloud Native Foundation.
|
||||
// Copyright (c) Cloud Native Foundation.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
|
@ -8,6 +8,7 @@ using Amqp.Types;
|
|||
using CloudNative.CloudEvents.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Net.Mime;
|
||||
|
||||
|
@ -145,7 +146,7 @@ namespace CloudNative.CloudEvents.Amqp
|
|||
}
|
||||
}
|
||||
|
||||
private static bool HasCloudEventsContentType(Message message, out string? contentType)
|
||||
private static bool HasCloudEventsContentType(Message message, [NotNullWhen(true)] out string? contentType)
|
||||
{
|
||||
contentType = message.Properties.ContentType?.ToString();
|
||||
return MimeUtilities.IsCloudEventsContentType(contentType);
|
||||
|
@ -249,4 +250,4 @@ namespace CloudNative.CloudEvents.Amqp
|
|||
return applicationProperties;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
<PackageReference Include="AMQPNetLite" Version="2.4.2" />
|
||||
<PackageReference Include="AMQPNetLite.Serialization" Version="2.4.2" />
|
||||
<ProjectReference Include="..\CloudNative.CloudEvents\CloudNative.CloudEvents.csproj" />
|
||||
<!-- Source-only package with nullable reference annotations. -->
|
||||
<PackageReference Include="Nullable" Version="1.3.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -185,9 +185,9 @@ namespace CloudNative.CloudEvents.Avro
|
|||
// will fail and that's okay since the type is useless without the proper schema.
|
||||
using var sr = new StreamReader(typeof(AvroEventFormatter)
|
||||
.Assembly
|
||||
.GetManifestResourceStream("CloudNative.CloudEvents.Avro.AvroSchema.json"));
|
||||
.GetManifestResourceStream("CloudNative.CloudEvents.Avro.AvroSchema.json")!);
|
||||
|
||||
return (RecordSchema) Schema.Parse(sr.ReadToEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Cloud Native Foundation.
|
||||
// Copyright (c) Cloud Native Foundation.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
|
@ -345,7 +345,7 @@ namespace CloudNative.CloudEvents.NewtonsoftJson
|
|||
{
|
||||
throw new ArgumentException($"Structured mode property '{DataBase64PropertyName}' must be a string, when present.");
|
||||
}
|
||||
cloudEvent.Data = Convert.FromBase64String((string?) dataBase64Token);
|
||||
cloudEvent.Data = Convert.FromBase64String((string) dataBase64Token!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -498,7 +498,7 @@ namespace CloudNative.CloudEvents.NewtonsoftJson
|
|||
/// </summary>
|
||||
/// <param name="data">The CloudEvent to infer the data content from. Must not be null.</param>
|
||||
/// <returns>The inferred data content type, or null if no inference is performed.</returns>
|
||||
protected override string? InferDataContentType(object data) => data is byte[]? null : JsonMediaType;
|
||||
protected override string? InferDataContentType(object data) => data is byte[] ? null : JsonMediaType;
|
||||
|
||||
/// <summary>
|
||||
/// Encodes structured mode data within a CloudEvent, writing it to the specified <see cref="JsonWriter"/>.
|
||||
|
@ -524,7 +524,14 @@ namespace CloudNative.CloudEvents.NewtonsoftJson
|
|||
}
|
||||
else
|
||||
{
|
||||
ContentType dataContentType = new ContentType(GetOrInferDataContentType(cloudEvent));
|
||||
string? dataContentTypeText = GetOrInferDataContentType(cloudEvent);
|
||||
// This would only happen in a derived class which overrides GetOrInferDataContentType further...
|
||||
// This class infers application/json for anything other than byte arrays.
|
||||
if (dataContentTypeText is null)
|
||||
{
|
||||
throw new ArgumentException("Data content type cannot be inferred");
|
||||
}
|
||||
ContentType dataContentType = new ContentType(dataContentTypeText);
|
||||
if (IsJsonMediaType(dataContentType.MediaType))
|
||||
{
|
||||
writer.WritePropertyName(DataPropertyName);
|
||||
|
@ -710,4 +717,4 @@ namespace CloudNative.CloudEvents.NewtonsoftJson
|
|||
protected override void DecodeStructuredModeDataBase64Property(JToken dataBase64Token, CloudEvent cloudEvent) =>
|
||||
throw new ArgumentException($"Data unexpectedly represented using '{DataBase64PropertyName}' within structured mode CloudEvent.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace CloudNative.CloudEvents
|
|||
throw new ArgumentException($"The {nameof(CloudEventFormatterAttribute)} on type {targetType} has no converter type specified.", nameof(targetType));
|
||||
}
|
||||
|
||||
object instance;
|
||||
object? instance;
|
||||
try
|
||||
{
|
||||
instance = Activator.CreateInstance(formatterType);
|
||||
|
|
|
@ -65,7 +65,7 @@ namespace CloudNative.CloudEvents.Core
|
|||
public static MemoryStream AsStream(ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
var segment = GetArraySegment(memory);
|
||||
return new MemoryStream(segment.Array, segment.Offset, segment.Count, false);
|
||||
return new MemoryStream(segment.Array!, segment.Offset, segment.Count, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -79,7 +79,7 @@ namespace CloudNative.CloudEvents.Core
|
|||
|
||||
// TODO: If we introduce an additional netstandard2.1 target, we can use encoding.GetString(memory.Span)
|
||||
var segment = GetArraySegment(memory);
|
||||
return encoding.GetString(segment.Array, segment.Offset, segment.Count);
|
||||
return encoding.GetString(segment.Array!, segment.Offset, segment.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -92,7 +92,7 @@ namespace CloudNative.CloudEvents.Core
|
|||
{
|
||||
Validation.CheckNotNull(destination, nameof(destination));
|
||||
var segment = GetArraySegment(source);
|
||||
await destination.WriteAsync(segment.Array, segment.Offset, segment.Count).ConfigureAwait(false);
|
||||
await destination.WriteAsync(segment.Array!, segment.Offset, segment.Count).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -108,13 +108,14 @@ namespace CloudNative.CloudEvents.Core
|
|||
var segment = GetArraySegment(memory);
|
||||
// We probably don't actually need to check the offset: if the count is the same as the length,
|
||||
// I can't see how the offset can be non-zero. But it doesn't *hurt* as a check.
|
||||
return segment.Offset == 0 && segment.Count == segment.Array.Length
|
||||
return segment.Offset == 0 && segment.Count == segment.Array!.Length
|
||||
? segment.Array
|
||||
: memory.ToArray();
|
||||
}
|
||||
|
||||
// Note: when this returns, the Array property of the returned segment is guaranteed not to be null.
|
||||
private static ArraySegment<byte> GetArraySegment(ReadOnlyMemory<byte> memory) =>
|
||||
MemoryMarshal.TryGetArray(memory, out var segment)
|
||||
MemoryMarshal.TryGetArray(memory, out var segment) && segment.Array is not null
|
||||
? segment
|
||||
: new ArraySegment<byte>(memory.ToArray());
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright 2021 Cloud Native Foundation.
|
||||
// Copyright 2021 Cloud Native Foundation.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
|
@ -57,7 +58,7 @@ namespace CloudNative.CloudEvents.Core
|
|||
var header = new MediaTypeHeaderValue(contentType.MediaType);
|
||||
foreach (string parameterName in contentType.Parameters.Keys)
|
||||
{
|
||||
header.Parameters.Add(new NameValueHeaderValue(parameterName, contentType.Parameters[parameterName].ToString()));
|
||||
header.Parameters.Add(new NameValueHeaderValue(parameterName, contentType.Parameters[parameterName]));
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
@ -76,7 +77,7 @@ namespace CloudNative.CloudEvents.Core
|
|||
/// </summary>
|
||||
/// <param name="contentType">The content type to check. May be null, in which case the result is false.</param>
|
||||
/// <returns>true if the given content type denotes a (non-batch) CloudEvent; false otherwise</returns>
|
||||
public static bool IsCloudEventsContentType(string? contentType) =>
|
||||
public static bool IsCloudEventsContentType([NotNullWhen(true)] string? contentType) =>
|
||||
contentType is string &&
|
||||
contentType.StartsWith(MediaType, StringComparison.InvariantCultureIgnoreCase) &&
|
||||
!contentType.StartsWith(BatchMediaType, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
@ -86,7 +87,7 @@ namespace CloudNative.CloudEvents.Core
|
|||
/// </summary>
|
||||
/// <param name="contentType">The content type to check. May be null, in which case the result is false.</param>
|
||||
/// <returns>true if the given content type represents a CloudEvent batch; false otherwise</returns>
|
||||
public static bool IsCloudEventsBatchContentType(string? contentType) =>
|
||||
public static bool IsCloudEventsBatchContentType([NotNullWhen(true)] string? contentType) =>
|
||||
contentType is string && contentType.StartsWith(BatchMediaType, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
// Copyright (c) Cloud Native Foundation.
|
||||
// Copyright (c) Cloud Native Foundation.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
using CloudNative.CloudEvents.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
|
@ -130,7 +131,7 @@ namespace CloudNative.CloudEvents.Http
|
|||
return ToCloudEventInternalAsync(httpRequestMessage.Headers, httpRequestMessage.Content, formatter, extensionAttributes, nameof(httpRequestMessage));
|
||||
}
|
||||
|
||||
private static async Task<CloudEvent> ToCloudEventInternalAsync(HttpHeaders headers, HttpContent content,
|
||||
private static async Task<CloudEvent> ToCloudEventInternalAsync(HttpHeaders headers, HttpContent? content,
|
||||
CloudEventFormatter formatter, IEnumerable<CloudEventAttribute>? extensionAttributes, string paramName)
|
||||
{
|
||||
Validation.CheckNotNull(formatter, nameof(formatter));
|
||||
|
@ -142,7 +143,7 @@ namespace CloudNative.CloudEvents.Http
|
|||
}
|
||||
else
|
||||
{
|
||||
string? versionId = MaybeGetVersionId(headers) ?? MaybeGetVersionId(content.Headers);
|
||||
string? versionId = MaybeGetVersionId(headers) ?? MaybeGetVersionId(content?.Headers);
|
||||
if (versionId is null)
|
||||
{
|
||||
throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(paramName));
|
||||
|
@ -151,7 +152,8 @@ namespace CloudNative.CloudEvents.Http
|
|||
?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", paramName);
|
||||
|
||||
var cloudEvent = new CloudEvent(version, extensionAttributes);
|
||||
foreach (var header in headers.Concat(content.Headers))
|
||||
var allHeaders = content is null ? headers : headers.Concat(content.Headers);
|
||||
foreach (var header in allHeaders)
|
||||
{
|
||||
string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key);
|
||||
if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
|
||||
|
@ -231,7 +233,7 @@ namespace CloudNative.CloudEvents.Http
|
|||
return ToCloudEventBatchInternalAsync(httpRequestMessage.Content, formatter, extensionAttributes, nameof(httpRequestMessage));
|
||||
}
|
||||
|
||||
private static async Task<IReadOnlyList<CloudEvent>> ToCloudEventBatchInternalAsync(HttpContent content,
|
||||
private static async Task<IReadOnlyList<CloudEvent>> ToCloudEventBatchInternalAsync(HttpContent? content,
|
||||
CloudEventFormatter formatter, IEnumerable<CloudEventAttribute>? extensionAttributes, string paramName)
|
||||
{
|
||||
Validation.CheckNotNull(formatter, nameof(formatter));
|
||||
|
@ -331,16 +333,16 @@ namespace CloudNative.CloudEvents.Http
|
|||
}
|
||||
|
||||
private static ByteArrayContent ToByteArrayContent(ReadOnlyMemory<byte> content) =>
|
||||
MemoryMarshal.TryGetArray(content, out var segment)
|
||||
MemoryMarshal.TryGetArray(content, out var segment) && segment.Array is not null
|
||||
? new ByteArrayContent(segment.Array, segment.Offset, segment.Count)
|
||||
// TODO: Just throw?
|
||||
: new ByteArrayContent(content.ToArray());
|
||||
|
||||
// TODO: This would include "application/cloudeventsarerubbish" for example...
|
||||
private static bool HasCloudEventsContentType(HttpContent content) =>
|
||||
private static bool HasCloudEventsContentType([NotNullWhen(true)] HttpContent? content) =>
|
||||
MimeUtilities.IsCloudEventsContentType(content?.Headers?.ContentType?.MediaType);
|
||||
|
||||
private static bool HasCloudEventsBatchContentType(HttpContent content) =>
|
||||
private static bool HasCloudEventsBatchContentType([NotNullWhen(true)] HttpContent? content) =>
|
||||
MimeUtilities.IsCloudEventsBatchContentType(content?.Headers?.ContentType?.MediaType);
|
||||
|
||||
private static string? MaybeGetVersionId(HttpHeaders? headers) =>
|
||||
|
@ -348,4 +350,4 @@ namespace CloudNative.CloudEvents.Http
|
|||
? headers.GetValues(HttpUtilities.SpecVersionHttpHeader).First()
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ namespace CloudNative.CloudEvents.Http
|
|||
}
|
||||
else
|
||||
{
|
||||
string versionId = httpListenerRequest.Headers[HttpUtilities.SpecVersionHttpHeader];
|
||||
string? versionId = httpListenerRequest.Headers[HttpUtilities.SpecVersionHttpHeader];
|
||||
if (versionId is null)
|
||||
{
|
||||
throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(httpListenerRequest));
|
||||
|
@ -191,12 +191,18 @@ namespace CloudNative.CloudEvents.Http
|
|||
var headers = httpListenerRequest.Headers;
|
||||
foreach (var key in headers.AllKeys)
|
||||
{
|
||||
// It would be highly unusual for either the key or the value to be null, but
|
||||
// the contract claims it's possible. Skip it if so.
|
||||
if (key is null || headers[key] is not string headerValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(key);
|
||||
if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string attributeValue = HttpUtilities.DecodeHeaderValue(headers[key]);
|
||||
string attributeValue = HttpUtilities.DecodeHeaderValue(headerValue);
|
||||
cloudEvent.SetAttributeFromString(attributeName, attributeValue);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue