A basic web server

Ponyo Basics

Article Source

Ponyo.Net.Http provides tools for building web servers in Standard ML. (Todo: implement HTTP client.) It uses one OS thread per request and routes each request to a function. Let's begin with the simplest web server we can write.

(* server.sml *)
structure Http = Ponyo.Net.Http

fun handler (_) = Http.Response.new ("Hello world!")

fun main () =
    Http.Server.listenAndServe ("", 8080, handler)

Compile and run it, navigate to localhost:8080 to check it out!

$ ponyo make server.sml -o server
$ ./server

As you can see, Ponyo.Net.Http.Server prints the HTTP request each time a request is made. To make a more advanced server, we can use the request argument passed to the handler instead of ignoring it. For basic routing, pattern matching can be the simplest way to go.

structure Http = Ponyo.Net.Http

fun handler (request: Http.Request.t) : Http.Response.t =
    case Http.Request.path (request) of
        "/hello" => Http.Response.new ("Hello world!")
      | _ => Http.Response.new ("Uh-oh, page not found.")

fun main () =
    Http.Server.listenAndServe ("", 8080, handler)

But this can be painful and verbose for more complex routing. So Ponyo.Net.Http.Router provides a basic router with slightly more advanced functionality. (Todo: implement slightly slightly more advanced functionality.) Ponyo.Net.Http.Router.basic takes a list of Method.t-string-Router.t tuples. Additionally, it allows you to specify wildcard routes. The first match in the list is taken.

structure Http = Ponyo.Net.Http

fun response (text: string) : Http.Router.t =
    fn (_) => Http.Response.new (text)

fun main () =
    Http.Server.listenAndServe ("", 8080, Http.Router.basic [
        (Http.Router.Get, "/hello", response "Hello world!"),
        (Http.Router.Get, "/*", response "Uh-oh, page not found.")
    ])

Additional information about the request is provided by the Request module. We can print the query string for instance. (* Todo: cleanup Headers data structure and demonstrate printing remote address.*)

structure Format = Ponyo.Format
structure Http = Ponyo.Net.Http
structure Request = Http.Request
structure Response = Http.Response

fun handler (format: string) (request: Request.t) : Response.t =
    let
        val qs = Request.query (request)
    in
        Format.printf format [qs]
    end

fun main () =
    Http.Server.listenAndServe ("", 8080,
        handler ("<h1>Hello World!</h1><h2>%</h2>"))

If you are looking for more examples, remember this site runs on Ponyo. The source can be found at site/server/server.sml.

Why all the todos? The Ponyo handbook is a community document and will grow and improve as the community grows and improves. Want to see some changes? Put up a PR on Github!