@@ -4,6 +4,7 @@ module Happstack.Server.Internal.Handler
44 ( request
55 , parseResponse
66 , putRequest
7+ , consumeChunks
78 ) where
89
910import qualified Paths_happstack_server as Paths
@@ -154,24 +155,32 @@ parseResponse inputStr =
154155 mbCL
155156 return $ Response {rsCode= code,rsHeaders= headers,rsBody= body,rsFlags= RsFlags ContentLength ,rsValidator= Nothing }
156157
157- -- http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
158- -- note this does NOT handle extenions
159- consumeChunks :: L. ByteString-> (L. ByteString ,L. ByteString )
160- consumeChunks str = let (parts,tr,rest) = consumeChunksImpl str in (L. concat . (++ [tr]) . map snd $ parts,rest)
158+ -- | Decode an HTTP/1.1 Transfer-Encoding: chunked body.
159+ -- See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html (chunk extensions are not handled).
160+ -- Returns @(decoded body, bytes following the chunked stream)@. The trailer (optional headers
161+ -- after the terminating 0-chunk, RFC 2616 §3.6.1) is dropped from the body — trailers are
162+ -- headers, not body content.
163+ consumeChunks :: L. ByteString -> (L. ByteString , L. ByteString )
164+ consumeChunks str = let (parts,_tr,rest) = consumeChunksImpl str in (L. concat . map snd $ parts, rest)
161165
166+ -- | Worker for 'consumeChunks'. Returns @(chunks, trailer, remainder)@, where each chunk is
167+ -- @(declared-length, body-bytes)@ and the terminating 0-chunk has empty body.
162168consumeChunksImpl :: L. ByteString -> ([(Int64 , L. ByteString )], L. ByteString , L. ByteString )
163169consumeChunksImpl str
164170 | L. null str = ([] ,L. empty,str)
165- | chunkLen == 0 = let (last ,rest') = L. splitAt lenLine1 str
171+ | chunkLen == 0 = let (_sizeLine ,rest') = L. splitAt lenLine1 str
166172 (tr',rest'') = getTrailer rest'
167- in ([(0 ,last )],tr',rest'')
168- | otherwise = ((chunkLen,part ): crest,tr,rest2)
173+ in ([(0 ,L. empty )],tr',rest'')
174+ | otherwise = ((chunkLen,body ): crest,tr,rest2)
169175 where
170176 line1 = head $ lazylines str
171177 lenLine1 = (L. length line1) + 1 -- endchar
172178 chunkLen = (fst $ head $ readHex $ L. unpack line1)
173179 len = chunkLen + lenLine1 + 2
174- (part,rest) = L. splitAt len str
180+ -- Each frame on the wire is "<hex-size>\r\n<body-bytes>\r\n". Skip the size-line and
181+ -- take exactly chunkLen bytes so the trailing CRLF stays out of the decoded body.
182+ (frame,rest) = L. splitAt len str
183+ body = L. take chunkLen . L. drop lenLine1 $ frame
175184 (crest,tr,rest2) = consumeChunksImpl rest
176185 getTrailer s = L. splitAt index s
177186 where index | crlfLC `L.isPrefixOf` s = 2
0 commit comments