Cannot get WebSocket request by using Microsoft Owiin selfhost - c#

I found the example on the internet. Somehow, I cannot make it run correctly.
I did a lot of search but still cannot find out what is the mistake?!
Hope, someone can help to figure it out.
Server code:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Tracing;
using HTek.Core.MEF;
using HTek.Core.Server.Web.MEF;
using HTek.Core.Server.Web.Tracing;
using Microsoft.Owin;
using Owin;
// http://owin.org/extensions/owin-WebSocket-Extension-v0.4.0.htm
using WebSocketAccept = System.Action<System.Collections.Generic.IDictionary<string, object>, // options
System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>>; // callback
using WebSocketCloseAsync = System.Func<int /* closeStatus */,
string /* closeDescription */,
System.Threading.CancellationToken /* cancel */,
System.Threading.Tasks.Task>;
using WebSocketReceiveAsync = System.Func<System.ArraySegment<byte> /* data */,
System.Threading.CancellationToken /* cancel */, System.Threading.Tasks.Task<System.Tuple<int /* messageType */,
bool /* endOfMessage */,
int /* count */>>>;
using WebSocketSendAsync = System.Func<System.ArraySegment<byte> /* data */,
int /* messageType */,
bool /* endOfMessage */,
System.Threading.CancellationToken /* cancel */,
System.Threading.Tasks.Task>;
using WebSocketReceiveResult = System.Tuple<int, // type
bool, // end of message?
int>; // count
namespace Server.WebApi
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var webApiConfiguration = ConfigureWebApi();
// Use the extension method provided by the WebApi.Owin library:
app.UseWebApi((HttpConfiguration)webApiConfiguration);
app.Use(UpgradeToWebSockets);
}
private HttpConfiguration ConfigureWebApi()
{
WebComposition provider = new WebComposition();
Composition.SetCurrentProvider(provider);
Composition.Start();
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("DefaultApi",
"api/{controller}/{id}",
new
{
id = RouteParameter.Optional
});
provider.ConfigureWebApi(config);
//// tracing config
config.Services.Replace(typeof(ITraceWriter), new Tracer());
return config;
}
private static Task UpgradeToWebSockets(IOwinContext context, Func<Task> next)
{
WebSocketAccept accept = context.Get<WebSocketAccept>("websocket.Accept");
if (accept == null)
{
// Not a websocket request
return next();
}
accept(null, WebSocketEcho);
return Task.FromResult<object>(null);
}
private static async Task WebSocketEcho(IDictionary<string, object> websocketContext)
{
var sendAsync = (WebSocketSendAsync)websocketContext["websocket.SendAsync"];
var receiveAsync = (WebSocketReceiveAsync)websocketContext["websocket.ReceiveAsync"];
var closeAsync = (WebSocketCloseAsync)websocketContext["websocket.CloseAsync"];
var callCancelled = (CancellationToken)websocketContext["websocket.CallCancelled"];
byte[] buffer = new byte[1024];
WebSocketReceiveResult received = await receiveAsync(new ArraySegment<byte>(buffer), callCancelled);
object status;
while (!websocketContext.TryGetValue("websocket.ClientCloseStatus", out status)
|| (int)status == 0)
{
// Echo anything we receive
await
sendAsync(new ArraySegment<byte>(buffer, 0, received.Item3),
received.Item1,
received.Item2,
callCancelled);
received = await receiveAsync(new ArraySegment<byte>(buffer), callCancelled);
}
await
closeAsync((int)websocketContext["websocket.ClientCloseStatus"],
(string)websocketContext["websocket.ClientCloseDescription"],
callCancelled);
}
}
}
private static void Main(string[] args)
{
string baseAddress = "http://localhost:9000/";
// Start OWIN host
using (WebApp.Start<Startup>(url: baseAddress))
{
Logger<Program>.InfoFormat("Server running on {0}", baseAddress);
Console.ReadLine();
}
}
And HTML client:
var wsUri = "ws://localhost:9000";
var output;
function init()
{
output = document.getElementById("output");
testWebSocket();
}
function testWebSocket() {
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
}
function onOpen(evt)
{
writeToScreen("CONNECTED");
doSend("WebSocket rocks");
}
function onClose(evt)
{
writeToScreen("DISCONNECTED");
}
function onMessage(evt)
{
writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
websocket.close();
}
function onError(evt)
{
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}
function doSend(message)
{
writeToScreen("SENT: " + message);
websocket.send(message);
}
function writeToScreen(message)
{
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
window.addEventListener("load", init, false);
Everything looks good. Excepted, 'accept'(in 'UpgradeToWebSockets') is always NULL.

WebSocket is not supports in old Windows releases such as Windows 7.
You might upgrade to a newer release such as Windows 10.

Related

RestSharp returns IRestResponse.StatusCode == 0 to Polly onRetry

I wrote a fairly simple wrapper around the RestSharp client adding some retry logic to it using Polly.
I setup Fiddler to test it and simulated some "Bad" responses.
The problem I have is in the onRetry delegate, the result.Result.StatusCode bit seems to sometimes log as 0 instead of the actual bad status code (502 in some of my testings).
However, with my unit tests it seems that its working perfectly. Race conditions here maybe?
Any idea why this is happening?
Thanks in advance.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Polly;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Net;
namespace FundsAFE.Graphite
{
public class RequestExecutor
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private IRestClient client;
private IRestRequest request;
private Policy<IRestResponse> retryPolicy;
public IRestResponse LastErrorResponse { get; set; }
private static readonly List<HttpStatusCode> invalidStatusCodes = new List<HttpStatusCode> {
HttpStatusCode.BadGateway,
HttpStatusCode.Unauthorized,
HttpStatusCode.InternalServerError,
HttpStatusCode.RequestTimeout,
HttpStatusCode.BadRequest,
HttpStatusCode.Forbidden,
HttpStatusCode.GatewayTimeout
};
public RequestExecutor(IRestClient client, IRestRequest request)
{
this.client = client;
this.request = request;
}
public IRestResponse Execute(int retryCount, int delay)
{
retryPolicy = Policy
.HandleResult<IRestResponse>(resp => invalidStatusCodes.Contains(resp.StatusCode) || !IsValidJson(resp))
.WaitAndRetry(retryCount, i => TimeSpan.FromMilliseconds(delay), (result, timeSpan, currentRetryCount, context) =>
{
//Status code here is sometimes 0???
logger.Error($"Request failed with {result.Result.StatusCode}. Waiting {timeSpan} before next retry. Retry attempt {currentRetryCount}");
LastErrorResponse = result.Result;
});
var policyResponse = retryPolicy.ExecuteAndCapture(() =>
{
var url = client.BuildUri(request);
logger.Debug(url.ToString());
var response = client.Execute(request);
return response;
});
if(policyResponse.Result != null)
{
return policyResponse.Result;
} else
{
return LastErrorResponse;
}
}
public static bool IsValidJson(IRestResponse response)
{
if (response.Content.Length == 0)
{
//Empty response treated as invalid
return false;
}
try
{
var parsed = JObject.Parse(response.Content);
}
catch (JsonReaderException e)
{
//Will catch any mallformed json
return false;
}
return true;
}
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using FundsAFE.Graphite;
using Moq;
using RestSharp;
using System.Net;
using FluentAssertions;
using System;
using FluentAssertions.Extensions;
namespace FundsAFE.Test.Moq
{
[TestClass]
public class MoqUnitTestRequest
{
public Mock<IRestClient> CreateMockClientWithStatusCodeAndContent(HttpStatusCode code, string content)
{
Mock<IRestClient> mockClient = new Mock<IRestClient>();
mockClient.Setup(c => c.Execute(It.IsAny<IRestRequest>())).Returns(
new RestResponse
{
Content = content,
StatusCode = code
}
);
mockClient.Setup(c => c.BuildUri(It.IsAny<IRestRequest>())).Returns(
new Uri("http://fake.fake")
);
return mockClient;
}
[DataTestMethod]
[DataRow(HttpStatusCode.BadGateway)]
[DataRow(HttpStatusCode.Unauthorized)]
[DataRow(HttpStatusCode.InternalServerError)]
[DataRow(HttpStatusCode.RequestTimeout)]
[DataRow(HttpStatusCode.BadRequest)]
[DataRow(HttpStatusCode.Forbidden)]
[DataRow(HttpStatusCode.GatewayTimeout)]
public void TestBadStatusCodesAndRetry(HttpStatusCode httpStatusCode) {
//Arrange
Mock<IRestRequest> mockRequest = new Mock<IRestRequest>();
Mock<IRestClient> mockClient = CreateMockClientWithStatusCodeAndContent(httpStatusCode, "fakecontent");
RequestExecutor requestExecutor = new RequestExecutor(mockClient.Object, mockRequest.Object);
int retries = 10;
int delay = 50;
int totalWaitTime = (retries * delay) - 10; //10ms error margin
//Act and Verify
var response = requestExecutor.Execute(retryCount: retries, delay: 101);
mockClient.Verify(x => x.Execute(It.IsAny<IRestRequest>()), Times.Exactly(retries + 1)); //1st failed attempt + 10 retries = 11
//Assert
requestExecutor.ExecutionTimeOf(re => re.Execute(retries, delay)).Should().BeGreaterOrEqualTo(totalWaitTime.Milliseconds());
response.Should().NotBeNull();
response.StatusCode.Should().Be(httpStatusCode);
requestExecutor.LastErrorResponse.StatusCode.Should().Be(httpStatusCode);
}
[DataTestMethod]
//Empty content
[DataRow("")]
//Missing closing quote
[DataRow("{\"fruit\": \"Apple,\"size\": \"Large\",\"color\": \"Red\"}")]
//Missing angle bracket
[DataRow("\"q1\": {\"question\": \"Which one is correct team name in NBA?\",\"options\": \"New York Bulls\",\"Los Angeles Kings\",\"Golden State Warriros\",\"Huston Rocket\"],\"answer\": \"Huston Rocket\"}")]
//Missing curly bracket
[DataRow("\"sport\": {\"q1\": {\"question\": \"Which one is correct team name in NBA?\",\"options\": \"New York Bulls\",\"Los Angeles Kings\",\"Golden State Warriros\",\"Huston Rocket\"],\"answer\": \"Huston Rocket\"}")]
public void TestBadContentRetries(string content)
{
//Arrange
Mock<IRestRequest> mockRequest = new Mock<IRestRequest>();
Mock<IRestClient> mockClient = CreateMockClientWithStatusCodeAndContent(HttpStatusCode.OK, content);
RequestExecutor requestExecutor = new RequestExecutor(mockClient.Object, mockRequest.Object);
int retries = 10;
int delay = 50;
int totalWaitTime = (retries * delay) - 10; //10ms error margin
//Act and Verify
var response = requestExecutor.Execute(retryCount: retries, delay: delay);
mockClient.Verify(x => x.Execute(It.IsAny<IRestRequest>()), Times.Exactly(retries + 1)); //1st failed attempt + 10 retries = 11
//Assert
requestExecutor.ExecutionTimeOf(re => re.Execute(retries, delay)).Should().BeGreaterOrEqualTo(totalWaitTime.Milliseconds());
response.Should().NotBeNull();
}
}
}
It looks from the RESTsharp source code as if there are cases (eg some exception cases) where RESTsharp might well return you an IRestResponse with resp.StatusCode == 0.
The logging code in onRetry should probably be checking the wider set of status properties on IRestResponse, not just IRestResponse.StatusCode

separate spaces for each user

Sorry for my bad english. The application reads data from the accelerometer of the smartphone. He has a server in which when a smartphone is connected, a pair of id-data about the position in space is created. At the moment, data from all users come to the server. The task is to do the opposite: each time a user connects, a separate space is created that receives data from only one id.
Server code:
var express = require('express');
var compression = require('compression');
var http = require('http');
var path = require('path');
var socketIO = require('socket.io');
const port = process.env.PORT || 5000;
var cors = require('cors');
var app = express();
var server = http.Server(app);
var io = socketIO(server);
app.use(cors()) ;// Use this after the variable declaration
app.set('port', port);
app.use(compression());
app.use(express.static(__dirname + '/'));
app.use('/static', express.static(__dirname + '/static'));
// Routing
app.get('/', function (request, response) {
response.sendFile(path.join(__dirname, 'view.html'));
console.log(Object.keys(player_move).length);
if (Object.keys(player_move).length >= 1){
let data = {
id: socketId,
x: player_move[socketId].x,
z: player_move[socketId].z,
};
response.send(data);
console.log('connected');
}else{
response.send("no");
console.log('not connected');
};
});
app.use(express.static(__dirname + '/client/'));
// Routing
app.get('/view', function (request, response) {
response.sendFile(path.join(__dirname, '/client/index.html'));
});
server.listen(port, function () {
console.log('Старт сервера по адресу localhost:' + port);
console.log('Старт просмотра по адресу localhost: ' + port + ' /view');
});
var player_move = {};
var socketId;
io.on('connection', function (socket) {
socket.on('new player', function () {
console.log('new player');
socket.emit('ok');
});
socket.on('disconnect', function () {
});
socket.on('movement', function () {
console.log('movement');
console.log('player_move', player_move);
if (player_move[socketId]) {
let data = {
id: socketId,
x: player_move[socketId].x,
z: player_move[socketId].z,
};
socket.emit('ok', data);
}
});
socket.on('phone_data', function (data) {
console.log('phone_date', data);
socketId = socket.id;
player_move[socket.id] = {
x: data.x,
z: data.z
};
});
// GET method route
app.get('/', function (req, res) {
res.send('Contact');
console.log("est kontakt");
});
});
socket code:
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
using UnityEngine.Networking;
public class SocketController : MonoBehaviour
{
[SerializeField]
PlayerController player;
// Start is called before the first frame update
void Start()
{
StartCoroutine(GetRequest("http://plantgo.ru:5000"));
}
private void Awake()
{
//socket.url = "ws://127.0.0.1:5000/socket.io/?EIO=4&transport=websocket";
}
// Update is called once per frame
void Update()
{
}
//private IEnumerator Connector()
//{
// while (true)
// {
// socket.Emit("movement");
// yield return new WaitForSeconds(0.10f);
// }
// wait 1 seconds and continue
// }
class PlayerInfo
{
public string id;
public string x;
public string z;
}
IEnumerator GetRequest(string uri)
{
while (true)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
// Request and wait for the desired page.
yield return webRequest.SendWebRequest();
string[] pages = uri.Split('/');
int page = pages.Length - 1;
if (webRequest.isNetworkError)
{
Debug.Log(pages[page] + ": Error: " + webRequest.error);
}
else
{
Debug.Log(pages[page] + ":\nReceived: " + webRequest.downloadHandler.text);
if (webRequest.downloadHandler.text == "no")
{
continue;
}
else
{
PlayerInfo playerInfo = (PlayerInfo)JsonUtility.FromJson<PlayerInfo>(webRequest.downloadHandler.text);
Debug.Log(playerInfo);
player.PlayerMove(float.Parse(playerInfo.x, CultureInfo.InvariantCulture), float.Parse(playerInfo.z, CultureInfo.InvariantCulture));
}
}
}
//yield return new WaitForSeconds(0.1f);
}
}
}
when I start the server, several users can control the character at once, since they are in the same game space, it is necessary for each to have its own.

Google API Client for .Net: Implement retry when a request fails

How can I implement retries in case a request that is part of a batch request fails when interacting with google's API. In their documentation, they suggest adding "Exponential Backoff" algorithm. I'm using the following code snippet in their documentation:
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { CalendarService.Scope.Calendar },
"user", CancellationToken.None, new FileDataStore("Calendar.Sample.Store"));
}
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Google Calendar API Sample",
});
// Create a batch request.
var request = new BatchRequest(service);
request.Queue<CalendarList>(service.CalendarList.List(),
(content, error, i, message) =>
{
// Put your callback code here.
});
request.Queue<Event>(service.Events.Insert(
new Event
{
Summary = "Learn how to execute a batch request",
Start = new EventDateTime() { DateTime = new DateTime(2014, 1, 1, 10, 0, 0) },
End = new EventDateTime() { DateTime = new DateTime(2014, 1, 1, 12, 0, 0) }
}, "YOUR_CALENDAR_ID_HERE"),
(content, error, i, message) =>
{
// Put your callback code here.
});
// You can add more Queue calls here.
// Execute the batch request, which includes the 2 requests above.
await request.ExecuteAsync();
Here is a simple helper class to make it easy to implement exponential backoff for a lot of the situations that Google talks about on their API error page: https://developers.google.com/calendar/v3/errors
How to Use:
Edit the class below to include your client secret and application name as you set up on https://console.developers.google.com
In the startup of your application (or when you ask the user to authorize), call GCalAPIHelper.Instance.Auth();
Anywhere you would call the Google Calendar API (eg Get, Insert, Delete, etc), instead use this class by doing: GCalAPIHelper.Instance.CreateEvent(event, calendarId); (you may need to expand this class to other API endpoints as your needs require)
using Google;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Calendar.v3;
using Google.Apis.Calendar.v3.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using static Google.Apis.Calendar.v3.CalendarListResource.ListRequest;
/*======================================================================================
* This file is to implement Google Calendar .NET API endpoints WITH exponential backoff.
*
* How to use:
* - Install the Google Calendar .NET API (nuget.org/packages/Google.Apis.Calendar.v3)
* - Edit the class below to include your client secret and application name as you
* set up on https://console.developers.google.com
* - In the startup of your application (or when you ask the user to authorize), call
* GCalAPIHelper.Instance.Auth();
* - Anywhere you would call the Google Calendar API (eg Get, Insert, Delete, etc),
* instead use this class by doing:
* GCalAPIHelper.Instance.CreateEvent(event, calendarId); (you may need to expand
* this class to other API endpoints as your needs require)
*======================================================================================
*/
namespace APIHelper
{
public class GCalAPIHelper
{
#region Singleton
private static GCalAPIHelper instance;
public static GCalAPIHelper Instance
{
get
{
if (instance == null)
instance = new GCalAPIHelper();
return instance;
}
}
#endregion Singleton
#region Private Properties
private CalendarService service { get; set; }
private string[] scopes = { CalendarService.Scope.Calendar };
private const string CLIENTSECRETSTRING = "YOUR_SECRET"; //Paste in your JSON client secret here. Don't forget to escape special characters!
private const string APPNAME = "YOUR_APPLICATION_NAME"; //Paste in your Application name here
#endregion Private Properties
#region Constructor and Initializations
public GCalAPIHelper()
{
}
public void Auth(string credentialsPath)
{
if (service != null)
return;
UserCredential credential;
byte[] byteArray = Encoding.ASCII.GetBytes(CLIENTSECRETSTRING);
using (var stream = new MemoryStream(byteArray))
{
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
scopes,
Environment.UserName,
CancellationToken.None,
new FileDataStore(credentialsPath, true)).Result;
}
service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = APPNAME
});
}
#endregion Constructor and Initializations
#region Private Methods
private TResponse DoActionWithExponentialBackoff<TResponse>(CalendarBaseServiceRequest<TResponse> request)
{
return DoActionWithExponentialBackoff(request, new HttpStatusCode[0]);
}
private TResponse DoActionWithExponentialBackoff<TResponse>(CalendarBaseServiceRequest<TResponse> request, HttpStatusCode[] otherBackoffCodes)
{
int delay = 100;
while (delay < 1000) //If the delay gets above 1 second, give up
{
try
{
return request.Execute();
}
catch (GoogleApiException ex)
{
if (ex.HttpStatusCode == HttpStatusCode.Forbidden || //Rate limit exceeded
ex.HttpStatusCode == HttpStatusCode.ServiceUnavailable || //Backend error
ex.HttpStatusCode == HttpStatusCode.NotFound ||
ex.Message.Contains("That’s an error") || //Handles the Google error pages like https://i.imgur.com/lFDKFro.png
otherBackoffCodes.Contains(ex.HttpStatusCode))
{
Common.Log($"Request failed. Waiting {delay} ms before trying again");
Thread.Sleep(delay);
delay += 100;
}
else
throw;
}
}
throw new Exception("Retry attempts failed");
}
#endregion Private Methods
#region Public Properties
public bool IsAuthorized
{
get { return service != null; }
}
#endregion Public Properties
#region Public Methods
public Event CreateEvent(Event eventToCreate, string calendarId)
{
EventsResource.InsertRequest eventCreateRequest = service.Events.Insert(eventToCreate, calendarId);
return DoActionWithExponentialBackoff(eventCreateRequest);
}
public Event InsertEvent(Event eventToInsert, string calendarId)
{
EventsResource.InsertRequest eventCopyRequest = service.Events.Insert(eventToInsert, calendarId);
return DoActionWithExponentialBackoff(eventCopyRequest);
}
public Event UpdateEvent(Event eventToUpdate, string calendarId, bool sendNotifications = false)
{
EventsResource.UpdateRequest eventUpdateRequest = service.Events.Update(eventToUpdate, calendarId, eventToUpdate.Id);
eventUpdateRequest.SendNotifications = sendNotifications;
return DoActionWithExponentialBackoff(eventUpdateRequest);
}
public Event GetEvent(Event eventToGet, string calendarId)
{
return GetEvent(eventToGet.Id, calendarId);
}
public Event GetEvent(string eventIdToGet, string calendarId)
{
EventsResource.GetRequest eventGetRequest = service.Events.Get(calendarId, eventIdToGet);
return DoActionWithExponentialBackoff(eventGetRequest);
}
public CalendarListEntry GetCalendar(string calendarId)
{
CalendarListResource.GetRequest calendarGetRequest = service.CalendarList.Get(calendarId);
return DoActionWithExponentialBackoff(calendarGetRequest);
}
public Events ListEvents(string calendarId, DateTime? startDate = null, DateTime? endDate = null, string q = null, int maxResults = 250)
{
EventsResource.ListRequest eventListRequest = service.Events.List(calendarId);
eventListRequest.ShowDeleted = false;
eventListRequest.SingleEvents = true;
eventListRequest.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime;
if (startDate != null)
eventListRequest.TimeMin = startDate;
if (endDate != null)
eventListRequest.TimeMax = endDate;
if (!string.IsNullOrEmpty(q))
eventListRequest.Q = q;
eventListRequest.MaxResults = maxResults;
return DoActionWithExponentialBackoff(eventListRequest);
}
public CalendarList ListCalendars(string accessRole)
{
CalendarListResource.ListRequest calendarListRequest = service.CalendarList.List();
calendarListRequest.MinAccessRole = (MinAccessRoleEnum)Enum.Parse(typeof(MinAccessRoleEnum), accessRole);
return DoActionWithExponentialBackoff(calendarListRequest);
}
public void DeleteEvent(Event eventToDelete, string calendarId, bool sendNotifications = false)
{
DeleteEvent(eventToDelete.Id, calendarId, sendNotifications);
}
public void DeleteEvent(string eventIdToDelete, string calendarId, bool sendNotifications = false)
{
EventsResource.DeleteRequest eventDeleteRequest = service.Events.Delete(calendarId, eventIdToDelete);
eventDeleteRequest.SendNotifications = sendNotifications;
DoActionWithExponentialBackoff(eventDeleteRequest, new HttpStatusCode[] { HttpStatusCode.Gone });
}
#endregion Public Methods
}
}
derekantrican has an answer that I based mine off of. Two things, if the resource is "notFound" waiting for it won't do any good. That's them responding to the request with the object not being found, so there's no need for back off. I'm not sure if there are other codes where I need to handle yet, but I will be looking at it closer. According to Google: https://cloud.google.com/iot/docs/how-tos/exponential-backoff all 5xx and 429 should be retried.
Also, Google wants this to be an exponential back off; not linear. So the code below handles it in a exponential way. They also want you to add a random amount of MS to the retry timeout. I don't do this, but it would be easy to do. I just don't think that it matters that much.
I also needed the requests to be async so I updated the work methods to this type. See derekantrican's examples on how to call the methods; these are just the worker methods. Instead of returning "default" on the notFound, you could also re-throw the exception and handle it upstream, too.
private async Task<TResponse> DoActionWithExponentialBackoff<TResponse>(DirectoryBaseServiceRequest<TResponse> request)
{
return await DoActionWithExponentialBackoff(request, new HttpStatusCode[0]);
}
private async Task<TResponse> DoActionWithExponentialBackoff<TResponse>(DirectoryBaseServiceRequest<TResponse> request, HttpStatusCode[] otherBackoffCodes)
{
int timeDelay = 100;
int retries = 1;
int backoff = 1;
while (retries <= 5)
{
try
{
return await request.ExecuteAsync();
}
catch (GoogleApiException ex)
{
if (ex.HttpStatusCode == HttpStatusCode.NotFound)
return default;
else if (ex.HttpStatusCode == HttpStatusCode.Forbidden || //Rate limit exceeded
ex.HttpStatusCode == HttpStatusCode.ServiceUnavailable || //Backend error
ex.Message.Contains("That’s an error") || //Handles the Google error pages like https://i.imgur.com/lFDKFro.png
otherBackoffCodes.Contains(ex.HttpStatusCode))
{
//Common.Log($"Request failed. Waiting {delay} ms before trying again");
Thread.Sleep(timeDelay);
timeDelay += 100 * backoff;
backoff = backoff * (retries++ + 1);
}
else
throw ex; // rethrow exception
}
}
throw new Exception("Retry attempts failed");
}

RabbitMQ returns same message again and again

I try to unit test a rather basic scenario - worker queue with 2 workers and 1 publisher scenario, but it keeps returning the same message over and over again from the queue.
The following code in the test just puts the 1 to 100 messages to the queue and 2 consumers eat them up. The problem is that they keep just getting message 1 and 2. I tried to separate the acknowledgement into a method, since in my application it takes time for a message to get process (commented method Confirm) - then it threw an exception that token is unknown:
The AMQP operation was interrupted: AMQP close-reason, initiated by
Peer, code=406, text="PRECONDITION_FAILED - unknown delivery tag 1",
classId=60, methodId=80, cause=
It seems that the acknowledgement is broken somehow. I tried to switch it off - no luck either.
Class:
using System;
using System.Text;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
namespace Backend.MQ.OCR
{
public class BatchQueue : QueueBase<BatchMessage>
{
private readonly IModel _channel;
private const string QPrefix = "ocrbatches_";
private readonly QueueingBasicConsumer _consumer;
private ulong _latesttoken = ulong.MaxValue;
private readonly string _jobid;
public BatchQueue(string connectionString, String jobid):
base(connectionString)
{
_jobid = jobid;
var factory = new ConnectionFactory()
{
HostName = connectionString
};
var connection = factory.CreateConnection();
_channel = connection.CreateModel();
_channel.QueueDeclare(Name, true, false, false, null);
//binding consumers
_channel.BasicQos(0, 1, false);
_consumer = new QueueingBasicConsumer(_channel);
_channel.BasicConsume(Name, false, _consumer);
}
public override void Publish(BatchMessage msg)
{
var message = JsonConvert.SerializeObject(msg);
var body = Encoding.UTF8.GetBytes(message);
var properties = _channel.CreateBasicProperties();
properties.SetPersistent(true);
_channel.BasicPublish("", Name, properties, body);
#if DEBUG
System.Diagnostics.Trace.WriteLine("[x] Sent task:" + msg);
#endif
}
private string Name
{
get { return QPrefix + _jobid; }
}
public override BatchMessage Receive()
{
var ea =
(BasicDeliverEventArgs)_consumer.Queue.Dequeue();
var body = ea.Body;
_channel.BasicAck(ea.DeliveryTag, false);
return JsonConvert.DeserializeObject<BatchMessage>(Encoding.UTF8.GetString(body));
}
public override void Confirm()
{
//if (_latesttoken < ulong.MaxValue) _channel.BasicAck(_latesttoken, false);
}
}
}
Unit tests:
#if NUNIT
using TestClass = NUnit.Framework.TestFixtureAttribute;
using TestMethod = NUnit.Framework.TestAttribute;
using TestCleanup = NUnit.Framework.TearDownAttribute;
using TestInitialize = NUnit.Framework.SetUpAttribute;
using ClassCleanup = NUnit.Framework.TestFixtureTearDownAttribute;
using ClassInitialize = NUnit.Framework.TestFixtureSetUpAttribute;
#else
#endif
using System.Threading.Tasks;
using System.Threading;
using System;
using System.Collections.Generic;
using Backend.MQ.OCR;
using Microsoft.VisualStudio.TestTools.UnitTesting;
#if NUNIT
using MAssert = NUnit.Framework.Assert;
#else
using MAssert = Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
#endif
namespace MQ.Test
{
[TestClass]
public class BatchQueueTest
{
[TestMethod]
public void Concurrencytest()
{
var batchname = Guid.NewGuid().ToString();
var queue = new BatchQueue("localhost", batchname);
var tasks = new List<Task>();
var counter = 0;
for (int i = 0; i < 100; i++)
{
queue.Publish(new BatchMessage()
{
Files = new List<string>() { i.ToString() }
});
}
for (int i = 0; i < 2; i++)
{
var task = Task.Factory.StartNew(() =>
{
var q = new BatchQueue("localhost", batchname);
var res = q.Receive();
while (res != null)
{
System.Diagnostics.Trace.WriteLine(res.Files[0]);
q.Confirm();
Interlocked.Increment(ref counter);
}
});
tasks.Add(task);
}
var ok = Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(30));
MAssert.IsTrue(ok, "Tasks didnt complete in time");
MAssert.AreEqual(counter, 100, "Not all messages have been processed");
}
}
}
Your unit test starts two tasks. Before the while loop you receive a message but you keep confirming the same message inside the while loop:
var q = new BatchQueue("localhost", batchname);
//Receive message 1 or 2
var res = q.Receive();
while (res != null)
{ //Infinite loop
System.Diagnostics.Trace.WriteLine(res.Files[0]);
q.Confirm();
Interlocked.Increment(ref counter);
}
Try to put var res = q.Receive(); inside the loop

VersionOne: query.v1 C# OAuth2 gets 401 Unauthorized error but rest-1.oauth.v1/Data/ does work

I am able to do queries using OAuth2 and this:
/rest-1.oauth.v1/Data/Story?sel=Name,Number&Accept=text/json
However I am unable to get the OAuth2 and the new query1.v1 to work against the Sumnmer2013 VersionOne. I am getting (401) Unauthorized and specified method is not supported using two different URLs.
Here is the code that includes the working /rest-1.oauth.v1 and the non-working query1.v1 and non-working query.legacy.v1. Scroll towards the bottom of the code to see the Program Main (starting point of code)
Please advise on what I am missing here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using OAuth2Client;
namespace ExampleMemberListCSharp
{
class Defaults
{
public static string Scope = "apiv1";
//public static string EndpointUrl = "http://localhost/VersionOne.Web";
public static string EndpointUrl = "https://versionone-test.web.acme.com/summer13_demo";
public static string ApiQueryWorks = "/rest-1.oauth.v1/Data/Member?Accept=text/json";
public static string ApiQuery = "/rest-1.oauth.v1/Data/Story?sel=Name,Number&Accept=text/json";
}
static class WebClientExtensions
{
public static string DownloadStringOAuth2(this WebClient client, IStorage storage, string scope, string path)
{
var creds = storage.GetCredentials();
client.AddBearer(creds);
try
{
return client.DownloadString(path);
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
if (((HttpWebResponse)ex.Response).StatusCode != HttpStatusCode.Unauthorized)
throw;
var secrets = storage.GetSecrets();
var authclient = new AuthClient(secrets, scope);
var newcreds = authclient.refreshAuthCode(creds);
var storedcreds = storage.StoreCredentials(newcreds);
client.AddBearer(storedcreds);
return client.DownloadString(path);
}
throw;
}
}
public static string UploadStringOAuth2(this WebClient client, IStorage storage
, string scope, string path, string pinMethod, string pinQueryBody)
{
var creds = storage.GetCredentials();
client.AddBearer(creds);
client.UseDefaultCredentials = true;
try
{
return client.UploadString(path, pinMethod, pinQueryBody);
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
if (((HttpWebResponse)ex.Response).StatusCode != HttpStatusCode.Unauthorized)
throw;
var secrets = storage.GetSecrets();
var authclient = new AuthClient(secrets, scope);
var newcreds = authclient.refreshAuthCode(creds);
var storedcreds = storage.StoreCredentials(newcreds);
client.AddBearer(storedcreds);
client.UseDefaultCredentials = true;
return client.UploadString(path, pinMethod, pinQueryBody);
}
throw;
}
}
}
class AsyncProgram
{
private static async Task<string> DoRequestAsync(string path)
{
var httpclient = HttpClientFactory.WithOAuth2("apiv1");
var response = await httpclient.GetAsync(Defaults.EndpointUrl + Defaults.ApiQuery);
var body = await response.Content.ReadAsStringAsync();
return body;
}
public static int MainAsync(string[] args)
{
var t = DoRequestAsync(Defaults.EndpointUrl + Defaults.ApiQuery);
Task.WaitAll(t);
Console.WriteLine(t.Result);
return 0;
}
}
class Program
{
static void Main(string[] args)
{
IStorage storage = Storage.JsonFileStorage.Default;
using (var webclient = new WebClient())
{
// this works:
var body = webclient.DownloadStringOAuth2(storage, "apiv1", Defaults.EndpointUrl + Defaults.ApiQuery);
Console.WriteLine(body);
}
IStorage storage2 = Storage.JsonFileStorage.Default;
using (var webclient2 = new WebClient())
{
// This does NOT work. It throws an exception of (401) Unauthorized:
var body2 = webclient2.UploadStringOAuth2(storage2, "apiv1", Defaults.EndpointUrl + "/query.v1", "SEARCH", QueryBody);
// This does NOT work. It throws an exception of The remote server returned an error: (403): Forbidden."
var body3 = webclient2.UploadStringOAuth2(storage2, "apiv1", Defaults.EndpointUrl + "/query.legacy.v1", "SEARCH", QueryBody);
// These do NOT work. Specified method is not supported:
var body4 = webclient2.UploadStringOAuth2(storage2, "apiv1", Defaults.EndpointUrl + "/oauth.v1/query.legacy.v1", "SEARCH", QueryBody);
var body5 = webclient2.UploadStringOAuth2(storage2, "apiv1", Defaults.EndpointUrl + "/oauth.v1/query.legacy.v1", "SEARCH", QueryBody);
}
Console.ReadLine();
AsyncProgram.MainAsync(args);
}
public const string QueryBody = #"
from: Story
select:
- Name
";
}
}
At this time, the query.v1 endpoint requires the query-api-1.0 scope to be granted.
You'll have to add that to your scope list (it can be simply space-separated e.g. apiv1 query-api-1.0) and visit the grant URL again to authorize the permissions.
This somewhat vital piece of information doesn't seem to appear in the docs on community.versionone.com, so it looks like an update is in order.
Also, only the rest-1.oauth.v1 and query.v1 endpoints respond to OAuth2 headers at this time. A future release will see it apply to all endpoints and remove the endpoint duplication for the two types of authentication
I have had problems in the past trying to use an HTTP method other than POST to transmit the query. Security software, IIS settings, and proxies may all handle such requests in unexpected ways.

Categories

Resources