@hackage hack2009.4.26

a sexy Haskell Webserver Interface

Hack: a sexy Haskell Webserver Interface

Hack is a brain-dead port of the brilliant Ruby Rack webserver interface.

What does a Hack app look like

module Main where

import Hack
import Hack.Handler.Kibro

hello :: Application
hello = \env -> return $ Response 
    { status  = 200
    , headers = [ ("Content-Type", "text/plain") ]
    , body    = "Hello World"
    }

main = run hello

1 minute tutorial

Install Hack

cabal install hack

Install Kibro (the only handler at the moment)

cabal install kibro

Install lighttpd 1.4.19 (used by kibro)

wget http://www.lighttpd.net/download/lighttpd-1.4.19.tar.gz
tar zxfv lighttpd-1.4.19.tar.gz
cd lighttpd-1.4.19
./configure --prefix=$HOME
make
make install

Create a new Kibro project

kibro new hello-world

Test if Kibro works

cd hello-world
kibro start

Create a Hack app

put the following code in src/Main.hs

module Main where

import Hack
import Hack.Handler.Kibro

hello :: Application
hello = \env -> return $ Response 
    { status  = 200
    , headers = [ ("Content-Type", "text/plain") ]
    , body    = "Hello World"
    }

main = run hello

restart kibro

kibro restart

Middleware

demo usage of middleware

module Main where

import Hack
import Hack.Utils
import Hack.SimpleRoute
import Hack.Handler.Kibro

import Data.Default
import MPS
import Prelude hiding ((.))

hello :: Application
hello = \env -> def {body = env.show} .return

app :: Application
app = route [("/hello", hello), ("", hello)] empty_app

main = run app

create a middleware

inside Hack.hs:

type MiddleWare = Application -> Application

since Haskell has curry, middleware api can be of type

Anything -> Application -> Application

just pass an applied middleware into a chain.

finally the source code of SimpleRoute.hs:

{-# LANGUAGE QuasiQuotes #-}

module Hack.Contrib.SimpleRouter where

import Hack
import Hack.Utils
import List (find)
import Prelude hiding ((.), (^), (>))
import MPS

type RoutePath = (String, Application)

route :: [RoutePath] -> MiddleWare
route h _ = \env ->
  let path             = env.path_info
      script           = env.script_name
      mod_env location = env 
        { script_name  = script ++ location
        , path_info    = path.drop (location.length)
        }
  in
  case h.find (fst > flip starts_with path) of
    Nothing -> not_found [$here|Not Found: #{path}|]
    Just (location, app) -> app (mod_env location)

Use the middleware stack

Rack provides a builder DSL, Hack just use a function. From Utils.hs:

-- usage: app.use [content_type, cache]
use :: [MiddleWare] -> MiddleWare
use = reduce (<<<)

Handlers

Just like Rack, once an application is written using Hack, it should work on any web server that provides a Hack handler. I'm only familiar with Kibro, so it became the first handler that's included.

The handler should expose only one function of type:

run :: Application -> IO ()

Spec

Hack spec = Rack spec :)

Please read The Rack interface specification.