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.ByteString
you get with the Simple module, and theResponse BodyReader
you get with the TLS module.You noticed that a
BodyReader
is 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. (BodyReader
might have been calledChunkGetter
).bip
below is like what you wrote: after extracting theBodyReader
/IO ByteString
from 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 inbop
below, 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
BodyReader
by hand-written recursion. But the purpose of thehttp-client
library is to make things likehttp-conduit
possible. There the result ofwithResponse
has 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-conduit
material, theResponse
contained a conduit like this; theBodyReader
part was later factored out intohttp-client
so it could be used by different streaming libraries likepipes
.So to take a simple example, in the corresponding http material for the
streaming
andstreaming-bytestring
libraries,withHTTP
gives 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
withHTTP
forstreaming-bytestring
just hides the recursive looping that we needed to use theBodyReader
material fromhttp-client
directly. It's the same e.g. with thewithHTTP
you 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 theBodyReader
fromhttp-client
to do this, and this was the main purpose of the library.