I have tried to find easiest ways to create a simple web server with F#. There are three most simple ways to do it.
The goal is to create a simple web service that maps web request urls to the files in the site folder. If file with such name exists then return its content as html. Assume that all html files located in ‘D:\mySite\‘.
HttpListener
First and probably the most promising option was created by Julian Kay and described in his post “Creating a simple HTTP Server with F#“. I slightly modified source code to satisfy my initial goal. You can find detailed description of how it works in Julian’s post. (Works from FSI)
open System open System.Net open System.Text open System.IO let siteRoot = @"D:\mySite\" let host = "http://localhost:8080/" let listener (handler:(HttpListenerRequest->HttpListenerResponse->Async<unit>)) = let hl = new HttpListener() hl.Prefixes.Add host hl.Start() let task = Async.FromBeginEnd(hl.BeginGetContext, hl.EndGetContext) async { while true do let! context = task Async.Start(handler context.Request context.Response) } |> Async.Start let output (req:HttpListenerRequest) = let file = Path.Combine(siteRoot, Uri(host).MakeRelativeUri(req.Url).OriginalString) printfn "Requested : '%s'" file if (File.Exists file) then File.ReadAllText(file) else "File does not exist!" listener (fun req resp -> async { let txt = Encoding.ASCII.GetBytes(output req) resp.ContentType <- "text/html" resp.OutputStream.Write(txt, 0, txt.Length) resp.OutputStream.Close() }) // TODO: add your code here
Self-hosted WCF service
The second option is a tuned self-hosted WCF service. This approach was proposed by Brian McNamara as an answer to the StackOverflow question “F# web server library“. (Works from FSI)
#r "System.ServiceModel.dll" #r "System.ServiceModel.Web.dll" open System open System.IO open System.ServiceModel open System.ServiceModel.Web let siteRoot = @"D:\mySite\" [<ServiceContract>] type MyContract() = [<OperationContract>] [<WebGet(UriTemplate="{file}")>] member this.Get(file:string) : Stream = printfn "Requested : '%s'" file WebOperationContext.Current.OutgoingResponse.ContentType <- "text/html" let bytes = File.ReadAllBytes(Path.Combine(siteRoot, file)) upcast new MemoryStream(bytes) let startAt address = let host = new WebServiceHost(typeof<MyContract>, new Uri(address)) host.AddServiceEndpoint(typeof<MyContract>, new WebHttpBinding(), "") |> ignore host.Open() host let server = startAt "http://localhost:8080/" // TODO: add your code here server.Close()
NancyFx
The third one is based on NancyFx. It is lightweight, low-ceremony, framework for building HTTP based services on .Net and Mono. Nancy is a popular framework in C# world, but does not have a natural support of F#. The F# code looks not so easy and simple as it could be. If you want to make it work, you need to create console application and install the Nancy and Nancy.Hosting.Self NuGet packages.
module WebServers open System open System.IO open Nancy open Nancy.Hosting.Self open Nancy.Conventions let (?) (this : obj) (prop : string) : obj = (this :?> DynamicDictionary).[prop] let siteRoot = @"d:\mySite\" type WebServerModule() as this = inherit NancyModule() do this.Get.["{file}"] <- fun parameters -> new Nancy.Responses.HtmlResponse( HttpStatusCode.OK, (fun (s:Stream) -> let file = (parameters?file).ToString() printfn "Requested : '%s'" file let bytes = File.ReadAllBytes(Path.Combine(siteRoot, file)) s.Write(bytes,0,bytes.Length) )) |> box let startAt host = let nancyHost = new NancyHost(new Uri(host)) nancyHost.Start() nancyHost let server = startAt "http://localhost:8080/" printfn "Press [Enter] to exit." Console.ReadKey() |> ignore server.Stop()
Further reading
- “Writing an Agent-Based Web Server” by Tomas Petricek and Jon Skeet.
- “An F# Web Server From Sockets and Up” by Anton Tayanovskyy.
- “A Simple Web Server in F#” by Maxim Moiseev
- “Working with WebSharper Sitelets” by IntelliFactory.
- “Frank” – a resource-oriented wrapper library for working with the Web API and is developed in F#.
- “Frack” – an implementation of the Open Web Interface for .NET (OWIN), a .NET Web Server Gateway Interface, written in F#.
- “Suave” – a simple web development F# library providing a lightweight web server and a set of combinators to manipulate route flow and task composition.
- “Kayak” – an event-driven networking library for .NET. It allows you to easily create TCP clients and servers. Kayak contains an HTTP/1.1 server implementation.
- “Building Web, Cloud, and Mobile Solutions with F#” by Daniel Mohl.