Http Server in Standard ML

Here’s the translation of the HTTP Server example from Go to Standard ML, formatted in Markdown suitable for Hugo:

(* Writing a basic HTTP server using the HttpServer module *)

structure HttpServer = struct
    (* A handler is a function that takes a request and returns a response *)
    type handler = {method: string, uri: string, headers: (string * string) list} -> string

    (* A simple handler that returns "hello\n" *)
    fun hello _ = "hello\n"

    (* A handler that echoes the request headers *)
    fun headers {method=_, uri=_, headers=hdrs} =
        String.concatWith "\n" (map (fn (name, value) => name ^ ": " ^ value) hdrs)

    (* Function to start the server *)
    fun serve port handlers =
        let
            fun dispatch {method, uri, headers} =
                case List.find (fn (path, _) => uri = path) handlers of
                    SOME (_, handler) => handler {method=method, uri=uri, headers=headers}
                  | NONE => "404 Not Found"
            
            fun acceptLoop socket =
                let
                    val (client, _) = Socket.accept socket
                    val request = readRequest client
                    val response = dispatch request
                    val _ = Socket.sendString client response
                    val _ = Socket.close client
                in
                    acceptLoop socket
                end
            
            val socket = INetSock.TCP.socket()
            val _ = Socket.bind (socket, INetSock.any port)
            val _ = Socket.listen (socket, 5)
        in
            acceptLoop socket
        end

    (* Main function to set up routes and start the server *)
    fun main () =
        let
            val handlers = [
                ("/hello", hello),
                ("/headers", headers)
            ]
        in
            serve 8090 handlers
        end
end

(* Run the server *)
val _ = HttpServer.main()

This Standard ML code demonstrates a basic HTTP server implementation. Here’s an explanation of the key components:

  1. We define a handler type, which is a function that takes a request (represented as a record with method, URI, and headers) and returns a string response.

  2. Two handlers are implemented:

    • hello: A simple handler that always returns “hello\n”.
    • headers: A more sophisticated handler that echoes all the HTTP request headers in the response.
  3. The serve function sets up the server:

    • It creates a socket, binds it to the specified port, and listens for incoming connections.
    • For each connection, it reads the request, dispatches it to the appropriate handler, and sends the response.
  4. The dispatch function matches the request URI to the registered handlers and calls the appropriate handler.

  5. The main function sets up the routes (mapping URIs to handlers) and starts the server on port 8090.

To run this server:

  1. Save the code in a file, e.g., http_server.sml.
  2. Compile and run it using your Standard ML compiler. For example, with MLton:
$ mlton http_server.sml
$ ./http_server

The server will start and listen on port 8090. You can then access it using curl or a web browser:

$ curl localhost:8090/hello
hello

$ curl localhost:8090/headers
User-Agent: curl/7.68.0
Accept: */*

Note that this is a basic implementation and doesn’t include all the features of a full-fledged HTTP server. In a real-world scenario, you’d want to add error handling, proper HTTP status codes, and more robust request parsing.