hack: a sexy Haskell Webserver Interface

[ bsd3, deprecated, library, web ] [ Propose Tags ]
Deprecated in favor of hack2

Hack: a sexy Haskell Webserver Interface. Hack is a brain-dead port of the brilliant Ruby Rack http://rack.rubyforge.org/ webserver interface.


[Skip to Readme]

Modules

[Index]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 2009.4.20, 2009.4.21, 2009.4.22, 2009.4.23, 2009.4.25, 2009.4.26, 2009.4.27, 2009.4.28, 2009.4.29, 2009.4.30, 2009.4.51, 2009.4.52, 2009.5.19, 2009.7.15, 2009.10.30, 2012.2.6
Change log changelog.md
Dependencies base, data-default (>=0.2) [details]
License LicenseRef-GPL
Author Wang, Jinjing
Maintainer Wang, Jinjing <nfjinjing@gmail.com>
Category Web
Home page http://github.com/nfjinjing/hack/tree/master
Uploaded by JinjingWang at 2009-04-30T02:26:08Z
Distributions NixOS:2012.2.6
Reverse Dependencies 28 direct, 3 indirect [details]
Downloads 12097 total (44 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for hack-2009.4.52

[back to package description]

Hack: a sexy Haskell Webserver Interface

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

Hack is made of 3 parts:

  • hack: the spec
  • hack-contrib: middleware
  • hack-handler: drivers

The spec should be as stable as possible, hack-contrib is about middleware and tools, and hack-handler provides server connectivity.

Hack explained in one line

type Application = Env -> IO Response

What does a Hack app look like

module Main where

import Hack
import Hack.Handler.Happstack

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

main = run hello

1 minute tutorial

update cabal

cabal update

install hack

cabal install happy
cabal install hack
cabal install hack-contrib

pick a backend

cabal install hack-handler-happstack

Create a Hack app

put the following code in src/Main.hs

module Main where

import Hack
import Hack.Handler.Happstack

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

main = run hello

run

ghc --make -O2 Main.hs
./Main

It should be running on http://127.0.0.1:3000 now.

Middleware

demo usage of middleware

module Main where

import Hack
import Hack.Handler.Happstack
import Hack.Contrib.Utils
import Hack.Contrib.Middleware.SimpleRouter

import Data.Default

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

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 SimpleRouter.hs:

module Hack.Contrib.Middleware.SimpleRouter where

import Hack
import Hack.Contrib.Utils

import MPSUTF8
import Prelude hiding ((.), (^), (>))
import List (find, isPrefixOf)

type RoutePath = (String, Application)

route :: [RoutePath] -> Middleware
route h app = \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 > (`isPrefixOf` path) ) of
    Nothing -> app env
    Just (location, app) -> app (mod_env location)

Use the middleware stack

Rack provides a builder DSL, Hack just use a function. From Contrib.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.

Tip

encoding can be tricky

  1. hack uses utf8 bytes for IO
  2. when dealing with html, sometimes, e.g. using template haskell, you have to
    1. encode unicode string using: escape_unicode_xml for plain text
    2. before rendering to response body, do unescape_unicode_xml
    3. then use u2b to convert unicode string to utf8 bytes, and push to response
  3. see, xml encoding and utf8 byte encoding are different, so we need to convert twice
  4. why bother unescape_unicode_xml at all? since we want the user to be able to see unicode chars when viewing source!
  5. these helpers are in hack-contrib and mps
  6. the default error stream in hack takes unicode string as input. It will be fixed in the next release.

Resources

Reference