hspec-wai icon indicating copy to clipboard operation
hspec-wai copied to clipboard

Support for testing chunked transfer encoding response bodies

Open ocramz opened this issue 10 months ago • 6 comments

Chunked response bodies have a specific format, which prepends chunk length as a hex integer,.e.g

4␍␊            (chunk size is four octets)
Wiki           (four octets of data)
␍␊             (end of chunk)

7␍␊            (chunk size is seven octets)
pedia i        (seven octets of data)
␍␊             (end of chunk)

B␍␊            (chunk size is eleven octets)
n ␍␊chunks.    (eleven octets of data)
␍␊             (end of chunk)

0␍␊            (chunk size is zero octets, no more chunks)
␍␊             (end of final chunk with zero data octets)

https://en.wikipedia.org/wiki/Chunked_transfer_encoding

It would be nice to support this in hspec-wai. It would be sufficient to implement a MatchBody that does the chunking (and optional validation of the chunk lengths) and applies a predicate to the chunks.

ocramz avatar Feb 16 '25 12:02 ocramz

A simple implementation of the internals using ReadP from base and aeson (with no length validation):

-- | assumes that each chunk can be decoded as a separate JSON object
chunkedP :: A.FromJSON a => RP.ReadP [a]
chunkedP = mconcat <$> RP.manyTill chunkP RP.eof

chunkP :: A.FromJSON a => RP.ReadP [a]
chunkP = do
  _ <- hexP >> RP.skipSpaces
  cs <- RP.option [] jsonString -- NB last chunk in the stream is a 0-length string
  RP.skipSpaces
  pure cs

jsonString :: A.FromJSON a => RP.ReadP [a]
jsonString = do
  chars <- RP.munch1 (\c -> not $ c `elem` ['\n', '\r'])
  pure $ maybeToList $ A.decode $ LBS8.pack chars


-- | read a hexadecimal number as a string
hexP :: RP.ReadP [Char]
hexP = RP.munch1 predicate
  where
    predicate c = isDigit c || c `elem` ("abcdef" :: [Char])

ocramz avatar Feb 16 '25 12:02 ocramz

I'm not sure if I understand what you are asking for?

Do you want to test a WAI application? Or do you want to test that a WAI server (e.g. warp) produces the correct wire format?

sol avatar Feb 17 '25 22:02 sol

I originally thought of addressing the latter scenario, testing a server, which would fit with the use cases of this library.

ocramz avatar Feb 18 '25 01:02 ocramz

I originally thought of addressing the latter scenario, testing a server, which would fit with the use cases of this library.

I'm still not sure if I understand you correctly. Do you want to write more tests for warp, or are you working on an alternative WAI server implementation?

sol avatar Feb 18 '25 02:02 sol

Hi, neither of the two :) I call a "server" any wai Application.

I am proposing here to add a helper to test the serialization produced as response by Applications that comply with the chunked transfer format.

Hence my notes above, regarding MatchBody.

ocramz avatar Feb 18 '25 08:02 ocramz

I'm still not sure if I understand your use case. Naïvely, I assume that it is the responsibility of the server (read warp) to create the wire format.

If you have a specific use case that is currently not addressed, then please provide a concrete and minimal code example that illustrates your use case.

sol avatar Feb 18 '25 16:02 sol