src/Microsoft.Azure.Relay/TokenRenewer.cs (90 lines of code) (raw):
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.Relay
{
using System;
using System.Threading;
using System.Threading.Tasks;
class TokenEventArgs : EventArgs
{
public SecurityToken Token { get; internal set; }
public Exception Exception { get; internal set; }
}
class TokenRenewer
{
readonly Timer renewTimer;
readonly HybridConnectionListener listener;
readonly string appliesTo;
readonly TimeSpan tokenValidFor;
public TokenRenewer(HybridConnectionListener listener, string appliesTo, TimeSpan tokenValidFor)
{
Fx.Assert(listener != null, "listener is required");
Fx.Assert(!string.IsNullOrEmpty(appliesTo), "appliesTo is required");
this.listener = listener;
this.appliesTo = appliesTo;
this.tokenValidFor = tokenValidFor;
this.renewTimer = new Timer(s => OnRenewTimer(s), this, Timeout.Infinite, Timeout.Infinite);
}
public event EventHandler<TokenEventArgs> TokenRenewed;
public event EventHandler<TokenEventArgs> TokenRenewException;
object ThisLock
{
get { return this.renewTimer; }
}
public Task<SecurityToken> GetTokenAsync()
{
return this.GetTokenAsync(false);
}
async Task<SecurityToken> GetTokenAsync(bool raiseTokenRenewedEvent)
{
try
{
RelayEventSource.Log.GetTokenStart(this.listener);
var token = await this.listener.TokenProvider.GetTokenAsync(this.appliesTo, this.tokenValidFor).ConfigureAwait(false);
RelayEventSource.Log.GetTokenStop(this.listener, token.ExpiresAtUtc);
if (raiseTokenRenewedEvent)
{
this.TokenRenewed?.Invoke(this, new TokenEventArgs { Token = token });
}
this.ScheduleRenewTimer(token);
return token;
}
catch (Exception e) when (!Fx.IsFatal(e))
{
this.OnTokenRenewException(e);
throw;
}
}
public void Close()
{
this.renewTimer.Change(Timeout.Infinite, Timeout.Infinite);
}
static async void OnRenewTimer(object state)
{
var thisPtr = (TokenRenewer)state;
try
{
await thisPtr.GetTokenAsync(true).ConfigureAwait(false);
}
catch (Exception exception) when (!Fx.IsFatal(exception))
{
RelayEventSource.Log.HandledExceptionAsWarning(thisPtr.listener, exception);
}
}
void ScheduleRenewTimer(SecurityToken token)
{
TimeSpan interval = token.ExpiresAtUtc.Subtract(DateTime.UtcNow);
if (interval < TimeSpan.Zero)
{
// TODO: RelayEventSource.Log.WcfEventWarning(Diagnostics.TraceCode.Security, this.traceSource, "Not renewing since " + interval + " < TimeSpan.Zero!");
return;
}
// TokenProvider won't return a token which is within 5min of expiring so we don't have to pad here.
interval = interval < RelayConstants.ClientMinimumTokenRefreshInterval ? RelayConstants.ClientMinimumTokenRefreshInterval : interval;
interval = TimeoutHelper.Min(interval, TimeoutHelper.MaxWait);
RelayEventSource.Log.TokenRenewScheduled(interval, this.listener);
this.renewTimer.Change(interval, Timeout.InfiniteTimeSpan);
}
void OnTokenRenewException(Exception exception)
{
this.TokenRenewException?.Invoke(this, new TokenEventArgs { Exception = exception });
}
}
}