Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Http/Headers/ref/Microsoft.Net.Http.Headers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<Nullable>annotations</Nullable>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
<Compile Include="Microsoft.Net.Http.Headers.netcoreapp.cs" />
Expand Down
110 changes: 55 additions & 55 deletions src/Http/Headers/ref/Microsoft.Net.Http.Headers.netcoreapp.cs

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions src/Http/Headers/src/BaseHeaderParser.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Primitives;

namespace Microsoft.Net.Http.Headers
Expand All @@ -12,11 +13,11 @@ protected BaseHeaderParser(bool supportsMultipleValues)
{
}

protected abstract int GetParsedValueLength(StringSegment value, int startIndex, out T parsedValue);
protected abstract int GetParsedValueLength(StringSegment value, int startIndex, [MaybeNull] out T parsedValue);

public sealed override bool TryParseValue(StringSegment value, ref int index, out T parsedValue)
public sealed override bool TryParseValue(StringSegment value, ref int index, [MaybeNull] out T parsedValue)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can [NotNullWhen(true)] be applied here too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately not. The SupportsMultipleValues pattern makes nullable annotations really awkward to use .

{
parsedValue = default(T);
parsedValue = default;

// If multiple values are supported (i.e. list of values), then accept an empty string: The header may
// be added multiple times to the request/response message. E.g.
Expand Down
33 changes: 16 additions & 17 deletions src/Http/Headers/src/CacheControlHeaderValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
Expand Down Expand Up @@ -31,12 +32,12 @@ public class CacheControlHeaderValue
// Cache-Control headers, only one instance of CacheControlHeaderValue is created (if all headers contain valid
// values, otherwise we may have multiple strings containing the invalid values).
private static readonly HttpHeaderParser<CacheControlHeaderValue> Parser
= new GenericHeaderParser<CacheControlHeaderValue>(true, GetCacheControlLength);
= new GenericHeaderParser<CacheControlHeaderValue>(true, GetCacheControlLength!);

private static readonly Action<StringSegment> CheckIsValidTokenAction = CheckIsValidToken;

private bool _noCache;
private ICollection<StringSegment> _noCacheHeaders;
private ICollection<StringSegment>? _noCacheHeaders;
private bool _noStore;
private TimeSpan? _maxAge;
private TimeSpan? _sharedMaxAge;
Expand All @@ -47,10 +48,10 @@ private static readonly HttpHeaderParser<CacheControlHeaderValue> Parser
private bool _onlyIfCached;
private bool _public;
private bool _private;
private ICollection<StringSegment> _privateHeaders;
private ICollection<StringSegment>? _privateHeaders;
private bool _mustRevalidate;
private bool _proxyRevalidate;
private IList<NameValueHeaderValue> _extensions;
private IList<NameValueHeaderValue>? _extensions;

public CacheControlHeaderValue()
{
Expand Down Expand Up @@ -240,7 +241,7 @@ public override string ToString()
return sb.ToString();
}

public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as CacheControlHeaderValue;

Expand Down Expand Up @@ -325,7 +326,7 @@ public override int GetHashCode()

public static CacheControlHeaderValue Parse(StringSegment input)
{
int index = 0;
var index = 0;
// Cache-Control is unusual because there are no required values so the parser will succeed for an empty string, but still return null.
var result = Parser.ParseValue(input, ref index);
if (result == null)
Expand All @@ -335,9 +336,9 @@ public static CacheControlHeaderValue Parse(StringSegment input)
return result;
}

public static bool TryParse(StringSegment input, out CacheControlHeaderValue parsedValue)
public static bool TryParse(StringSegment input, [NotNullWhen(true)] out CacheControlHeaderValue? parsedValue)
{
int index = 0;
var index = 0;
// Cache-Control is unusual because there are no required values so the parser will succeed for an empty string, but still return null.
if (Parser.TryParseValue(input, ref index, out parsedValue) && parsedValue != null)
{
Expand All @@ -347,7 +348,7 @@ public static bool TryParse(StringSegment input, out CacheControlHeaderValue par
return false;
}

private static int GetCacheControlLength(StringSegment input, int startIndex, out CacheControlHeaderValue parsedValue)
private static int GetCacheControlLength(StringSegment input, int startIndex, out CacheControlHeaderValue? parsedValue)
{
Contract.Requires(startIndex >= 0);

Expand All @@ -361,16 +362,18 @@ private static int GetCacheControlLength(StringSegment input, int startIndex, ou
// Cache-Control header consists of a list of name/value pairs, where the value is optional. So use an
// instance of NameValueHeaderParser to parse the string.
var current = startIndex;
NameValueHeaderValue nameValue = null;
var nameValueList = new List<NameValueHeaderValue>();
while (current < input.Length)
{
if (!NameValueHeaderValue.MultipleValueParser.TryParseValue(input, ref current, out nameValue))
if (!NameValueHeaderValue.MultipleValueParser.TryParseValue(input, ref current, out var nameValue))
{
return 0;
}

nameValueList.Add(nameValue);
if (nameValue != null)
{
nameValueList.Add(nameValue);
}
}

// If we get here, we were able to successfully parse the string as list of name/value pairs. Now analyze
Expand Down Expand Up @@ -539,10 +542,8 @@ private static bool TrySetTokenOnlyValue(NameValueHeaderValue nameValue, ref boo
private static bool TrySetOptionalTokenList(
NameValueHeaderValue nameValue,
ref bool boolField,
ref ICollection<StringSegment> destination)
ref ICollection<StringSegment>? destination)
{
Contract.Requires(nameValue != null);

if (nameValue.Value == null)
{
boolField = true;
Expand Down Expand Up @@ -603,8 +604,6 @@ private static bool TrySetOptionalTokenList(

private static bool TrySetTimeSpan(NameValueHeaderValue nameValue, ref TimeSpan? timeSpan)
{
Contract.Requires(nameValue != null);

if (nameValue.Value == null)
{
return false;
Expand Down
33 changes: 17 additions & 16 deletions src/Http/Headers/src/ContentDispositionHeaderValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
Expand All @@ -26,10 +27,10 @@ public class ContentDispositionHeaderValue
private static readonly char[] SingleQuote = new char[] { '\'' };

private static readonly HttpHeaderParser<ContentDispositionHeaderValue> Parser
= new GenericHeaderParser<ContentDispositionHeaderValue>(false, GetDispositionTypeLength);
= new GenericHeaderParser<ContentDispositionHeaderValue>(false, GetDispositionTypeLength!);

// Use list instead of dictionary since we may have multiple parameters with the same name.
private ObjectCollection<NameValueHeaderValue> _parameters;
private ObjectCollection<NameValueHeaderValue>? _parameters;
private StringSegment _dispositionType;

private ContentDispositionHeaderValue()
Expand Down Expand Up @@ -128,7 +129,7 @@ public long? Size
// Remove parameter
if (sizeParameter != null)
{
_parameters.Remove(sizeParameter);
_parameters!.Remove(sizeParameter);
}
}
else if (value < 0)
Expand All @@ -142,7 +143,7 @@ public long? Size
else
{
string sizeString = value.GetValueOrDefault().ToString(CultureInfo.InvariantCulture);
_parameters.Add(new NameValueHeaderValue(SizeString, sizeString));
_parameters!.Add(new NameValueHeaderValue(SizeString, sizeString));
}
}
}
Expand Down Expand Up @@ -180,7 +181,7 @@ public override string ToString()
return _dispositionType + NameValueHeaderValue.ToString(_parameters, ';', true);
}

public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as ContentDispositionHeaderValue;

Expand All @@ -202,16 +203,16 @@ public override int GetHashCode()
public static ContentDispositionHeaderValue Parse(StringSegment input)
{
var index = 0;
return Parser.ParseValue(input, ref index);
return Parser.ParseValue(input, ref index)!;
}

public static bool TryParse(StringSegment input, out ContentDispositionHeaderValue parsedValue)
public static bool TryParse(StringSegment input, [NotNullWhen(true)] out ContentDispositionHeaderValue? parsedValue)
{
var index = 0;
return Parser.TryParseValue(input, ref index, out parsedValue);
return Parser.TryParseValue(input, ref index, out parsedValue!);
}

private static int GetDispositionTypeLength(StringSegment input, int startIndex, out ContentDispositionHeaderValue parsedValue)
private static int GetDispositionTypeLength(StringSegment input, int startIndex, out ContentDispositionHeaderValue? parsedValue)
{
Contract.Requires(startIndex >= 0);

Expand Down Expand Up @@ -253,7 +254,7 @@ private static int GetDispositionTypeLength(StringSegment input, int startIndex,

private static int GetDispositionTypeExpressionLength(StringSegment input, int startIndex, out StringSegment dispositionType)
{
Contract.Requires((input != null) && (input.Length > 0) && (startIndex < input.Length));
Contract.Requires((input.Length > 0) && (startIndex < input.Length));

// This method just parses the disposition type string, it does not parse parameters.
dispositionType = null;
Expand Down Expand Up @@ -318,7 +319,7 @@ private void SetDate(string parameter, DateTimeOffset? date)
// Remove parameter
if (dateParameter != null)
{
_parameters.Remove(dateParameter);
_parameters!.Remove(dateParameter);
}
}
else
Expand All @@ -343,7 +344,7 @@ private StringSegment GetName(string parameter)
var nameParameter = NameValueHeaderValue.Find(_parameters, parameter);
if (nameParameter != null)
{
string result;
string? result;
// filename*=utf-8'lang'%7FMyString
if (parameter.EndsWith("*", StringComparison.Ordinal))
{
Expand Down Expand Up @@ -375,7 +376,7 @@ private void SetName(StringSegment parameter, StringSegment value)
// Remove parameter
if (nameParameter != null)
{
_parameters.Remove(nameParameter);
_parameters!.Remove(nameParameter);
}
}
else
Expand Down Expand Up @@ -497,7 +498,7 @@ private unsafe string EncodeMime(StringSegment input)
}

// Attempt to decode MIME encoded strings
private bool TryDecodeMime(StringSegment input, out string output)
private bool TryDecodeMime(StringSegment input, [NotNullWhen(true)] out string? output)
{
Contract.Assert(input != null);

Expand Down Expand Up @@ -582,7 +583,7 @@ private static void HexEscape(StringBuilder builder, char c)

// Attempt to decode using RFC 5987 encoding.
// encoding'language'my%20string
private bool TryDecode5987(StringSegment input, out string output)
private bool TryDecode5987(StringSegment input, [NotNullWhen(true)] out string? output)
{
output = null;

Expand All @@ -593,7 +594,7 @@ private bool TryDecode5987(StringSegment input, out string output)
}

var decoded = new StringBuilder();
byte[] unescapedBytes = null;
byte[]? unescapedBytes = null;
try
{
var encoding = Encoding.GetEncoding(parts[0].ToString());
Expand Down
13 changes: 7 additions & 6 deletions src/Http/Headers/src/ContentRangeHeaderValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
Expand All @@ -12,7 +13,7 @@ namespace Microsoft.Net.Http.Headers
public class ContentRangeHeaderValue
{
private static readonly HttpHeaderParser<ContentRangeHeaderValue> Parser
= new GenericHeaderParser<ContentRangeHeaderValue>(false, GetContentRangeLength);
= new GenericHeaderParser<ContentRangeHeaderValue>(false, GetContentRangeLength!);

private StringSegment _unit;
private long? _from;
Expand Down Expand Up @@ -113,7 +114,7 @@ public long? Length
get { return _from != null; }
}

public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as ContentRangeHeaderValue;

Expand Down Expand Up @@ -176,16 +177,16 @@ public override string ToString()
public static ContentRangeHeaderValue Parse(StringSegment input)
{
var index = 0;
return Parser.ParseValue(input, ref index);
return Parser.ParseValue(input, ref index)!;
}

public static bool TryParse(StringSegment input, out ContentRangeHeaderValue parsedValue)
{
var index = 0;
return Parser.TryParseValue(input, ref index, out parsedValue);
return Parser.TryParseValue(input, ref index, out parsedValue!);
}

private static int GetContentRangeLength(StringSegment input, int startIndex, out ContentRangeHeaderValue parsedValue)
private static int GetContentRangeLength(StringSegment input, int startIndex, out ContentRangeHeaderValue? parsedValue)
{
Contract.Requires(startIndex >= 0);

Expand Down Expand Up @@ -351,7 +352,7 @@ private static bool TryCreateContentRange(
int toLength,
int lengthStartIndex,
int lengthLength,
out ContentRangeHeaderValue parsedValue)
[NotNullWhen(true)]out ContentRangeHeaderValue? parsedValue)
{
parsedValue = null;

Expand Down
6 changes: 3 additions & 3 deletions src/Http/Headers/src/CookieHeaderParser.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using Microsoft.Extensions.Primitives;

Expand All @@ -13,7 +14,7 @@ internal CookieHeaderParser(bool supportsMultipleValues)
{
}

public override bool TryParseValue(StringSegment value, ref int index, out CookieHeaderValue parsedValue)
public override bool TryParseValue(StringSegment value, ref int index, [MaybeNull] out CookieHeaderValue? parsedValue)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need both [MaybeNull] and ?? Should this also use [NotNullWhen(true)]?

Copy link
Contributor Author

@pranavkm pranavkm Jun 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing MaybeNull would be the way to go. This can return a null value while returning true, so NotNullWhen doesn't quite work.

{
parsedValue = null;

Expand Down Expand Up @@ -43,7 +44,7 @@ public override bool TryParseValue(StringSegment value, ref int index, out Cooki
return SupportsMultipleValues;
}

CookieHeaderValue result = null;
CookieHeaderValue? result = null;
if (!CookieHeaderValue.TryGetCookieLength(value, ref current, out result))
{
return false;
Expand All @@ -64,7 +65,6 @@ public override bool TryParseValue(StringSegment value, ref int index, out Cooki

private static int GetNextNonEmptyOrWhitespaceIndex(StringSegment input, int startIndex, bool skipEmptyValues, out bool separatorFound)
{
Contract.Requires(input != null);
Contract.Requires(startIndex <= input.Length); // it's OK if index == value.Length.

separatorFound = false;
Expand Down
Loading