on master help sign in
Initial thoughts for ver 0.2
7b0cde9Michael Snoyman
Network/Wai.hs+120−246
@@ -35,31 +35,41 @@ module Network.Wai
35
35
36 -- $show_read
36 -- $show_read
37 -- ** Request method
37 -- ** Request method
38− Method (..)
38+ Method
39− , methodFromBS
39+ , methodOPTIONS
40− , methodToBS
40+ , methodGET
41− -- ** URL scheme (http versus https)
41+ , methodHEAD
42− , UrlScheme (..)
42+ , methodPOST
43+ , methodPUT
44+ , methodDELETE
45+ , methodTRACE
46+ , methodCONNECT
43 -- ** HTTP protocol versions
47 -- ** HTTP protocol versions
44− , HttpVersion (..)
48+ , HttpVersion
45− , httpVersionFromBS
49+ , http09
46− , httpVersionToBS
50+ , http10
51+ , http11
52+ -- ** Case-insensitive byte strings
53+ , CIByteString (..)
54+ , mkCIByteString
47 -- ** Request header names
55 -- ** Request header names
48− , RequestHeader (..)
56+ , RequestHeader
49− , requestHeader
50− , requestHeaderFromBS
51− , requestHeaderToBS
52− , requestHeaderToLower
53 -- ** Response header names
57 -- ** Response header names
54− , ResponseHeader (..)
58+ , ResponseHeader
55− , responseHeader
56− , responseHeaderFromBS
57− , responseHeaderToBS
58− , responseHeaderToLower
59 -- ** Response status code
59 -- ** Response status code
60 , Status (..)
60 , Status (..)
61− , statusCode
61+ , status200
62− , statusMessage
62+ , status301
63+ , status302
64+ , status303
65+ , status400
66+ , status401
67+ , status403
68+ , status404
69+ , status405
70+ , status500
71+ -- ** Response body
72+ , ResponseBody (..)
63 -- ** Source
73 -- ** Source
64 , Source (..)
74 , Source (..)
65 -- * Enumerator
75 -- * Enumerator
@@ -73,7 +83,9 @@ module Network.Wai
73
83
74import qualified Data.ByteString as B
84import qualified Data.ByteString as B
75import qualified Data.ByteString.Char8 as B8
85import qualified Data.ByteString.Char8 as B8
86+import qualified Data.ByteString.Lazy as L
76import Data.Char (toLower)
87import Data.Char (toLower)
88+import Data.String (IsString (..))
77
89
78-- $show_read
90-- $show_read
79--
91--
@@ -85,82 +97,62 @@ import Data.Char (toLower)
85-- | HTTP request method. This data type is extensible via the Method
97-- | HTTP request method. This data type is extensible via the Method
86-- constructor. Request methods are case-sensitive, and comparison is achieved
98-- constructor. Request methods are case-sensitive, and comparison is achieved
87-- by converting to a 'B.ByteString' via 'methodToBS'.
99-- by converting to a 'B.ByteString' via 'methodToBS'.
88−data Method =
89− OPTIONS
90− | GET
91− | HEAD
92− | POST
93− | PUT
94− | DELETE
95− | TRACE
96− | CONNECT
97− | Method B.ByteString
100+type Method = B.ByteString
98− deriving (Show, Read)
101+
99−
102+methodOPTIONS :: Method
100−instance Eq Method where
103+methodOPTIONS = B8.pack "OPTIONS"
101− x == y = methodToBS x == methodToBS y
104+
102−
105+methodGET :: Method
103−methodFromBS :: B.ByteString -> Method
106+methodGET = B8.pack "GET"
104−methodFromBS bs
107+
105− | B.length bs <= 7 = case B8.unpack bs of
108+methodHEAD :: Method
106− "OPTIONS" -> OPTIONS
107− "GET" -> GET
108− "HEAD" -> HEAD
109− "POST" -> POST
110− "PUT" -> PUT
111− "DELETE" -> DELETE
112− "TRACE" -> TRACE
113− "CONNECT" -> CONNECT
114− _ -> Method bs
115− | otherwise = Method bs
116−
117−methodToBS :: Method -> B.ByteString
118−methodToBS OPTIONS = B8.pack "OPTIONS"
119−methodToBS GET = B8.pack "GET"
120−methodToBS HEAD = B8.pack "HEAD"
109+methodHEAD = B8.pack "HEAD"
110+
111+methodPOST :: Method
121−methodToBS POST = B8.pack "POST"
112+methodPOST = B8.pack "POST"
113+
114+methodPUT :: Method
122−methodToBS PUT = B8.pack "PUT"
115+methodPUT = B8.pack "PUT"
116+
117+methodDELETE :: Method
123−methodToBS DELETE = B8.pack "DELETE"
118+methodDELETE = B8.pack "DELETE"
119+
120+methodTRACE :: Method
124−methodToBS TRACE = B8.pack "TRACE"
121+methodTRACE = B8.pack "TRACE"
122+
123+methodCONNECT :: Method
125−methodToBS CONNECT = B8.pack "CONNECT"
124+methodCONNECT = B8.pack "CONNECT"
126−methodToBS (Method bs) = bs
127−
128−data UrlScheme = HTTP | HTTPS
129− deriving (Show, Eq)
130
125
131-- | Version of HTTP protocol used in current request. This data type is
126-- | Version of HTTP protocol used in current request. This data type is
132-- extensible via the HttpVersion constructor. Comparison is achieved by
127-- extensible via the HttpVersion constructor. Comparison is achieved by
133-- converting to a 'B.ByteString' via 'httpVersionToBS'.
128-- converting to a 'B.ByteString' via 'httpVersionToBS'.
134−data HttpVersion =
129+type HttpVersion = B.ByteString
135− Http09
136− | Http10
137− | Http11
138− | HttpVersion B.ByteString
139− deriving (Show, Read)
140
130
141−instance Eq HttpVersion where
131+http09 :: HttpVersion
142− x == y = httpVersionToBS x == httpVersionToBS y
132+http09 = B8.pack "0.9"
143
133
144−-- | This function takes the information after \"HTTP\/\". For example:
134+http10 :: HttpVersion
145−--
135+http10 = B8.pack "1.0"
146−-- @ 'httpVersionFromBS' ('B8.pack' \"1.0\") == 'Http10' @
136+
147−httpVersionFromBS :: B.ByteString -> HttpVersion
137+http11 :: HttpVersion
148−httpVersionFromBS bs
138+http11 = B8.pack "1.1"
149− | B.length bs == 3 = case B8.unpack bs of
139+
150− "0.9" -> Http09
140+data CIByteString = CIByteString
151− "1.0" -> Http10
141+ { ciOriginal :: !B.ByteString
152− "1.1" -> Http11
142+ , ciLowerCase :: !B.ByteString
153− _ -> HttpVersion bs
143+ }
154− | otherwise = HttpVersion bs
144+
155−
145+mkCIByteString :: B.ByteString -> CIByteString
156−-- | Returns the version number, for example:
146+mkCIByteString bs = CIByteString bs $ B8.map toLower bs
157−--
147+
158−-- @ 'B8.unpack' ('httpVersionToBS' 'Http10') == \"1.0\" @
148+instance Show CIByteString where
159−httpVersionToBS :: HttpVersion -> B.ByteString
149+ show = show . ciOriginal
160−httpVersionToBS Http09 = B8.pack "0.9"
150+instance Eq CIByteString where
161−httpVersionToBS Http10 = B8.pack "1.0"
151+ x == y = ciLowerCase x == ciLowerCase y
162−httpVersionToBS Http11 = B8.pack "1.1"
152+instance Ord CIByteString where
163−httpVersionToBS (HttpVersion bs) = bs
153+ x <= y = ciLowerCase x <= ciLowerCase y
154+instance IsString CIByteString where
155+ fromString = mkCIByteString . fromString
164
156
165-- | Headers sent from the client to the server. Clearly, this is not a
157-- | Headers sent from the client to the server. Clearly, this is not a
166-- complete list of all possible headers, but rather a selection of common
158-- complete list of all possible headers, but rather a selection of common
@@ -172,68 +164,7 @@ httpVersionToBS (HttpVersion bs) = bs
172--
164--
173-- Equality determined by conversion via 'requestHeaderToLower'. Request
165-- Equality determined by conversion via 'requestHeaderToLower'. Request
174-- headers are *not* case sensitive (a change from version 0.0 of WAI).
166-- headers are *not* case sensitive (a change from version 0.0 of WAI).
175−data RequestHeader =
167+type RequestHeader = CIByteString
176− Accept
177− | AcceptCharset
178− | AcceptEncoding
179− | AcceptLanguage
180− | Authorization
181− | Cookie
182− | ReqContentLength
183− | ReqContentType
184− | Host
185− | Referer
186− | RequestHeader B.ByteString B.ByteString
187− deriving (Show, Read)
188−
189−lowerBS :: B.ByteString -> B.ByteString
190−lowerBS = B8.map toLower
191−
192−requestHeader :: B.ByteString -> RequestHeader
193−requestHeader x = RequestHeader x $ lowerBS x
194−
195−instance Eq RequestHeader where
196− x == y = requestHeaderToLower x == requestHeaderToLower y
197−
198−requestHeaderFromBS :: B.ByteString -> RequestHeader
199−requestHeaderFromBS bs = case B8.unpack bs of
200− "Accept" -> Accept
201− "Accept-Charset" -> AcceptCharset
202− "Accept-Encoding" -> AcceptEncoding
203− "Accept-Language" -> AcceptLanguage
204− "Authorization" -> Authorization
205− "Cookie" -> Cookie
206− "Content-Length" -> ReqContentLength
207− "Content-Type" -> ReqContentType
208− "Host" -> Host
209− "Referer" -> Referer
210− _ -> requestHeader bs
211−
212−requestHeaderToBS :: RequestHeader -> B.ByteString
213−requestHeaderToBS Accept = B8.pack "Accept"
214−requestHeaderToBS AcceptCharset = B8.pack "Accept-Charset"
215−requestHeaderToBS AcceptEncoding = B8.pack "Accept-Encoding"
216−requestHeaderToBS AcceptLanguage = B8.pack "Accept-Language"
217−requestHeaderToBS Authorization = B8.pack "Authorization"
218−requestHeaderToBS Cookie = B8.pack "Cookie"
219−requestHeaderToBS ReqContentLength = B8.pack "Content-Length"
220−requestHeaderToBS ReqContentType = B8.pack "Content-Type"
221−requestHeaderToBS Host = B8.pack "Host"
222−requestHeaderToBS Referer = B8.pack "Referer"
223−requestHeaderToBS (RequestHeader bs _) = bs
224−
225−requestHeaderToLower :: RequestHeader -> B.ByteString
226−requestHeaderToLower Accept = B8.pack "accept"
227−requestHeaderToLower AcceptCharset = B8.pack "accept-charset"
228−requestHeaderToLower AcceptEncoding = B8.pack "accept-encoding"
229−requestHeaderToLower AcceptLanguage = B8.pack "accept-language"
230−requestHeaderToLower Authorization = B8.pack "authorization"
231−requestHeaderToLower Cookie = B8.pack "cookie"
232−requestHeaderToLower ReqContentLength = B8.pack "content-Length"
233−requestHeaderToLower ReqContentType = B8.pack "content-Type"
234−requestHeaderToLower Host = B8.pack "host"
235−requestHeaderToLower Referer = B8.pack "referer"
236−requestHeaderToLower (RequestHeader _ bs) = bs
237
168
238-- | Headers sent from the server to the client. Clearly, this is not a
169-- | Headers sent from the server to the client. Clearly, this is not a
239-- complete list of all possible headers, but rather a selection of common
170-- complete list of all possible headers, but rather a selection of common
@@ -244,61 +175,7 @@ requestHeaderToLower (RequestHeader _ bs) = bs
244--
175--
245-- Equality determined by conversion via 'responseHeaderToLower'. Response
176-- Equality determined by conversion via 'responseHeaderToLower'. Response
246-- headers are *not* case sensitive (a change from version 0.0 of WAI).
177-- headers are *not* case sensitive (a change from version 0.0 of WAI).
247−data ResponseHeader =
248− ContentEncoding
249− | ContentLanguage
250− | ContentLength
251− | ContentDisposition
252− | ContentType
253− | Expires
254− | Location
255− | Server
256− | SetCookie
257− | ResponseHeader B.ByteString B.ByteString
258− deriving (Show)
259−
260−instance Eq ResponseHeader where
261− x == y = responseHeaderToBS x == responseHeaderToBS y
262−
263−responseHeader :: B.ByteString -> ResponseHeader
264−responseHeader x = ResponseHeader x $ lowerBS x
265−
266−responseHeaderFromBS :: B.ByteString -> ResponseHeader
267−responseHeaderFromBS bs = case B8.unpack bs of
268− "Content-Encoding" -> ContentEncoding
269− "Content-Language" -> ContentLanguage
270− "Content-Length" -> ContentLength
271− "Content-Disposition" -> ContentDisposition
272− "Content-Type" -> ContentType
273− "Expires" -> Expires
274− "Location" -> Location
275− "Server" -> Server
276− "Set-Cookie" -> SetCookie
277− _ -> responseHeader bs
278−
279−responseHeaderToBS :: ResponseHeader -> B.ByteString
280−responseHeaderToBS ContentEncoding = B8.pack "Content-Encoding"
281−responseHeaderToBS ContentLanguage = B8.pack "Content-Language"
282−responseHeaderToBS ContentLength = B8.pack "Content-Length"
283−responseHeaderToBS ContentDisposition = B8.pack "Content-Disposition"
284−responseHeaderToBS ContentType = B8.pack "Content-Type"
285−responseHeaderToBS Expires = B8.pack "Expires"
286−responseHeaderToBS Location = B8.pack "Location"
287−responseHeaderToBS Server = B8.pack "Server"
288−responseHeaderToBS SetCookie = B8.pack "Set-Cookie"
289−responseHeaderToBS (ResponseHeader bs _) = bs
290−
291−responseHeaderToLower :: ResponseHeader -> B.ByteString
292−responseHeaderToLower ContentEncoding = B8.pack "content-encoding"
293−responseHeaderToLower ContentLanguage = B8.pack "content-language"
294−responseHeaderToLower ContentLength = B8.pack "content-length"
295−responseHeaderToLower ContentDisposition = B8.pack "content-disposition"
296−responseHeaderToLower ContentType = B8.pack "content-type"
297−responseHeaderToLower Expires = B8.pack "expires"
298−responseHeaderToLower Location = B8.pack "location"
299−responseHeaderToLower Server = B8.pack "server"
300−responseHeaderToLower SetCookie = B8.pack "set-cookie"
301−responseHeaderToLower (ResponseHeader _ bs) = bs
178+type ResponseHeader = CIByteString
302
179
303-- | This attempts to provide the most common HTTP status codes, not all of
180-- | This attempts to provide the most common HTTP status codes, not all of
304-- them. Use the Status constructor when you want to create a status code not
181-- them. Use the Status constructor when you want to create a status code not
@@ -306,48 +183,41 @@ responseHeaderToLower (ResponseHeader _ bs) = bs
306--
183--
307-- The 'Eq' instance tests equality based only on the numeric status code
184-- The 'Eq' instance tests equality based only on the numeric status code
308-- value. See 'statusCode'.
185-- value. See 'statusCode'.
309−data Status =
186+data Status = Status { statusCode :: Int, statusMessage :: B.ByteString }
310− Status200
311− | Status301
312− | Status302
313− | Status303
314− | Status400
315− | Status401
316− | Status403
317− | Status404
318− | Status405
319− | Status500
320− | Status Int B.ByteString
321 deriving Show
187 deriving Show
322
188
323instance Eq Status where
189instance Eq Status where
324 x == y = statusCode x == statusCode y
190 x == y = statusCode x == statusCode y
325
191
326−statusCode :: Status -> Int
192+status200 :: Status
327−statusCode Status200 = 200
193+status200 = Status 200 $ B8.pack "OK"
328−statusCode Status301 = 301
194+
329−statusCode Status302 = 302
195+status301 :: Status
330−statusCode Status303 = 303
331−statusCode Status400 = 400
332−statusCode Status401 = 401
333−statusCode Status403 = 403
334−statusCode Status404 = 404
335−statusCode Status405 = 405
336−statusCode Status500 = 500
337−statusCode (Status i _) = i
338−
339−statusMessage :: Status -> B.ByteString
340−statusMessage Status200 = B8.pack "OK"
341−statusMessage Status301 = B8.pack "Moved Permanently"
196+status301 = Status 301 $ B8.pack "Moved Permanently"
342−statusMessage Status302 = B8.pack "Found"
197+
198+status302 :: Status
199+status302 = Status 302 $ B8.pack "Found"
200+
201+status303 :: Status
343−statusMessage Status303 = B8.pack "See Other"
202+status303 = Status 303 $ B8.pack "See Other"
203+
204+status400 :: Status
344−statusMessage Status400 = B8.pack "Bad Request"
205+status400 = Status 400 $ B8.pack "Bad Request"
206+
207+status401 :: Status
345−statusMessage Status401 = B8.pack "Unauthorized"
208+status401 = Status 401 $ B8.pack "Unauthorized"
209+
210+status403 :: Status
346−statusMessage Status403 = B8.pack "Forbidden"
211+status403 = Status 403 $ B8.pack "Forbidden"
212+
213+status404 :: Status
347−statusMessage Status404 = B8.pack "Not Found"
214+status404 = Status 404 $ B8.pack "Not Found"
215+
216+status405 :: Status
348−statusMessage Status405 = B8.pack "Method Not Allowed"
217+status405 = Status 405 $ B8.pack "Method Not Allowed"
218+
219+status500 :: Status
349−statusMessage Status500 = B8.pack "Internal Server Error"
220+status500 = Status 500 $ B8.pack "Internal Server Error"
350−statusMessage (Status _ m) = m
351
221
352-- | This is a source for 'B.ByteString's. It is a function (wrapped in a
222-- | This is a source for 'B.ByteString's. It is a function (wrapped in a
353-- newtype) that will return Nothing if the data has been completely consumed,
223-- newtype) that will return Nothing if the data has been completely consumed,
@@ -408,13 +278,17 @@ data Request = Request
408 , serverName :: B.ByteString
278 , serverName :: B.ByteString
409 , serverPort :: Int
279 , serverPort :: Int
410 , requestHeaders :: [(RequestHeader, B.ByteString)]
280 , requestHeaders :: [(RequestHeader, B.ByteString)]
411− , urlScheme :: UrlScheme
281+ , isSecure :: Bool
412 , requestBody :: Source
282 , requestBody :: Source
413 , errorHandler :: String -> IO ()
283 , errorHandler :: String -> IO ()
414 -- | The client\'s host information.
284 -- | The client\'s host information.
415 , remoteHost :: B.ByteString
285 , remoteHost :: B.ByteString
416 }
286 }
417
287
288+data ResponseBody = ResponseFile FilePath
289+ | ResponseEnumerator Enumerator
290+ | ResponseLBS L.ByteString
291+
418data Response = Response
292data Response = Response
419 { status :: Status
293 { status :: Status
420 , responseHeaders :: [(ResponseHeader, B.ByteString)]
294 , responseHeaders :: [(ResponseHeader, B.ByteString)]
@@ -422,7 +296,7 @@ data Response = Response
422 -- files from the disk. This datatype facilitates this optimization; if
296 -- files from the disk. This datatype facilitates this optimization; if
423 -- 'Left' is returned, the server will send the file from the disk by
297 -- 'Left' is returned, the server will send the file from the disk by
424 -- whatever means it wishes. If 'Right', it will call the 'Enumerator'.
298 -- whatever means it wishes. If 'Right', it will call the 'Enumerator'.
425− , responseBody :: Either FilePath Enumerator
299+ , responseBody :: ResponseBody
426 }
300 }
427
301
428type Application = Request -> IO Response
302type Application = Request -> IO Response
Network/Wai/Handler/SimpleServer.hs+13−12
@@ -1,5 +1,6 @@
1{-# LANGUAGE DeriveDataTypeable #-}
1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE OverloadedStrings #-}
3---------------------------------------------------------
4---------------------------------------------------------
4-- |
5-- |
5-- Module : Network.Wai.Handler.SimpleServer
6-- Module : Network.Wai.Handler.SimpleServer
@@ -21,6 +22,7 @@ import Network.Wai
21import qualified System.IO
22import qualified System.IO
22
23
23import qualified Data.ByteString as BS
24import qualified Data.ByteString as BS
25+import qualified Data.ByteString.Lazy as L
24import qualified Data.ByteString.Char8 as B8
26import qualified Data.ByteString.Char8 as B8
25import Network
27import Network
26 ( listenOn, accept, sClose, PortID(PortNumber), Socket
28 ( listenOn, accept, sClose, PortID(PortNumber), Socket
@@ -104,30 +106,28 @@ parseRequest port lines' handle remoteHost' = do
104 case lines' of
106 case lines' of
105 (_:_:_) -> return ()
107 (_:_:_) -> return ()
106 _ -> failure $ NotEnoughLines $ map SL.unpack lines'
108 _ -> failure $ NotEnoughLines $ map SL.unpack lines'
107− (method', rpath', gets) <- parseFirst $ head lines'
109+ (method, rpath', gets) <- parseFirst $ head lines'
108− let method = methodFromBS method'
109 let rpath = '/' : case SL.unpack rpath' of
110 let rpath = '/' : case SL.unpack rpath' of
110 ('/':x) -> x
111 ('/':x) -> x
111 _ -> SL.unpack rpath'
112 _ -> SL.unpack rpath'
112− let heads = map (first requestHeaderFromBS . parseHeaderNoAttr)
113+ let heads = map (first mkCIByteString . parseHeaderNoAttr) $ tail lines'
113− $ tail lines'
114− let host' = lookup Host heads
114+ let host' = lookup "Host" heads
115 unless (isJust host') $ failure HostNotIncluded
115 unless (isJust host') $ failure HostNotIncluded
116 let host = fromJust host'
116 let host = fromJust host'
117 let len = fromMaybe 0 $ do
117 let len = fromMaybe 0 $ do
118− bs <- lookup ReqContentLength heads
118+ bs <- lookup "Content-Length" heads
119 let str = SL.unpack bs
119 let str = SL.unpack bs
120 Safe.readMay str
120 Safe.readMay str
121 let (serverName', _) = SL.breakChar ':' host
121 let (serverName', _) = SL.breakChar ':' host
122 return $ Request
122 return $ Request
123 { requestMethod = method
123 { requestMethod = method
124− , httpVersion = Http11
124+ , httpVersion = http11
125 , pathInfo = SL.pack rpath
125 , pathInfo = SL.pack rpath
126 , queryString = gets
126 , queryString = gets
127 , serverName = serverName'
127 , serverName = serverName'
128 , serverPort = port
128 , serverPort = port
129 , requestHeaders = heads
129 , requestHeaders = heads
130− , urlScheme = HTTP
130+ , isSecure = False
131 , requestBody = requestBodyHandle handle len
131 , requestBody = requestBodyHandle handle len
132 , errorHandler = System.IO.hPutStr System.IO.stderr
132 , errorHandler = System.IO.hPutStr System.IO.stderr
133 , remoteHost = B8.pack remoteHost'
133 , remoteHost = B8.pack remoteHost'
@@ -161,18 +161,19 @@ sendResponse h res = do
161 BS.hPut h $ SL.pack $ show $ statusCode $ status res
161 BS.hPut h $ SL.pack $ show $ statusCode $ status res
162 BS.hPut h $ statusMessage $ status res
162 BS.hPut h $ statusMessage $ status res
163 BS.hPut h $ SL.pack "\r\n"
163 BS.hPut h $ SL.pack "\r\n"
164− mapM_ putHeader $ responseHeaders res
164+ mapM_ putHeader $ map (first ciOriginal) $ responseHeaders res
165 BS.hPut h $ SL.pack "\r\n"
165 BS.hPut h $ SL.pack "\r\n"
166 case responseBody res of
166 case responseBody res of
167− Left fp -> unsafeSendFile h fp
167+ ResponseFile fp -> unsafeSendFile h fp
168− Right (Enumerator enum) -> enum myPut h >> return ()
168+ ResponseEnumerator (Enumerator enum) -> enum myPut h >> return ()
169+ ResponseLBS lbs -> L.hPutStr h lbs
169 where
170 where
170 myPut _ bs = do
171 myPut _ bs = do
171 putStrLn $ "sending a chunk of size " ++ show (BS.length bs)
172 putStrLn $ "sending a chunk of size " ++ show (BS.length bs)
172 BS.hPut h bs
173 BS.hPut h bs
173 return (Right h)
174 return (Right h)
174 putHeader (x, y) = do
175 putHeader (x, y) = do
175− BS.hPut h $ responseHeaderToBS x
176+ BS.hPut h x
176 BS.hPut h $ SL.pack ": "
177 BS.hPut h $ SL.pack ": "
177 BS.hPut h y
178 BS.hPut h y
178 BS.hPut h $ SL.pack "\r\n"
179 BS.hPut h $ SL.pack "\r\n"
README.lhs+13−13
@@ -13,9 +13,9 @@ You want a minimal example? Here it is!
13>
13>
14> app :: Application
14> app :: Application
15> app _ = return Response
15> app _ = return Response
16−> { status = Status200
16+> { status = status200
17−> , responseHeaders = [(ContentType, "text/plain")]
17+> , responseHeaders = [("Content-Type", "text/plain")]
18−> , responseBody = Right $ fromLBS "Hello, Web!"
18+> , responseBody = ResponseLBS "Hello, Web!"
19> }
19> }
20>
20>
21> main :: IO ()
21> main :: IO ()
@@ -51,9 +51,9 @@ Now we redefine `responseBody` to refer to that file:
51> app2 _ = return index
51> app2 _ = return index
52>
52>
53> index = Response
53> index = Response
54−> { status = Status200
54+> { status = status200
55−> , responseHeaders = [(ContentType, "text/html")]
55+> , responseHeaders = [("Content-Type", "text/html")]
56−> , responseBody = Left "index.html"
56+> , responseBody = ResponseFile "index.html"
57> }
57> }
58
58
59
59
@@ -75,15 +75,15 @@ different `Response`s:
75> _ -> return notFound
75> _ -> return notFound
76>
76>
77> plainIndex = Response
77> plainIndex = Response
78−> { status = Status200
78+> { status = status200
79−> , responseHeaders = [(ContentType, "text/plain")]
79+> , responseHeaders = [("Content-Type", "text/plain")]
80−> , responseBody = Left "index.html"
80+> , responseBody = ResponseFile "index.html"
81> }
81> }
82>
82>
83> notFound = Response
83> notFound = Response
84−> { status = Status404
84+> { status = status404
85−> , responseHeaders = [(ContentType, "text/plain")]
85+> , responseHeaders = [("Content-Type", "text/plain")]
86−> , responseBody = Right $ fromLBS "404 - Not Found"
86+> , responseBody = ResponseLBS "404 - Not Found"
87> }
87> }
88
88
89
89
@@ -100,7 +100,7 @@ works without GHC extensions:
100
100
101 notFound = Response
101 notFound = Response
102 { status = Status404
102 { status = Status404
103− , responseHeaders = [(ContentType, B8.pack "text/plain")]
103+ , responseHeaders = [("Content-Type", B8.pack "text/plain")]
104 , responseBody = Right $ fromLBS $ LB8.pack "404 - Not Found"
104 , responseBody = Right $ fromLBS $ LB8.pack "404 - Not Found"
105 }
105 }
106
106
README.md+14−14
@@ -1,4 +1,4 @@
1−<!-- Generated from README.lhs, do not modify this file! -->
1+<!-- Generated from README.lhs, do not modify! -->
2WAI: Web Application Interface
2WAI: Web Application Interface
3==============================
3==============================
4
4
@@ -14,9 +14,9 @@ You want a minimal example? Here it is!
14
14
15 app :: Application
15 app :: Application
16 app _ = return Response
16 app _ = return Response
17− { status = Status200
17+ { status = status200
18− , responseHeaders = [(ContentType, "text/plain")]
18+ , responseHeaders = [("Content-Type", "text/plain")]
19− , responseBody = Right $ fromLBS "Hello, Web!"
19+ , responseBody = ResponseLBS "Hello, Web!"
20 }
20 }
21
21
22 main :: IO ()
22 main :: IO ()
@@ -50,9 +50,9 @@ Now we redefine `responseBody` to refer to that file:
50 app2 _ = return index
50 app2 _ = return index
51
51
52 index = Response
52 index = Response
53− { status = Status200
53+ { status = status200
54− , responseHeaders = [(ContentType, "text/html")]
54+ , responseHeaders = [("Content-Type", "text/html")]
55− , responseBody = Left "index.html"
55+ , responseBody = ResponseFile "index.html"
56 }
56 }
57
57
58
58
@@ -73,15 +73,15 @@ Depending on the path info provided with each `Request` we can serve different `
73 _ -> return notFound
73 _ -> return notFound
74
74
75 plainIndex = Response
75 plainIndex = Response
76− { status = Status200
76+ { status = status200
77− , responseHeaders = [(ContentType, "text/plain")]
77+ , responseHeaders = [("Content-Type", "text/plain")]
78− , responseBody = Left "index.html"
78+ , responseBody = ResponseFile "index.html"
79 }
79 }
80
80
81 notFound = Response
81 notFound = Response
82− { status = Status404
82+ { status = status404
83− , responseHeaders = [(ContentType, "text/plain")]
83+ , responseHeaders = [("Content-Type", "text/plain")]
84− , responseBody = Right $ fromLBS "404 - Not Found"
84+ , responseBody = ResponseLBS "404 - Not Found"
85 }
85 }
86
86
87
87
@@ -95,7 +95,7 @@ For the sake of efficiency, WAI uses the [bytestring] package. We used GHCs [ov
95
95
96 notFound = Response
96 notFound = Response
97 { status = Status404
97 { status = Status404
98− , responseHeaders = [(ContentType, B8.pack "text/plain")]
98+ , responseHeaders = [("Content-Type", B8.pack "text/plain")]
99 , responseBody = Right $ fromLBS $ LB8.pack "404 - Not Found"
99 , responseBody = Right $ fromLBS $ LB8.pack "404 - Not Found"
100 }
100 }
101
101
wai.cabal+1−1
@@ -1,5 +1,5 @@
1Name: wai
1Name: wai
2−Version: 0.1.0
2+Version: 0.2.0
3Synopsis: Web Application Interface.
3Synopsis: Web Application Interface.
4Description: Provides a common protocol for communication between web aplications and web servers.
4Description: Provides a common protocol for communication between web aplications and web servers.
5License: BSD3
5License: BSD3