Google App Engine, Dart, and Websockets

I’ve spent over a year programming in Dart, and the past month working with the Google App Engine. Getting the two to work together was very simple, thanks to excellent tutorials. But this week I’m working on more of an edge case, something that requires websockets. Here’s how I got it to work.

Prerequisites

Follow the tutorial spelling out how to get your Dart server up and running on the Google App Engine – it doesn’t take too long, just start here and keep going.

Quick Overview

Dart on Google App Engine runs inside a ‘managed VM’, which means in runs in a different environment than the typical sandbox Google provides (full comparison chart here). You get more power and more tools, but you have to flip all the switches to turn it on. We’ll need to tell GAE to open up a port for websockets, tell Dart what to do with it, and tell your local system to allow it.

Dart Code

Here’s a simplified version of my bin/server.dart file:

import 'dart:async';
import 'dart:io';
import 'package:appengine/appengine.dart';

main() {
 runAppEngine(httpRequestHandler);
 startWebSocketServer(8081);
}

void httpRequestHandler(HttpRequest req){
  //Handle requests normally
}

startWebSocketServer(int port) async {
  HttpServer server = await HttpServer.bind(InternetAddress.ANY_IP_V4, port);
  await for (HttpRequest req in server){
    WebSocket socket = await WebSocketTransformer.upgrade(req);
    //Use the socket!
  }
}

The function runAppEngine should be familiar. All we add is a bit to handle requests coming to port 8081. If the async / await keywords throw you off, take a look – it’s very powerful and make asynchronous code much easier to read.

app.yaml

Before this will work, you need to edit your app.yaml file to include some network settings. Here’s (the bottom chunk of) mine:

network:
 forwarded_ports:
 - 8081
 instance_tag: websocketserver

I’ve decided to send my websocket requests to port 8081, so I need to ‘forward’ that port to my server – GAE would bounce it otherwise. I’ve also set an instance_tag, which we’ll use later in the firewall settings on the Google Developers Console.

Allowing port 8081 in VirtualBox

Depending on your OS, this may be way off, but it was a necessary step for me on Mac OSX. I use VirtualBox, which helps run the docker file containing my Dart server for local testing. The port I had chosen, 8081, was blocked by VirtualBox by default.

Open up VirtualBox, and click Settings > Network > Port Forwarding. Add an entry that ‘forwards’ port 8081 to port 8081 (yep – dumb, I agree):

Protocol: TCP
Host IP: 127.0.0.1
Host Port: 8081
Guest Port 8081

Google App Engine

Hopefully by this point it works locally. But for it to work on Google App Engine, you need to use a static IP address instead of a URI, and you need to allow traffic through the default firewall.

In the Google Developers Console, under your project, scroll down to Networks and click on External IP Addresses. Change the type of the IP address your server is running on from ephemeral to static. So now you have a new websocket address, something like ws://1.2.3.4:8081/

Under Network, click on Firewall Rules. You’ll have to add a rule to allow traffic to your websocket port. Set the source filter to ‘allow from any source’ and add your settings to the ‘allowed ports’ section (mine just says “tcp:8081”). You can leave the ‘target tags’ blank, but you should add the instance_tag from your app.yaml file to make the rule more strict and accurate (‘websocketserver’ in my case).

And that’s it! Your websocket should work both locally and remotely now. If you want to test it out without writing more code, try a Chrome Extension like Simple Websocket Client.

Further Resources

Leave a Reply

Your email address will not be published. Required fields are marked *