A Simple Haskell Web Server

February 14, 2008

I am always interested in web development as that is the industry I work in. As I also have an interest in Haskell I decided to try out using Haskell for web development.

There are some innovative web frameworks developed in Haskell, such as HAppS and WASH. There are also simpler approachs such as CGI and FastCGI libraries. I decided to develop my own simple web server and framework as I was interested in how Haskell would fare with more conventional and simpler HTML and database backed web sites, but was after more flexibility to experiment than CGI based approaches can provide.

What I’ve ended up with is not production quality by any means, but it provides a good and simple base to experiment and work from. Thanks to the HTTP and network libraries it worked out to around 20 lines of code. Below I present the primitive interface of the web server and its implmentation.

The Interface

The primitive interface for my Simple Haskell Web Framework (SHWF) reflects the HTTP protocol. It defines a RequestHandler as a function that accepts a Request and will return a Response. The Request and Response

To run a web server with a RequestHandler the runHttpServer method can be called.

type RequestHandler = Request -> IO Response

runHttpServer :: RequestHandler -> IO ()

The Example

As an example of how the interface is used and as a basic test I developed a ‘Hello World’ application. helloWorldHandler is a RequestHandler that will return a simple HTML document to every request. The main function simply runs the server with that RequestHandler

main :: IO ()
main = runHttpServer helloWorldHandler

helloWorldHandler :: RequestHandler
helloWorldHandler _ =  return $ successReponse $
    prettyHtml helloWorldDoc

successResponse :: String -> Response
successResponse = Response (2,0,0) "" []

helloWorldDoc :: Html
helloWorldDoc = header << thetitle << "Hello World"
            +++ body << h1 << "Hello World"

The Implementation

The runHttpServer function creates the socket that will accept connections. It will then loop forever accepting connections on that socket and handling them.

runHttpServer :: RequestHandler -> IO ()
runHttpServer r = withSocketsDo $ do
  sock <- listenOn (PortNumber 8080)
  forever $ acceptConnection sock $ handleHttpConnection r

acceptConnection is a convenience method for accepting connections on a socket. It is passed a continuation that will be called with a Handle that can be used to read and write data to and from the socket.

acceptConnection :: Socket -> (Handle -> IO ()) -> IO ()
acceptConnection s k = accept s >>= \(h,_,_) -> forkIO $ k h

To integrate with the HTTP library it is necessary to provide and instance of the Stream class for Handle. The functions are quite straight forward, but it’s important to note that readLine is expected to return the newline character on the end.

instance Stream Handle where
  readLine h = hGetLine h >>= \l -> return $ Right $ l ++ "\n"
  readBlock h n = replicateM n (hGetChar h) >>= return . Right
  writeBlock h s = mapM_ (hPutChar h) s >>= return . Right
  close = hClose

In implementing the handleHttpConnection I decided to experiment with arrows. Any function of type a -> m b can be made in to a Kleisli arrow. This means that RequestHandler can easily be made an arrow of type Kleisli IO Request Response. Using arrow functions it is possible to transform this to the required type of Kelisli IO Handle (). I’m not sure that handleHttpConnectioni has turned out as clean as it could be. I’m not sure whether Kleisli arrows are not appropriate here, or whether I can simplify what I have.

handleHttpConnection :: RequestHandler -> Handle -> IO ()
handleHttpConnection r c = runKleisli
    (receiveRequest >>> handleRequest r >>> handleResponse) c >>
    close c
  where
    receiveRequest = Kleisli receiveHTTP
    handleRequest r = right (Kleisli r)
    handleResponse = Kleisli (print ||| respondHTTP c)

Improvments

The minimal interface should also make it easy to improve the server. Some improvements that I hope will be straight forward include:

  • Exception handling, especially around the socket operations,
  • Logging,
  • Configuration, at least to allow a different port.

Conclusion

The server implemented is not production quality, but it does suit my purposes. It is only around 20 lines, so is simple enough to understand. I’m hoping that its small size and ease of understanding makes it easier to experiment with.

About these ads

8 Responses to “A Simple Haskell Web Server”


  1. interesting, have you seen mohws? darcs get –partial http://code.haskell.org/mohws/

  2. sakhunzai Says:

    Excellent thing to startup with

    keep it up!


  3. [...] the meantime, if you want to jump into another language, try writing a web server in Haskell or a blog in Common Lisp. Both links are actually quite interesting reads, although they are quite [...]

  4. Steve Asher Says:

    Could you post a link to the full source code? Thanks.

  5. Anton Says:

    runHttpServer :: RequestHandler -> IO ()
    runHttpServer r = withSocketsDo $ do
    sock <- listenOn (PortNumber 8080)
    forever $ acceptConnection sock $ handleHttpConnection r

    What is the function 'forever' ?

  6. Anton Says:

    Ssory, i’ve found it in Contol.Monad

  7. Marylyn Says:

    I am really impressed with your writing skills as well as with the layout on your blog.
    Is this a paid theme or did you modify it yourself?
    Anyway keep up the nice quality writing, it is rare to see a nice blog like this one today.

  8. Antonetta Says:

    Great weblog here! Also your site so much up fast! What web host are yyou using?
    Can I am getting your associate link to your host? I deire my site
    loaded up as fast as yours lol


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: