Учитывая общий менеджер HTTP, кажется, что если requestBody
имеет тип requestBodySource
, и если для тела запроса предоставлена неправильная длина, тогда последующие запросы будут храниться на одном и том же HTTP-адресе менеджер около 20 секунд. Возможно, что-то связано с взаимодействием общего состояния и GivesPopper
, что, возможно, вызывает эту проблему. Вот пример кода, который его воспроизводит - мы используем requestb.in для отправки неправильной загрузки по длине, а затем попытаемся прочитать другой действительный URL-адрес на requestb.in.Повреждение общего состояния HTTP-менеджера при отправке неправильной длины для потока
{-# 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-conduit» – jberryman
Конечно, открылся один ... во время публикации, я не был уверен, что это была ошибка или неправильное использование функции потоковой передачи на моем часть. – Sal