I'm using http-client tutorial to get response body using TLS connection. Since I can observe that print is called by withResponse, why doesn't print force entire response to the output in the following fragment?
withResponse request manager $ \response -> do
putStrLn $ "The status code was: " ++
body <- (responseBody response)
print body
I need to write this instead:
response <- httpLbs request manager
putStrLn $ "The status code was: " ++
show (statusCode $ responseStatus response)
print $ responseBody response
Body I want to print is a lazy ByteString. I'm still not sure whether I should expect print to print the entire value.
instance Show ByteString where
showsPrec p ps r = showsPrec p (unpackChars ps) r
This doesn't have to do with laziness, but with the difference between the
Response L.ByteStringyou get with the Simple module, and theResponse BodyReaderyou get with the TLS module.You noticed that a
BodyReaderis anIO ByteString. But in particular it is an action that can be repeated, each time with the next chunk of bytes. It follows the protocol that it never sends a null bytestring except when it's at the end of file. (BodyReadermight have been calledChunkGetter).bipbelow is like what you wrote: after extracting theBodyReader/IO ByteStringfrom theResponse, it performs it to get the first chunk, and prints it. But doesn't repeat the action to get more - so in this case we just see the first couple chapters of Genesis. What you need is a loop to exhaust the chunks, as inbopbelow, which causes the whole King James Bible to spill into the console.The loop keeps going back to get more chunks until it gets an empty string, which represents eof, so in the terminal it prints through to the end of the Apocalypse.
This is behavior is straightforward but slightly technical. You can only work with a
BodyReaderby hand-written recursion. But the purpose of thehttp-clientlibrary is to make things likehttp-conduitpossible. There the result ofwithResponsehas the typeResponse (ConduitM i ByteString m ()).ConduitM i ByteString m ()is how conduit types of a byte stream; this byte stream would contain the whole file.In the original form of the
http-client/http-conduitmaterial, theResponsecontained a conduit like this; theBodyReaderpart was later factored out intohttp-clientso it could be used by different streaming libraries likepipes.So to take a simple example, in the corresponding http material for the
streamingandstreaming-bytestringlibraries,withHTTPgives you a response of typeResponse (ByteString IO ()).ByteString IO ()is the type of a stream of bytes arising in IO, as its name suggests;ByteString Identity ()would be the equivalent of a lazy bytestring (effectively a pure list of chunks.) TheByteString IO ()will in this case represent the whole bytestream down to the Apocalypse. So with the importsthe program is identical to a lazy bytestring program:
Indeed it is slightly simpler, since you don't have "extract the bytes from IO`:
but just write
you just "print" them directly. If you want to view just a bit from the middle of the KJV, you can instead do what you would with a lazy bytestring, and end with:
Then you will see something about Abraham.
The
withHTTPforstreaming-bytestringjust hides the recursive looping that we needed to use theBodyReadermaterial fromhttp-clientdirectly. It's the same e.g. with thewithHTTPyou find inpipes-http, which represents a stream of bytestring chunks asProducer ByteString IO (), and the same withhttp-conduit. In all of these cases, once you have your hands on the byte stream you handle it in the ways typical of the streaming IO framework without handwritten recursion. All of them use theBodyReaderfromhttp-clientto do this, and this was the main purpose of the library.