给定一个共享的HTTP管理器,如果requestBody
是requestBodySource
类型,并且如果为请求体提供了错误的长度,那么随后的请求将在同一个HTTP管理器上浪费大约20秒。似乎是共享状态和GivesPopper
的相互作用导致了这个问题。下面是一个再现它的示例代码——我们使用requestb。在发送错误的长度上传,然后尝试读取另一个有效的URL在requestb.in。
{-# LANGUAGE OverloadedStrings #-}
import Data.Conduit.Binary (sourceFile)
import Network.HTTP.Conduit
import Network.HTTP.Types
import qualified Data.ByteString.Lazy as LBS
import System.IO
import Control.Monad.Trans.Resource (runResourceT)
import Control.Concurrent.Async (async,waitCatch)
import Control.Exception (displayException)
main :: IO ()
main = do
{- Set up a ResourceT region with an available HTTP manager. -}
httpmgr <- newManager tlsManagerSettings
httpmgr2 <- newManager tlsManagerSettings
let file ="out" -- some byte contents with length > 1
lenb <- System.IO.withFile file ReadMode hFileSize
let inbytes = sourceFile file
initReq <- parseUrl "http://requestb.in/saxbx3sa"
putreq <- async $ runResourceT $ do
let req = initReq { method = "POST",
-- let us send wrong length in requestBodySource
requestBody = (requestBodySource (fromIntegral $ lenb - 1) inbytes)}
resp <- httpLbs req httpmgr
return (statusCode . responseStatus $ resp)
putreqRes <- waitCatch putreq
case putreqRes of
Left e -> print $ displayException $ e
Right r -> print $ r
getreq <- async $ runResourceT $ do
-- Let us do a GET on a different resource to see if it works
initReq <- parseUrl "http://requestb.in/1l15sz21"
let req = initReq { method = "GET"}
resp <- httpLbs req httpmgr
return (statusCode . responseStatus $ resp)
getreqRes <- waitCatch getreq
case getreqRes of
Left e -> print $ displayException $ e
Right r -> print $ r
输出-第一次错误上传作为HTTP 200
通过,随后的GET
请求立即导致HTTP 400
错误:
*Main> main
200
"StatusCodeException (Status {statusCode = 400, statusMessage = "Bad Request"})
[("Date","Wed, 29 Jun 2016 11:54:59 GMT"),("Content-Type","text/html"),
("Content-Length","177"),("Connection","close"),("Server","-nginx"),
("CF-RAY","-"),("X-Response-Body-Start","<html>\r\n<head><title>400 Bad
Request</title></head>\r\n<body bgcolor=\"white\">\r\n<center><h1>400 Bad
Request</h1></center>\r\n<hr><center>cloudflare-
nginx</center>\r\n</body>\r\n</html>\r\n"),("X-Request-URL","GET
http://requestb.in:80/saxbx3sa")] (CJ {expose = []})"
使用不同的http管理器代替GET
请求将返回HTTP 200
。所以,在http管理器中共享状态似乎是这里的问题。
有其他人观察到它吗?我通过github问题HTTP Manager
,但还没有看到这个报告。在错误的流长度上,行为不应该像这里发生的那样破坏HTTP管理器。
我还模拟了requestBodySource的源文件,其中长度是正确的,但是由于模拟失败(模拟网络问题),源文件中途终止。在这种情况下,没有错误。因此,我们似乎只有一种情况,即发送错误的长度而没有任何失败,将导致某种共享状态在这里损坏,并在25秒内释放。
如果有人对这里发生的事情有任何见解,那将非常有帮助。我有一个变通办法,强制执行正确的流长度。但是,我想了解发生了什么,这样我就可以避免在生产中遇到这种情况。
这是http-client
报告的一个问题。它让调用者来确保传递的内容长度是正确的。似乎是与服务器的共享连接处于坏状态。根据实际长度与预期长度的不同,下一个请求的开始可能被视为上一个请求正文的结束,从而导致下一个请求被服务器错误地解释。
这个问题已经修复并通过拉取请求合并到主干中。解决方案是添加一个简单的长度验证。