diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+The following license covers this documentation, and the source code, except
+where otherwise indicated.
+
+Copyright 2010, Suite Solutions. 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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "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 HOLDERS 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/Network/Wai/Handler/WebSockets.hs b/Network/Wai/Handler/WebSockets.hs
new file mode 100644
--- /dev/null
+++ b/Network/Wai/Handler/WebSockets.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Network.Wai.Handler.WebSockets
+    ( intercept
+    ) where
+
+import Network.Wai (Request, requestHeaders, rawPathInfo, requestHeaders)
+import Network.Socket (Socket)
+import Data.Char (toLower)
+import Data.Enumerator (Iteratee)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Char8 as S
+import Network.WebSockets (WebSockets, runWebSockets, RequestHttpPart(RequestHttpPart), Protocol)
+import qualified Network.WebSockets as WS
+import Network.Socket.Enumerator (iterSocket)
+
+intercept :: Protocol p
+          => (WS.Request -> WebSockets p ())
+          -> Request
+          -> Maybe (Socket -> Iteratee ByteString IO ())
+intercept app req =
+    case lookup "upgrade" $ requestHeaders req of
+        Just s | S.map toLower s=="websocket" -> Just $ \socket -> do
+            runWebSockets req' app (iterSocket socket)
+        _ -> Nothing
+  where
+    req' = RequestHttpPart (rawPathInfo req) (requestHeaders req)
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,7 @@
+#!/usr/bin/env runhaskell
+
+> module Main where
+> import Distribution.Simple
+
+> main :: IO ()
+> main = defaultMain
diff --git a/server.lhs b/server.lhs
new file mode 100644
--- /dev/null
+++ b/server.lhs
@@ -0,0 +1,170 @@
+websockets example
+==================
+
+This is the Haskell implementation of the example for the WebSockets library. We
+implement a simple multi-user chat program. A live demo of the example is
+available [here](http://jaspervdj.be/websockets-example). In order to understand
+this example, keep the [reference](http://jaspervdj.be/websockets/reference)
+nearby to check out the functions we use.
+
+> {-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
+> import Data.Char (isPunctuation, isSpace)
+> import Data.Monoid (mappend)
+> import Data.Text (Text)
+> import Control.Exception (fromException)
+> import Control.Monad (forM_)
+> import Control.Concurrent (MVar, newMVar, modifyMVar_, readMVar)
+> import Control.Monad.IO.Class (liftIO)
+> import qualified Data.Text as T
+> import qualified Data.Text.IO as T
+
+> import qualified Network.WebSockets as WS
+> import qualified Network.Wai
+> import qualified Network.Wai.Handler.Warp as Warp
+> import qualified Network.Wai.Handler.WebSockets as WaiWS
+> import qualified Network.Wai.Application.Static as Static
+> import Data.FileEmbed (embedDir)
+
+We represent a client by his username and a 'WS.Sender'. We can use this sender
+to asynchronously send 'Text' to the client later. Note that using `WS.Hybi00`
+here does not imply that our server is only compatible with the `hybi-00`
+version of the protocol, for more details on this, see the
+[Network.WebSockets](http://jaspervdj.be/websockets/reference/Network-WebSockets.html) 
+reference.
+
+> type Client = (Text, WS.Sink WS.Hybi00)
+
+The state kept on the server is simply a list of connected clients. We've added
+an alias and some utility functions, so it will be easier to extend this state
+later on.
+
+> type ServerState = [Client]
+
+Create a new, initial state
+
+> newServerState :: ServerState
+> newServerState = []
+
+Get the number of active clients
+
+> numClients :: ServerState -> Int
+> numClients = length
+
+Check if a user already exists (based on username)
+
+> clientExists :: Client -> ServerState -> Bool
+> clientExists client = any ((== fst client) . fst)
+
+Add a client (first, you should verify the client is not already connected using
+'clientExists')
+
+> addClient :: Client -> ServerState -> ServerState
+> addClient client clients = client : clients
+
+Remove a client
+
+> removeClient :: Client -> ServerState -> ServerState
+> removeClient client = filter ((/= fst client) . fst)
+
+Send a message to all clients, and log it on stdout.
+
+> broadcast :: Text -> ServerState -> IO ()
+> broadcast message clients = do
+>     T.putStrLn message
+>     forM_ clients $ \(_, sink) -> WS.sendSink sink $ WS.textData message
+
+The main function first creates a new state for the server, then spawns the
+actual server. For this purpose, we use the simple server provided by
+'WS.runServer'.
+
+> main :: IO ()
+> main = do
+>     putStrLn "http://localhost:9160/client.html"
+>     state <- newMVar newServerState
+>     Warp.runSettings Warp.defaultSettings
+>       { Warp.settingsPort = 9160
+>       , Warp.settingsIntercept = WaiWS.intercept (application state)
+>       } staticApp
+
+> staticApp :: Network.Wai.Application
+> staticApp = Static.staticApp Static.defaultFileServerSettings
+>   { Static.ssFolder = Static.embeddedLookup $ Static.toEmbedded $(embedDir "static")
+>   }
+
+When a client connects, we accept the connection, regardless of the path.
+
+> application :: MVar ServerState -> WS.Request -> WS.WebSockets WS.Hybi00 ()
+> application state rq = do
+>     WS.acceptRequest rq
+
+We log some information here: in particular, we are interested in the protocol
+version our client is using (for debugging purposes).
+
+>     WS.getVersion >>= liftIO . putStrLn . ("Client version: " ++)
+
+If we want to be able to send data to this client later, from another thread, we
+obtain a sink. We will add this to the server state later.
+
+>     sink <- WS.getSink
+
+When a client is succesfully connected, we read the first message. This should
+be in the format of "Hi, I am Jasper", where Jasper is the requested username.
+
+>     msg <- WS.receiveData
+>     clients <- liftIO $ readMVar state
+>     case msg of
+
+Check that the first message has the right format
+
+>         _   | not (prefix `T.isPrefixOf` msg) ->
+>                 WS.sendTextData ("Wrong announcement" :: Text)
+
+Check the validity of the username
+
+>             | any ($ fst client)
+>                 [T.null, T.any isPunctuation, T.any isSpace] ->
+>                     WS.sendTextData ("Name cannot " `mappend`
+>                         "contain punctuation or whitespace, and " `mappend`
+>                         "cannot be empty" :: Text)
+
+Check that the given username is not already taken
+
+>             | clientExists client clients ->
+>                 WS.sendTextData ("User already exists" :: Text)
+
+All is right!
+
+>             | otherwise -> do
+
+We send a "Welcome!", according to our own little protocol. We add the client to
+the list and broadcast the fact that he has joined. Then, we give control to the
+'talk' function.
+
+>                liftIO $ modifyMVar_ state $ \s -> do
+>                    let s' = addClient client s
+>                    WS.sendSink sink $ WS.textData $
+>                        "Welcome! Users: " `mappend`
+>                        T.intercalate ", " (map fst s)
+>                    broadcast (fst client `mappend` " joined") s'
+>                    return s'
+>                talk state client
+>           where
+>             prefix = "Hi! I am "
+>             client = (T.drop (T.length prefix) msg, sink)
+
+The talk function continues to read messages from a single client until he
+disconnects. All messages are broadcasted to the other clients.
+
+> talk :: WS.Protocol p => MVar ServerState -> Client -> WS.WebSockets p ()
+> talk state client@(user, _) = flip WS.catchWsError catchDisconnect $ do
+>     msg <- WS.receiveData
+>     liftIO $ readMVar state >>= broadcast
+>         (user `mappend` ": " `mappend` msg)
+>     talk state client
+>   where
+>     catchDisconnect e = case fromException e of
+>         Just WS.ConnectionClosed -> liftIO $ modifyMVar_ state $ \s -> do
+>             let s' = removeClient client s
+>             broadcast (user `mappend` " disconnected") s'
+>             return s'
+>         _ -> return ()
diff --git a/static/client.html b/static/client.html
new file mode 100644
--- /dev/null
+++ b/static/client.html
@@ -0,0 +1,42 @@
+<html>
+    <head>
+        <title>Haskell WebSockets example</title>
+        <script type="text/JavaScript"
+            src="http://code.jquery.com/jquery-1.6.3.min.js"></script>
+        <script type="text/JavaScript" src="client.js"></script>
+        <link rel="stylesheet" type="text/css" href="screen.css"></script>
+    </head>
+    <body>
+        <h1>Haskell WebSockets example</h1>
+        <div id="main">
+            <div id="warnings">
+            </div>
+            <div id="join-section">
+                <h2>Join</h2>
+                <form id="join-form" action="">
+                    <label for="user">Username: </label>
+                    <input id="user" type="text" size="12" />
+                    <input id="send" type="submit" value="Join" />
+                </form>
+            </div>
+            <div id="users-section" style="display: none">
+                <h2>Users</h2>
+                <ul id="users">
+                </ul>
+            </div>
+            <div id="chat-section" style="display: none">
+                <h2>Chat</h2>
+                <div id="messages">
+                </div>
+                <br />
+                <form id="message-form" action="">
+                    <input id="text" type="text" size="40" />
+                    <input id="send" type="submit" value="Send" />
+                </form>
+            </div>
+        </div>
+        <div id="footer">
+            Source code available <a href="http://github.com/jaspervdj/websockets/tree/master/example">here</a>
+        </div>
+    </body>
+</html>
diff --git a/static/client.js b/static/client.js
new file mode 100644
--- /dev/null
+++ b/static/client.js
@@ -0,0 +1,80 @@
+function createWebSocket(path) {
+    var host = window.location.hostname;
+    if(host == '') host = 'localhost';
+    var uri = 'ws://' + host + ':9160' + path;
+
+    var Socket = "MozWebSocket" in window ? MozWebSocket : WebSocket;
+    return new Socket(uri);
+}
+
+var users = [];
+
+function refreshUsers() {
+    $('#users').html('');
+    for(i in users) {
+        $('#users').append($(document.createElement('li')).text(users[i]));
+    }
+}
+
+function onMessage(event) {
+    var p = $(document.createElement('p')).text(event.data);
+
+    $('#messages').append(p);
+    $('#messages').animate({scrollTop: $('#messages')[0].scrollHeight});
+
+    if(event.data.match(/^[^:]* joined/)) {
+        var user = event.data.replace(/ .*/, '');
+        users.push(user);
+        refreshUsers();
+    }
+
+    if(event.data.match(/^[^:]* disconnected/)) {
+        var user = event.data.replace(/ .*/, '');
+        var idx = users.indexOf(user);
+        users = users.slice(0, idx).concat(users.slice(idx + 1));
+        refreshUsers();
+    }
+}
+
+$(document).ready(function () {
+    $('#join-form').submit(function () {
+        $('#warnings').html('');
+        var user = $('#user').val();
+        var ws = createWebSocket('/');
+
+        ws.onopen = function() {
+            ws.send('Hi! I am ' + user);
+        };
+
+        ws.onmessage = function(event) {
+            if(event.data.match('^Welcome! Users: ')) {
+                /* Calculate the list of initial users */
+                var str = event.data.replace(/^Welcome! Users: /, '');
+                if(str != "") {
+                    users = str.split(", ");
+                    refreshUsers();
+                }
+
+                $('#join-section').hide();
+                $('#chat-section').show();
+                $('#users-section').show();
+
+                ws.onmessage = onMessage;
+
+                $('#message-form').submit(function () {
+                    var text = $('#text').val();
+                    ws.send(text);
+                    $('#text').val('');
+                    return false;
+                });
+            } else {
+                $('#warnings').append(event.data);
+                ws.close();
+            }
+        };
+
+        $('#join').append('Connecting...');
+
+        return false;
+    });
+});
diff --git a/static/screen.css b/static/screen.css
new file mode 100644
--- /dev/null
+++ b/static/screen.css
@@ -0,0 +1,86 @@
+html {
+    font-family: sans-serif;
+    background-color: #335;
+    font-size: 16px;
+}
+
+body {
+}
+
+h1 {
+    text-align: center;
+    font-size: 20px;
+    color: #fff;
+    padding: 10px 10px 20px 10px;
+}
+
+h2 {
+    border-bottom: 1px solid black;
+    display: block;
+    font-size: 18px;
+}
+
+div#main {
+    width: 600px;
+    margin: 0px auto 0px auto;
+    padding: 0px;
+    background-color: #fff;
+    height: 460px;
+}
+
+div#warnings {
+    color: red;
+    font-weight: bold;
+    margin: 10px;
+}
+
+div#join-section {
+    float: left;
+    margin: 10px;
+}
+
+div#users-section {
+    width: 170px;
+    float: right;
+    padding: 0px;
+    margin: 10px;
+}
+
+ul#users {
+    list-style-type: none;
+    padding-left: 0px;
+    height: 300px;
+    overflow: auto;
+}
+
+div#chat-section {
+    width: 390px;
+    float: left;
+    margin: 10px;
+}
+
+div#messages {
+    margin: 0px;
+    height: 300px;
+    overflow: auto;
+}
+
+div#messages p {
+    margin: 0px;
+    padding: 0px;
+}
+
+div#footer {
+    text-align: center;
+    font-size: 12px;
+    color: #fff;
+    margin: 10px 0px 30px 0px;
+}
+
+div#footer a {
+    color: #fff;
+}
+
+div.clear {
+    clear: both;
+}
diff --git a/wai-websockets.cabal b/wai-websockets.cabal
new file mode 100644
--- /dev/null
+++ b/wai-websockets.cabal
@@ -0,0 +1,52 @@
+Name:                wai-websockets
+Version:             0.5.0
+Synopsis:            Provide a bridge betweeen WAI and the websockets package.
+License:             BSD3
+License-file:        LICENSE
+Author:              Michael Snoyman
+Maintainer:          michael@snoyman.com
+Homepage:            http://github.com/yesodweb/wai
+Category:            Web, Yesod
+Build-Type:          Simple
+Cabal-Version:       >=1.8
+Stability:           Stable
+Description:         This is primarily intended for use with Warp and its settingsIntercept.
+
+extra-source-files: static/client.js, static/client.html, static/screen.css
+
+Library
+  Build-Depends:     base                          >= 3        && < 5
+                   , bytestring                >= 0.9.1.4  && < 0.10
+                   , wai                           >= 0.4      && < 0.5
+                   , enumerator                >= 0.4.8    && < 0.5
+                   , network-enumerator            >= 0.1.2    && < 0.2
+                   , blaze-builder                 >= 0.2.1.4  && < 0.4
+                   , case-insensitive              >= 0.2      && < 0.4
+                   , network                   >= 2.2.1.5 && < 2.4
+                   , websockets                    >= 0.4      && < 0.5
+  Exposed-modules:   Network.Wai.Handler.WebSockets
+  ghc-options:       -Wall
+
+Executable           wai-websockets-example
+  Build-Depends:     base >=3 && < 5,
+                     wai-websockets,
+                     websockets,
+                     network-enumerator,
+                     warp,
+                     wai,
+                     wai-app-static,
+                     bytestring,
+                     case-insensitive,
+                     blaze-builder,
+                     enumerator,
+                     transformers,
+                     network,
+                     text,
+                     file-embed
+
+  ghc-options:       -Wall -threaded
+  main-is:           server.lhs
+
+source-repository head
+  type:     git
+  location: git://github.com/yesodweb/wai.git
