Почему мой https getter в Haskell так медленен по сравнению с завитом?

Я пишу простой код получения HTTPS в Haskell. После получения ответа я сохраняю его в файл со сжатием. Однако моя версия очень медленная по сравнению с комбинацией curl и gzip. Как я могу сделать это быстрее, что завиток? Подробности ниже.

Код на Haskell (fetcher.hs):

import Control.Lens
import qualified Codec.Compression.GZip as GZip
import qualified Data.ByteString.Lazy as BL
import Network.Wreq

writeURIBodyToFile :: FilePath -> String -> IO()
writeURIBodyToFile filePath uri = do
  response <- get uri
  let body = (response ^. responseBody)
  BL.writeFile filePath (GZip.compress body)

main :: IO ()
main = writeURIBodyToFile "out.html.gz" "https://www.sahibinden.com/ilan/vasita-otomobil-seat-hatasiz-boyasiz-tramersiz-dsg-leon-469484363/detay/"

Результат на Haskell:

$ ghc -o fetcher fetcher.hs
$ time ./fetcher 

real    0m9.240s
user    0m8.840s
sys     0m0.232s

результат скручивания:

$ time curl "https://www.sahibinden.com/ilan/vasita-otomobil-seat-hatasiz-boyasiz-tramersiz-dsg-leon-469484363/detay/" | gzip > out.html.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  102k  100  102k    0     0   331k      0 --:--:-- --:--:-- --:--:--  332k

real    0m0.524s
user    0m0.156s
sys     0m0.040s

Изменение: Я также пытался с http-проводник пакет, ничего не изменилось.

import qualified Data.ByteString.Lazy as BL
import           Network.HTTP.Simple

main :: IO ()
main = do
    response <- httpLBS "https://www.sahibinden.com/ilan/vasita-otomobil-seat-hatasiz-boyasiz-tramersiz-dsg-leon-469484363/detay/"
    BL.writeFile "outnew.html" $ getResponseBody response

Edit2: я также проверил соединение с tcpdump, и нет проблем с подключением.

Edit3: GHCi GHCi, version 7.10.3

Edit4: команда компиляции ghc -o fetcher fetcher.hs

Edit5: проблема не может быть воспроизведена с этим кодом на февраль 2019 года:

{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Simple
import qualified Data.ByteString.Char8 as B8

main :: IO ()
main = httpBS "https://www.sahibinden.com/ilan/vasita-otomobil-mercedes-benz-mercedes-benz-c-180-fascination-7g-tronic-ozel-renk-652750468/detay" >>= B8.putStrLn . getResponseBody

Результат:

$ ghc -o fetcher fetcher.hs
$ time ./fetcher 
real    0m0,549s
user    0m0,093s
sys     0m0,021s

Edit6: снова проблема не может быть воспроизведена в первом примере кода GHCi, version 8.0.2 от февраля 2019 г. GHCi, version 8.0.2

Ответ 1

я думаю, что ваш HTTP-клиент не учитывает http-заголовок Content-Length и просто продолжает загружать, пока удаленный сервер не закроет соединение, что

a: потенциально намного медленнее, чем простое чтение заголовка Content-Length, многие веб-серверы держат сокеты открытыми гораздо дольше, чем нужно (обычно для схемы повторного использования сокетов)

б: распространенная тема среди наивных/простых http клиентов.

Вы можете подтвердить это с помощью небольшого httpcat-сервера:

printf "HTTP/1.0 200 OK\r\nContent-Length: 3\r\n\r\nabcx" | nc -l 9999

Теперь нажмите http://127.0.0.1:9999 и проверьте ответ, клиент http, оптимизированный для учета заголовка Content-Length, скажет, что тело ответа равно abc, а клиент http, не оптимизированный для рассмотрения заголовка Content-Length, сказать, что тело ответа abcx

enter image description here

примечание: эта команда должна работать на Unix-подобных системах (Linux, * BSD, MacOS), но, вероятно, не будет работать на Windows-системах. если вы используете Windows, она будет работать на Cygwin (и, вероятно, работать на WSL, но я не пробовал, я все еще использую Windows 7, которая не поддерживает WSL)