diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2013, 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/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
diff --git a/hack2-contrib-extra.cabal b/hack2-contrib-extra.cabal
new file mode 100644
--- /dev/null
+++ b/hack2-contrib-extra.cabal
@@ -0,0 +1,48 @@
+Name:                 hack2-contrib-extra
+Version:              2013.6.16
+Build-type:           Simple
+Synopsis:             Hack2 contrib extra
+Description:          Extra middlewares and utilities for Hack2
+License:              BSD3
+License-file:         LICENSE
+Author:               Jinjing Wang
+Maintainer:           Jinjing Wang <nfjinjing@gmail.com>
+Build-Depends:        base
+Cabal-version:        >= 1.2
+category:             Web
+homepage:             https://github.com/nfjinjing/hack2-contrib
+data-files:           readme.md, changelog.md
+tested-with:          GHC==7.6.3
+
+library
+
+  build-depends: 
+                    base >=4 && < 100
+                  , cgi
+                  , network
+                  , old-locale
+                  , old-time
+                  , directory
+                  , filepath
+                  , containers
+                  , bytestring
+                  , data-default
+                  , time
+                  , air
+                  , air-extra
+                  , hack2
+                  , hack2-contrib
+                  
+  hs-source-dirs: src/
+  
+  exposed-modules:  
+                    
+                    Hack2.Contrib.RequestExtra
+                    Hack2.Contrib.Middleware.RegexpRouter
+                    Hack2.Contrib.Middleware.UTF8Body
+
+
+
+
+
+
diff --git a/readme.md b/readme.md
new file mode 100644
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,1 @@
+Extra Middleware and Helper for Hack2
diff --git a/src/Hack2/Contrib/Middleware/RegexpRouter.hs b/src/Hack2/Contrib/Middleware/RegexpRouter.hs
new file mode 100644
--- /dev/null
+++ b/src/Hack2/Contrib/Middleware/RegexpRouter.hs
@@ -0,0 +1,23 @@
+-- | matching a list of regexp agains a path_info, if matched, consponding app
+--   is used, otherwise, pass the env down to lower middleware
+
+module Hack2.Contrib.Middleware.RegexpRouter (regexp_router) where
+
+import Data.Maybe
+import Hack2
+import Hack2.Contrib.Utils
+import Data.List (find)
+import Air.Env
+import Air.Extra
+import Prelude ()
+import qualified Data.ByteString.Char8 as B
+
+type RoutePath = (String, Application)
+
+regexp_router :: [RoutePath] -> Middleware
+regexp_router h app = \env ->
+  let path = env.path_info.B.unpack
+  in
+  case h.find (fst > flip match path > isJust) of
+    Nothing -> app env
+    Just (_, found_app) -> found_app env
diff --git a/src/Hack2/Contrib/Middleware/UTF8Body.hs b/src/Hack2/Contrib/Middleware/UTF8Body.hs
new file mode 100644
--- /dev/null
+++ b/src/Hack2/Contrib/Middleware/UTF8Body.hs
@@ -0,0 +1,15 @@
+module Hack2.Contrib.Middleware.UTF8Body (utf8_body) where
+
+import Prelude ()
+import Air.Env
+import Air.Extra (u2b)
+import Air.Heavy (unescape_xml)
+import Hack2
+import qualified Data.ByteString.Char8 as B
+import Hack2.Contrib.Utils (as_string)
+
+utf8_body :: Middleware
+utf8_body app = \env -> do
+  r <- app env
+  let raw_body = r.body
+  return r {body = raw_body.as_string (unescape_xml > u2b)}
diff --git a/src/Hack2/Contrib/RequestExtra.hs b/src/Hack2/Contrib/RequestExtra.hs
new file mode 100644
--- /dev/null
+++ b/src/Hack2/Contrib/RequestExtra.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Hack2.Contrib.RequestExtra where
+
+import Prelude ()
+import Data.Maybe
+import Air.Env
+import Air.Extra
+import Data.ByteString.Char8 (ByteString)
+import Hack2 hiding (body)
+import Hack2.Contrib.Request
+import Hack2.Contrib.Utils
+import qualified Data.ByteString.Char8 as B
+
+
+media_type :: Env -> ByteString
+media_type env = case env.content_type.B.unpack.split "\\s*[;,]\\s*" of
+  [] -> ""
+  x:_ -> x.lower.B.pack
+
+
+
+media_type_params :: Env -> [(ByteString, ByteString)]
+media_type_params env
+  | env.content_type.B.unpack.empty = []
+  | otherwise = 
+      env
+        .content_type
+        .B.unpack
+        .split "\\s*[;,]\\s"
+        .drop 1
+        .map (split "=")
+        .select (length > is 2)
+        .map tuple2
+        .map_fst (lower > B.pack)
+        .map_snd (B.pack)
+
+
+content_charset :: Env -> ByteString
+content_charset env = env.media_type_params.lookup "charset" .fromMaybe ""
