on master help sign in
Connection-Length support
babcbfeMichael Snoyman
Network/Wai/Handler/Warp.hs+34−23
@@ -80,9 +80,9 @@ serveConnection port app conn remoteHost' = do
80 res <- app env
80 res <- app env
81 remaining <- readIORef ilen
81 remaining <- readIORef ilen
82 _ <- B.hGet conn remaining -- FIXME just skip, don't read
82 _ <- B.hGet conn remaining -- FIXME just skip, don't read
83− sendResponse (httpVersion env) conn res
83+ keepAlive <- sendResponse (httpVersion env) conn res
84 hFlush conn
84 hFlush conn
85− when (isChunked $ httpVersion env) serveConnection'
85+ when keepAlive serveConnection'
86
86
87 catchEOFError :: IOError -> IO ()
87 catchEOFError :: IOError -> IO ()
88 catchEOFError e | isEOFError e = case ioeGetHandle e of
88 catchEOFError e | isEOFError e = case ioeGetHandle e of
@@ -169,8 +169,8 @@ parseFirst s = do
169 let (rpath, qstring) = B.break (== '?') query
169 let (rpath, qstring) = B.break (== '?') query
170 return (method, rpath, qstring, hsecond)
170 return (method, rpath, qstring, hsecond)
171
171
172−headers :: HttpVersion -> Status -> ResponseHeaders -> Builder
172+headers :: HttpVersion -> Status -> ResponseHeaders -> Bool -> Builder
173−headers httpversion status responseHeaders = mconcat
173+headers httpversion status responseHeaders isChunked' = mconcat
174 [ fromByteString "HTTP/"
174 [ fromByteString "HTTP/"
175 , fromByteString httpversion
175 , fromByteString httpversion
176 , fromChar ' '
176 , fromChar ' '
@@ -179,7 +179,7 @@ headers httpversion status responseHeaders = mconcat
179 , fromByteString $ statusMessage status
179 , fromByteString $ statusMessage status
180 , fromByteString "\r\n"
180 , fromByteString "\r\n"
181 , mconcat $ map go responseHeaders
181 , mconcat $ map go responseHeaders
182− , if isChunked httpversion
182+ , if isChunked'
183 then fromByteString "Transfer-Encoding: chunked\r\n\r\n"
183 then fromByteString "Transfer-Encoding: chunked\r\n\r\n"
184 else fromByteString "\r\n"
184 else fromByteString "\r\n"
185 ]
185 ]
@@ -194,26 +194,37 @@ headers httpversion status responseHeaders = mconcat
194isChunked :: HttpVersion -> Bool
194isChunked :: HttpVersion -> Bool
195isChunked = (==) http11
195isChunked = (==) http11
196
196
197−sendResponse :: HttpVersion -> Handle -> Response -> IO ()
197+sendResponse :: HttpVersion -> Handle -> Response -> IO Bool
198−sendResponse hv handle res = do
198+sendResponse hv handle res =
199− responseEnumerator res $ \s hs ->
199+ responseEnumerator res go
200− chunk'
201− $ E.enumList 1 [headers hv s hs]
202− $$ E.joinI $ builderToByteString
203− $$ iterHandle handle
204 where
200 where
201+ go s hs =
205− chunk' i =
202+ chunk'
206− if isChunked hv
203+ $ E.enumList 1 [headers hv s hs isChunked']
207− then E.joinI $ chunk $$ i
204+ $$ E.joinI $ builderToByteString
208− else i
205+ $$ (iterHandle handle >> return isKeepAlive)
209− chunk :: E.Enumeratee Builder Builder IO ()
210− chunk = E.checkDone $ E.continue . step
211− step k E.EOF = k (E.Chunks [chunkedTransferTerminator]) >>== return
212− step k (E.Chunks []) = E.continue $ step k
213− step k (E.Chunks builders) =
214− k (E.Chunks [chunked]) >>== chunk
215 where
206 where
207+ hasLength = lookup "content-length" hs /= Nothing
208+ isChunked' = isChunked hv && not hasLength
209+ isKeepAlive = isChunked' || hasLength
210+ chunk' i =
211+ if isChunked'
212+ then E.joinI $ chunk $$ i
213+ else
214+ (if isKeepAlive
215+ then E.joinI $ after $$ i
216+ else i)
217+ chunk :: E.Enumeratee Builder Builder IO Bool
218+ chunk = E.checkDone $ E.continue . step
219+ step k E.EOF = k (E.Chunks [chunkedTransferTerminator]) >>== return
220+ step k (E.Chunks []) = E.continue $ step k
221+ step k (E.Chunks builders) =
222+ k (E.Chunks [chunked]) >>== chunk
223+ where
216− chunked = chunkedTransferEncoding $ mconcat builders
224+ chunked = chunkedTransferEncoding $ mconcat builders
225+ after = E.checkDone $ E.continue . after'
226+ after' k E.EOF = k (E.Chunks [fromByteString "\r\n\r\n"]) >>== return
227+ after' k chunks = k chunks >>== after
217
228
218parseHeaderNoAttr :: ByteString -> (ByteString, ByteString)
229parseHeaderNoAttr :: ByteString -> (ByteString, ByteString)
219parseHeaderNoAttr s =
230parseHeaderNoAttr s =
pong.hs+3−1
@@ -5,5 +5,7 @@ import Blaze.ByteString.Builder (fromByteString)
5
5
6main = run 3000 $ const $ return $ responseBuilder
6main = run 3000 $ const $ return $ responseBuilder
7 status200
7 status200
8− [("Content-Type", "text/plain")]
8+ [ ("Content-Type", "text/plain")
9+ , ("Content-Length", "4")
10+ ]
9 $ fromByteString "PONG"
11 $ fromByteString "PONG"