Skip to content
This repository has been archived by the owner on Nov 16, 2023. It is now read-only.

Architecture

Aaron Bockover edited this page Nov 15, 2017 · 3 revisions

Workbooks Client ⇄ Agent Communication Model

Workbooks uses HTTP as the communications model between the client running on your computer, and the agents running inside our workbook apps, either alongside the client on the same computer, or in the iOS and Android emulators. Both clients and agents run a server, used for different purposes.

The Client Server

The client web server starts as soon as the client does. It has two jobs:

  1. It powers the workbook editor surface—it helps us load the Monaco editor, CSS, and also load resources from NuGet packages! Workbooks integrations can ship both agent-side and client-side integrations—the latter are loaded via the client web server.

  2. It also serves as a way for agents to identify themselves to the client. When a workbook is started, we don't always know how to connect to the agent, but we can tell the agent how to connect to us. This initial connection from the agent to the client gives us a URI we can use to connect to the agent.

The Agent Server

The agent server starts once the workbook app starts up—it is the main entry point for everything that the client does on the agent. It exposes a simple RPC-style server: messages come in from the client, are deserialized into real types, and a handler is invoked.

Messages can return results to the client as part of the HTTP response, but they are not required to—they may use the message channel that is opened between the client and the agent (using a long-polling HTTP connection) to pass messages back to the client. This is used to send back evaluation results, live updating for console output capture, and results from compilation/execution integrations using our SDK.

Binding Particulars

Both servers are always bound to a random port, on the local interface. There are slight differences, however, in the way that that binding is done, as you can see in HttpServer, which is the base that powers both the client and agent servers.

For the Mac client and Xamarin-based agents, we can always bind to 127.0.0.1, as Xamarin's implementation of HttpListener is fully managed and does not have the restrictions that the http.sys-based implementation on Windows does.

For the Windows client, and for agents running on Windows, we ran into multiple sets of issues:

  1. Isues with IPv6 vs. IPv4 connectivity, particularly when connecting to the iOS and Android agents, which need to have their connections tunneled. localhost can mean either ::1 or 127.0.0.1, and IPv6 seems to be preferred. When the tunnel tries to pass this, the operating system on the other end resets the connection—as far as it's concerned, we're talking to a closed port, as the agent isn't listening on IPv6. For various reasons, we couldn't change the agents or tunnels—we had to change the server to be explicit about using IPv4.

  2. However, the HttpListener on Windows has limitations on ports and prefixes, and these changed in the Windows 10 timeframe. On previous versions of Windows, you can't bind to 127.0.0.1, only localhost, whereas on Windows 10, you're able to bind to both prefixes. Thus, we detect the current Windows version and adjust our binding prefix.