public void RemoveTask(RevaleeTask task) { if (task == null) { throw new ArgumentNullException("task"); } if (_EseInstance == null) { throw new InvalidOperationException("Storage provider has not been opened."); } EseConnection connection = _ConnectionPool.OpenConnection(); try { using (Table table = connection.GetTable(_TableNameCallbacks, OpenTableGrbit.Updatable)) { using (var transaction = new Transaction(connection)) { Api.JetSetCurrentIndex(connection, table, null); Api.MakeKey(connection, table, task.CallbackId, MakeKeyGrbit.NewKey); if (Api.TrySeek(connection, table, SeekGrbit.SeekEQ)) { Api.JetDelete(connection, table); transaction.Commit(CommitTransactionGrbit.None); } } } } finally { _ConnectionPool.CloseConnection(connection); } }
public RevaleeTask GetTask(Guid callbackId) { if (_EseInstance == null) { throw new InvalidOperationException("Storage provider has not been opened."); } EseConnection connection = _ConnectionPool.OpenConnection(); try { using (Table table = connection.GetTable(_TableNameCallbacks, OpenTableGrbit.Updatable)) { IDictionary <string, JET_COLUMNID> columnIds = connection.GetSchema(_TableNameCallbacks); Api.JetSetCurrentIndex(connection, table, null); Api.MakeKey(connection, table, callbackId, MakeKeyGrbit.NewKey); if (Api.TrySeek(connection, table, SeekGrbit.SeekEQ)) { Guid? storedCallbackId = Api.RetrieveColumnAsGuid(connection, table, columnIds[_ColumnNameCallbackId]); DateTime?createdTime = Api.RetrieveColumnAsDateTime(connection, table, columnIds[_ColumnNameCreatedTime]); DateTime?callbackTime = Api.RetrieveColumnAsDateTime(connection, table, columnIds[_ColumnNameCallbackTime]); string callbackUrl = Api.RetrieveColumnAsString(connection, table, columnIds[_ColumnNameCallbackUrl]); int? attemptsRemainingColumn = Api.RetrieveColumnAsInt32(connection, table, columnIds[_ColumnNameAttemptsRemaining]); string authorizationCipher = Api.RetrieveColumnAsString(connection, table, columnIds[_ColumnNameAuthorizationCipher]); Uri callbackUri = null; if (callbackTime.HasValue && Uri.TryCreate(callbackUrl, UriKind.Absolute, out callbackUri) && createdTime.HasValue && storedCallbackId.HasValue && attemptsRemainingColumn.HasValue) { RevaleeTask revivedTask = RevaleeTask.Revive( DateTime.SpecifyKind(callbackTime.Value, DateTimeKind.Utc), callbackUri, DateTime.SpecifyKind(createdTime.Value, DateTimeKind.Utc), storedCallbackId.Value, attemptsRemainingColumn.Value, string.IsNullOrEmpty(authorizationCipher) ? null : authorizationCipher); return(revivedTask); } } } return(null); } finally { _ConnectionPool.CloseConnection(connection); } }
public void AddTask(RevaleeTask task) { if (task == null) { throw new ArgumentNullException("task"); } lock (_SyncRoot) { _PersistenceProvider.AddTask(task); } _AwaitingTaskCollection.AddOrReplace(task.CallbackId, task, task.CallbackTime); Supervisor.Telemetry.IncrementAwaitingTasksValue(); ResetTaskAlarm(); }
public void AddTask(RevaleeTask task) { if (task == null) { throw new ArgumentNullException("task"); } if (_EseInstance == null) { throw new InvalidOperationException("Storage provider has not been opened."); } EseConnection connection = _ConnectionPool.OpenConnection(); try { using (Table table = connection.GetTable(_TableNameCallbacks, OpenTableGrbit.Updatable)) { IDictionary<string, JET_COLUMNID> columnIds = connection.GetSchema(_TableNameCallbacks); using (var transaction = new Transaction(connection)) { using (var update = new Update(connection, table, JET_prep.Insert)) { Api.SetColumn(connection, table, columnIds[_ColumnNameCallbackId], task.CallbackId); Api.SetColumn(connection, table, columnIds[_ColumnNameCreatedTime], EnforceMinimumDateTime(task.CreatedTime)); Api.SetColumn(connection, table, columnIds[_ColumnNameCallbackTime], EnforceMinimumDateTime(task.CallbackTime)); Api.SetColumn(connection, table, columnIds[_ColumnNameCallbackUrl], task.CallbackUrl.OriginalString, Encoding.Unicode); Api.SetColumn(connection, table, columnIds[_ColumnNameAttemptsRemaining], task.AttemptsRemaining); if (task.AuthorizationCipher != null) { Api.SetColumn(connection, table, columnIds[_ColumnNameAuthorizationCipher], task.AuthorizationCipher, Encoding.Unicode); } update.Save(); } transaction.Commit(CommitTransactionGrbit.None); } } } finally { _ConnectionPool.CloseConnection(connection); } }
public void AddTask(RevaleeTask task) { if (task == null) { throw new ArgumentNullException("task"); } if (_EseInstance == null) { throw new InvalidOperationException("Storage provider has not been opened."); } EseConnection connection = _ConnectionPool.OpenConnection(); try { using (Table table = connection.GetTable(_TableNameCallbacks, OpenTableGrbit.Updatable)) { IDictionary <string, JET_COLUMNID> columnIds = connection.GetSchema(_TableNameCallbacks); using (var transaction = new Transaction(connection)) { using (var update = new Update(connection, table, JET_prep.Insert)) { Api.SetColumn(connection, table, columnIds[_ColumnNameCallbackId], task.CallbackId); Api.SetColumn(connection, table, columnIds[_ColumnNameCreatedTime], EnforceMinimumDateTime(task.CreatedTime)); Api.SetColumn(connection, table, columnIds[_ColumnNameCallbackTime], EnforceMinimumDateTime(task.CallbackTime)); Api.SetColumn(connection, table, columnIds[_ColumnNameCallbackUrl], task.CallbackUrl.OriginalString, Encoding.Unicode); Api.SetColumn(connection, table, columnIds[_ColumnNameAttemptsRemaining], task.AttemptsRemaining); if (task.AuthorizationCipher != null) { Api.SetColumn(connection, table, columnIds[_ColumnNameAuthorizationCipher], task.AuthorizationCipher, Encoding.Unicode); } update.Save(); } transaction.Commit(CommitTransactionGrbit.None); } } } finally { _ConnectionPool.CloseConnection(connection); } }
private static void FinalizeAcceptedResponse(HttpListenerRequest request, HttpListenerResponse response, RevaleeTask task) { string remoteAddress = request.RemoteEndPoint.Address.ToString(); try { response.StatusCode = 200; response.StatusDescription = "OK"; byte[] confirmation_number = Encoding.UTF8.GetBytes(task.CallbackId.ToString()); response.ContentLength64 = confirmation_number.LongLength; response.OutputStream.Write(confirmation_number, 0, confirmation_number.Length); } finally { response.Close(); } Supervisor.Telemetry.RecordAcceptedRequest(); Supervisor.LogEvent(string.Format("Request accepted for {0} @ {1:d} {1:t} from {2}. [{3}]", task.CallbackUrl.OriginalString, task.CallbackTime, remoteAddress, task.CallbackId), TraceEventType.Verbose); }
public void CancelTask(RevaleeTask task) { if (task == null) { throw new ArgumentNullException("task"); } RevaleeTask awaitingTask; if (_AwaitingTaskCollection.TryGetValue(task.CallbackId, out awaitingTask)) { if (awaitingTask.CallbackUrl.ToString().StartsWith(task.CallbackUrl.ToString(), StringComparison.OrdinalIgnoreCase)) { RevaleeTask removedTask; bool wasTaskRemoved = _AwaitingTaskCollection.TryRemove(awaitingTask.CallbackId, out removedTask); lock (_SyncRoot) { _PersistenceProvider.RemoveTask(awaitingTask); } if (wasTaskRemoved) { Supervisor.Telemetry.DecrementAwaitingTasksValue(); } } } else { RevaleeTask storedTask = RetrieveTask(task.CallbackId); if (storedTask != null) { if (storedTask.CallbackUrl.ToString().StartsWith(task.CallbackUrl.ToString(), StringComparison.OrdinalIgnoreCase)) { lock (_SyncRoot) { _PersistenceProvider.RemoveTask(storedTask); } } } } }
public void RemoveTask(RevaleeTask Task) { return; }
private static string FormatFormPayload(RevaleeTask task) { var payload = new StringBuilder(); payload.Append("CallbackId="); payload.Append(task.CallbackId.ToString("D")); payload.Append("&CallbackTime="); payload.Append(task.CallbackTime.ToString(@"yyyy-MM-dd\THH:mm:ss.fff\Z", CultureInfo.InvariantCulture)); payload.Append("&CurrentServiceTime="); payload.Append(DateTime.UtcNow.ToString(@"yyyy-MM-dd\THH:mm:ss.fff\Z", CultureInfo.InvariantCulture)); return payload.ToString(); }
private static void CompleteRetryableTask(RevaleeTask task, Exception exception) { if (task.AttemptsRemaining > 0) { RetryTask(task); } else { // Out of attempts CompleteFailedTask(task, exception); } }
private static void CompleteFailedTask(RevaleeTask task, Exception exception) { Supervisor.State.CompleteTask(task); Supervisor.LogException(exception, TraceEventType.Error, task.CallbackUrl.OriginalString); Supervisor.Telemetry.RecordWaitTime(CalculateWaitTime(DateTime.UtcNow, task)); Supervisor.Telemetry.RecordFailedCallback(); }
private static void CompleteSuccessfulTask(RevaleeTask task) { Supervisor.State.CompleteTask(task); Supervisor.LogEvent(string.Format("Successful callback to {0}. [{1}]", task.CallbackUrl.OriginalString, task.CallbackId), TraceEventType.Verbose); Supervisor.Telemetry.RecordWaitTime(CalculateWaitTime(DateTime.UtcNow, task)); Supervisor.Telemetry.RecordSuccessfulCallback(); RetryHeuristics.OnSuccess(task.CallbackUrl); }
private void ProcessWebResponse(RevaleeTask task, HttpWebResponse response) { switch (DetermineResult(response.StatusCode)) { case CallbackResult.Success: CompleteSuccessfulTask(task); break; case CallbackResult.NonretryableError: CompleteFailedTask(task, response.StatusCode); break; default: CompleteRetryableTask(task, response.StatusCode); break; } }
public void Process(HttpListenerRequest request, HttpListenerResponse response) { try { if (request.HttpMethod != "PUT") { FinalizeRejectedResponse(request, response, 405, "Method Not Supported", null); return; } if (request.QueryString.Count < 2) { FinalizeRejectedResponse(request, response, 400, "Bad Request", null); return; } Uri url = RetrieveUrlParameter(request); if (url == null) { FinalizeRejectedResponse(request, response, 400, "Bad Request", null); return; } DateTime? time = RetrieveTimeParameter(request); if (!time.HasValue) { FinalizeRejectedResponse(request, response, 400, "Bad Request", url); return; } if (UrlContainsDangerousMarkup(url)) { FinalizeRejectedResponse(request, response, 400, "Bad Request", url); return; } RevaleeUrlAuthorization authorization = Supervisor.Configuration.AuthorizedTargets.Match(url); if (authorization == null) { // Keep all authorization errors homogeneous from the client perspective RevaleeUrlAuthorization.ObfuscateExecutionTime(); FinalizeRejectedResponse(request, response, 401, "Unauthorized", url); return; } if (!authorization.IsAuthorizedRequestSource(request.RemoteEndPoint.Address)) { // Keep all authorization errors homogeneous from the client perspective RevaleeUrlAuthorization.ObfuscateExecutionTime(); FinalizeRejectedResponse(request, response, 401, "Unauthorized", url); return; } if (Supervisor.Work.IsOverloaded) { FinalizeRejectedResponse(request, response, 503, "Service Unavailable", url); return; } string authorizationCipher = RetrieveAuthorizationHeader(request); RevaleeTask newTask = new RevaleeTask(time.Value, url, authorization.RetryCount, authorizationCipher); Supervisor.State.AddTask(newTask); FinalizeAcceptedResponse(request, response, newTask); return; } catch (HttpListenerException hlex) { Supervisor.LogException(hlex, TraceEventType.Error, request.RawUrl); FinalizeRejectedResponse(request, response, 500, "Error Occurred", null); return; } }
private static void CompleteFailedTask(RevaleeTask task, string message) { Supervisor.State.CompleteTask(task); Supervisor.LogEvent(message, TraceEventType.Error); Supervisor.Telemetry.RecordWaitTime(CalculateWaitTime(DateTime.UtcNow, task)); Supervisor.Telemetry.RecordFailedCallback(); }
private static string FormatWebExceptionMessage(RevaleeTask task, WebException webException) { return string.Format("Unsuccessful callback to {0} with status '{1}'. [{2}]", task.CallbackUrl.OriginalString, webException.Status.ToString(), task.CallbackId); }
private static string FormatHttpErrorMessage(RevaleeTask task, HttpStatusCode statusCode) { return string.Format("Unsuccessful callback to {0} due to HTTP status code {1}. [{2}]", task.CallbackUrl.OriginalString, (int)statusCode, task.CallbackId); }
private static string FormatGenericExceptionMessage(RevaleeTask task, Exception exception) { return string.Format("{0} [{1}]", exception.Message, task.CallbackUrl.OriginalString); }
private static void CompleteRetryableTask(RevaleeTask task, string message) { if (task.AttemptsRemaining > 0) { Supervisor.LogEvent(message, TraceEventType.Warning); RetryTask(task); } else { // Out of attempts CompleteFailedTask(task, message); } }
public IEnumerable <RevaleeTask> ListAllTasks() { if (_EseInstance == null) { throw new InvalidOperationException("Storage provider has not been opened."); } EseConnection connection = this._ConnectionPool.OpenConnection(); try { using (Table table = connection.GetTable(_TableNameCallbacks, OpenTableGrbit.DenyWrite | OpenTableGrbit.Preread | OpenTableGrbit.ReadOnly | OpenTableGrbit.Sequential)) { IDictionary <string, JET_COLUMNID> columnIds = connection.GetSchema(_TableNameCallbacks); if (Api.TryMoveFirst(connection, table)) { JET_SESID jetSession = connection; JET_TABLEID jetTable = table; JET_COLUMNID jetColumnCallbackId = columnIds[_ColumnNameCallbackId]; JET_COLUMNID jetColumnCreatedTime = columnIds[_ColumnNameCreatedTime]; JET_COLUMNID jetColumnCallbackTime = columnIds[_ColumnNameCallbackTime]; JET_COLUMNID jetColumnCallbackUrl = columnIds[_ColumnNameCallbackUrl]; JET_COLUMNID jetColumnAttemptsRemaining = columnIds[_ColumnNameAttemptsRemaining]; JET_COLUMNID jetColumnAuthorizationCipher = columnIds[_ColumnNameAuthorizationCipher]; do { Guid? callbackId = Api.RetrieveColumnAsGuid(jetSession, jetTable, jetColumnCallbackId); DateTime?createdTime = Api.RetrieveColumnAsDateTime(jetSession, jetTable, jetColumnCreatedTime); DateTime?callbackTime = Api.RetrieveColumnAsDateTime(jetSession, jetTable, jetColumnCallbackTime); string callbackUrl = Api.RetrieveColumnAsString(jetSession, jetTable, jetColumnCallbackUrl); int? attemptsRemainingColumn = Api.RetrieveColumnAsInt32(jetSession, jetTable, jetColumnAttemptsRemaining); string authorizationCipher = Api.RetrieveColumnAsString(jetSession, jetTable, jetColumnAuthorizationCipher); Uri callbackUri = null; if (callbackTime.HasValue && Uri.TryCreate(callbackUrl, UriKind.Absolute, out callbackUri) && createdTime.HasValue && callbackId.HasValue && attemptsRemainingColumn.HasValue) { RevaleeTask revivedTask = RevaleeTask.Revive( DateTime.SpecifyKind(callbackTime.Value, DateTimeKind.Utc), callbackUri, DateTime.SpecifyKind(createdTime.Value, DateTimeKind.Utc), callbackId.Value, attemptsRemainingColumn.Value, string.IsNullOrEmpty(authorizationCipher) ? null : authorizationCipher); yield return(revivedTask); } } while (Api.TryMoveNext(jetSession, jetTable)); } } } finally { _ConnectionPool.CloseConnection(connection); } yield break; }
private static HttpWebRequest PrepareWebRequest(RevaleeTask task) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(FormatCallbackRequestUrl(task.CallbackUrl)); request.AllowAutoRedirect = true; request.MaximumAutomaticRedirections = 10; request.KeepAlive = false; request.Method = "POST"; request.Pipelined = false; request.Timeout = 30000; request.UserAgent = "Revalee"; request.ContentType = "application/x-www-form-urlencoded"; if (!string.IsNullOrEmpty(task.AuthorizationCipher)) { string responseCipher = BuildResponseAuthorizationCipher(task.AuthorizationCipher, task.CallbackId); if (responseCipher != null) { request.Headers.Add("Revalee-Auth", responseCipher); } } string postedData = FormatFormPayload(task); request.ContentLength = postedData.Length; using (Stream stream = request.GetRequestStream()) { stream.Write(Encoding.UTF8.GetBytes(postedData), 0, postedData.Length); } return request; }
public IEnumerable <RevaleeTask> ListTasksDueBetween(DateTime startTime, DateTime endTime) { if (_EseInstance == null) { throw new InvalidOperationException("Storage provider has not been opened."); } DateTime rangeStartTime = EnforceMinimumDateTime(NormalizeDateTime(startTime)); DateTime rangeEndTime = EnforceMinimumDateTime(NormalizeDateTime(endTime)); // Inclusive Upper Limit does not work properly for the CLR DateTime type. // Add the smallest amount of time that the Esent engine will detect to include the ending range inclusively. rangeEndTime = rangeEndTime.AddMilliseconds(1.0); EseConnection connection = this._ConnectionPool.OpenConnection(); try { using (Table table = connection.GetTable(_TableNameCallbacks, OpenTableGrbit.DenyWrite | OpenTableGrbit.Preread | OpenTableGrbit.ReadOnly | OpenTableGrbit.Sequential)) { IDictionary <string, JET_COLUMNID> columnIds = connection.GetSchema(_TableNameCallbacks); Api.JetSetCurrentIndex(connection, table, "due"); Api.MakeKey(connection, table, rangeStartTime, MakeKeyGrbit.NewKey); if (Api.TrySeek(connection, table, SeekGrbit.SeekGE)) { Api.MakeKey(connection, table, rangeEndTime, MakeKeyGrbit.NewKey); if (Api.TrySetIndexRange(connection, table, SetIndexRangeGrbit.RangeInclusive | SetIndexRangeGrbit.RangeUpperLimit)) { JET_SESID jetSession = connection; JET_TABLEID jetTable = table; JET_COLUMNID jetColumnCallbackId = columnIds[_ColumnNameCallbackId]; JET_COLUMNID jetColumnCreatedTime = columnIds[_ColumnNameCreatedTime]; JET_COLUMNID jetColumnCallbackTime = columnIds[_ColumnNameCallbackTime]; JET_COLUMNID jetColumnCallbackUrl = columnIds[_ColumnNameCallbackUrl]; JET_COLUMNID jetColumnAttemptsRemaining = columnIds[_ColumnNameAttemptsRemaining]; JET_COLUMNID jetColumnAuthorizationCipher = columnIds[_ColumnNameAuthorizationCipher]; do { Guid? callbackId = Api.RetrieveColumnAsGuid(jetSession, jetTable, jetColumnCallbackId); DateTime?createdTime = Api.RetrieveColumnAsDateTime(jetSession, jetTable, jetColumnCreatedTime); DateTime?callbackTime = Api.RetrieveColumnAsDateTime(jetSession, jetTable, jetColumnCallbackTime); string callbackUrl = Api.RetrieveColumnAsString(jetSession, jetTable, jetColumnCallbackUrl); int? attemptsRemainingColumn = Api.RetrieveColumnAsInt32(jetSession, jetTable, jetColumnAttemptsRemaining); string authorizationCipher = Api.RetrieveColumnAsString(jetSession, jetTable, jetColumnAuthorizationCipher); Uri callbackUri = null; if (callbackTime.HasValue && Uri.TryCreate(callbackUrl, UriKind.Absolute, out callbackUri) && createdTime.HasValue && callbackId.HasValue && attemptsRemainingColumn.HasValue) { RevaleeTask revivedTask = RevaleeTask.Revive( DateTime.SpecifyKind(callbackTime.Value, DateTimeKind.Utc), callbackUri, DateTime.SpecifyKind(createdTime.Value, DateTimeKind.Utc), callbackId.Value, attemptsRemainingColumn.Value, string.IsNullOrEmpty(authorizationCipher) ? null : authorizationCipher); yield return(revivedTask); } } while (Api.TryMoveNext(jetSession, jetTable)); } } } } finally { _ConnectionPool.CloseConnection(connection); } yield break; }
private void ProcessWebException(RevaleeTask task, WebException wex) { HttpWebResponse failedResponse = (HttpWebResponse)wex.Response; if (failedResponse != null) { switch (DetermineResult(failedResponse.StatusCode)) { case CallbackResult.NonretryableError: CompleteFailedTask(task, failedResponse.StatusCode); break; default: CompleteRetryableTask(task, failedResponse.StatusCode); break; } } else { CompleteRetryableTask(task, wex); } }
public void CompleteTask(RevaleeTask task) { if (task == null) { throw new ArgumentNullException("task"); } lock (_SyncRoot) { _PersistenceProvider.RemoveTask(task); Supervisor.Telemetry.DecrementAwaitingTasksValue(); } }
private static void CompleteFailedTask(RevaleeTask task, HttpStatusCode statusCode) { Supervisor.State.CompleteTask(task); Supervisor.LogEvent(string.Format("Unsuccessful callback to {0} due to HTTP status code {1}. [{2}]", task.CallbackUrl.OriginalString, (int)statusCode, task.CallbackId), TraceEventType.Error); Supervisor.Telemetry.RecordWaitTime(CalculateWaitTime(DateTime.UtcNow, task)); Supervisor.Telemetry.RecordFailedCallback(); }
public void UpdateTask(RevaleeTask task) { if (task == null) { throw new ArgumentNullException("task"); } lock (_SyncRoot) { _PersistenceProvider.RemoveTask(task); _PersistenceProvider.AddTask(task); } }
private static void CompleteRetryableTask(RevaleeTask task, HttpStatusCode statusCode) { if (task.AttemptsRemaining > 0) { RetryTask(task); } else { // Out of attempts CompleteFailedTask(task, statusCode); } }
public void ReenlistTask(RevaleeTask task) { if (task == null) { throw new ArgumentNullException("task"); } _AwaitingTaskCollection.AddOrReplace(task.CallbackId, task, task.CallbackTime); ResetTaskAlarm(); }
private static void RetryTask(RevaleeTask task) { // Update the persisted attempt counts Supervisor.State.UpdateTask(task); // Reenlist task to be retried later TimeSpan retryDelay = RetryHeuristics.OnRetryableFailure(task.CallbackUrl); DateTime delayedCallbackTime = DateTime.UtcNow.Add(retryDelay); Supervisor.State.ReenlistTask(task, delayedCallbackTime); Supervisor.LogEvent(string.Format("Retrying callback to {0} after waiting {1}. [{2}]", task.CallbackUrl.OriginalString, retryDelay, task.CallbackId), TraceEventType.Information); }
public void ReenlistTask(RevaleeTask task, DateTime due) { if (task == null) { throw new ArgumentNullException("task"); } if (due.Kind != DateTimeKind.Utc) { throw new ArgumentException("DateTime argument not provided in UTC.", "due"); } _AwaitingTaskCollection.AddOrReplace(task.CallbackId, task, due); ResetTaskAlarm(); }
private static TimeSpan CalculateWaitTime(DateTime time, RevaleeTask task) { if (task.CallbackTime > task.CreatedTime) { if (task.CallbackTime < time) { return time.Subtract(task.CallbackTime); } else { return TimeSpan.Zero; } } else { if (task.CreatedTime < time) { return time.Subtract(task.CreatedTime); } else { return TimeSpan.Zero; } } }
public void AddTask(RevaleeTask Task) { return; }