Повреждение общего состояния диспетчера HTTP при отправке неправильной длины для потока

Учитывая общий HTTP-менеджер, кажется, что если requestBody имеет тип requestBodySource и если для тела запроса указана неправильная длина, то последующие запросы будут выполняться в том же HTTP-менеджере примерно на 20 секунд. Кажется, есть что-то во взаимодействии общего состояния, и GivesPopper возможно, это вызывает эту проблему. Вот пример кода, который воспроизводит это: мы используем requestb.in для отправки загрузки неправильной длины, а затем пытаемся прочитать другой действительный 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 секунд.

Если у кого-то есть какие-либо идеи о том, что здесь происходит, это будет очень полезно. У меня есть обходной путь обеспечения правильной длины потоковой передачи. Однако я хотел бы понять, что происходит, чтобы не столкнуться с этой ситуацией в продакшене.


person Sal    schedule 29.06.2016    source источник
comment
Вероятно, будет лучше помочь открыть проблему http-conduit   -  person jberryman    schedule 29.06.2016
comment
Конечно, открыл один ... во время публикации я не был уверен, было ли это ошибкой или неправильным использованием функции потоковой передачи с моей стороны.   -  person Sal    schedule 29.06.2016


Ответы (1)


Это проблема с http-client как указано здесь. На усмотрение вызывающей стороны остается убедиться, что переданная длина контента верна. Похоже, что общее соединение с сервером находится в плохом состоянии. В зависимости от фактической длины и ожидаемой длины начало следующего запроса может рассматриваться как конец тела предыдущего запроса, что приводит к неправильной интерпретации следующего запроса сервером.

Это было исправлено и добавлено в магистраль с помощью запроса на включение. Решение состояло в том, чтобы добавить простую проверку длины.

person Sal    schedule 05.07.2016