Run Node.js in a Docker container


In this post, I explore how to run a Node.js web application in a Docker container based on the StrongLoop Process Manager image. I also have a requirement to export and deploy the Docker container to a server that lacks internet access.

docker-strongloop-pm.png

Create a new Node.js web app using express-example-app as the starting point

git clone https://github.com/strongloop/express-example-app.git

Head into the app folder and install dependencies

npm install

Start application using PM

slc start

Access http://localhost:3001 in a browser to ensure it works.

Shutdown PM

slc ctl shutdown

Create a new Docker container using StrongLoop PM image as the starting point

docker run --detach --restart=no --publish 8701:8701 --publish 3001:3001 --name strong-pm-container strongloop/strong-pm

Deploy example app by running following command in directory of app

slc deploy http://localhost:8701/ master

Access http://localhost:3001 in a browser to ensure it works.

Discover container’s id

docker ps -a

Commit container to a new image

docker commit 653811fd29f3 myimage

Save image to tar file

docker save -o myimage.tar myimage

Load image file in a new Docker instance (on another machine)

docker load -i myimage.tar

Run image in a new container

docker run --detach --restart=no --publish 3001:3001 --name strong-pm-container myimage

Access http://localhost:3001 in a browser to ensure it works.

Make things smart with HomeKit and Raspberry Pi


As an avid iOS user I have been keen on using HomeKit. That’s when I read about a new – and currently free – HomeKit app in the iOS App Store called Hesperus. I don’t have a HomeKit compatible thing at home, but a quick internet search revealed that I could run a HomeKit compatible service called homebridge on a Raspberry Pi. This post only goes so far as configuring a fictitious light bulb plugin that can be controlled remotely.

Setup Raspberry Pi Image

I decided to download a headless (console-only) version of Raspbian called RASPBIAN JESSIE LITE. Instructions for setting up an SD card appropriately can be found here. I tend to use the Windows based Win32 Disk Imager, I trust it and have used it – for as long as I can remember – to write Ubuntu ARM images.

Powering the Raspberry Pi

I didn’t want to use an HDMI display with the Raspberry Pi, and wanted to power it using my laptop. I have used a USB to serial adapter to do that in the past. This time, I went with Adafruit’s USB Serial TTL cable as described in this lesson. That done, I was able to power up and login to the Raspberry Pi using a serial terminal. I tend to use screen on Linux or Mac OS X

screen /dev/ttyUSB0

Configuring Wi-Fi

RASPBIAN JESSIE LITE lacks a full-fledged user interface, making Wi-Fi configuration slightly painful. I am using a Wi-Fi stick and had some issues getting the driver to work. Hopefully, you’ve got a Raspberry Pi 3, or a compatible Wi-Fi stick that does not require too much tinkering. You can also use Ethernet. The following can be used to check whether your network interface can be listed

ifconfig

Look for an interface called wlan0 if using Wi-Fi, or eth0 if using ethernet.

This is how you can create a configuration file for your Wi-Fi access point

wpa_passphrase your_SSID your_passphrase > your_SSID.conf

Copy the contents of your_SSID.conf and paste them into /etc/wpa_supplicant/wpa_supplicant.conf using any text editor. I used vi thus

sudo vi /etc/wpa_supplicant/wpa_supplicant.conf

Having done that, Wi-Fi was up and running. I had internet access, and could access the Raspberry Pi on the local network via ssh.

Installing packages

A few additional Linux packages and configuration steps are required before homebridge may be installed. Packages can be installed thus

sudo apt-get update
sudo apt install nodejs npm git libavahi-compat-libdnssd-dev

Updating Node.js

The version of Node.js installed by apt-get is rather dated, and will not work with homebridge. To update node, use the following commands

sudo npm install -g n
sudo n stable

Install homebridge

homebridge can be installed using npm thus

sudo npm install -g homebridge

Find and install plugins

To do anything interesting with homebridge you’ll require a plugin, and have it configured in ~/.homebridge/config.json. One simple plugin called homebridge-fakebulb can be installed thus

sudo npm install -g homebridge-fakebulb

Its sample configuration file can be used to create the config.json file mentioned above. This is what my config.json looks like

{
    "bridge": {
        "name": "Homebridge",
        "username": "CC:22:3D:E3:CE:32",
        "port": 51826,
        "pin": "031-45-155"
    },

    "description": "This has some fake accessories",

    "accessories": [
        {
            "accessory":      "FakeBulb",
            "name":           "Test lamp",
            "bulb_name":      "Lamp 1"
        }
    ],

    "platforms": []
}

Use with HomeKit

Apple’s HomeKit has been app-less since launch. Siri is the only way you were able to control HomeKit devices. HomeKit has a rich API and it didn’t take long for paid apps to appear in the App Store. Hesperus is a new free app that I opted to use to control homebridge.

Here’s Hesperus with the Homebridge peripheral paired and working, showing the Test lamp device’s status. I can control the Test lamp (turn it on/off) anywhere I have an internet connection because I have an Apple TV (generation 4 – but 3 should also work) at home. Apple TV needs to be signed into the same iCould account as the iOS device paired with the Homebridge peripheral.

IMG_1979

JSON-RPC


If your HTML5 application requires RPC (remote procedure call) semantics, JSON-RPC is an easy specification to implement. The code below allows sending requests and receiving responses, and leverages JQuery’s custom events to raise notifications (requests sent by server without an id).

        var nextId = 1;
        var messages = new Array();

        function Request(method, data) {
            this.method = method;
            // params is a C# keyword hence it is called data
            if (data)
                this.data = data;
        }

        Request.prototype.execute = function (callback) {
            if (callback) {
                this.id = nextId++;
                messages[this.id] = callback;
            }
            // Implement send so that the request gets sent to a server
            send(JSON.stringify(this));
        }

        // Whoever receives a stringified message from server must call dispatchMessage
        function dispatchMessage(message) {
            var o = JSON.parse(message);

            if (Array.isArray(o)) {
                dispatchBatch(o);
            } else {
                dispatch(o);
            }
        }

        function dispatchBatch(array) {
            array.forEach(function (message) {
                dispatch(message);
            })
        }

        function dispatch(message) {
            if (message.method) {
                // request
                $(document).trigger(message.method, message);
            } else {
                // response
                var callback = messages[message.id];
                if (callback) {
                    messages = messages.filter(function (elem) {
                        return elem.id == message.id; // remove
                    });
                    callback(message);
                } else {
                    console.log("Unknown response " + JSON.stringify(message));
                }
            }
        }

The following snippet shows how to handle a notification called foo, sent by the server

        $(document).on("foo", function (e, request) {
            // do something with request.data
        });

The following code demonstrates how to send a new request for a method called bar, and handle the corresponding response

            // data is some object that will be stringified
            var request = new Request("bar", data);
            request.execute(function (response) {
                // do something with response.error or response.result
            })

I’ll leave the server-side code to handle JSON-RPC as an exercise to the reader, or for a post in the future.

Using hints inside text fields instead of labels


The following code example demonstrates replacing labels with hints that appear as temporary values within text fields; akin to placeholder attribute in HTML5.

A custom attribute called data-hint-value contains the hint value to which a text field gets initialized. That value represents what typically would be the value of a label associated with the text field. It is cleared when the text field receives focus, and filled with hint value if text field is empty on blur.

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>
    <link rel="shortcut icon" href="favicon.ico">
    <script type="text/javascript" src="js/jquery-2.1.4.min.js"></script>
</head>
<body>
    <input id="name" type="text" data-hint-value="your name" />
    <script type="text/javascript">

        function setAllTextToHint() {
            $(":text").each(function (index) {
                hint = $(this).attr("data-hint-value");
                if (hint) $(this).val(hint);
            });
        }

        function isTextValueValid(id) {
            var ret = false;
            $(":text").each(function (index) {
                thisId = $(this).attr("id");
                if (thisId == id) {
                    hint = $(this).attr("data-hint-value");
                    value = $(this).val();
                    if (value == hint || value == "") {
                        alert("Please specify " + hint);
                        $(this).val(hint);
                        $(this).focus();
                        return;
                    } else {
                        ret = true;
                        return;
                    }
                }
            });
            return ret;
        }

        $(":text").focus(function () {
            hint = $(this).attr("data-hint-value");
            if ($(this).val() == hint)
                $(this).val("");
        });

        $(":text").blur(function () {
            hint = $(this).attr("data-hint-value");
            if ($(this).val() == "") {
                $(this).val(hint);
            }
        });
    </script>

setAllTextToHint is a helper function that sets all text fields to their hint values. isTextValueValid may be used to check whether a text field contains some value, and alerts the user when it does not. Tweak these as you see fit.

Manipulating JSON using Json.NET


Json.NET makes it convenient to manipulate JSON in C# using dynamic programming. Let’s start with a JSON representation we want to create

{
	"menu": {
		"id": "file",
		"value": "File",
		"popup": {
			"menuitem": [
				{
					"value": "New",
					"onclick": "CreateNewDoc()"
				},
				{
					"value": "Open",
					"onclick": "OpenDoc()"
				},
				{
					"value": "Close",
					"onclick": "CloseDoc()"
				}
			]
		}
	}
}

Here’s how the Json.NET object representation can be created in C# using dynamic programming

dynamic jobj = JObject.FromObject(new 
{
	menu = new
	{
		id = "file",
		value = "File",
		popup = new
		{
			menuitem = new []
			{
				new
				{
					value = "New",
					onclick = "CreateNewDoc()"
				},
				new
				{
					value = "Open",
					onclick = "OpenDoc()"
				},
				new
				{
					value = "Close",
					onclick = "CloseDoc()"
				}
			}
		}
	}
});

To serialize it

var json = JsonConvert.SerializeObject(jobj);
Console.WriteLine(json);

To deserialize JSON string representation to dynamic object

dynamic jobj = JsonConvert.DeserializeObject(json);
Console.WriteLine(jobj.menu.id);

It is fairly easy to extend the object representation and add new items

jobj.foo = new JArray()
{
	new JObject() {
		new JProperty("bar", 10)
	},
	new JObject() {
		new JProperty("bar", 20)
	}
};

That adds a new property to jobj called foo that references a new array containing two objects.

WebSockets with WCF


This post demonstrates an elementary chat service constructed using WCF and WebSockets. A custom binding that leverages WebSocket support in httpTransport is used. JSON is serialized and deserialized using byteStreamMessageEncoding encoding. Use httpsTransport for secure transport.

Service interface

The service interface is used to receive connection requests and messages from clients. It has only one method, as shown below.

using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;

namespace ChatService
{
    [ServiceContract(CallbackContract = typeof(IChatServiceCallback))]
    interface IChatService
    {
        [OperationContract(IsOneWay = true, Action = "*")]
        Task SendMessage(Message message);
    }
}

Callback interface

The callback interface is used to send messages back to the clients.

using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;

namespace ChatService
{
    [ServiceContract]
    interface IChatServiceCallback
    {
        [OperationContract(IsOneWay = true, Action = "*")]
        Task ReceiveMessage(Message message);
    }
}

Service implementation

The service implementation receives messages from clients, and fires them off to other clients, using their respective callback interface. Messages are sent to clients who have sent messages to a chat room, and are still connected.

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net.WebSockets;
using System.Runtime.Serialization.Json;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;

namespace ChatService
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class ChatServiceImplementation : IChatService
    {
        static ConcurrentDictionary<string, ConcurrentDictionary<string, Chatter>> rooms = 
            new ConcurrentDictionary<string, ConcurrentDictionary<string, Chatter>>();

        public async Task SendMessage(Message message)
        {
            if (message.IsEmpty) return;

            byte[] body = message.GetBody<byte[]>();
            MemoryStream stream = new MemoryStream(body);
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Chatter));
            Chatter chatter = (Chatter)ser.ReadObject(stream);
            IChatServiceCallback callback = OperationContext.Current.GetCallbackChannel<IChatServiceCallback>();
            chatter.Callback = callback;

            IChannel channel = (IChannel)callback;
            channel.Faulted += channel_Faulted;
            channel.Closed += channel_Closed;

            ConcurrentDictionary<string, Chatter> room;
            if (!rooms.TryGetValue(chatter.Room, out room))
            {
                room = new ConcurrentDictionary<string, Chatter>();
                rooms.TryAdd(chatter.Room, room);
            }
            Chatter existingChatter;
            if (!room.TryGetValue(chatter.Nickname, out existingChatter))
            {
                room.TryAdd(chatter.Nickname, chatter);
            }
            else if (existingChatter.Callback != chatter.Callback)
            {
                existingChatter.Callback = chatter.Callback;
            }
            foreach (Chatter c in room.Values)
            {
                if (((IChannel)c.Callback).State == CommunicationState.Opened)
                    await c.Callback.ReceiveMessage(CreateMessage(body));
            }
        }

        private void channel_Closed(object sender, EventArgs e)
        {
            // Clean up
        }

        private void channel_Faulted(object sender, EventArgs e)
        {
            // Clean up
        }

        private Message CreateMessage(byte[] message)
        {
            Message channelMessage = ByteStreamMessage.CreateMessage(new ArraySegment<byte>(message));

            channelMessage.Properties["WebSocketMessageProperty"] =
                new WebSocketMessageProperty { MessageType = WebSocketMessageType.Text };

            return channelMessage;
        }
    }
}

Here’s the Chatter class, used to store state.

using System.Runtime.Serialization;

namespace ChatService
{
    [DataContract()]
    class Chatter : IExtensibleDataObject
    {
        [DataMember(Name = "nickname", IsRequired = true)]
        public string Nickname { get; set; }
        [DataMember(Name = "room", IsRequired = true)]
        public string Room { get; set; }
        [DataMember(Name = "message", IsRequired = true)]
        public string Message { get; set; }
        public IChatServiceCallback Callback { get; set; }

        public ExtensionDataObject ExtensionData { get; set; }
    }
}

App.config

App.config below creates a customBinding and associates it with the chat service.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="webSocketHttpBinding">
          <byteStreamMessageEncoding/>
          <httpTransport>
            <webSocketSettings transportUsage="Always" createNotificationOnConnection="true"/>
          </httpTransport>
        </binding>
      </customBinding>
    </bindings>
    <services>
      <service name="ChatService.ChatServiceImplementation">
        <endpoint address="http://localhost:8004/chatservice" binding="customBinding" bindingConfiguration="webSocketHttpBinding" contract="ChatService.IChatService"/>
      </service>
    </services>
  </system.serviceModel>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
  </startup>
</configuration>

Self hosted console app

A console app that hosts the service is shown below.

using System;
using System.ServiceModel;

namespace ChatServiceHost
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost host = new ServiceHost(typeof(ChatService.ChatServiceImplementation));
            host.Open();

            Console.WriteLine("Hit Enter to quit.");
            Console.ReadLine();
        }
    }
}

Chat web page

The following web page uses jQuery and WebSocket to send/receive messages to/from chat rooms.

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Chat</title>
    <script type="text/javascript"
            src="https://code.jquery.com/jquery-2.1.4.min.js">
    </script>
</head>
<body>
    <form>
        Nickname <br/>
        <input id="nickname" type="text" value="nickname" /><br/>
        Room 

        <input id="room" type="text" value="room" /><br/>
        Message 

        <input id="message" type="text" value="message" /><br/>
        <input id="send" type="button" value="Send" />
    </form>

    <p>
        Messages
        <div id="messages">

        </div>
    </p>
    <img src="image/HTML5_Logo_64.png" />

    <script type="text/javascript">
        var url = 'ws://localhost:8004/chatservice'; // base url
        var connection = null;
        $(document).ready(documentReady);
        function documentReady() {
            $("#send").click(sendClick);
        }
        function sendClick() {
            if (connection == null) {
                connection = new WebSocket(url);
            } else {
                sendMessage();
                return;
            }
            connection.onopen = sendMessage;
            connection.onmessage = receiveMessage;
            connection.onerror = function (e) {
                alert('error ' + e);
                connection = null;
            };
        }
        
        function sendMessage() {
            var chatter = new Object();
            chatter.nickname = $('#nickname').val();
            chatter.room = $('#room').val();
            chatter.message = $('#message').val();
            connection.send(JSON.stringify(chatter));
        }
        function receiveMessage(e) {
            var chatter = JSON.parse(e.data);
            var message = chatter.nickname + '@' + chatter.room + ' said ' + chatter.message + '<br/>';
            $('#messages').prepend(message);
        }
    </script></body>
</html>

Testing

Open the HTML file in a modern web browser, and you’ll see the chat page. Open the same page in additional tabs. Once you send a message to one or more chat rooms in a tab, messages posted from other tabs using different nicknames to the same chat rooms, should appear in the Messages area in reverse chronological order.

One really pesky problem is the following exception you get, when you try to send the same Message instance to multiple callbacks

A property with the name 'TransactionFlowProperty' already exists.

I have also experimented with testing scalability by hitting the service using thor, and have noted that InstanceContextMode.Single behavior is the most responsive and reliable.

I’d like to acknowledge Zhuyun Dai’s article at CodeProject for giving useful insights with regards to using byteStreamMessageEncoding.

Listening to FM radio using RTL-SDR


I am tinkering with an RTL-SDR dongle to listen to FM radio, on a MacBook Pro with OS X Yosemite, and a Windows 8.1 VM running on Parallels Desktop 10. There are several software options available. I’ll go into those that I tried, others that I didn’t, and one that is surprisingly good.

librtlsdr

This is a multi-platform library available as open source. On Mac OS X, you can obtain it using homebrew

brew install librtlsdr

Here’s how you can use the rtl_fm sample available with the library, to record wide-band FM

rtl_fm -f 88700000 -M wbfm - | ffmpeg -f s16le -ar 17000 -ac 2 -i - wbfm.wav

I pipe the output of rtl_fm, which is in signed 16-bit little-endian PCM format, to ffmpeg to produce a WAV file. The WAV file can then be played using Audacity, or any music player of your choice.

SDR#

SDR# is a Windows freeware that used to be open source in the past. It is fairly easy to listen to FM radio by following the SDR# FM radio tutorial from Adafruit. The quality of audio is not so good on a Windows VM.

I attempted to build an older open source version of SDR# using Xamarin Studio for Mac OS X. It fails to run because run-time dependencies such as libsndfile and portaudio, installed through homebrew, are 64-bit binaries. 64-bit build of mono 3.12.0 and libgdiplus (the latter depends on cairo installed through homebrew) from source also does not work due to crash in native code invoked by System.Windows.Forms.XplatUICarbon.CGDisplayBounds. Windows Forms on 64-bit Mono on Mac OS X is currently a no-go.

There’s HDSDR, another Windows freeware, that I haven’t tried. A Mac OS X port of gnuradio is something else I want to try, but it is only available through MacPorts. I don’t use MacPorts, and building gnuradio from source looks daunting due to the number of dependencies.

Radio Receiver Chrome App

Radio Receiver

The open source Radio Receiver App from a developer at Google, is probably the best way to listen to FM radio using RTL-SDR. The sound quality is awesome, and it’s mostly implemented in JavaScript! How cool is that!?