diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2011, Jinjing Wang
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Jinjing Wang nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Nemesis b/Nemesis
new file mode 100644
--- /dev/null
+++ b/Nemesis
@@ -0,0 +1,34 @@
+nemesis = do
+  
+  clean
+    [ "**/*.hi"
+    , "**/*.o"
+    , "manifest"
+    , "main"
+    , "nemesis-tmp.*"
+    , "Test"
+    ]
+  
+
+  desc "prepare cabal dist"
+  task "dist" $ do
+    sh "cabal clean"
+    sh "cabal configure"
+    sh "cabal sdist"
+
+
+  desc "put all .hs files in manifest"
+  task "manifest" $ do
+    sh "find . | grep 'hs$' > manifest"
+
+
+  desc "start console"
+  task "i" (sh "ghci -isrc src/Hack2/Handler/HappstackServer.hs")
+
+  desc "test"
+  task "test" $ do
+    sh "runghc -isrc src/Test.hs"
+  
+  desc "show sloc"
+  task "stat" $ do
+    sh "cloc -match-f=hs$ --quiet src"
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,4 @@
+#! /usr/bin/env runhaskell
+
+> import Distribution.Simple
+> main = defaultMain
diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,4 @@
+2011.6.10
+----------
+
+* init
diff --git a/hack2-handler-happstack-server.cabal b/hack2-handler-happstack-server.cabal
new file mode 100644
--- /dev/null
+++ b/hack2-handler-happstack-server.cabal
@@ -0,0 +1,22 @@
+Name:                 hack2-handler-happstack-server
+Version:              2011.6.10
+Build-type:           Simple
+Synopsis:             Hack2 Happstack server handler
+Description:          Hack2 Happstack server handler
+License:              BSD3
+License-file:         LICENSE
+Author:               Jinjing Wang
+Maintainer:           Jinjing Wang <nfjinjing@gmail.com>
+Build-Depends:        base
+Cabal-version:        >= 1.2
+category:             Web
+homepage:             http://github.com/nfjinjing/hack2-handler-happstack-server
+data-files:           readme.md, changelog.md, Nemesis
+
+library
+  ghc-options: -Wall
+  build-depends: base >= 4.0 && < 5, cgi, network, bytestring, data-default >= 0.2, hack2 >= 2011.6.10, happstack-server < 6, containers, mtl
+  hs-source-dirs: src/
+  exposed-modules:  
+                    Hack2.Handler.HappstackServer
+                    
diff --git a/readme.md b/readme.md
new file mode 100644
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,9 @@
+Happstack server handler
+-----------------
+
+* preferred for production
+
+Known issues
+------------
+
+* The backend does not distinguish between `http://example/abc/` and `http://example/abc`, (notice the absence of trailing slash).
diff --git a/src/Hack2/Handler/HappstackServer.hs b/src/Hack2/Handler/HappstackServer.hs
new file mode 100644
--- /dev/null
+++ b/src/Hack2/Handler/HappstackServer.hs
@@ -0,0 +1,154 @@
+-- code structure written by John MacFarlane, 
+-- I filled in some missing pieces and make it compile.
+
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+
+module Hack2.Handler.HappstackServer (run, runWithConfig, ServerConf(..), appToServerPart) where
+
+
+import Control.Arrow ((>>>))
+import "mtl" Control.Monad.State
+import Data.Char
+import Data.Default
+import Data.List
+import Data.Maybe
+import qualified Hack2 as Hack2
+import Happstack.Server.SimpleHTTP as Happstack hiding (port, escape)
+import Network.URI (escapeURIString, isAllowedInURI)
+import Control.Applicative
+
+import qualified Data.ByteString.Char8 as S
+import qualified Data.ByteString.Lazy.Char8 as L
+
+import qualified Data.Map as M
+import qualified Happstack.Server.SimpleHTTP as H
+
+data ServerConf = ServerConf { port :: Int, serverName :: L.ByteString }
+  deriving (Show)
+
+instance Default ServerConf where
+  def = ServerConf { port = 3000, serverName = "localhost"}
+
+runWithConfig :: ServerConf -> Hack2.Application -> IO ()
+runWithConfig conf = simpleHTTP nullConf { H.port = port conf } . appToServerPart conf
+
+run :: Hack2.Application -> IO ()
+run = runWithConfig def
+
+appToServerPart :: ServerConf -> Hack2.Application -> ServerPart (Happstack.Response)
+appToServerPart conf app = askRq >>= liftIO . (hackRToServerPartR <$>) . app . reqToEnv
+  where
+    reqToEnv req =
+      def
+        { Hack2.requestMethod   = convertRequestMethod $ rqMethod req
+        , Hack2.scriptName      = L.empty
+        , Hack2.pathInfo        = L.pack $ escape $ "/" ++ (intercalate "/" $ rqPaths req)
+        , Hack2.queryString     = L.pack $ escape $ dropWhile (== '?') $ rqQuery req
+        , Hack2.serverName      = serverName conf
+        , Hack2.serverPort      = (snd $ rqPeer req)
+        , Hack2.httpHeaders     = headersToHttp (rqHeaders req)
+        , Hack2.hackInput       = (\(Body x) -> x) (rqBody req)
+        , Hack2.hackHeaders     = [("RemoteHost", L.pack $ fst $ rqPeer req)]
+        }
+    
+    escape = escapeURIString isAllowedInURI
+    
+    convertRequestMethod Happstack.OPTIONS     =     Hack2.OPTIONS
+    convertRequestMethod Happstack.GET         =     Hack2.GET
+    convertRequestMethod Happstack.HEAD        =     Hack2.HEAD
+    convertRequestMethod Happstack.POST        =     Hack2.POST
+    convertRequestMethod Happstack.PUT         =     Hack2.PUT
+    convertRequestMethod Happstack.DELETE      =     Hack2.DELETE
+    convertRequestMethod Happstack.TRACE       =     Hack2.TRACE
+    convertRequestMethod Happstack.CONNECT     =     Hack2.CONNECT
+    
+
+headersToHttp :: Headers -> [(L.ByteString, L.ByteString)]
+headersToHttp = M.toList >>> map snd >>> map headerToPair
+  where
+    headerToPair (HeaderPair k v) = 
+      (L.pack $ normalizeHeader $ S.unpack k, s2l $ S.intercalate " " v)
+
+hackRToServerPartR :: Hack2.Response -> Happstack.Response
+hackRToServerPartR r = Happstack.Response 
+  { rsCode      = Hack2.status r
+  , rsHeaders   = httpToHeaders $ Hack2.headers r
+  , rsFlags     = RsFlags {rsfContentLength = False}
+  , rsBody      = Hack2.body r
+  , rsValidator = Nothing 
+  }
+
+l2s :: L.ByteString -> S.ByteString
+l2s = S.concat . L.toChunks
+
+s2l :: S.ByteString -> L.ByteString
+s2l = L.fromChunks . return
+
+httpToHeaders :: [(L.ByteString, L.ByteString)] -> Headers
+httpToHeaders = map pairToHeader >>> M.fromList
+  where 
+    pairToHeader (k,v) = 
+      ((S.pack $ map toLower $ L.unpack k), HeaderPair (l2s k) [l2s v])
+
+
+-- happstack converts all request header to lowercase ...
+-- so we need to convert it back ...
+normalizeHeader :: String -> String
+normalizeHeader s = fromMaybe s $ find (map toLower >>> (== s) ) headerList
+
+headerList :: [String]
+headerList = 
+  [ "Cache-Control"        
+  , "Connection"           
+  , "Date"                 
+  , "Pragma"               
+  , "Transfer-Encoding"    
+  , "Upgrade"              
+  , "Via"                  
+  , "Accept"               
+  , "Accept-Charset"       
+  , "Accept-Encoding"      
+  , "Accept-Language"      
+  , "Authorization"        
+  , "Cookie"               
+  , "Expect"               
+  , "From"                 
+  , "Host"                 
+  , "If-Modified-Since"    
+  , "If-Match"             
+  , "If-None-Match"        
+  , "If-Range"             
+  , "If-Unmodified-Since"  
+  , "Max-Forwards"         
+  , "Proxy-Authorization"  
+  , "Range"                
+  , "Referer"              
+  , "User-Agent"           
+  , "Age"                  
+  , "Location"             
+  , "Proxy-Authenticate"   
+  , "Public"               
+  , "Retry-After"          
+  , "Server"               
+  , "Set-Cookie"           
+  , "TE"                   
+  , "Trailer"              
+  , "Vary"                 
+  , "Warning"              
+  , "WWW-Authenticate"     
+  , "Allow"                
+  , "Content-Base"         
+  , "Content-Encoding"     
+  , "Content-Language"     
+  , "Content-Length"       
+  , "Content-Location"     
+  , "Content-MD5"          
+  , "Content-Range"        
+  , "Content-Type"         
+  , "ETag"                 
+  , "Expires"              
+  , "Last-Modified"        
+  , "Content-Transfer-Encodeing"
+  ]
