Category Archives: ASP.NET Core

SignalR Core Part 2/3: ASP.Net Core Sockets

Disclaimer: SignalR Core is still in early stages and changing rapidly. All information in this post is subject to change.

To test some of the scenarios described in the first part of this mini-series I came up with an idea for a relatively simple application where users can report to the server the weather at their location and their report will be broadcast to all the connected clients. I called this application SocialWeather. The central part of the system is an ASP.Net Core application running SignalR Core server. The server can handle massages received in one of the following formats – JSON, Protobuf and pipe (the pipe format is a simple format I created where the data is separated by the pipe (|) and the message ends with the new line character (\n)). I also created 3 different clients – a JavaScript clients using the JSON format, a C# client using Protobuf and a lua client using the pipe format. The JavaScript client is part of a web page served by the same application that hosts the server. The C# client is a console application that that can send and receive Protobuf messages. The lua client is the most interesting as it runs on an ESP8266 development board with the NodeMCU firmware. The whole system is using the “socket” level of the new SignalR which is a kind of counterpart of persistent connection in the previous version of SignalR (so no hubs API here). All the clients use bare websockets to connect to the server (in other words there is no SignalR client involved).

The SocialWeather server, which includes the JavaScript client is a sample project in the SignalR repo and you can run it yourself – just clone the repo, run build.cmd/build.sh to install the correct version of the runtime and restore packages. Then go to the samples/SocialWeather folder and start the server with dotnet run.

The C# and lua clients are in my personal repo. Running the C# client is straightforward. After you clone the repo you need to restore packages update the URL to the SignalR server and run the client with dotnet run. Each time you press Enter the client will generate a random weather report and send it to the server. If you type “!q:” the client will exit.

The lua client is meant to run on an ESP8266 compatible board with NodeMCU firmware installed. Preparing the board to run the client requires a bit of work. The first step is to set up serial communication to the board. If the module you have is equipped with a USB port (I have the Lolin v3 board which does have a USB port) it should be enough to install a VCP (Virtual COM Port) drivers (you can find the drivers here http://www.ftdichip.com/FTDrivers.htm or here http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx). If your board doesn’t have a USB port, you will need to use an additional module (e.g.  Arduino)  for USB-to-serial translation (you can find tutorials about setting it up on the web).

The next step is to make sure that your board has the right firmware. It needs to be a NodeMCU firmware with net, http, wifi, and websocket support. You can request a build here and follow steps in this tutorial to re-image your board. When the board is ready you should be able to connect to it using a serial terminal (I used screen on Mac and Putty on Windows). On Windows determine the COM port number the board is using using the Device Manager. On Mac it will be one of the /dev/tty* devices. The speed needs to be set to 115200. When using Putty make sure the Flow Control setting is set to None or the communication will not be working correctly.

puttyflowcontrol

Once on the device you need to connect it to your wireless network by sending the following commands (you need to replace SSID with the name of the network and PWD with the password or an empty string for open networks):

wifi.setmode(wifi.STATION)
wifi.sta.config(SSID, PWD)
wifi.sta.connect()

We are now ready to run the client. If you haven’t already, clone the repo containing the client go to src/lua-client folder and update the URL to the SocialWeather SignalR server. Now you can transfer the file to the device (if you are connected to the device with the terminal you need to disconnect). The nodemcu-uploader Python script does the job.

If all stars aligned correctly you should be able now to start the client by executing:

dofile(“social-weather.lua”)

it will print a confirmation message once it connected  successfully to the server and then will print weather reports sent by other client. You can also send your own weather report just by typing:

ws:send(“72|2|0|98052\n”)

and pressing Enter.

The command may look a bit cryptic but is quite simple. The ws is a handle to the websocket instance created by the social-weather.lua script. send is a method of the websocket class so we literally invoke the send method of websocket interactively. The argument is a SocialWeather report in the pipe format: a pipe separated list of values – temperature, weather, time, zipe code – terminated with \n.

This is what it looks like when you run all three clients:

social-weather

Let’s take a look at how things work under the hood. On the server side the central part is the SocialEndPoint class which handles the clients and processes their requests. If you look at the loop that processes requests it does not do any parsing on its own. Instead it offloads parsing to a formatter and deals with strongly typed instances. Formatter is a class that knows how to turn a message into an object of a given type and vice versa. In case of the SocialWeather application the only kind of messages sent to and from the server are weather reports so this is the only type formatters need to understand.

When sending messages to clients the process is reversed. The server gives the formatter a strongly typed object and leaves it to formatter to turn it into a valid wire format.

How does the server know which formatter to use for a given connection? All available formatters need to be registered in the DI container as well as mapped to a type they can handle and format. When establishing the connection, the client sends the format type it understands as a query string parameter. The server stores this value in the connection metadata and uses later to resolve the correct formatter for the connection.

On the client side things are even simpler. The lua client has just 30 lines of code half of which is concerned with printing weather reports in a human readable form. Because the format of the connection cannot change once the connection is established message parsing can be hardcoded. The rest is just setting up the websocket to connect to the server and react to incoming message notifications.

The C# client is equally simple. It contains two asynchronous loops – one for receiving messages (weather reports) from the server and one for sending messages to the server. Again, handling the wire format (which in this case is Protobuf) is hardcoded in the client.

This is pretty much all I have on ASP.Net Core Sockets. With a simple application we were able to validate that the new version of SignalR can handle many scenarios the old one couldn’t. We were able to connect to the server from different platforms/environments without using a dedicated SignalR client. The server was capable of handling clients that use different and custom message formats – including a binary format (Protobuf). Finally, all this could be achieved with a small amount of relatively simple code.

SignalR Core Part 1/3: Design Considerations

Disclaimer: SignalR Core is still in early stages and changing rapidly. All information in this post is subject to change.

A few months ago, we started working on the new version of SignalR that will be part of the ASP.Net Core framework. Originally we just wanted to port existing code and iterate on it. However, we soon realized that doing so would prevent us from enabling new scenarios we wanted to support. The original version of SignalR was designed around long polling (note that back in the day support for websockets was not as common as it is today – it was not supported by many web browsers, it was not supported in .NET Framework 4, it was not (and still isn’t) supported natively on Windows 7 and Windows 2008 R2). A JSON based protocol was baked in and could not be replaced which blocked a possibility of using other (e.g. binary) formats. Starting the connection was heavy and complicated – it required sending 3 HTTP requests whose responses had to be correlated with messages sent over the newly created transport (you can find a detailed description of the protocol in SignalR on the wire – an informal description of the SignalR protocol – a post I wrote on this very subject). This basically meant that a dedicated client was required to talk to a SignalR server. In the old design the server was centered around MessageBus – all messages and actions had to go through the message bus. This made the code very complex and error prone especially in scale-out scenarios where all the servers were required to have the same data. The state (e.g. cursors/message ids, groups tokens etc.) was kept on the client which would then send it back to the server when needed (e.g. when reconnecting). The need of keeping the state up-to-date significantly increased the size of the messages exchanged between the server and the client in most of the non-trivial scenarios.

In the new version of SignalR we wanted to remove some of the limitations of the old version. First, we decided to no longer use long polling as the model transport. Rather, we started with a premise that a full duplex channel is available. While this might sound a lot of like websockets we are thinking that it will be possible to take it further in the future and support other protocols like TCP/IP. Note, it does not mean that the long polling and server sent events transports are going away. Only, that we would not drag better transports down to the standards of worse transports (e.g. websockets supported binary format but long polling (until XmlHttpRequest2) and server sent events didn’t so in the old version of SignalR there was no support for binary messages. In the new version we’d rather base64 encode messages if needed and let users use what websockets offers). Second, we did not want to bake in any specific protocol or message format. Sure, for hub invocations we will still need to be able to get the name of the hub method and the arguments but we will no longer care how this is represented on the wire. This opens the way to using custom (including binary) formats. Third, establishing the connection should be lightweight and connection negotiation can be skipped for persistent duplex transports (like websockets). If a transport is not persistent or uses separate channels for sending and receiving data connection negotiation is required – it creates a connection id which will be used to identify all the requests sent by a given client. However, if there are no multiple requests because the transport is full duplex and persistent (like in the case of websockets) the connection id is not needed – once the connection is established in the first request it is used to transfer the data in both directions. In practice, this means that you can connect to a SignalR server without a SignalR client – just by using bare websockets.

There are also a few things that we decided not to support in the new SignalR. One of the biggest ones was the ability re-establish a connection automatically if the client loses a connection to the server. While it may not be obvious, the reconnect feature has a huge impact on the design, complexity and performance of SignalR. Looking at what happens during reconnect should make it clear. When a client loses a connection, it tries to re-establish it by sending the reconnect request to the server. The reconnect request contains the id of the last message the client received and the groups token containing the information about groups the client belongs to. This means that the server needs to send the message id with each message so the client can tell the server what was the last message it received. The more topics the client is subscribed to the bigger the message id gets up to the point where the message id is much bigger than the actual message.

Now, when the server receives a reconnect request it reads the message id and tries to resend all the messages the client missed. To be able to do that the server needs to keep track of all messages sent to each client and buffer at least some recent messages so that it can resend them when needed. Indeed, the server has a buffer per connection which it uses to store recent messages. The default size of that buffer is 1000 messages which creates a lot of memory pressure. The size of the buffer can be configured to make it smaller but this will increase the probability of losing messages when a reconnect happens.

The groups token has similar issues – the more groups the client belongs to the bigger the token gets. It needs to be sent to the client each time the client joins or leaves a group so the client can send it back in case of reconnects to re-establish group membership. The size of the token limits the number of groups a client can belong to – if the groups token gets too big the reconnect attempt will fail due to the URL being bigger than the limit.

While auto-reconnect will no longer be supported in SignalR users can build their own solution to this problem. Even today people try restarting their connection if it was closed by adding a handler to the Closed event in which they start a new connection. It can be done in a similar fashion in SignalR core. It’s true that the client will no longer receive messages it missed but this could happen even in the old SignalR – if the number of message the client missed was greater than the size of the message buffer the newest messages would overwrite the oldest messages (message buffer is a ring buffer) so the client would never receive the oldest messages.

Another scenario we decided not to support in the new version of SignalR was allowing clients to jump servers (a multi-server scenarios). Before, the client could connect to any server and then reconnect or send a data to any other server in the farm. This required that all servers had all the data to be able to handle requests from any client. The way it was implemented was that when a server receive a message it would publish it to all the other SignalR servers via MessageBus. This resulted in a huge number of messages being sent between SignalR servers.

(Side note. Interestingly, the scenario of reconnecting to a different server than the one the client was originally connected to often did not work correctly due to server misconfiguration. The connection token and groups token are encrypted by the server before sending them to the client. When the server receives the connection token and/or groups token it needs to be able decrypt it. If it cannot, it rejects the request with the 400 (Bad Request) error. The server uses the machine key to encrypt/decrypt the data so, all machines in the farm must have the same machine key or the connection token (which is included in each request) encrypted on one server can’t be decrypted on another server and the request fails. What I have seen several times was that servers in the farm had different machine keys so, reconnecting to a different server did not actually work.)

In the new SignalR the idea is that the client sticks only to one server. In multi-server scenarios there is a client to server map stored externally which tells which client is connected to which server. When a server needs to send a message to a client it no longer needs to send the message to all other servers because the client might be connected to one of them. Rather, it checks what server the client is connected to and sends the message only to this client thus the traffic among SignalR server is greatly reduced.

The last change I want to talk about, somewhat related to the previous topic, is removing the built-in scale-out support. In the previous version of SignalR there was one official way of scaling out SignalR servers – a scale out provider would subclass the ScaleoutMessageBus and leave all the heavy lifting to SignalR. It sounds good in theory but with the time it became apparent that with regards to scale-out there is no “one size fits all” solution. Scaling out an applications turned out to be very specific to the application goals and design. As a result, many users had to implement their own solution to scaling out their applications yet still paid the cost of the built-in scale-out (even when using just one server there is an in-memory message bus all messages go through). While, scale-out support is no longer built-in the project contains a Redis based scale-out solution that can be used as-is or as a guidance to create a custom scale-out solution.

These are I think the biggest design/architecture decision we have made. I believe that they will allow to make SignalR simpler, more reliable and performant.

Running ASP.NET Core Applications with IIS and Antares (Azure Websites)

I have seen a few articles (including official docs on http://docs.asp.net) about publishing and running  ASP.NET Core applications in IIS (or Azure/Antares). Unfortunately, I was not satisfied by either of them. Yes, they showed steps you need to follow to make things work. Yes, they touched on some aspects of how things work. No, they did not explain what’s really happening, why it’s happening and how the blocks fit together. Hence this post – something I would like to read if I wanted to run my ASP.NET Core application using IIS or Azure.

Before we can get into details we need to understand how things work at a high level. The most important thing is that ASP.NET Core applications are no longer tightly coupled to IIS as it was with previous versions. Rather, IIS is acting now merely as a reverse proxy and the application itself runs as a separate process using the Kestrel HTTP server. Decoupling ASP.NET from IIS was necessary to enable running ASP.NET Core applications on other platforms. It also makes development easier because it allows to avoid the overhead of IIS/IIS Express during development by making it possible to run your application directly using Kestrel. Note that in production environment it is recommended to always run Kestrel behind a reverse proxy like IIS or  NGINX.

Going back to IIS HTTP requests are handled as follows:

  1. IIS receives a request
  2. IIS (ASP.NET Core Module) starts the ASP.NET Core application in a separate process (if the application is not already running) – i.e. the application no longer runs as w3wp.exe but as dotnet.exe or myapp.exe
  3. IIS forwards the  request to the application
  4. Application processes the request and send the response to IIS
  5. IIS forwards the response to the client

Out of these 5 steps, step two is the most interesting, the most complicated and the most fragile. There is a few pieces that need to be aligned to successfully start an ASP.NET Core application from IIS: ASP.NET Core Module, web.config file and application configuration. Let’s take a look at them one by one and discuss their role.

ASP.NET Core Module (sometimes abbreviated to ANCM) is a native IIS module that starts the application and implements reverse proxy functionality. It is installed as part of ASP.NET Core tooling for Visual Studio or can be installed separately – (the Windows (Server Hosting) package from https://www.microsoft.com/net/download.

web.config – tells IIS to use ASP.NET Core Module to process requests. It also tells ASP.NET Core Module what process (application) to start. Note, that you don’t use web.config to configure your application – it is only used by IIS. Here is how a typical web.confing of an ASP.NET Core application looks like:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="dotnet" arguments=".\HelloWorld.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
  </system.webServer>
</configuration>

Application configuration – a typical Main method of an ASP.NET Core application looks like this:

var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

from the perspective of running the application using IIS the lines that are important are UseKestrel and UseIISIntegration.  UseKestrel configures Kestrel as the application web server. This is important from IIS perspective since you can’t use WebListener and IIS together at the moment (https://github.com/aspnet/IISIntegration/issues/8). UseIISIntegration does a bit of magic to fulfill ASP.NET Core Module expectations and registers the IISMiddleware.

With the information above we can now drill into how IIS starts ASP.NET Core applications. When IIS  receives a request for an ASP.NET Core application it passes the request to the ASP.NET Core Module. IIS was configured to do this by the following entry in the the web.config file:

<handlers>
  <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>

Upon receiving the request the ASP.NET Core Module will attempt to start the application if it is not already running. The name of the process to start and its arguments are specified in web.config  as the processPath and arguments attributes on the aspNetCore element. ASP.NET Core Module also sets a few environment variables for the application process – ASPNETCORE_PORT , ASPNETCORE_APPL_PATH and ASPNETCORE_TOKEN. Here is where the UseIISIntegration magic happens. When the application starts, the code inside UseIISIntegration method tries to read these environment variables and if they are not empty they will be used to configure the url/port the application will listen on. (If the above environment variables are not set UseIISIntegration won’t try to configure anything so that you can use your own settings when running the application directly (i.e. without IIS)). One important detail to pay attention to is where you put the call to UseIISIntegration when configuring your application with WebHostBuilder. You need to make sure that you don’t try to set server urls after you called UseIISIntegration otherwise the url set by UseIISIntegration will get overwritten and your application will be listening on a different port that Asp.NET Core Module expects. As a result things will not work.

ASPNETCORE_TOKEN is a pairing token. IIS middleware added to the pipeline by UseIISIntegration will check each request if it contains this value and will reject requests that don’t. This is to prevent from accepting requests that did not come from IIS.

These are the fundamental blocks needed to run your ASP.NET Core application with IIS and on Azure (Antares). You can now go and set things up as described in some tutorials and actually understand what you are doing and why.

There is, however, a  second level of confusion which happens when you start using Visual Studio and you see additional magic. The first thing that is confusing is web.config. You create a new ASP.NET Core application, open web.config and you see this line instead of what I showed above:

<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>

You start wondering what are these %LAUNCHER_*% environment variables, who is supposed to set them and how IIS (or whoever) knows what values to put there. Honestly, I don’t exactly know how these environment variables work but I treat them as placeholders that Visual Studio replaces when you start your application with F5/Ctrl+F5. When you publish your application to run in a production environment you can’t have these placeholders in web.config – no one knows about them and no one is going to replace them or set values (I also don’t think you can just set environment variables with these names and they will be picked up automatically – this is why I call these strings placeholders – they look as if they were environment variables but I don’t think they behave as environment variables). So how does this work then? If you look at your project.json file you will see a "scripts" section looking more or less like this:

"scripts":
{
"postpublish":
"dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%"
}

It just tells dotnet to run the publish-iis tool after the application is published. What is publish-iis (or a better question would be “what publish-iis isn’t”)? publish-iis isn’t… doing much. There are a lot of misconceptions about the publish-iis tool but it actually is a very simple tool. It goes to the folder where the application was published (not your project folder) and checks if it contains a web.config file. If it doesn’t it will create one. If it does it will check what kind of application you have (i.e. whether it is targeting full CLR or Core CLR and – for Core CLR – whether it is a portable or standalone application) and will set the values of the processPath and arguments attributes removing %LAUNCHER_PATH% and %LAUNCHER_ARGS% placeholders on the way. Note that publish-iis is not a Visual Studio tool. It’s independent and whether you are using Visual Studio or you publish your application from command line using dotnet publish it will work as long as it is configured as a postpublish script in your project.json. That’s pretty much what publish-iis is.

Troubleshooting

Since there are a few pieces that need to be aligned to run ASP.NET Core application with IIS things tend to go wrong. If you don’t know how these pieces are supposed to work together (i.e. you did not read the first part of this post) you can search the internet, try random hints from stackoverflow and pray and most likely you still won’t be able to make your application work. But now you know know how things are supposed to work so you can be much more effective in troubleshooting problems. I will only focus on the infamous 502.3 Bad Gateway error as this is the most common one. I read about other failures (https://docs.asp.net/en/latest/publishing/iis.html) but so far have not seen any of them. So, if your application does not work with IIS what to do?

  • Make sure ASP.NET Core Module is installed. It will be installed on your dev box because it is installed with Visual Studio Web Tooling but it may not be on the server you are deploying your application to
  • Try running your published application without IIS – in the command prompt go to the folder where the application was published to and run dotnet {myapp}.dll (a portable Core CLR app) or {myapp}.exe (a standalone application or an application targeting full CLR)
  • If above works – make sure dotnet.exe is on the global %PATH%. It might be on the %PATH% for you but IIS is running using a different account which may not have path to dotnet.exe set. Add the path to the folder dotnet.exe lives in (typically C:\Program Files\dotnet) to the global %PATH% environment variable. I had to do iisreset.exe to make the change effective in IIS
  • Check web.config file of the published application – verify it has actual values and not %LAUNCHER_PATH%/%LAUNCHER_ARGS% placeholders. Make sure the processPath  corresponds to the application type (i.e. you can’t start a portable application with {myapp}.exe because there is only {myapp}.dll in the folder)
  • Check event log – ASP.NET Core Module writes to event log and you can find some useful (and some bogus) entries in the event log
  • Turn on logging – you probably noticed stdoutLogFile and stdoutLogEnabled attributes on the aspNetCore element. They are very helpful to diagnose issues – especially issues related to application start up. If you set stdoutLogEnabled to true ASP.NET Core Module will write all the output written by the application to the console to the stdoutLogFile file. Note that the folder configured in the stdoutLogFile attribute must exist, otherwise (at least at the moment) the file won’t be created. In case of Azure/Antares the path should look like:  \\?\%home%\LogFiles\stdout  (the \\?\%home%\LogFiles folder always exists). In fact if you publish your application to Azure/Antares using Visual Studio tooling it will make publish-iis set stdoutLogPath to point to the folder above and you can turn on logging by merely setting stdoutLogEnabled to true.
    Note that std out logging should not be used as a poor man’s file logging. It’s very useful to diagnose startup issues since they often happen before loggers are created and/or configured but if you want to log to a file configure your application to use a real logging and logging framework. (There is also a plan to create a simple file logger https://github.com/aspnet/Logging/issues/441)

app_offline

The last thing to mention is app_offline.htm. app_offline.htm is a feature of ASP.NET Core Module where ASP.NET Core Module will monitor your application directory and if it notices the app_offline.htm file (note – at the moment the file must not be empty: https://github.com/aspnet/IISIntegration/issues/174) it will stop your application and will respond to requests with the contents of the app_offline.htm file. This makes application deployment much easier since you no longer need to deal with the problem of locked files that are loaded into the process of a running application. Once you remove the app_offline.htm file from the folder ASP.NET Core Module will start your application on the first request. app_offline.htm is used by the deployment tool (WebDeploy) that ships with Visual Studio (note that in preview1 this tool uses app_offline.htm only when deploying to Azure/Antares but it is supposed to be fixed so that app_offline.htm is used by default when deploying to file system (think: IIS)).

So, these are the basics of running ASP.NET Core application with IIS. The topic is much broader but the details described in this post will hopefully make working with IIS  in the ASP.NET Core world less painful and help those who need to transition from previous versions of ASP.NET.