on master help sign in
Removed SimpleServer (now warp)
commitb9be2fc7d9d463857bf977da73cb32ed53dd1df0 authorMichael Snoyman <michael@snoyman.com> dateFri 24 Dec 2010, 10:59 (16 years ago) parent9cc38b6
4 files changed+22−247
Network/Wai/Handler/CGI.hs+22−2 Network/Wai/Handler/Helper.hs−31 Network/Wai/Handler/SimpleServer.hs−212 wai-extra.cabal−2
Network/Wai/Handler/CGI.hs+22−2
@@ -8,7 +8,6 @@ module Network.Wai.Handler.CGI
88 ) where
99
1010import Network.Wai
11−import Network.Wai.Handler.Helper
1211import System.Environment (getEnvironment)
1312import Data.Maybe (fromMaybe)
1413import qualified Data.ByteString.Char8 as B
@@ -19,13 +18,15 @@ import qualified System.IO
1918import qualified Data.String as String
2019import Data.Enumerator
2120 ( Enumerator, Step (..), Stream (..), continue, yield
22− , enumList, ($$), joinI
21+ , enumList, ($$), joinI, returnI, (>>==)
2322 )
2423import Data.Monoid (mconcat)
2524import Blaze.ByteString.Builder (fromByteString, toLazyByteString)
2625import Blaze.ByteString.Builder.Char8 (fromChar, fromString)
2726import Blaze.ByteString.Builder.Enumerator (builderToByteString)
2827import Control.Monad.IO.Class (liftIO)
28+import Data.ByteString.Lazy.Internal (defaultChunkSize)
29+import System.IO (Handle)
2930
3031safeRead :: Read a => a -> String -> a
3132safeRead d s =
@@ -146,3 +147,22 @@ cleanupVarName "CONTENT_TYPE" = "Content-Type"
146147cleanupVarName "CONTENT_LENGTH" = "Content-Length"
147148cleanupVarName "SCRIPT_NAME" = "CGI-Script-Name"
148149cleanupVarName x = String.fromString x -- FIXME remove?
150+
151+requestBodyHandle :: Handle -> Int -> Enumerator B.ByteString IO a
152+requestBodyHandle h =
153+ requestBodyFunc go
154+ where
155+ go i = Just `fmap` B.hGet h (min i defaultChunkSize)
156+
157+requestBodyFunc :: (Int -> IO (Maybe B.ByteString))
158+ -> Int
159+ -> Enumerator B.ByteString IO a
160+requestBodyFunc _ 0 step = returnI step
161+requestBodyFunc h len (Continue k) = do
162+ mbs <- liftIO $ h len
163+ case mbs of
164+ Nothing -> continue k
165+ Just bs -> do
166+ let newLen = len - B.length bs
167+ k (Chunks [bs]) >>== requestBodyFunc h newLen
168+requestBodyFunc _ _ step = returnI step
Network/Wai/Handler/Helper.hsdeleted−31
@@ -1,31 +0,0 @@
1−{-# LANGUAGE RankNTypes #-}
2−module Network.Wai.Handler.Helper
3− ( requestBodyHandle
4− , requestBodyFunc
5− ) where
6−
7−import System.IO (Handle)
8−import qualified Data.ByteString as B
9−import Data.ByteString.Lazy.Internal (defaultChunkSize)
10−import qualified Data.Enumerator as E
11−import Data.Enumerator ((>>==))
12−import Control.Monad.IO.Class (liftIO)
13−
14−requestBodyHandle :: Handle -> Int -> forall a. E.Enumerator B.ByteString IO a
15−requestBodyHandle h =
16− requestBodyFunc go
17− where
18− go i = Just `fmap` B.hGet h (min i defaultChunkSize)
19−
20−requestBodyFunc :: (Int -> IO (Maybe B.ByteString))
21− -> Int
22− -> forall a. E.Enumerator B.ByteString IO a
23−requestBodyFunc _ 0 step = E.returnI step
24−requestBodyFunc h len (E.Continue k) = do
25− mbs <- liftIO $ h len
26− case mbs of
27− Nothing -> E.continue k
28− Just bs -> do
29− let newLen = len - B.length bs
30− k (E.Chunks [bs]) >>== requestBodyFunc h newLen
31−requestBodyFunc _ _ step = E.returnI step
Network/Wai/Handler/SimpleServer.hsdeleted−212
@@ -1,212 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−---------------------------------------------------------
5−-- |
6−-- Module : Network.Wai.Handler.SimpleServer
7−-- Copyright : Michael Snoyman
8−-- License : BSD3
9−--
10−-- Maintainer : Michael Snoyman <michael@snoyman.com>
11−-- Stability : Stable
12−-- Portability : portable
13−--
14−-- A simplistic HTTP server handler for Wai.
15−--
16−---------------------------------------------------------
17−module Network.Wai.Handler.SimpleServer
18− ( run
19− , sendResponse
20− , parseRequest
21− ) where
22−
23−import Network.Wai
24−import Network.Wai.Handler.Helper
25−import qualified System.IO
26−
27−import Data.ByteString (ByteString)
28−import qualified Data.ByteString.Char8 as B
29−import Network
30− ( listenOn, accept, sClose, PortID(PortNumber), Socket
31− , withSocketsDo)
32−import Control.Exception (bracket, finally, Exception, throwIO)
33−import System.IO (Handle, hClose, hFlush)
34−import System.IO.Error (isEOFError, ioeGetHandle)
35−import Control.Concurrent (forkIO)
36−import Control.Monad (unless)
37−import Data.Maybe (fromMaybe)
38−
39−import Data.Typeable (Typeable)
40−
41−import Control.Arrow (first)
42−
43−import Data.Enumerator (($$), (>>==))
44−import qualified Data.Enumerator as E
45−import Data.Enumerator.IO (iterHandle)
46−import Blaze.ByteString.Builder.Enumerator (builderToByteString)
47−import Blaze.ByteString.Builder.HTTP
48− (chunkedTransferEncoding, chunkedTransferTerminator)
49−import Blaze.ByteString.Builder (fromByteString, Builder)
50−import Blaze.ByteString.Builder.Char8 (fromChar, fromString)
51−import Data.Monoid (mconcat)
52−
53−run :: Port -> Application -> IO ()
54−run port = withSocketsDo .
55− bracket
56− (listenOn $ PortNumber $ fromIntegral port)
57− sClose .
58− serveConnections port
59−type Port = Int
60−
61−serveConnections :: Port -> Application -> Socket -> IO ()
62−serveConnections port app socket = do
63− (conn, remoteHost', _) <- accept socket
64− _ <- forkIO $ serveConnection port app conn remoteHost'
65− serveConnections port app socket
66−
67−serveConnection :: Port -> Application -> Handle -> String -> IO ()
68−serveConnection port app conn remoteHost' = do
69− catch
70− (finally
71− serveConnection'
72− (hClose conn))
73− catchEOFError
74− where serveConnection' = do
75− env <- parseRequest port conn remoteHost'
76− res <- app env
77− sendResponse (httpVersion env) conn res
78− -- FIXME drain request body
79− hFlush conn
80− serveConnection'
81−
82− catchEOFError :: IOError -> IO ()
83− catchEOFError e | isEOFError e = case ioeGetHandle e of
84− Just h -> unless (h == conn) (ioError e)
85− Nothing -> ioError e
86− | otherwise = ioError e
87−
88−parseRequest :: Port -> Handle -> String -> IO Request
89−parseRequest port conn remoteHost' = do
90− headers' <- takeUntilBlank conn id
91− parseRequest' port headers' conn remoteHost'
92−
93−takeUntilBlank :: Handle
94− -> ([ByteString] -> [ByteString])
95− -> IO [ByteString]
96−takeUntilBlank h front = do
97− l <- stripCR `fmap` B.hGetLine h
98− if B.null l
99− then return $ front []
100− else takeUntilBlank h $ front . (:) l
101−
102−stripCR :: ByteString -> ByteString
103−stripCR bs
104− | B.null bs = bs
105− | B.last bs == '\r' = B.init bs
106− | otherwise = bs
107−
108−data InvalidRequest =
109− NotEnoughLines [String]
110− | BadFirstLine String
111− | NonHttp
112− deriving (Show, Typeable)
113−instance Exception InvalidRequest
114−
115−-- | Parse a set of header lines and body into a 'Request'.
116−parseRequest' :: Port
117− -> [ByteString]
118− -> Handle
119− -> String
120− -> IO Request
121−parseRequest' port lines' handle remoteHost' = do
122− (firstLine, otherLines) <-
123− case lines' of
124− x:xs -> return (x, xs)
125− [] -> throwIO $ NotEnoughLines $ map B.unpack lines'
126− (method, rpath', gets, httpversion) <- parseFirst firstLine
127− let rpath = '/' : case B.unpack rpath' of
128− ('/':x) -> x
129− _ -> B.unpack rpath'
130− let heads = map (first mkCIByteString . parseHeaderNoAttr) otherLines
131− let host = fromMaybe "" $ lookup "host" heads
132− let len = fromMaybe 0 $ do
133− bs <- lookup "Content-Length" heads
134− let str = B.unpack bs
135− case reads str of
136− (x, _):_ -> Just x
137− _ -> Nothing
138− let (serverName', _) = B.break (== ':') host
139− return $ Request
140− { requestMethod = method
141− , httpVersion = httpversion
142− , pathInfo = B.pack rpath
143− , queryString = gets
144− , serverName = serverName'
145− , serverPort = port
146− , requestHeaders = heads
147− , isSecure = False
148− , requestBody = requestBodyHandle handle len
149− , errorHandler = System.IO.hPutStr System.IO.stderr
150− , remoteHost = B.pack remoteHost'
151− }
152−
153−parseFirst :: ByteString
154− -> IO (ByteString, ByteString, ByteString, HttpVersion)
155−parseFirst s = do
156− let pieces = B.words s
157− (method, query, http') <-
158− case pieces of
159− [x, y, z] -> return (x, y, z)
160− _ -> throwIO $ BadFirstLine $ B.unpack s
161− let (hfirst, hsecond) = B.splitAt 5 http'
162− unless (hfirst == B.pack "HTTP/") $ throwIO NonHttp
163− let (rpath, qstring) = B.break (== '?') query
164− return (method, rpath, qstring, hsecond)
165−
166−headers :: HttpVersion -> Status -> ResponseHeaders -> Builder
167−headers httpversion status responseHeaders = mconcat
168− [ fromByteString "HTTP/"
169− , fromByteString httpversion
170− , fromChar ' '
171− , fromString $ show $ statusCode status
172− , fromChar ' '
173− , fromByteString $ statusMessage status
174− , fromByteString "\r\n"
175− , mconcat $ map go responseHeaders
176− , fromByteString "Transfer-Encoding: chunked\r\n\r\n" -- FIXME perhaps put in non-chunked for HTTP 1.0 clients?
177− ]
178− where
179− go (x, y) = mconcat
180− [ fromByteString $ ciOriginal x
181− , fromByteString ": "
182− , fromByteString y
183− , fromByteString "\r\n"
184− ]
185−
186−sendResponse :: HttpVersion -> Handle -> Response -> IO ()
187−sendResponse hv handle res = do
188− responseEnumerator res $ \s hs ->
189− E.joinI $ chunk
190− $$ E.enumList 1 [headers hv s hs]
191− $$ E.joinI $ builderToByteString
192− $$ iterHandle handle
193− where
194− chunk :: E.Enumeratee Builder Builder IO ()
195− chunk = E.checkDone $ E.continue . step
196− step k E.EOF = k (E.Chunks [chunkedTransferTerminator]) >>== return
197− step k (E.Chunks []) = E.continue $ step k
198− step k (E.Chunks builders) =
199− k (E.Chunks [chunked]) >>== chunk
200− where
201− chunked = chunkedTransferEncoding $ mconcat builders
202−
203−parseHeaderNoAttr :: ByteString -> (ByteString, ByteString)
204−parseHeaderNoAttr s =
205− let (k, rest) = B.span (/= ':') s
206− rest' = if not (B.null rest) &&
207− B.head rest == ':' &&
208− not (B.null $ B.tail rest) &&
209− B.head (B.tail rest) == ' '
210− then B.drop 2 rest
211− else rest
212− in (k, rest')
wai-extra.cabal−2
@@ -26,13 +26,11 @@ Library
2626 enumerator >= 0.4 && < 0.5,
2727 blaze-builder >= 0.2.1.3 && < 0.3
2828 Exposed-modules: Network.Wai.Handler.CGI
29− Network.Wai.Handler.SimpleServer
3029 Network.Wai.Middleware.CleanPath
3130 Network.Wai.Middleware.Gzip
3231 Network.Wai.Middleware.Jsonp
3332 Network.Wai.Zlib
3433 Network.Wai.Parse
35− Network.Wai.Handler.Helper
3634 ghc-options: -Wall
3735
3836source-repository head