64 return !
base.Task.IsCanceled;
154 return $"StreamId={StreamId}; Type={Type}; Flags={Flags}; PayloadLength={PayloadLength}";
518 Trace(
$"{_request.Content}",
"SendRequestBodyAsync");
556 Trace(
"Finished sending request body.",
"SendRequestBodyAsync");
563 Trace(
$"Failed to send request body: {value}",
"SendRequestBodyAsync");
620 Trace(
"Waiting to send request body content for 100-Continue.",
"WaitFor100ContinueAsync");
633 http2Stream.Trace(
"100-Continue timer expired.",
"WaitFor100ContinueAsync");
635 http2Stream._expect100ContinueWaiter?.TrySetResult(result:
true);
660 Trace(
$"Stream reset. Request={_requestCompletionState}, Response={_responseCompletionState}.",
"SendReset");
672 Trace(
$"Stream complete. Request={_requestCompletionState}, Response={_responseCompletionState}.",
"Complete");
761 Trace(
$"Invalid request pseudo-header ID {index}.",
"OnStaticIndexedHeader");
783 Trace(
$"Invalid request pseudo-header ID {index}.",
"OnStaticIndexedHeader");
812 Trace(
$"Status code is {statusCode}",
"OnStatus");
825 Trace(
"Received extra status header.",
"OnStatus");
833 Trace(
$"Status pseudo-header received in {_responseProtocolState} state.",
"OnStatus");
845 Trace(
"Received 100-Continue status.",
"OnStatus");
857 Trace(
$"Expecting 100 Continue but received final status {statusCode}.",
"OnStatus");
881 Trace(
"Received header before status.",
"OnHeader");
912 Trace(
"Invalid response pseudo-header '" +
Encoding.
ASCII.GetString(name) +
"'.",
"OnHeader");
969 Trace(
"Trailing headers received without endStream",
"OnHeadersComplete");
1317 while (
buffer.Length > 0)
1342 _creditWaiter.Amount =
buffer.Length;
1442 http2Stream._hasWaiter =
false;
1486 stream.Trace(
$"[FlowControl] InitialClientStreamWindowSize: {StreamWindowSize}, StreamWindowThreshold: {StreamWindowThreshold}, WindowScaleThresholdMultiplier: {WindowScaleThresholdMultiplier}",
".ctor");
1497 if (
stream.ExpectResponseData)
1544 stream.Trace(
$"[FlowControl] Updated Stream Window. StreamWindowSize: {StreamWindowSize}, StreamWindowThreshold: {StreamWindowThreshold}",
"AdjustWindowDynamic");
1548 stream.Trace(
$"[FlowControl] StreamWindowSize reached the configured maximum of {MaxStreamWindowSize}.",
"AdjustWindowDynamic");
1594 result._initialBurst = 4;
1632 connection.
Trace(
$"[FlowControl] Sending RTT PING with payload {_pingCounter}",
"OnDataOrHeadersReceived");
1646 connection.
Trace(
$"[FlowControl] Unexpected PING ACK in state {_state}",
"OnPingAckReceived");
1659 connection.
Trace(
$"[FlowControl] Unexpected RTT PING ACK payload {payload}, should be {_pingCounter}.",
"OnPingAckReceived");
1687 connection.
Trace(
$"[FlowControl] Updated MinRtt: {MinRtt.TotalMilliseconds} ms",
"RefreshRtt");
1826 Trace(
$"Initial connection-level WINDOW_UPDATE, windowUpdateAmount={value}",
"SetupAsync");
1945 return ValueTask.FromResult(result:
false);
1949 return ValueTask.FromResult(result:
true);
1973 Trace(
$"{"ActiveLength
"}={_outgoingBuffer.ActiveLength}",
"FlushOutgoingBytesAsync");
2023 Trace(
"HTTP/2 handshake failed. Server returned " + @
string,
"ReadFrameAsync");
2068 Trace(
$"Frame 0: {frameHeader}.",
"ProcessIncomingFramesAsync");
2093 Trace(
$"Frame {frameNum}: {frameHeader}.",
"ProcessIncomingFramesAsync");
2136 Trace(
"ProcessIncomingFramesAsync: " +
ex.Message,
"ProcessIncomingFramesAsync");
2162 Trace(
$"{frameHeader}",
"ProcessHeadersFrame");
2225 Trace(
$"{frameHeader}",
"ProcessAltSvcFrame");
2287 while (
source.Length > 0)
2300 if (
num2 >
int.MaxValue)
2378 Trace(
$"Received PING frame, content:{num} ack: {frameHeader.AckFlag}",
"ProcessPingFrame");
2400 Trace(
$"{frameHeader}. {"amount"}={num}",
"ProcessWindowUpdateFrame");
2576 thisRef.Trace(
"Started writing.",
"SendSettingsAckAsync");
2589 state.thisRef.Trace($
"Started writing. {"pingContent
"}={state.pingContent}",
"SendPingAsync");
2604 s.thisRef.Trace(s.streamId, $
"Started writing. {"errorCode
"}={s.errorCode}",
"SendRstStreamAsync");
2627 Trace(
"HeartBeat: " +
ex.Message,
"HeartBeat");
2646 Trace(
$"{"index"}={index}",
"WriteIndexedHeader");
2674 Trace(
$"{"name
"}={name}, {"values"}={string.Join(",
", values.ToArray())}",
"WriteLiteralHeader");
2702 Trace(
"value=" +
value,
"WriteLiteralHeaderValue");
2716 Trace(
$"{"Length
"}={bytes.Length}",
"WriteBytes");
2730 Trace(
"",
"WriteHeaderCollection");
2732 if (
headers.HeaderStore ==
null)
2786 Trace(
"",
"WriteHeaders");
2788 if (
request.HasHeaders &&
request.Headers.TransferEncodingChunked ==
true)
2790 request.Headers.TransferEncodingChunked =
false;
2889 int num = (item.Length - 1) / 16384 + 1;
2892 await PerformWriteAsync(
writeBytes, (
this,
http2Stream,
item,
request.Content ==
null,
mustFlush),
delegate((
Http2Connection thisRef,
Http2Stream http2Stream,
ReadOnlyMemory<byte> headerBytes,
bool endStream,
bool mustFlush)
s,
Memory<byte> writeBuffer)
2896 s.thisRef.Trace(s.http2Stream.StreamId, $
"Started writing. Total header bytes={s.headerBytes.Length}",
"SendHeadersAsync");
2898 s.thisRef.AddStream(
s.http2Stream);
2911 s.thisRef.Trace(
s.http2Stream.StreamId,
$"Wrote HEADERS frame. Length={item2.Length}, flags={frameFlags}",
"SendHeadersAsync");
2913 while (
item3.Length > 0)
2925 s.thisRef.Trace(
s.http2Stream.StreamId,
$"Wrote CONTINUATION frame. Length={item2.Length}, flags={frameFlags}",
"SendHeadersAsync");
2928 return s.mustFlush ||
s.endStream;
2972 s.thisRef.Trace(s.streamId, $
"Started writing. {"Length
"}={writeBuffer.Length}",
"SendStreamDataAsync");
2975 s.current.CopyTo(writeBuffer.Slice(9));
2993 s.thisRef.Trace(s.streamId,
"Started writing.",
"SendEndStreamAsync");
3006 s.thisRef.Trace(s.streamId, $
"Started writing. {"amount
"}={s.amount}",
"SendWindowUpdateAsync");
3082 Trace(
"",
"FinalTeardown");
3119 Trace(
$"{request}",
"SendAsync");
3123 bool mustFlush = request.Content !=
null && request.HasHeaders && request.Headers.ExpectContinue ==
true;
3125 bool flag = request.Content !=
null &&
request.Content.AllowDuplex;
3129 bool flag2 = requestBodyTask.IsCompleted || !flag;
3145 Trace(
$"Sending request content failed: {value}",
"SendAsync");
static uint ReadUInt32BigEndian(ReadOnlySpan< byte > source)
static int ReadInt32BigEndian(ReadOnlySpan< byte > source)
static void WriteUInt32BigEndian(Span< byte > destination, uint value)
static void WriteUInt16BigEndian(Span< byte > destination, ushort value)
static void WriteInt64BigEndian(Span< byte > destination, long value)
static ushort ReadUInt16BigEndian(ReadOnlySpan< byte > source)
static long ReadInt64BigEndian(ReadOnlySpan< byte > source)
static bool TryReadUInt16BigEndian(ReadOnlySpan< byte > source, out ushort value)
static void WriteInt32BigEndian(Span< byte > destination, int value)
bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
bool ICollection< KeyValuePair< TKey, TValue > >. Remove(KeyValuePair< TKey, TValue > keyValuePair)
void CopyTo(KeyValuePair< TKey, TValue >[] array, int index)
int Initialize(int capacity)
void Add(TKey key, TValue value)
static readonly long Frequency
static long GetTimestamp()
static void SuppressFinalize(object obj)
static void ValidateCopyToArguments(Stream destination, int bufferSize)
Task WriteAsync(byte[] buffer, int offset, int count)
Task< int > ReadAsync(byte[] buffer, int offset, int count)
static byte Min(byte val1, byte val2)
string GetCookieHeader(Uri uri)
static readonly Version Version20
static Exception CreateOperationCanceledException(Exception innerException, CancellationToken cancellationToken)
static void ThrowIfCancellationRequested(CancellationToken cancellationToken)
static void ProcessReceivedCookies(HttpResponseMessage response, CookieContainer cookieContainer)
ValueTask< int > RequestCreditAsync(int amount, CancellationToken cancellationToken)
void AdjustCredit(int amount)
void ResetForAwait(CancellationToken cancellationToken)
ValueTask< int > AsValueTask()
bool TrySetResult(int result)
static EmptyReadStream Instance
static bool DisableDynamicHttp2WindowSizing
static int MaxHttp2StreamWindowSize
static double Http2StreamWindowScaleThresholdMultiplier
void Decode(ReadOnlySpan< byte > data, bool endHeaders, IHttpHeadersHandler handler)
static bool EncodeIndexedHeaderField(int index, Span< byte > destination, out int bytesWritten)
static bool EncodeStringLiterals(ReadOnlySpan< string > values, string separator, Encoding valueEncoding, Span< byte > destination, out int bytesWritten)
static bool EncodeLiteralHeaderFieldWithoutIndexing(int index, string value, Encoding valueEncoding, Span< byte > destination, out int bytesWritten)
static bool EncodeStringLiteral(string value, Encoding valueEncoding, Span< byte > destination, out int bytesWritten)
static bool EncodeLiteralHeaderFieldWithoutIndexingNewName(string name, ReadOnlySpan< string > values, string separator, Encoding valueEncoding, Span< byte > destination, out int bytesWritten)
readonly HttpResponseMessage _responseMessage
override void Write(ReadOnlySpan< byte > buffer)
override void CopyTo(Stream destination, int bufferSize)
override void Dispose(bool disposing)
Http2ReadStream(Http2Stream http2Stream)
override ValueTask WriteAsync(ReadOnlyMemory< byte > destination, CancellationToken cancellationToken)
override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
override ValueTask< int > ReadAsync(Memory< byte > destination, CancellationToken cancellationToken)
override int Read(Span< byte > destination)
override int Read(Span< byte > buffer)
override ValueTask WriteAsync(ReadOnlyMemory< byte > buffer, CancellationToken cancellationToken)
override Task FlushAsync(CancellationToken cancellationToken)
override ValueTask< int > ReadAsync(Memory< byte > buffer, CancellationToken cancellationToken)
override void Dispose(bool disposing)
Http2WriteStream(Http2Stream http2Stream)
readonly Http2Connection _connection
CancellationTokenRegistration RegisterRequestBodyCancellation(CancellationToken cancellationToken)
static readonly(HeaderDescriptor descriptor, byte[] value)[] s_hpackStaticHeaderTable
CancellationTokenRegistration _waitSourceCancellation
Http2Connection Connection
void MoveTrailersToResponseMessage(HttpResponseMessage responseMessage)
@ ExpectingTrailingHeaders
@ ExpectingIgnoredHeaders
void OnReset(Exception resetException, Http2ProtocolErrorCode? resetStreamErrorCode=null, bool canRetry=false)
System.Net.MultiArrayBuffer _responseBuffer
void AdjustHeaderBudget(int amount)
readonly HttpRequestMessage _request
HttpResponseMessage GetAndClearResponse()
void CopyTo(HttpResponseMessage responseMessage, Stream destination, int bufferSize)
async Task ReadResponseHeadersAsync(CancellationToken cancellationToken)
bool int bytesRead TryReadFromBuffer(Span< byte > buffer, bool partOfSyncRead=false)
static ReadOnlySpan< byte > StatusHeaderName
bool bool isEmptyResponse TryEnsureHeaders()
Http2StreamWindowManager _windowManager
int _headerBudgetRemaining
ManualResetValueTaskSourceCore< bool > _waitSource
ResponseProtocolState _responseProtocolState
async ValueTask< bool > WaitFor100ContinueAsync(CancellationToken cancellationToken)
async ValueTask SendDataAsync(ReadOnlyMemory< byte > buffer, CancellationToken cancellationToken)
void Initialize(int streamId, int initialWindowSize)
void OnWindowUpdate(int amount)
bool _requestBodyAbandoned
HttpResponseHeaders _trailers
void OnResponseData(ReadOnlySpan< byte > buffer, bool endStream)
async ValueTask< int > ReadDataAsync(Memory< byte > buffer, HttpResponseMessage responseMessage, CancellationToken cancellationToken)
async Task CopyToAsync(HttpResponseMessage responseMessage, Stream destination, int bufferSize, CancellationToken cancellationToken)
readonly CancellationTokenSource _requestBodyCancellationSource
Http2Stream(HttpRequestMessage request, Http2Connection connection)
void Trace(string message, [CallerMemberName] string memberName=null)
bool bool sendReset CancelResponseBody()
int ReadData(Span< byte > buffer, HttpResponseMessage responseMessage)
async Task SendRequestBodyAsync(CancellationToken cancellationToken)
readonly TaskCompletionSource< bool > _expect100ContinueWaiter
static readonly int[] s_hpackStaticStatusCodeTable
ValueTask WaitForDataAsync(CancellationToken cancellationToken)
StreamCompletionState _requestCompletionState
void OnHeader(ReadOnlySpan< byte > name, ReadOnlySpan< byte > value)
void OnStatus(int statusCode)
StreamCompletionState _responseCompletionState
Exception _resetException
void OnHeadersComplete(bool endStream)
CreditWaiter _creditWaiter
void OnHeader(HeaderDescriptor descriptor, ReadOnlySpan< byte > value)
readonly object _creditSyncObject
HttpResponseMessage _response
void CheckResponseBodyState()
static readonly NopHeadersHandler Instance
WriteQueueEntry(int writeBytes, T state, Func< T, Memory< byte >, bool > writeAction, CancellationToken cancellationToken)
WriteQueueEntry(int writeBytes, CancellationToken cancellationToken)
bool InvokeWriteAction(Memory< byte > writeBuffer)
bool TryDisableCancellation()
readonly Func< T, Memory< byte >, bool > _writeAction
override bool InvokeWriteAction(Memory< byte > writeBuffer)
readonly CancellationTokenRegistration _cancellationRegistration
TaskCompletionSource _shutdownWaiter
void WriteLiteralHeaderValues(ReadOnlySpan< string > values, string separator, Encoding valueEncoding, ref System.Net.ArrayBuffer headerBuffer)
void Trace(int streamId, string message, [CallerMemberName] string memberName=null)
static ReadOnlyMemory< byte > ReadOnlyMemory< byte > rest SplitBuffer(ReadOnlyMemory< byte > buffer, int maxSize)
Task FlushAsync(CancellationToken cancellationToken)
static void ThrowProtocolError(Http2ProtocolErrorCode errorCode)
Task SendWindowUpdateAsync(int streamId, int amount)
Task SendSettingsAckAsync()
void WriteIndexedHeader(int index, ref System.Net.ArrayBuffer headerBuffer)
long _keepAlivePingTimeoutTimestamp
override long GetIdleTicks(long nowTicks)
readonly long _keepAlivePingTimeout
void WriteHeaders(HttpRequestMessage request, ref System.Net.ArrayBuffer headerBuffer)
void SignalShutdownWaiter()
RttEstimator _rttEstimator
static ReadOnlyMemory< byte > first
void ExtendWindow(int amount)
readonly long _keepAlivePingDelay
async ValueTask< Http2Stream > SendHeadersAsync(HttpRequestMessage request, CancellationToken cancellationToken, bool mustFlush)
void ProcessPingFrame(FrameHeader frameHeader)
void ProcessRstStreamFrame(FrameHeader frameHeader)
ValueTask WaitForShutdownAsync()
void WriteHeaderCollection(HttpRequestMessage request, HttpHeaders headers, ref System.Net.ArrayBuffer headerBuffer)
async Task ProcessIncomingFramesAsync()
TaskCompletionSource< bool > _availableStreamsWaiter
void ProcessSettingsFrame(FrameHeader frameHeader, bool initialFrame=false)
System.Net.ArrayBuffer _incomingBuffer
void WriteLiteralHeader(string name, ReadOnlySpan< string > values, Encoding valueEncoding, ref System.Net.ArrayBuffer headerBuffer)
void WriteLiteralHeaderValue(string value, Encoding valueEncoding, ref System.Net.ArrayBuffer headerBuffer)
readonly Channel< WriteQueueEntry > _writeChannel
bool _lastPendingWriterShouldFlush
void RefreshPingTimestamp()
uint _maxConcurrentStreams
ReadOnlySpan< byte > GetFrameData(ReadOnlySpan< byte > frameData, bool hasPad, bool hasPriority)
Task SendEndStreamAsync(int streamId)
System.Net.ArrayBuffer _outgoingBuffer
volatile KeepAliveState _keepAliveState
void ProcessPriorityFrame(FrameHeader frameHeader)
override string ToString()
static readonly byte[] s_http2ConnectionPreface
Task PerformWriteAsync< T >(int writeBytes, T state, Func< T, Memory< byte >, bool > writeAction, CancellationToken cancellationToken=default(CancellationToken))
async Task SendStreamDataAsync(int streamId, ReadOnlyMemory< byte > buffer, bool finalFlush, CancellationToken cancellationToken)
readonly HttpConnectionPool _pool
long _keepAlivePingPayload
async ValueTask SetupAsync()
Task SendRstStreamAsync(int streamId, Http2ProtocolErrorCode errorCode)
void SignalAvailableStreamsWaiter(bool result)
ValueTask< bool > WaitForAvailableStreamsAsync()
override void Trace(string message, [CallerMemberName] string memberName=null)
static readonly UnboundedChannelOptions s_channelOptions
async Task ProcessOutgoingFramesAsync()
void Abort(Exception abortException)
void ProcessAltSvcFrame(FrameHeader frameHeader)
readonly CreditManager _connectionWindow
readonly HPackDecoder _hpackDecoder
async Task FlushOutgoingBytesAsync()
static string[] t_headerValues
void ProcessGoAwayFrame(FrameHeader frameHeader)
static void ThrowRetry(string message, Exception innerException=null)
void RemoveStream(Http2Stream http2Stream)
int _initialServerStreamWindowSize
async ValueTask ProcessHeadersFrame(FrameHeader frameHeader)
long _nextPingRequestTimestamp
static void ThrowProtocolError()
void ChangeMaxConcurrentStreams(uint newValue)
static void ThrowRequestAborted(Exception innerException=null)
readonly Dictionary< int, Http2Stream > _httpStreams
void WriteBytes(ReadOnlySpan< byte > bytes, ref System.Net.ArrayBuffer headerBuffer)
Exception _abortException
async Task< HttpResponseMessage > SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken)
int _markedByTelemetryStatus
void ProcessDataFrame(FrameHeader frameHeader)
static Exception GetRequestAbortedException(Exception innerException=null)
bool _expectingSettingsAck
Http2Connection(HttpConnectionPool pool, Stream stream)
async ValueTask< FrameHeader > ReadFrameAsync(bool initialFrame=false)
Http2Stream GetStream(int streamId)
void WriteIndexedHeader(int index, string value, ref System.Net.ArrayBuffer headerBuffer)
void ProcessWindowUpdateFrame(FrameHeader frameHeader)
void AddStream(Http2Stream http2Stream)
void ProcessPingAck(long payload)
readonly HttpKeepAlivePingPolicy _keepAlivePingPolicy
Task SendPingAsync(long pingContent, bool isAck=false)
void ChangeInitialWindowSize(int newSize)
string GetResponseHeaderValueWithCaching(HeaderDescriptor descriptor, ReadOnlySpan< byte > value, Encoding valueEncoding)
void TraceConnection(Stream stream)
void LogExceptions(Task task)
static int ParseStatusCode(ReadOnlySpan< byte > value)
readonly byte[] _http2EncodedAuthorityHostHeader
byte[] Http2AltSvcOriginUri
void HandleAltSvc(IEnumerable< string > altSvcHeaderValues, TimeSpan? responseAge)
HttpConnectionSettings Settings
HeaderEncodingSelector< HttpRequestMessage > _responseHeaderEncodingSelector
HeaderEncodingSelector< HttpRequestMessage > _requestHeaderEncodingSelector
TimeSpan _expect100ContinueTimeout
int _initialHttp2StreamWindowSize
HttpKeepAlivePingPolicy _keepAlivePingPolicy
TimeSpan _keepAlivePingTimeout
TimeSpan _keepAlivePingDelay
CookieContainer _cookieContainer
int _maxResponseHeadersLength
ValueTask InternalCopyToAsync(Stream stream, TransportContext context, CancellationToken cancellationToken)
HttpContentHeaders Headers
static HttpMethod Normalize(HttpMethod method)
HttpRequestHeaders Headers
HttpResponseHeaders Headers
HttpStatusCode StatusCode
static readonly HttpTelemetry Log
static readonly System.Net.NetEventSource Log
static Exception SetCurrentStackTrace(Exception source)
static string net_http_invalid_response
static string net_http_invalid_response_missing_frame
static string net_http_response_headers_exceeded_length
static string net_http_content_readonly_stream
static string Format(string resourceFormat, object p1)
static string net_http_client_execution_error
static string net_http_http2_connection_not_established
static string net_http_server_shutdown
static string net_http_invalid_response_premature_eof_bytecount
static string net_http_request_aborted
static string net_http_invalid_response_multiple_status_codes
static string net_http_invalid_response_header_name
static string net_http_invalid_response_pseudo_header_in_trailer
static int Exchange(ref int location1, int value)
static long Read(ref long location)
static int Increment(ref int location)
new ConfiguredTaskAwaitable< TResult > ConfigureAwait(bool continueOnCapturedContext)
static Task FromException(Exception exception)
static Task FromCanceled(CancellationToken cancellationToken)
static Task CompletedTask
new TaskAwaiter< TResult > GetAwaiter()
static Task< Task > WhenAny(params Task[] tasks)
static readonly TimeSpan InfiniteTimeSpan
void OnStaticIndexedHeader(int index)
void OnHeader(ReadOnlySpan< byte > name, ReadOnlySpan< byte > value)
void OnCompleted(Action< object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
ValueTaskSourceStatus GetStatus(short token)
void GetResult(short token)
ValueTaskSourceOnCompletedFlags
@ RunContinuationsAsynchronously
static int MaxStreamWindowSize
void AjdustWindowStatic(int bytesConsumed, Http2Stream stream)
static readonly double StopWatchToTimesSpan
Http2StreamWindowManager(Http2Connection connection, Http2Stream stream)
static TimeSpan StopwatchTicksToTimeSpan(long stopwatchTicks)
void AdjustWindow(int bytesConsumed, Http2Stream stream)
static bool WindowScalingEnabled
static double WindowScaleThresholdMultiplier
void AdjustWindowDynamic(int bytesConsumed, Http2Stream stream)
int StreamWindowThreshold
static RttEstimator Create()
@ TerminatingMayReceivePingAck
void OnInitialSettingsAckReceived(Http2Connection connection)
void RefreshRtt(Http2Connection connection)
static readonly long PingIntervalInTicks
void OnPingAckReceived(long payload, Http2Connection connection)
void OnDataOrHeadersReceived(Http2Connection connection)
void OnInitialSettingsSent()
void SetResult(TResult result)
ValueTaskSourceStatus GetStatus(short token)
TResult GetResult(short token)
void OnCompleted(Action< object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
static ValueTask FromCanceled(CancellationToken cancellationToken)
ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)
static ValueTask FromException(Exception exception)
static TimeSpan FromSeconds(double value)
static readonly TimeSpan Zero