Reputation: 5406
Maybe a silly way to do things, but here it goes. I wanted to split the HTTP request send/receive from processing the response.
import Pipes
import qualified Pipes.HTTP as HTTP
import Pipes.Core
import qualified Pipes.ByteString as PB
import Network.Socket (withSocketsDo)
fetch m url = do
req <- lift $ HTTP.parseUrl url
resp <- lift $ HTTP.withHTTP req m return
url' <- respond $ HTTP.responseBody resp
fetch m url'
client :: Client String (Producer PB.ByteString IO ()) IO ()
client = do
b <- request "http://www.google.com"
lift $ runEffect $ b >-> PB.stdout
main = withSocketsDo $ HTTP.withManager HTTP.tlsManagerSettings $ \m ->
runEffect $ fetch m +>> client
Seems legitimate. But I get part of response printed and "Network.Socket.ByteString.recv: failed (Unknown error)". What do I need to do differently?
Upvotes: 1
Views: 211
Reputation: 35099
The error is this part:
HTTP.withHTTP req m return
There is a convention (not enforced by the types) that when a function begins with the word with
that you are not allowed to let the resource it allocates escape from the block it encloses. The reason why is that these withXXX
functions dispose of the resource when the block completes, so the resource is invalid past that point.
So what happened is that you tried to return
the response from the withHTTP
block, but by the next line of code the response has already been disposed of. The solution is to use pipes-safe
, which allows you to acquire resources safely within a pipe using Pipes.Safe.bracket
. All you have to do is provide an open and close action:
fetch m url = do
req <- lift $ HTTP.parseUrl url
url' <- bracket openSomething closeSomething $ \resp -> do
respond $ HTTP.responseBody resp
fetch m url'
That will ensure that resp
is not released until after the respond
completes.
Upvotes: 3