SignalR connection issues - asp.net

I'm getting some issues with SignalR (1.1.2) trying to create a basic realtime chat setup and after spending about a week on it (including trawling through the SignalR source) I'm sort of at the end of what I can try...
I have (I think) a rather complicated SignalR setup consisting of:
Load balanced servers
Redis message bus
Two sites on each server (ASP.NET Webforms VB.NET desktop site and MVC3 C# mobile site)
Each of the sites includes the hub of itself and the other site, so each page can send messages to each site.
Looking into the Chrome inspector (in this example on the mobile site), the hubs are both loaded, the negotiate step for mobile is successful but the connect attempt fails after 3 seconds with the error:
EventSource's response has a MIME type ("text/html") that is not "text/event-stream". Aborting the connection.
which is of course our custom 500 error page after Microsoft.Owin.Host.SystemWeb has thrown:
The connection id is in the incorrect format.
Once this happens, most of the time this will then get into some sort of weird loop where it will continue to throw hundreds of these errors and send off lots of pings followed by a longPolling connect
The solution works perfectly well in my development environment (single IIS instance) but moving to the load balanced test environment is where I see the errors.
I don't know if there's anything else I can add that may help but I'm happy to add it.
I've added the following to the web.config files on both sites:
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>
and
<add name="Access-Control-Allow-Origin" value="*"></add>
<add name="Access-Control-Allow-Headers" value="Content-Type" />
The global.asax files have:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
RedisScaleoutConfiguration redisConfig = new RedisScaleoutConfiguration([redisIP], [port], String.Empty, "Name");
redisConfig.Database = 9;
GlobalHost.DependencyResolver.UseRedis(redisConfig);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
HubConfiguration hubConfig = new HubConfiguration();
hubConfig.EnableCrossDomain = true;
hubConfig.EnableDetailedErrors = true;
RouteTable.Routes.MapHubs(hubConfig);
<snip>
}
The JS code I have is along the lines of:
function setUpSignalR() {
//Set up the connections
webConnection = $.hubConnection(pageInfo.webUrl);
mobConnection = $.hubConnection(pageInfo.mobUrl);
//Get the hubs for web and mobile
webHub = webConnection.createHubProxies().messagingHub;
mobHub = mobConnection.createHubProxies().messagingHub;
//Hook up the call back functions
<snip>
//Now, start it up!
mobConnection.logging = true;
mobConnection.start().done(function() {
mobHub.server.joinConversation(pageInfo.conversationGuid, "mobile").fail(function (error) { console.log('JoinConversation for mobile connection failed. Error: ' + error); });
webConnection.start().done(function() {
webHub.server.joinConversation(pageInfo.conversationGuid, "mobile").fail(function (error) { console.log('JoinConversation for web connection failed. Error: ' + error); });
});
});
}

From the SignalR troubleshooting document:
"The connection ID is in the incorrect format" or "The user identity
cannot change during an active SignalR connection" error
This error may be seen if authentication is being used, and the client
is logged out before the connection is stopped. The solution is to
stop the SignalR connection before logging the client out.

Related

ReCaptcha v3 works locally, but not on remote server (500 Internal Server Error)

I am unable to get captcha to work on server. It works fine locally and I get it to return a score without issue.
JavaScript after button is clicked.
(function () {
$('#btnLogin').click(function () {
$.ajax({
type: "POST",
url: "/captcha/lasttry.aspx/CaptchaVerify",
data: {},
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
var _response = JSON.parse(response.d);
if (_response.score < 0.5) {
$('#status').text("User is not human being! Score = " + _response.score);
$('#status').removeClass('text-info').addClass('text-danger');
}
else {
$('#status').text("User is human being . Score = " + _response.score);
$('#status').removeClass('text-error').addClass('text-success');
}
},
failure: function (response) {
$('#status').text("Error on Server!");
}
});
});
})();
CodeBehind:
[WebMethod]
public static string CaptchaVerify()
{
//It should only call once
if (response.score == 0)
{
var responseString = RecaptchaVerify(Token);
response = JsonConvert.DeserializeObject<ResponseToken>(responseString.Result);
}
return JsonConvert.SerializeObject(response);
}
private static string apiAddress = "https://www.google.com/recaptcha/api/siteverify";
private static async Task<string> RecaptchaVerify(string recaptchaToken)
{
//try/catch omitted to try to see error with customErrors off
String url = $"{apiAddress}?secret={recaptchaSecret}&response={recaptchaToken}";
HttpClient httpClient = new HttpClient();
String responseString = httpClient.GetStringAsync(url).Result;
return responseString;
}
When I run it remotely I get the following error (1st image Google console, 2nd Wireshark):
Firewall is open to Google, I have added the site to captcha admin. Keys are correct. they have been checked and re-checked. I am not sure if I am missing something in the web.config, or if there is a setting in IIS manager that I am missing.
You can't troubleshoot your problem just based on these error messages. You can try the following methods to get more detailed error messages:
Run the site directly on the server – depending on the configuration of your site/server, you may be able to see the real error if you load the site from a browser located on the same server. You may need to turn off 'show friendly http errors.'
Temporarily add the following within the appropriate tags in your web.config file:
<configuration>
<system.webServer>
<httpErrors errorMode="Detailed" />
</system.webServer>
<system.web>
<customErrors mode="Off" />
<compilation debug="true" />
</system.web>
</configuration>
After you have added those, load the page again to see if you can get a more detailed error.
Open up IIS Manager and try to open up some of the different features by clicking on the icon. If there is an error in the web.config file and it can’t even parse it, sometimes you will get a helpful error in IIS Manager when you try to open a feature.
Look in Event Viewer. Sometimes you can find the detailed error logged in there, particularly Application Event Viewer.
Setup Failed Request Tracing. This will often give you details on the 500 error. This is especially helpful if it is an intermittent 500 error.
Look through the web log files. This is especially helpful for an intermittent 500 error. You can often parse the log files to see if there is a trend with a specific page that is throwing a 500 error.
In case someone else comes across this problem in the future, it was the WinHTTP proxy on the production server. The following code fixed my issue:
WebProxy proxyObject = new WebProxy("http://proxyserver:80/",true);
WebRequest req = WebRequest.Create("http://www.contoso.com");
req.Proxy = proxyObject;
More info can be found at https://learn.microsoft.com/en-us/dotnet/api/system.net.webproxy?view=net-5.0

SignalR self host connection issue

I recently created a proof of concept console application using SignalR (self host). It worked a treat for our use. The client connected fine and I was able to send updates from the server to the client. Lovely!
I've now transferred the code from the Console application to a winforms application for a prettier UI. Now that same client won't connect to the server yet it will still connect to the old Console version.
Winforms code:
string url = "http://localhost:8080";
using (WebApp.Start(url))
{
// Let the app know the server is up
}
Console code:
string url = "http://localhost:8080";
using (WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
Client connection code:
if (!connected)
{
int i = 0;
// Try 3 times
while (i <= 2)
{
try
{
string server = Properties.Settings.Default.Server + ":" + Properties.Settings.Default.PortNumber.ToString();
connection = new HubConnection(server);
connection.StateChanged += connection_StateChanged;
hub = connection.CreateHubProxy("MyHub");
connection.Start().Wait();
hub.On<string>("addMessage", param => { UpdateAlarmStatus(param); });
return true;
}
catch (Exception)
{
i++;
}
}
return false;
}
else
{
return true;
}
The error the client is reporting is:
Exception:Thrown: "No connection could be made because the target machine actively refused it" (System.Net.Sockets.SocketException)
A System.Net.Sockets.SocketException was thrown: "No connection could be made because the target machine actively refused it"
Time: 25/01/2015 15:09:23
Thread:Worker Thread[8232]
Why would the target machine (localhost) refuse itself which the Console version doesn't? I've been looking at the code over and over and I cannot see where I'm going wrong. Can anyone point me in the right direction please?
Thank you for reading.
Paul.
I suspect this is an issue with the configuration of your machine/infrastructure rather than the code itself, which looks fine at first glance.
Have you checked the console debug output in Visual Studio? I recently encountered an issue with similar symptoms and that was what gave me the initial clue to keep investigating. In my particular case, an exception was written to the console debug output that didn't make it to the client.
SignalR will normally negotiate with the server automatically to determine the best transport method to use. In a .NET client, the available options are LongPollingTransport, ServerSentEventsTransport and WebSocketTransport. So for some reason, your console app can use at least one of those methods, whereas your WinForms client cannot.
You can perhaps enable tracing to give you more information to work with. To do this, enter the below before you create the hub proxy:
hubConnection.TraceLevel = TraceLevels.All;
hubConnection.TraceWriter = Console.Out;
ASP.NET doco on SignalR tracing

ASHX sends HttpWebRequest with Impersonation On, which works to URLs on same server but not on Remote ArcGIS server

This is a complicated issue, so bear with me.
Scenario: using a ASHX proxy to relay request to an ArcGIS server.
Trying to use ASP.NET impersonation, so that the logged in ASP.NET user credentials are used by the proxy, when sending request to the ArcGIS server.
Issue: the proxy request to ArcGIS server is refused 401, even though I know the impersonated account (sean.ryan-B + sean.ryan) does have access.
There are 4 machines:
1. machine hosting proxy page. I am logged in as: sean.ryan-B
2. a test machine. I am logged in as sean.ryan-B
3. my laptop. I am logged in as sean.ryan
4. the arcgis server.
All 4 machines are on the same domain.
web.config:
<authentication mode="Windows"/>
<identity impersonate="true" /> <!-- userName="EUROPE\sean.ryan-B" password="xxx" -->
<authorization>
<deny users="?"/>
</authorization>
Test-1. Opening a test page, in same web app as proxy, via the proxy:
http://myHost.com/sean/ProxyAsp.Net/ArcGisProxy.ashx?http://myHost.com/sean/ProxyAsp.Net
[ok on all boxes 1-3]
This looks OK - the impersonation seems look OK,
since with impersonation OFF: WindowsIdentity.GetCurrent().Name = the AppPool account
with impersonation ON: WindowsIdentity.GetCurrent().Name = EUROPE\sean.ryan or EUROPE\sean.ryan-B
Test-2. opening an image that is hosted on the same IIS (but a different site), via the proxy:
http://myHost.com/sean/ProxyAsp.Net/ArcGisProxy.ashx?http://myHost.com:10400/sites/CaSPER/SiteAssets/CaSPER.jpg
[ok on boxes 1-3]
Test-3. opening the ArcGIS map URL, via the proxy:
http://myHost.com/sean/ProxyAsp.Net/ArcGisProxy.ashx?http://mapserver1.com/ArcGIS/rest/services/Global/2D_BaseMap_SurfaceGeology/MapServer?f=json&callback=dojo.io.script.jsonp_dojoIoScript1._jsonpCallback
[fails on boxes 2,3 but succeeds on the proxy host (box 1)!]
code for the ASHX code-behind:
public partial class ArcGisProxy : IHttpHandler, IReadOnlySessionState //ASHX implements IReadOnlySessionState in order to be able to read from session
{
public void ProcessRequest(HttpContext context)
{
try
{
HttpResponse response = context.Response;
// Get the URL requested by the client (take the entire querystring at once
// to handle the case of the URL itself containing querystring parameters)
string uri = context.Request.Url.Query;
uri = uri.Substring(1); //the Substring(1) is to skip the ?, in order to get the request URL.
System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)WebRequest.Create(uri);
{
req.Credentials = CredentialCache.DefaultCredentials; //this works on local box, with -B account. this is the account the web browser is running under (rather than the account logged into CaSPER with, as ASHX has separate server session).
req.ImpersonationLevel = TokenImpersonationLevel.Impersonation;
}
//to turn off caching: req.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
req.Method = context.Request.HttpMethod;
req.ServicePoint.Expect100Continue = false;
req.Referer = context.Request.Headers["referer"];
// Set body of request for POST requests
req.Method = "GET";
// Send the request to the server
System.Net.WebResponse serverResponse = null;
try
{
serverResponse = req.GetResponse();
}
catch (System.Net.WebException webExc)
{
//logger.Log(GetMyUrl(), webExc, context.Request);
response.StatusCode = 500;
response.StatusDescription = webExc.Status.ToString();
response.Write(webExc.ToString());
response.Write(webExc.Response);
response.Write("Username = " + context.User.Identity.Name + " " + context.User.Identity.IsAuthenticated + " " + context.User.Identity.AuthenticationType);
response.End();
return;
}
// Set up the response to the client
....
......
response.End();
}
catch (Exception ex)
{
throw;
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
note: the following changes, meant proxy request to the map server DOES succeed:
a) set the identity in the web.config to explicitly set username, password to the sean.ryan-B account:
-OR-
b) set the App Pool account to be sean.ryan-B and turn OFF impersonation in the web.config file.
however these changes are not acceptable for Production.
The problem seems to be that:
- ASP.NET impersonation works well enough for test page + image hosted on same IIS (tests 1 and 2)
but NOT well enough for the map server.
as far as I know, the ArcGIS map server is using Negotiate, and then Kerberos authentication.
With WireShark, I monitored a successful proxy request, and found:
after 401, proxy sends GET with AUTH using SPNEGO (Kerberos)
Has anyone had similar issue with ArcGIS proxy ?
My theory is, that the impersonation on box 1 'works better', because browser is running on same box as the proxy.
Could the ArcGIS Server (or the IIS site it is using) be restricted to prevent accepting impersonation ?
Any suggestions welcome ...
p.s. had a hard time getting this post through - had to format most of it as code, as s-o is detecting it as source code !

How to set IHttpAsyncHandler a timeout?

I've tried to set the executionTimeout in the web.config file:
<compilation debug="false" targetFramework="4.5">
<httpRuntime executionTimeout="30"/>
Looking at the IIS Manager Requests page I can see the requests are not being terminated after 30 seconds.
Should I implement a Timer inside my IHttpAsyncHandler?
With the apparent lack of built-in support for IHttpAsyncHandler timeouts, presumably you must manage your own timeout. Perhaps this is by design; after all you are choosing an asynchronous pattern - who does MSFT think they are trying to set a default timeout for your long running task?
What I would do is use ThreadPool.RegisterWaitForSingleObject to manage your polling with an appropriate timeout. Here is a code sample I use to avoid waiting on a web service that never returns:
private const int REQUEST_TIMEOUT = 30000; // miliseconds (30 sec)
private void CallService()
{
try {
string url = "somewebservice.com";
WebRequest request = WebRequest.Create(url);
// Asynchronously fire off the request
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(MyRoutineThatUsesTheResults), request);
// Handle timed-out requests
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(RequestTimeout), request, REQUEST_TIMEOUT, true);
}
catch (Exception ex) {
_logger.Error("Error during web service request.", ex);
}
private void RequestTimeout(object state, bool timedOut)
{
if (timedOut) {
WebRequest request = (WebRequest)state;
_logger.WarnFormat("Request to {0} timed out (> {1} sec)", request.RequestUri.ToString(), REQUEST_TIMEOUT / 1000);
request.Abort();
}
}
You will need an IAsyncResult to work with this approach but that's an established pattern you shouldn't have trouble running down samples about.
Also, you will run into issues when IIS decides to recycle your app pool / tear down your app domain while your polling is still running. If that's a condition you want to handle, you can use HostingEnvironment.RegisterObject.
You can try to add this to your web.config file:
<system.web>
<pages asyncTimeout="30" />
</system.web>
Its for PageAsyncTask, but just might be honored for IHttpAsyncHandler too?
If not, you may have more luck with the new HttpTaskAsyncHandler in ASP.Net 4.5 version:
http://www.asp.net/vnext/overview/whitepapers/whats-new#_Toc318097378
You would have to use RegisterAsyncTask check the link below
http://msdn.microsoft.com/en-us/magazine/cc163725.aspx

Silverlight error while calling a service

I am trying to call a service from a silverlight application, but I am getting the following error.
Uncaught Error: Unhandled Error in Silverlight Application An exception occurred during the operation, making the result invalid. Check InnerException for exception details.
This works fine locally. I don't know if it make any sense, but locally if I add the url of the webservice on a browser, I am getting the details page of the service. In the other hand, on production server, it prompts me to download it.
Does anyone know something about this?
Thanks
public MainPage() {
InitializeComponent();
Loaded += new System.Windows.RoutedEventHandler(MainPage_Loaded);
}
private void MainPage_Loaded(object sender, System.Windows.RoutedEventArgs e) {
var newsFeedWcfClient = new NewsFeedWCFClient();
newsFeedWcfClient.GetNewsFeedItemsCompleted += newsFeedWcfClient_GetNewsFeedItemsCompleted;
newsFeedWcfClient.GetNewsFeedItemsAsync();
}
void newsFeedWcfClient_GetNewsFeedItemsCompleted(object sender, GetNewsFeedItemsCompletedEventArgs e) {
var source = (IList<NewsFeed>)e.Result;
IList<CustomNewsFeed> customNewsFeeds = new List<CustomNewsFeed>();
foreach (var item in source) {
customNewsFeeds.Add(new CustomNewsFeed() {
ProductID = item.Products.ProductID,
ProductTitle = item.Products.Title,
Status = item.Text,
Thumb = string.Format("{0}/{1}", item.Products.Product_Photos.Select(pp => pp.PhotoPath).FirstOrDefault(), item.Products.Product_Photos.Select(pp => pp.PhotoName).FirstOrDefault()),
UserID = item.User.Id,
UserName = item.User.Username
});
}
NewsFeedLB.ItemsSource = customNewsFeeds;
}
The fact that on the production server it "prompts you to download" would suggest that the production web server doesn't know what to do with your .svc or .asmx file. It is treating it like a normal file (.txt, .pdf etc).
Have you got all of the required items installed in production. For instance, you need the correct .NET runtime to be installed. Also, ASP.NET needs to be installed and then enabled.
To determine exactly what is happening I would recommend installing Fiddler and using it to trace what is happening when the Silverlight app calls the server. I have found this approach to be invaluable when troubleshooting Silverlight to Web Service communication problems.

Resources