public int ShoutOrThrow(System.Threading.CancellationToken cancellationToken)
{
// Pull a shout request message from the subscription.
string subscriptionPath = MakeSubscriptionPath(_init.SubscriptionName);
WriteLog("Pulling shout request messages from " + subscriptionPath + "...",
TraceEventType.Verbose);
var pullRequest = _init.PubsubService.Projects.Subscriptions.Pull(
new PullRequest()
{
MaxMessages = 1,
ReturnImmediately = false
}, subscriptionPath).ExecuteAsync();
Task.WaitAny(new Task[] { pullRequest }, cancellationToken);
var pullResponse = pullRequest.Result;
int messageCount = pullResponse.ReceivedMessages == null ? 0
: pullResponse.ReceivedMessages.Count;
WriteLog("Received " + messageCount + " messages.",
TraceEventType.Information);
if (messageCount < 1)
return 0; // Nothing pulled. Nothing to do.
// Examine the received message.
var shoutRequestMessage = pullResponse.ReceivedMessages[0];
var attributes = shoutRequestMessage.Message.Attributes;
string postStatusUrl;
string postStatusToken;
DateTime requestDeadline;
try
{
postStatusUrl = attributes["postStatusUrl"];
postStatusToken = attributes["postStatusToken"];
long unixDeadline = Convert.ToInt64(attributes["deadline"]);
requestDeadline = FromUnixTime(unixDeadline);
}
catch (Exception e)
{
WriteLog("Bad shout request message attributes.\n" + e.ToString(),
TraceEventType.Warning);
Acknowledge(shoutRequestMessage.AckId);
return -1;
}
// Tell the world we are shouting this request.
PublishStatus(postStatusUrl, postStatusToken, "shouting");
WriteLog("Shouting " + postStatusUrl, TraceEventType.Verbose);
try
{
// Decode the payload, the string we want to shout.
byte[] data = Convert.FromBase64String(shoutRequestMessage.Message.Data);
string decodedString = Encoding.UTF8.GetString(data);
// Watch the clock and cancellation token as we work. We need to extend the
// ack deadline if the request takes a while.
var tenSeconds = TimeSpan.FromSeconds(10);
DateTime ackDeadline = DateTime.UtcNow + tenSeconds;
ThrowIfAborted throwIfAborted = () =>
{
cancellationToken.ThrowIfCancellationRequested();
var now = DateTime.UtcNow;
if (requestDeadline < now)
throw new FatalException("Request timed out.");
if (ackDeadline < now)
{
// Tell the subscription we need more time:
WriteLog("Need more time...", TraceEventType.Verbose);
_init.PubsubService.Projects.Subscriptions.ModifyAckDeadline(
new ModifyAckDeadlineRequest
{
AckIds = new string[] { shoutRequestMessage.AckId },
AckDeadlineSeconds = 15,
}, MakeSubscriptionPath(_init.SubscriptionName)).Execute();
ackDeadline = now + tenSeconds;
}
};
// Shout it.
string upperText = ShoutString(decodedString, throwIfAborted);
// Publish the result.
PublishStatus(postStatusUrl, postStatusToken, "success", upperText);
Acknowledge(shoutRequestMessage.AckId);
return 1;
}
catch (OperationCanceledException)
{
return 1; // Service stopped. Nothing to report.
}
catch (FatalException e)
{
WriteLog("Fatal exception while shouting:\n" + e.Message, TraceEventType.Error);
Acknowledge(shoutRequestMessage.AckId);
PublishStatus(postStatusUrl, postStatusToken, "fatal", e.Message);
return -1;
}
catch (Exception e)
{
// Something went wrong while shouting. Report the error.
WriteLog("Exception while shouting:\n" + e.Message, TraceEventType.Error);
PublishStatus(postStatusUrl, postStatusToken, "error", e.Message);
return -1;
}
}