aspnetcore/src/Microsoft.Net.Http.Headers/RangeConditionHeaderValue.cs

166 lines
5.0 KiB
C#

// 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;
using System.Diagnostics.Contracts;
namespace Microsoft.Net.Http.Headers
{
public class RangeConditionHeaderValue
{
private static readonly HttpHeaderParser<RangeConditionHeaderValue> Parser
= new GenericHeaderParser<RangeConditionHeaderValue>(false, GetRangeConditionLength);
private DateTimeOffset? _lastModified;
private EntityTagHeaderValue _entityTag;
private RangeConditionHeaderValue()
{
// Used by the parser to create a new instance of this type.
}
public RangeConditionHeaderValue(DateTimeOffset lastModified)
{
_lastModified = lastModified;
}
public RangeConditionHeaderValue(EntityTagHeaderValue entityTag)
{
if (entityTag == null)
{
throw new ArgumentNullException(nameof(entityTag));
}
_entityTag = entityTag;
}
public RangeConditionHeaderValue(string entityTag)
: this(new EntityTagHeaderValue(entityTag))
{
}
public DateTimeOffset? LastModified
{
get { return _lastModified; }
}
public EntityTagHeaderValue EntityTag
{
get { return _entityTag; }
}
public override string ToString()
{
if (_entityTag == null)
{
return HttpRuleParser.DateToString(_lastModified.Value);
}
return _entityTag.ToString();
}
public override bool Equals(object obj)
{
var other = obj as RangeConditionHeaderValue;
if (other == null)
{
return false;
}
if (_entityTag == null)
{
return (other._lastModified != null) && (_lastModified.Value == other._lastModified.Value);
}
return _entityTag.Equals(other._entityTag);
}
public override int GetHashCode()
{
if (_entityTag == null)
{
return _lastModified.Value.GetHashCode();
}
return _entityTag.GetHashCode();
}
public static RangeConditionHeaderValue Parse(string input)
{
var index = 0;
return Parser.ParseValue(input, ref index);
}
public static bool TryParse(string input, out RangeConditionHeaderValue parsedValue)
{
var index = 0;
return Parser.TryParseValue(input, ref index, out parsedValue);
}
private static int GetRangeConditionLength(string input, int startIndex, out RangeConditionHeaderValue parsedValue)
{
Contract.Requires(startIndex >= 0);
parsedValue = null;
// Make sure we have at least 2 characters
if (string.IsNullOrEmpty(input) || (startIndex + 1 >= input.Length))
{
return 0;
}
var current = startIndex;
// Caller must remove leading whitespaces.
DateTimeOffset date = DateTimeOffset.MinValue;
EntityTagHeaderValue entityTag = null;
// Entity tags are quoted strings optionally preceded by "W/". By looking at the first two character we
// can determine whether the string is en entity tag or a date.
var firstChar = input[current];
var secondChar = input[current + 1];
if ((firstChar == '\"') || (((firstChar == 'w') || (firstChar == 'W')) && (secondChar == '/')))
{
// trailing whitespaces are removed by GetEntityTagLength()
var entityTagLength = EntityTagHeaderValue.GetEntityTagLength(input, current, out entityTag);
if (entityTagLength == 0)
{
return 0;
}
current = current + entityTagLength;
// RangeConditionHeaderValue only allows 1 value. There must be no delimiter/other chars after an
// entity tag.
if (current != input.Length)
{
return 0;
}
}
else
{
if (!HttpRuleParser.TryStringToDate(input.Substring(current), out date))
{
return 0;
}
// If we got a valid date, then the parser consumed the whole string (incl. trailing whitespaces).
current = input.Length;
}
parsedValue = new RangeConditionHeaderValue();
if (entityTag == null)
{
parsedValue._lastModified = date;
}
else
{
parsedValue._entityTag = entityTag;
}
return current - startIndex;
}
}
}