I'm having trouble opening a C# SignalR 2.0 persistent connection to https://api.spark.io/v1/events from my console application. The code just hangs on the call to connection.Start(). When I turn ON tracing, I just get a message that it's connecting. (see attachment)
It works fine if I connect to my self-hosted web server at http://localhost:8080/echo. I'm starting to think it's an SSL cert problem. In curl, I need to pass -k to get it working.
Here's the code:
using Microsoft.AspNet.SignalR.Client;
using Microsoft.AspNet.SignalR.Client.Http;
using Microsoft.AspNet.SignalR.Client.Transports;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleClient
{
class Program
{
public static void Main(string args)
{
// This works with localhost URL, but fails with api.spark.io URL:
//Connection connection = new Connection("http://localhost:8080/echo");
//Connection connection = new Connection("https://api.spark.io/v1/events", "access_token=<my access_code>");
Connection connection = new Connection("https://api.spark.io/v1/events");
connection.Headers.Add("Authorization", "Bearer <my access_code>");
connection.TraceLevel = TraceLevels.All;
connection.TraceWriter = Console.Out;
connection.Received += data => Console.WriteLine(data);
//connection.Start(new ServerSentEventsTransport()).Wait();
connection.Start().Wait();
string line = null;
while ((line = Console.ReadLine()) != null)
{
connection.Send(line).Wait();
}
}
}
STATE: WAITCONNECT => PROTOCONNECT handle 0x60002d110; line 1187 (connection #0)
SSLv3, TLS handshake, Server hello (2):
SSLv3, TLS handshake, CERT (11):
SSLv3, TLS handshake, Server finished (14):
SSLv3, TLS handshake, Client key exchange (16):
SSLv3, TLS change cipher, Client hello (1):
SSLv3, TLS handshake, Finished (20):
SSLv3, TLS change cipher, Client hello (1):
SSLv3, TLS handshake, Finished (20):
SSL connection using AES256-SHA
Server certificate:
subject: C=US; postalCode=55408; ST=Minnesota; L=Minneapolis; street=1010 W Lake St Suite 100-105; O=Spark Devices; OU=PremiumSSL Wildcard; CN=*.spark.io
start date: 2014-01-13 00:00:00 GMT
expire date: 2015-01-13 23:59:59 GMT
subjectAltName: api.spark.io matched
issuer: C=GB; ST=Greater Manchester; L=Salford; O=COMODO CA Limited; CN=COMODO High-Assurance Secure Server CA
Thanks, @bko! Here’s what I get. Any ideas about how to resolve this?
C:\>curl -v https://api.spark.io/v1/events/?access_token=<my token>
* Adding handle: conn: 0x1dbde00
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x1dbde00) send_pipe: 1, recv_pipe: 0
* About to connect() to api.spark.io port 443 (#0)
* Trying 107.21.7.96...
* Connected to api.spark.io (107.21.7.96) port 443 (#0)
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS alert, Server hello (2):
* SSL certificate problem: self signed certificate in certificate chain
* Closing connection 0
curl: (60) SSL certificate problem: self signed certificate in certificate chain
Here’s a possible clue: I’m able to visit the Spark events stream from my Chrome browser, and it seems to have no problem with the cert. As a result, I’d have to guess that it’s a curl-specific issue. If that’s the case, then it probably doesn’t make sense to try and solve curl’s problem (since my end-goal is to get the C# client code working). However, I’m open to being wrong about curl being unrelated… What are your thoughts?
I think if we can find out why curl thinks the cert is self-signed (and therefore cannot be trusted) we will find out why C#.NET doesn’t like it either. Chrome has the cert you need, the COMODO CA cert that is the root for the Spark cert.
This stackoverflow post appears to have been answered by someone who posted code that inspects the certificate information programmatically. I can adapt this code to my situation… Do you think seeing the certificate information will be a clue?
Good question, @kareem613. I have posted the question here: http://forums.asp.net/t/1981411.aspx?problem+receiving+SSE+s+from+a+non+Microsoft+web+site
@bko: For the SSL cert issue, I suspect that it hangs on line 16 because the stream never ends (which is behavior-by-design for SSE). I wonder if there’s another way to look at the cert for this SSE-style, “web-page-that-never-ends”?
Good progress! I think you could read the cert programmatically from an endpoint other than events since the cert is the same. The devices end point with your access token will return your cores:
Kareem, can you find docs that say that? Since SignalR is gaining traction in the C# community, it would help us to be able to answer that question early before entrepreneurs building on Spark get too far down the SignalR path, if in fact you can't use SignalR independently.
UPDATE: I was able to create a simple web client to do an HTTP GET, and read the response line-by-line using this code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace WebClientForSSE
{
class Program
{
static void Main(string[] args)
{
if (args == null || args.Length == 0)
{
throw new ApplicationException("Specify the URI of the resource to retrieve.");
}
WebClient client = new WebClient();
// Add a user agent header in case the
// requested URI contains a query.
//client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
using (Stream s = client.OpenRead(args[0]))
{
using (StreamReader r = new StreamReader(s))
{
string line = null;
while (null != (line = r.ReadLine()))
{
Console.WriteLine(line);
}
}
}
}
}
}
So, that rules out any SSL cert issues with SignalR. And now I have (dumb) a way to access the event data. I’d have to parse the events myself, but that shouldn’t be difficult. To simplify my implementation, I would prefer using SignalR for this. But if it refuses to play nicely, I may have no choice. :-/
If anyone out there in the Spark community figures this out, please reply to this thread!
It turns out that Microsoft’s SignalR framework wasn’t designed to be a standards-compliant SSE client, as I had hoped. It confused me because it claims to use a “server-sent events” transport.
Hi, @kareem613. This issue is resolved. I was trying to access the event stream data from a C# client console application written in C#. Here’s how I was able to do it:
UPDATE: I found that the above WebClient -based implementation was unsuitable for use in a Windows Service because I could not Close() the StreamReader instance until the stream ends (which took a VERY long while – the cloud server’s response stream only ends once in a while, and not “on command”). The “Shutdown Windows Service” use case requires the ability to close the response stream at any given moment without reaching the end of the stream. Fortunately, the HttpWebRequest class gives the programmer the ability to Abort() the web request mid-stream. Here’s my console prototype that simulates the behavior needed for a Windows Service: it will shut down when the CapsLock button is turned OFF.
using System;
using System.IO;
using System.Net;
namespace HttpRequestForSSE
{
class Program
{
static void Main(string[] args)
{
if (args == null || args.Length == 0)
{
throw new ApplicationException("Specify the URI of the resource to retrieve.");
}
HttpWebRequest request = null;
while (Console.CapsLock)
{
try
{
request = WebRequest.CreateHttp(args[0]);
WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
string line = null;
while ((Console.CapsLock) && (null != (line = reader.ReadLine())))
{
if (line.Equals(string.Empty))
{
Console.WriteLine("(empty)");
}
else
{
Console.WriteLine(line);
}
}
if (null == line)
{
Console.WriteLine("Response stream ended.");
}
else
{
Console.WriteLine("Listener stop request received.");
break;
}
}
finally
{
if(null != request)
{
request.Abort();
}
}
}
Console.WriteLine("Listener stopped.");
Console.ReadLine();
}
}
}
The other subtle feature of this prototype is that it immediately and automatically re-starts the response stream whenever it ends, so as not to miss any events.
I have implemented a C# library that handles all of the Cloud Core functions (variables, functions and events). I am still working on the documentation, but I am pretty pleased so far. I am opening an issue to add the ability to ignore invalid ssl certs. Will be in there soon!
Hi @cloris - that’s awesome! When you’ve finished your implementation, please shoot me a note (zach at spark dot io); happy to include it on our community resources page. Please document it well so that others can use it!
Thanks @zach… will do! I wanted to get it out on GitHub while I put the finishing touches on it. I will ping you when I’ve cleaned up the code, finished the wiki and all of the other stuff.