Dear Cabal maintainers,

A couple of days ago I was unable to cabal-install a library from
Hackage -- 00-index.tar.gz was not downloaded completely.  And if
obtaining one with wget, other .tar.gz files (packages) still could not
be fetched in full.

This problem has been reported already (see
http://hackage.haskell.org/trac/hackage/ticket/562).

06/11/09 09:49:25 changed by duncan:
> Why has this started cropping up all of a sudden? Never seen this
> before then 3 reports in as many days. Do we suspect HTTP-4000.0.6 ->
> 7 perhaps?

06/11/09 13:02:47 changed by michaeldever:
> So it's definitely a problem with the HTTP package in my opinion. I'm
> not sure if it is a problem with the packages proxy handling, as it
> does download some of the package, but not all of it.
>
> Seeing as both the Zlib library, and tar yield an end of stream error,
> its something that I'm reckoning is happening during transport.

06/13/09 09:05:26 changed by michaeldever:
> http://trac.haskell.org/http/ticket/8#comment:1

The bug is not in HTTP API, but in the way cabal-install uses it.

The type of HTTP response body is polymorphic within `HTTP' library
(rspBody :: Response a -> a) but it is specialized to Lazy.ByteString by
cabal-install's `getHTTP' function (Distribution/Client/HttpUtils.hs).

Once the type of response body is changed to _strict_ ByteString, files
get downloaded through proxy completely.

The attached module [proxy-POC.hs] makes this quite apparent (you need
to be behind a proxy; HTTP >= 4000.0.8):

    v...@takeshi:~/src$ time runhaskell proxy-POC.hs
    Content-Length:   1200593
    bytes downloaded: 2408
    proxy-POC.hs: user error (sizes differ)
    
    real    0m1.210s
    user    0m0.556s
    sys     0m0.052s
    v...@takeshi:~/src$ time runhaskell -DSTRICT proxy-POC.hs
    Content-Length:   1200593
    bytes downloaded: 1200593
    
    real    0m17.956s
    user    0m0.620s
    sys     0m0.028s
    v...@takeshi:~/src$ runhaskell proxy-POC.hs    # repeatable
    Content-Length:   1200593
    bytes downloaded: 2408
    proxy-POC.hs: user error (sizes differ)

There are only 4 lines that need to be changed (2 in HttpUtils.hs and 2
in Fetch.hs); see the accompanying patch.

...And could anyone explain me, why don't lazy ByteString cause cropped
downloads in proxy-free environment?

Thank you.

-- 
vvv
diff -ru /tmp/cabal-install-0.6.2/Distribution/Client/Fetch.hs ./Distribution/Client/Fetch.hs
--- /tmp/cabal-install-0.6.2/Distribution/Client/Fetch.hs	2009-02-19 15:07:52.000000000 +0200
+++ ./Distribution/Client/Fetch.hs	2009-09-18 18:34:41.989206160 +0300
@@ -73,6 +73,7 @@
          ( Response(..) )
 import Network.Stream
          ( ConnError(..) )
+import Data.ByteString.Lazy ( fromChunks )
 
 
 downloadURI :: Verbosity
@@ -89,7 +90,7 @@
     Right rsp
       | rspCode rsp == (2,0,0)
      -> do info verbosity ("Downloaded to " ++ path)
-           writeFileAtomic path (rspBody rsp)
+           writeFileAtomic path $ fromChunks [rspBody rsp]
      --FIXME: check the content-length header matches the body length.
      --TODO: stream the download into the file rather than buffering the whole
      --      thing in memory.
diff -ru /tmp/cabal-install-0.6.2/Distribution/Client/HttpUtils.hs ./Distribution/Client/HttpUtils.hs
--- /tmp/cabal-install-0.6.2/Distribution/Client/HttpUtils.hs	2009-02-19 15:07:52.000000000 +0200
+++ ./Distribution/Client/HttpUtils.hs	2009-09-18 18:35:34.722456689 +0300
@@ -15,8 +15,8 @@
          , setOutHandler, setErrHandler, setProxy, request)
 import Control.Monad
          ( mplus, join, liftM2 )
-import qualified Data.ByteString.Lazy as ByteString
-import Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString as ByteString
+import Data.ByteString (ByteString)
 #ifdef WIN32
 import System.Win32.Types
          ( DWORD, HKEY )
{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -Wall #-}
-- | Re: cabal-install update fails going through a HTTP proxy
-- [hackage ticket #562]
--
-- See http://hackage.haskell.org/trac/hackage/ticket/562#comment:9
--
module Main where

import Network.HTTP.Proxy (fetchProxy)
import Network.HTTP (Request(..), Response(..), Header(..), RequestMethod(..),
                     HeaderName(..))
import Network.HTTP.Headers (lookupHeader)
import Network.Browser (browse, request, setProxy, setOutHandler, setErrHandler)
import Network.Stream (Result)

import System.Environment (getArgs)
import Network.URI (URI(..), parseURI)
import Distribution.Simple.Utils (warn, debug)
import Distribution.Verbosity (normal, {-deafening,-} Verbosity)
import Data.Maybe (fromMaybe)
import Control.Monad (when)

--- Problems (with downloading via a proxy) disappear once strict
--- ByteStrings are used.  Thus the fix is to remove `.Lazy' suffixes.

-- #define STRICT
#ifdef STRICT
import qualified Data.ByteString as BS
import Data.ByteString (ByteString)
#else
import qualified Data.ByteString.Lazy as BS
import Data.ByteString.Lazy (ByteString)
#endif

{-----------------------------------------------------------------------
v...@takeshi:~/src$ time runhaskell proxy-POC.hs
Content-Length:   1200593
bytes downloaded: 2408
proxy-POC.hs: user error (sizes differ)

real    0m1.210s
user    0m0.556s
sys     0m0.052s
v...@takeshi:~/src$ time runhaskell -DSTRICT proxy-POC.hs
Content-Length:   1200593
bytes downloaded: 1200593

real    0m17.956s
user    0m0.620s
sys     0m0.028s
-----------------------------------------------------------------------}

main :: IO ()
main = do
  args <- getArgs
  rsp  <- fetch $ (args ++ ["http://hackage.haskell.org/packages/archive/";
                            ++ "00-index.tar.gz"]) !! 0
  let lenHeader = fromMaybe "" $ lookupHeader HdrContentLength (rspHeaders rsp)
      lenBody   = show $ BS.length $ rspBody rsp
  report lenHeader lenBody

report :: String -> String -> IO ()
report hdr bdy = do
  when (avail hdr) (putStrLn $ "Content-Length:   " ++ hdr)
  putStrLn ("bytes downloaded: " ++ bdy)
  when (avail hdr && hdr /= bdy) (fail "sizes differ")
    where avail = not . null

fetch :: String -> IO (Response ByteString)
fetch s = do
  case parseURI s of
    Nothing -> fail ("fetch: unable to parse URI: " ++ s)
    Just u  -> do
            Right rsp <- getHTTP normal u
            return rsp

------------------------------------------------------------------------
-- The following functions are copied (with minimal changes) from
-- cabal-install's 'Distribution.Client.HttpUtils'.

-- |Carry out a GET request, using the local proxy settings
getHTTP :: Verbosity -> URI -> IO (Result (Response ByteString))
getHTTP verbosity uri = do
                 -- p   <- proxy verbosity
                 p <- fetchProxy False
                 let req = mkRequest uri
                 (_, resp) <-
                     browse $ do
                          setErrHandler (warn verbosity . ("http error: "++))
                          setOutHandler (debug verbosity)
                          setProxy p
                          request req
                 return (Right resp)

mkRequest :: URI -> Request ByteString
mkRequest uri = Request{ rqURI     = uri
                       , rqMethod  = GET
                       , rqHeaders = [Header HdrUserAgent userAgent]
                         ++ [Header HdrCacheControl "no-cache"] -- XXX *new*
                       , rqBody    = BS.empty }
  -- where userAgent = "cabal-install/" ++ display Paths_cabal_install.version
  where userAgent = "proxy-POC.hs"
_______________________________________________
cabal-devel mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/cabal-devel

Reply via email to