In my last post I showed a simple F# OWIN self hosted server without an application framework. Today I want to show an even simpler example that doesn’t reference any of the Microsoft OWIN libraries, but instead uses an open source server implementation, Nowin. Thanks to Damien Hickey for pointing me in the right direction.
The great thing about the Open Web Interface for .NET (OWIN) is that it is simply a specification. There is no OWIN library that you have to install to allow web servers, application frameworks and middlewear built to the OWIN standard to communicate. There is no interface that they must implement. They simply need to provide an entry point for the OWIN application delegate (better know as the AppFunc):
Func<IDictionary<string , object>, Task>
For simple applications, where we don’t need routing, authentication, serialization, or an application framework, this means we can simply provide our own implementation of the AppFunc and pass it directly to an OWIN web server.
Nowin, by Boris Letocha, is a .NET web server, built directly against the standard .NET socket API. This means it should work on all platforms that support .NET without modification. The author claims that it has equivalent performance to NodeJS on Windows and can even match HttpListener. Although not ready for production, it makes a compelling implementation for simple test servers and stubs, which is how I intend to use it.
To use any OWIN web server with F#, we simply need to provide an AppFunc and since F# lambdas have an implicit cast to System.Func<..> we can simply provide the AppFunc in the form:
fun (env: IDictionary<string, obj>) -> Task.FromResult(null) :> Task
Let’s see it in action. First create an F# console application and install the Nowin server with NuGet:
Install-Package Nowin
Now we can host our Nowin server in the application’s entry point:
[<entrypoint>] let main argv = use server = Nowin.ServerBuilder .New() .SetEndPoint(new IPEndPoint(IPAddress.Any, port)) .SetOwinApp(fun env -> Task.FromResult(null) :> Task) .Build() server.Start() printfn "Server listening on http://localhost:%i/ \nhit <enter> to stop." port Console.ReadLine() |> ignore 0
Of course this server does nothing at all. It simply returns the default 200 OK response with no body. To do any useful work you need to read the OWIN environment, understand the request and create a response. To make this easier in F# I’ve created a simple OwinEnvironment type with just the properties I need. You could expand this to encompass whatever OWIN environment properties you need. Just look at the OWIN spec for this.
type OwinEnvironment = { httpMethod: string; requestBody: Stream; responseBody: Stream; setResponseStatusCode: (int -> unit); setResponseReasonPhrase: (string -> unit) }
Here is a function that takes the AppFunc environment and maps it to my OwinEnvironment type:
let getOwinEnvironment (env: IDictionary<string , obj>) = { httpMethod = env.["owin.RequestMethod"] :?> string; requestBody = env.["owin.RequestBody"] :?> Stream; responseBody = env.["owin.ResponseBody"] :?> Stream; setResponseStatusCode = fun (statusCode: int) -> env.["owin.ResponseStatusCode"] <- statusCode setResponseReasonPhrase = fun (reasonPhrase: string) -> env.["owin.ResponseReasonPhrase"] <- reasonPhrase }
Now that we have our strongly typed OwinEnvironment, we can grab the request stream and response stream and do some kind of mapping. Here is a function that does this. It also only accepts POST requests, but you could do whatever you like in the body. Note the transform function is where the work is done.
let handleOwinEnvironment (owin: OwinEnvironment) : unit = use writer = new StreamWriter(owin.responseBody) match owin.httpMethod with | "POST" -> use reader = new StreamReader(owin.requestBody) writer.Write(transform(reader.ReadToEnd())) | _ -> owin.setResponseStatusCode 400 owin.setResponseReasonPhrase "Bad Request" writer.Write("Only POST requests are allowed")
Just for completeness, here is a trivial transform example:
let transform (request: string) : string = sprintf "%s transformed" request
Now we can re-visit our console Main function and pipe everything together:
[<entrypoint>] let main argv = use server = Nowin.ServerBuilder .New() .SetEndPoint(new IPEndPoint(IPAddress.Any, port)) .SetOwinApp(fun env -> env |> getOwinEnvironment |> handleOwinEnvironment |> endWithCompletedTask) .Build() server.Start() printfn "Server listening on http://localhost:%i/ \nhitto stop." port Console.ReadLine() |> ignore 0
The endWithCompletedTask function, is a little convenience to hide the ugly synchronous Task return code:
let endWithCompletedTask = fun x -> Task.FromResult(null) :> Task
So as you can see, OWIN and Nowin make it very easy to create small web servers with F#. Next time you just need a simple service stub or test server, consider doing something like this, rather that using a heavyweight server and application framework such as IIS, MVC, WebAPI or WebForms.
You can find the complete code for the example in this Gist https://gist.github.com/mikehadlow/c88e82ee98619f22f174:
6 comments:
"There is no interface that they must implement. They simply need to provide an entry point for the OWIN application delegate (better know as the AppFunc):"
That *is* an interface. It's just not statically defined. I never understood the reasoning behind this dictionary design choice. Apps still need to know what's inside and take a hard runtime dependency on it.
A delegate is not an interface. Since string, Object, Func<>, IDictionary<> and Task are all defined in the standard libraries it means you don't need to take a hard dependency on a separate OWIN library in order to create OWIN compatible servers, application frameworks or middlewear.
"Apps still need to know what's inside and take a hard runtime dependency on it." Yes, they need to know what to implement (of course), but you are incorrect to say that they need to take a hard runtime dependency.
A dependency from B to A exists when you cannot change A without changing B. This is the case here. The contents of the dictionary are a dependency for the app. The dependency was not avoided but simply not statically declared.
It is true that this saves referencing a library. But what's the harm in referencing a library?!
Very nice post!! I don't work with F# (yet) but I like how elegant the code looks. Thank you
@Anonymous, see my slides for A Brief History of OWIN. The main driver for not requiring a shared library was each contributor's preference for his own existing library's abstractions and a desire to easily interop with the DLR languages that were still in active development at the time, i.e. IronPython and IronRuby.
@Mike, nice post! Thank you for trying out this combination. I have been wanting to give Nowin a spin for some time but never got around to it.
Post a Comment