Team XSockets.NET

Custom "No Host" Protocol

Creating a custom protocol may sound like a hard and complex task, and it can be! However, it can also be very easy since it is all about how much complexity you add to your protocol. To just a a protocol like the one we use for connecting Putty is very easy. Writing something like AMQP or MQTT will require a lot more from you :)

If you will write the client your self your first decision will probably be about how your frames should be handled. Read about messages framing here. It is a pretty old post by Stephen Cleary, but a good one.

If there already is a client you will have no choice, you have to implement the protocol the way the client expects it to work (like we had to do with WebSockets for example).

The Challange

Every now and then our users have unusual requests. Recently we had a user that wanted to connect to XSockets with WebSockets from a browser. However, he did not want to host the .html in a webserver but instead open a socket from a .html file on disk. Like file://....

Since XSockets WebSocket-protocol expects the user to have a valid URI as origin this is not possible with the default WebSocket-protocol in XSockets. So how can we solve this?

The Solution

As it turns out this was very easy to implement. Basically all we had to do was to implement a new WebSocket-protocol with a specific sub-protocol for this purpose. We just inherit the default WebSocket-protocol in XSockets and then override the Match method to check if the handshake is from a client using the NoHost subprotocol.

NoHost Protocol

The protocol is so small that it is pasted in below. The important parts is when we check for the NoHost protocol in the Match method and the SetUri that just says that this origin is localhost

/// <summary>
/// A subprotocol for allowing websockets without a valid URI
/// This means that you can connect with a browser from c:\somepath\somefile.html
/// </summary>
[Export(typeof(IXSocketProtocol), Rewritable = Rewritable.No)]
public class NoHostProtocol : Rfc6455Protocol, IRfc6455Protocol
{
    /// <summary>
    /// Check that the client is using websocket and the sub-protocol for NoHost
    /// </summary>
    /// <param name="handshake"></param>
    /// <returns></returns>
    public override async Task<bool> Match(IList<byte> handshake)
    {            
        var s = Encoding.UTF8.GetString(handshake.ToArray());
        Composable.GetExport<IXLogger>().Debug("NoHost-Handshake: {s}", s);
        return
            Regex.Match(s, @"(^Sec-WebSocket-Version:\s13)", RegexOptions.Multiline).Success
            &&
            Regex.Match(s, @"(^Sec-WebSocket-Protocol:\sNoHost)", RegexOptions.Multiline).Success;
    }

    /// <summary>
    /// We do not have a calid request uri, set it to be localhost
    /// </summary>
    public override void SetUri()
    {            
        ConnectionContext.RequestUri = new Uri("http://localhost");
    }        

    public override IXSocketProtocol NewInstance()
    {
        return new NoHostProtocol();
    }
}

Test

Since this protocol is WebSocket based we can try it from the browser via a file on disk. So I created a index.html file and wrote a few lines of JavaScript

Note that we do not use the XSockets JavaScript API in this sample. We could ofcourse use that, but we want to show that you can connect with native websockets as well.

You can see the source on GitHub, but the only important part is really the usage of the new SubProtocol. So creating the WebSocket looks like this

//Open a native websocket with the new sub-protocol
var ws = new WebSocket('ws://localhost:4502', ['NoHost']);

The image below show the sample in action where 2 browsers talk to each other from the location file://.

No Host - SUbProtocol

Source Code

This simple sample is available on GitHub

results matching ""

    No results matching ""