diff --git a/cabal-install/Distribution/Client/BuildReports/Upload.hs b/cabal-install/Distribution/Client/BuildReports/Upload.hs
index fad7a6edbca6ee6a7caae35ff376044acfd32382..9da6d9fb5e3633d9c6faa4a185fe9f3bc6331c96 100644
--- a/cabal-install/Distribution/Client/BuildReports/Upload.hs
+++ b/cabal-install/Distribution/Client/BuildReports/Upload.hs
@@ -43,7 +43,7 @@ postBuildReport :: Verbosity -> (String, String) -> URI -> BuildReport -> IO Bui
 postBuildReport verbosity auth uri buildReport = do
   let fullURI = uri { uriPath = "/package" </> display (BuildReport.package buildReport) </> "reports" }
   transport <- configureTransport verbosity Nothing
-  res <- postHttp transport fullURI (BuildReport.show buildReport) (Just auth)
+  res <- postHttp transport verbosity fullURI (BuildReport.show buildReport) (Just auth)
   case res of
     (303, redir) -> return $ undefined redir --TODO parse redir
     _ -> die "unrecognized response" -- give response
@@ -84,7 +84,7 @@ putBuildLog :: Verbosity -> (String, String)
 putBuildLog verbosity auth reportId buildLog = do
   let fullURI = reportId {uriPath = uriPath reportId </> "log"}
   transport <- configureTransport verbosity Nothing
-  res <- postHttp transport fullURI buildLog (Just auth)
+  res <- postHttp transport verbosity fullURI buildLog (Just auth)
   case res of
     (200, _) -> return ()
     _ -> die "unrecognized response" -- give response
diff --git a/cabal-install/Distribution/Client/HttpUtils.hs b/cabal-install/Distribution/Client/HttpUtils.hs
index 486ee0331edeff0ea9055157dafa4beb8a943751..7a5546651b29690ae0339a1d32ec0a44042a62d3 100644
--- a/cabal-install/Distribution/Client/HttpUtils.hs
+++ b/cabal-install/Distribution/Client/HttpUtils.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE BangPatterns #-}
 -----------------------------------------------------------------------------
 -- | Separate module for HTTP actions, using a proxy server if one exists
 -----------------------------------------------------------------------------
@@ -17,22 +18,24 @@ import Network.URI
          ( URI (..), URIAuth (..) )
 import Network.Browser
          ( browse, setOutHandler, setErrHandler, setProxy
-         , setAuthorityGen, request, setAllowBasicAuth)
+         , setAuthorityGen, request, setAllowBasicAuth, setUserAgent )
 import Control.Applicative
 import qualified Control.Exception as Exception
 import Control.Monad
-         ( when, guard, foldM )
-import qualified Data.ByteString.Lazy.Char8 as ByteString
+         ( when, guard )
+import qualified Data.ByteString.Lazy.Char8 as BS
 import Data.List
-         ( isPrefixOf )
+         ( isPrefixOf, find, intercalate )
 import Data.Maybe
-         ( listToMaybe )
+         ( catMaybes, listToMaybe, maybeToList )
 import qualified Paths_cabal_install (version)
 import Distribution.Verbosity (Verbosity)
 import Distribution.Simple.Utils
          ( die, info, warn, debug, notice, writeFileAtomic
          , copyFileVerbose,  withTempFile
          , rawSystemStdInOut, toUTF8, fromUTF8, normaliseLineEndings )
+import Distribution.Client.Utils
+         ( readMaybe, withTempFileName )
 import Distribution.System
          ( buildOS, buildArch )
 import Distribution.Text
@@ -44,353 +47,544 @@ import qualified System.FilePath.Posix as FilePath.Posix
 import System.FilePath
          ( (<.>) )
 import System.Directory
-         ( doesFileExist, renameFile, removeFile )
+         ( doesFileExist, renameFile )
 import System.IO.Error
          ( isDoesNotExistError )
 import Distribution.Simple.Program
-         ( simpleProgram, getProgramInvocationOutput, programInvocation
-         , ConfiguredProgram, ProgramInvocation(..), defaultProgramConfiguration )
+         ( Program, simpleProgram, ConfiguredProgram, programPath
+         , ProgramInvocation(..), programInvocation
+         , getProgramInvocationOutput )
 import Distribution.Simple.Program.Db
-         ( ProgramDb, configureProgram, lookupProgram )
+         ( ProgramDb, emptyProgramDb, addKnownPrograms
+         , configureAllKnownPrograms
+         , requireProgram, lookupProgram )
 import Distribution.Simple.Program.Run
         ( IOEncoding(..), getEffectiveEnvironment )
 import Numeric (showHex)
 import System.Directory (canonicalizePath)
-import System.IO (hClose, openTempFile, hPutStr)
+import System.IO (hClose, hPutStr)
 import System.FilePath (takeFileName, takeDirectory)
 import System.Random (randomRIO)
 import System.Exit (ExitCode(..))
 
-readMay :: Read a => String -> Maybe a
-readMay s = case [x | (x,t) <- reads s, ("","") <- lex t] of
-                [x] -> Just x
-                _ -> Nothing
 
+------------------------------------------------------------------------------
+-- Downloading a URI, given an HttpTransport
+--
 
-data DownloadResult = FileAlreadyInCache | FileDownloaded FilePath deriving (Eq)
+data DownloadResult = FileAlreadyInCache
+                    | FileDownloaded FilePath
+  deriving (Eq)
 
--- Trim
-trim :: String -> String
-trim = f . f
-      where f = reverse . dropWhile isSpace
+downloadURI :: HttpTransport
+            -> Verbosity
+            -> URI      -- ^ What to download
+            -> FilePath -- ^ Where to put it
+            -> IO DownloadResult
+downloadURI _transport verbosity uri path | uriScheme uri == "file:" = do
+  copyFileVerbose verbosity (uriPath uri) path
+  return (FileDownloaded path)
+  -- Can we store the hash of the file so we can safely return path when the
+  -- hash matches to avoid unnecessary computation?
 
--- |Get the local proxy settings
---TODO: print info message when we're using a proxy based on verbosity
-proxy :: Verbosity -> IO Proxy
-proxy _verbosity = do
-  p <- fetchProxy True
-  -- Handle empty proxy strings
-  return $ case p of
-    Proxy uri auth ->
-      let uri' = trim uri in
-      if uri' == "" then NoProxy else Proxy uri' auth
-    _ -> p
+downloadURI transport verbosity uri path =
+    withTempFileName (takeDirectory path) (takeFileName path) $ \tmpFile -> do
+      let etagPath = path <.> "etag"
+      targetExists   <- doesFileExist path
+      etagPathExists <- doesFileExist etagPath
+      -- In rare cases the target file doesn't exist, but the etag does.
+      etag <- if targetExists && etagPathExists
+                then Just <$> readFile etagPath
+                else return Nothing
+
+      result <- getHttp transport verbosity uri etag tmpFile
+
+      -- Only write the etag if we get a 200 response code.
+      -- A 304 still sends us an etag header.
+      case result of
+        (200, Just newEtag) -> writeFile etagPath newEtag
+        _ -> return ()
+
+      case fst result of
+        200 -> do
+            info verbosity ("Downloaded to " ++ path)
+            renameFile tmpFile path
+            return (FileDownloaded path)
+        304 -> do
+            notice verbosity "Skipping download: local and remote files match."
+            return FileAlreadyInCache
+        errCode ->  die $ "Failed to download " ++ show uri
+                       ++ " : HTTP code " ++ show errCode
+
+-- | Utility function for legacy support.
+isOldHackageURI :: URI -> Bool
+isOldHackageURI uri
+    = case uriAuthority uri of
+        Just (URIAuth {uriRegName = "hackage.haskell.org"}) ->
+            FilePath.Posix.splitDirectories (uriPath uri) == ["/","packages","archive"]
+        _ -> False
 
-userAgent :: String
-userAgent = concat [ "cabal-install/", display Paths_cabal_install.version
-                   , " (", display buildOS, "; ", display buildArch, ")"
-                   ]
 
-noPostYet :: URI -> String -> Maybe (String, String) -> IO (Int, String)
-noPostYet _ _ _ = die "Posting (for report upload) is not implemented yet"
+------------------------------------------------------------------------------
+-- Setting up a HttpTransport
+--
 
 data HttpTransport = HttpTransport {
-      getHttp :: URI -> Maybe String -> FilePath -> IO (Int, Maybe String),
-      postHttp :: URI -> String -> Maybe (String, String) -> IO (Int, String),
-      putHttpFile :: URI -> FilePath -> Maybe (String,String) -> IO (Int, String)
+      -- | GET a URI, with an optional ETag (to do a conditional fetch),
+      -- write the resource to the given file and return the HTTP status code,
+      -- and optional ETag.
+      getHttp  :: Verbosity -> URI -> Maybe ETag -> FilePath
+               -> IO (HttpCode, Maybe ETag),
+
+      -- | POST a resource to a URI, with optional auth (username, password)
+      -- and return the HTTP status code and any redirect URL.
+      postHttp :: Verbosity -> URI -> String -> Maybe Auth
+               -> IO (HttpCode, String),
+
+      -- | POST a file resource to a URI using multipart\/form-data encoding,
+      -- with optional auth (username, password) and return the HTTP status
+      -- code and any error string.
+      postHttpFile :: Verbosity -> URI -> FilePath -> Maybe Auth
+                   -> IO (HttpCode, String)
     }
+    --TODO: why does postHttp return a redirect, but postHttpFile return errors?
 
-uriToSecure :: URI -> URI
-uriToSecure x | uriScheme x == "http:" = x {uriScheme = "https:"}
-              | otherwise = x
+type HttpCode = Int
+type ETag     = String
+type Auth     = (String, String)
+
+noPostYet :: Verbosity -> URI -> String -> Maybe (String, String)
+          -> IO (Int, String)
+noPostYet _ _ _ _ = die "Posting (for report upload) is not implemented yet"
+
+supportedTransports :: [(String, Maybe Program, Bool,
+                         ProgramDb -> Maybe HttpTransport)]
+supportedTransports =
+    [ let prog = simpleProgram "curl" in
+      ( "curl", Just prog, True
+      , \db -> curlTransport <$> lookupProgram prog db )
 
-setupTransportDb :: Verbosity -> IO ProgramDb
-setupTransportDb verbosity = foldM (flip (configureProgram verbosity)) defaultProgramConfiguration progs
-    where progs = map simpleProgram ["curl","wget","powershell"]
+    , let prog = simpleProgram "wget" in
+      ( "wget", Just prog, True
+      , \db -> wgetTransport <$> lookupProgram prog db )
+
+    , let prog = simpleProgram "powershell" in
+      ( "powershell", Just prog, True
+      , \db -> powershellTransport <$> lookupProgram prog db )
+
+    , ( "plain-http", Nothing, False
+      , \_ -> Just plainHttpTransport )
+    ]
 
 configureTransport :: Verbosity -> Maybe String -> IO HttpTransport
-configureTransport verbosity prefTransport = do
-  db <- setupTransportDb verbosity
-  let
-      curlTrans = curlTransport verbosity <$> lookupProgram (simpleProgram "curl") db
-      wgetTrans = wgetTransport verbosity <$> lookupProgram (simpleProgram "wget") db
-      powershellTrans = powershellTransport verbosity <$> lookupProgram (simpleProgram "powershell") db
-      httpTrans = Just (plainHttpTransport verbosity)
-      trans = case prefTransport of
-                (Just "curl") -> curlTrans
-                (Just "wget") -> wgetTrans
-                (Just "powershell") -> powershellTrans
-                (Just "insecure-http") -> httpTrans
-                (Just t) -> error $ "Unknown transport specified: " ++ t
-                Nothing -> curlTrans <|> wgetTrans <|> powershellTrans
-  maybe (die $ "Could not find a secure https transport: Fallback to http by running with --http-transport=insecure-http") return trans
 
+configureTransport verbosity (Just name) =
+    -- the user secifically selected a transport by name so we'll try and
+    -- configure that one
+
+    case find (\(name',_,_,_) -> name' == name) supportedTransports of
+      Just (_, mprog, _tls, mkTrans) -> do
+
+        progdb <- case mprog of
+          Nothing   -> return emptyProgramDb
+          Just prog -> snd <$> requireProgram verbosity prog emptyProgramDb
+                       --      ^^ if it fails, it'll fail here
+
+        let Just trans = mkTrans progdb
+        return trans
+
+      Nothing -> die $ "Unknown HTTP transport specified: " ++ name
+                    ++ ". The supported transports are "
+                    ++ intercalate ", "
+                         [ name' | (name', _, _, _ ) <- supportedTransports ]
+
+configureTransport verbosity Nothing = do
+    -- the user hasn't selected a transport, so we'll pick the first one we
+    -- can configure successfully, provided that it supports tls
+
+    -- for all the transports except plain-http we need to try and find
+    -- their external executable
+    progdb <- configureAllKnownPrograms  verbosity $
+                addKnownPrograms
+                  [ prog | (_, Just prog, _, _) <- supportedTransports ]
+                  emptyProgramDb
+
+    let availableHttpsTransports =
+          [ mkTrans progdb
+          | (_, _, _tls@True, mkTrans) <- supportedTransports ]
+
+    case catMaybes availableHttpsTransports of
+      (trans:_) -> return trans
+      []        -> die $ "Could not find a https transport: fallback to plain"
+                      ++ "http by running with --http-transport=plain-http"
 
 statusParseFail :: URI -> String -> IO a
-statusParseFail uri r = die $ "Failed to download " ++ show uri ++ " : No Status Code could be parsed from Response: " ++ r
+statusParseFail uri r =
+    die $ "Failed to download " ++ show uri ++ " : "
+       ++ "No Status Code could be parsed from response: " ++ r
+
 
-curlTransport :: Verbosity -> ConfiguredProgram -> HttpTransport
-curlTransport verbosity prog = HttpTransport gethttp posthttp puthttpfile
+------------------------------------------------------------------------------
+-- The HttpTransports based on external programs
+--
+
+curlTransport :: ConfiguredProgram -> HttpTransport
+curlTransport prog =
+    HttpTransport gethttp posthttp posthttpfile
   where
-    gethttp uri' etag destPath = parseResponse =<< getProgramInvocationOutput verbosity (programInvocation prog args)
-      where args = [show uri,"-o",destPath,"-L","--write-out","%{http_code}","-A",userAgent,"-s","-S"]
-                   ++ maybe [] (\t -> ["--header","If-None-Match: " ++ t]) etag
-            parseResponse x = case readMay $ trim x of
-              Just i -> return (i, Nothing) -- TODO extract real etag
-              Nothing -> statusParseFail uri x
-            uri = uriToSecure uri'
+    gethttp verbosity uri' etag destPath = do
+        withTempFile (takeDirectory destPath)
+                     "curl-headers.txt" $ \tmpFile tmpHandle -> do
+          hClose tmpHandle
+          let args = [ show uri
+                   , "--output", destPath
+                   , "--location"
+                   , "--write-out", "%{http_code}"
+                   , "--user-agent", userAgent
+                   , "--silent", "--show-error"
+                   , "--dump-header", tmpFile ]
+                ++ concat
+                   [ ["--header", "If-None-Match: " ++ t]
+                   | t <- maybeToList etag ]
+
+          resp <- getProgramInvocationOutput verbosity
+                    (programInvocation prog args)
+          headers <- readFile tmpFile
+          (code, _err, etag') <- parseResponse uri resp headers
+          return (code, etag')
+      where
+        uri = uriToSecure uri'
 
     posthttp = noPostYet
 
-    puthttpfile uri' path auth = parseResponse =<< getProgramInvocationOutput verbosity (programInvocation prog args)
+    posthttpfile verbosity uri' path auth = do
+        let args = [ show uri
+                   , "--form", "package=@"++path
+                   , "--write-out", "%{http_code}"
+                   , "--user-agent", userAgent
+                   , "--silent", "--show-error"
+                   , "--header", "Accept: text/plain" ]
+                ++ concat
+                   [ ["--digest", "--user", uname ++ ":" ++ passwd]
+                   | (uname,passwd) <- maybeToList auth ]
+        resp <- getProgramInvocationOutput verbosity
+                  (programInvocation prog args)
+        (code, err, _etag) <- parseResponse uri resp ""
+        return (code, err)
       where
-        args = [show uri,"-F","package=@"++path,"--write-out","%{http_code}","-A",userAgent]
-               ++ maybe [] (\(u,p) -> ["--digest","-u",u++":"++p]) auth
-        parseResponse x = case readMay . trim =<< listToMaybe . take 1 . reverse . lines =<< return x of
-          Just i -> return (i,x) -- TODO extract error?
-          Nothing -> statusParseFail uri x
         uri = uriToSecure uri'
 
-wgetTransport :: Verbosity -> ConfiguredProgram -> HttpTransport
-wgetTransport verbosity prog = HttpTransport gethttp posthttp puthttpfile
+    -- on success these curl involcations produces an output like "200"
+    -- and on failure it has the server error response first
+    parseResponse uri resp headers =
+      let codeerr =
+            case reverse (lines resp) of
+              (codeLine:rerrLines) ->
+                case readMaybe (trim codeLine) of
+                  Just i  -> let errstr = unlines (reverse rerrLines)
+                              in Just (i, errstr)
+                  Nothing -> Nothing
+              []          -> Nothing
+
+          mb_etag :: Maybe ETag
+          mb_etag  = listToMaybe $ reverse
+                     [ etag
+                     | ["ETag:", etag] <- map words (lines headers) ]
+
+       in case codeerr of
+            Just (i, err) -> return (i, err, mb_etag)
+            _             -> statusParseFail uri resp
+
+
+wgetTransport :: ConfiguredProgram -> HttpTransport
+wgetTransport prog =
+    HttpTransport gethttp posthttp posthttpfile
   where
-    gethttp uri' etag destPath = parseResponse . snd =<< getProgramInvocationOutputAndErrors verbosity (programInvocation prog args)
+    gethttp verbosity uri' etag destPath = do
+        resp <- runWGet verbosity args
+        (code, _err, etag') <- parseResponse uri resp
+        return (code, etag')
       where
-        args = ["-S",show uri,"--output-document="++destPath,"--user-agent="++userAgent,"--tries=5","--timeout=15"]
-               ++ maybe [] (\t -> ["--header","If-None-Match: " ++ t]) etag
-        parseResponse x =
-          let resp = reverse . takeUntil ("HTTP/" `isPrefixOf`) . reverse . map (dropWhile isSpace) . lines $ x
-          in case readMay =<< listToMaybe . drop 1 . words =<< listToMaybe resp of
-            Just i -> return (i, Nothing) --TODO etags
-            Nothing -> statusParseFail uri x
+        args = [ show uri
+               , "--output-document=" ++ destPath
+               , "--user-agent=" ++ userAgent
+               , "--tries=5"
+               , "--timeout=15"
+               , "--server-response" ]
+            ++ concat
+               [ ["--header", "If-None-Match: " ++ t]
+               | t <- maybeToList etag ]
+
         uri = uriToSecure uri'
 
     posthttp = noPostYet
 
-    puthttpfile _uri _path _auth = die $ "Https upload with wget is not yet supported. Either ensure curl is in your path or fallback to http by running with --http-transport=insecure-http."
-
-    -- TODO this doesn't do proper multipart with wget, which is not easy. It should be fixed.
-    _puthttpfileBroken uri' path auth = withTempFile (takeDirectory path) (takeFileName path) $ \tmpFile tmpHandle -> do
-      boundary <- genBoundary
-      body <- generateMultipartBody (ByteString.pack boundary) path
-      ByteString.hPut tmpHandle body
-      hClose tmpHandle
-      let args = ["-S",show uri,"--user-agent="++userAgent,"--post-file="++tmpFile]
-                 ++ ["--header=\"Content-type: multipart/form-data boundary="++boundary++"\""]
-                 ++ maybe [] (\(u,p) -> ["--http-user="++u,"--http-password="++p]) auth
-
-          parseResponse x =
-            let resp = reverse . takeUntil ("HTTP/" `isPrefixOf`) . reverse . map (dropWhile isSpace) . lines $ x
-            in case readMay =<< listToMaybe . drop 1 . words =<< listToMaybe resp of
-              Just i -> return (i, x)
-              Nothing -> statusParseFail uri x
-          uri = uriToSecure uri'
-      parseResponse =<< getProgramInvocationOutput verbosity (programInvocation prog args)
-
-    takeUntil _ [] = []
-    takeUntil p (x:xs) = if p x then [x] else x : takeUntil p xs
-
-powershellTransport :: Verbosity -> ConfiguredProgram -> HttpTransport
-powershellTransport verbosity prog = HttpTransport gethttp posthttp puthttpfile
+    posthttpfile verbosity  uri' path auth =
+        withTempFile (takeDirectory path)
+                     (takeFileName path) $ \tmpFile tmpHandle -> do
+          (body, boundary) <- generateMultipartBody path
+          BS.hPut tmpHandle body
+          BS.writeFile "wget.in" body
+          hClose tmpHandle
+          let args = [ show uri
+                     , "--post-file=" ++ tmpFile
+                     , "--user-agent=" ++ userAgent
+                     , "--server-response"
+                     , "--header=Content-type: multipart/form-data; " ++ 
+                                              "boundary=" ++ boundary ]
+                  ++ concat
+                     [ [ "--http-user=" ++ uname
+                       , "--http-password=" ++ passwd ]
+                     | (uname,passwd) <- maybeToList auth ]
+
+          resp <- runWGet verbosity args
+          (code, err, _etag) <- parseResponse uri resp
+          return (code, err)
+      where
+        uri = uriToSecure uri'
+
+    runWGet verbosity args = do
+        -- wget returns its output on stderr rather than stdout
+        (_, resp, exitCode) <- getProgramInvocationOutputAndErrors verbosity
+                                 (programInvocation prog args)
+        -- wget returns exit code 8 for server "errors" like "304 not modified"
+        if exitCode == ExitSuccess || exitCode == ExitFailure 8
+          then return resp
+          else die $ "'" ++ programPath prog
+                  ++ "' exited with an error:\n" ++ resp
+
+    -- With the --server-response flag, wget produces output with the full
+    -- http server response with all headers, we want to find a line like
+    -- "HTTP/1.1 200 OK", but only the last one, since we can have multiple
+    -- requests due to redirects.
+    --
+    -- Unfortunately wget apparently cannot be persuaded to give us the body
+    -- of error responses, so we just return the human readable status message
+    -- like "Forbidden" etc.
+    parseResponse uri resp =
+      let codeerr = listToMaybe
+                    [ (code, unwords err)
+                    | (protocol:codestr:err) <- map words (reverse (lines resp))
+                    , "HTTP/" `isPrefixOf` protocol
+                    , code <- maybeToList (readMaybe codestr) ]
+          mb_etag :: Maybe ETag
+          mb_etag  = listToMaybe
+                    [ etag
+                    | ["ETag:", etag] <- map words (reverse (lines resp)) ]
+       in case codeerr of
+            Just (i, err) -> return (i, err, mb_etag)
+            _             -> statusParseFail uri resp
+
+
+powershellTransport :: ConfiguredProgram -> HttpTransport
+powershellTransport prog =
+    HttpTransport gethttp posthttp posthttpfile
   where
-    gethttp uri' etag destPath = do
-      _proxyInfo <- proxy verbosity
-      let
+    gethttp verbosity uri' etag destPath =
+        withTempFile (takeDirectory destPath)
+                     "psScript.ps1" $ \tmpFile tmpHandle -> do
+           hPutStr tmpHandle script
+           hClose tmpHandle
+           let args = ["-InputFormat", "None", "-File", tmpFile]
+           resp <- getProgramInvocationOutput verbosity
+                     (programInvocation prog args)
+           parseResponse resp
+      where
+        script =
+          concatMap (++";\n") $
+            [ "$wc = new-object system.net.webclient"
+            , "$wc.Headers.Add(\"user-agent\","++escape userAgent++")"]
+         ++ [ "$wc.Headers.Add(\"If-None-Match\"," ++ t ++ ")"
+            | t <- maybeToList etag ]
+         ++ [ "Try {"
+            ,  "$wc.DownloadFile("++ escape (show uri) ++
+                              "," ++ escape destPath ++ ")"
+            , "} Catch {Write-Error $_; Exit(5);}"
+            , "Write-Host \"200\""
+            , "Write-Host $wc.ResponseHeaders.Item(\"ETag\")"
+            , "Exit" ]
+
         uri = uriToSecure uri'
         escape x = '"' : x ++ "\"" --TODO write/find real escape.
-        proxySettings = [] --TODO extract real settings from proxyInfo
 
-        parseResponse x = case readMay . unlines . take 1 . lines $ trim x of
-          Just i -> return (i, Nothing) -- TODO extract real etag
+        parseResponse x = case readMaybe . unlines . take 1 . lines $ trim x of
+          Just i  -> return (i, Nothing) -- TODO extract real etag
           Nothing -> statusParseFail uri x
 
-        script = unlines . map (++";") $
-                 ["$wc = new-object system.net.webclient",
-                  "$wc.Headers.Add(\"user-agent\","++escape userAgent++")"]
-                 ++ maybe [] (\t -> ["$wc.Headers.Add(\"If-None-Match\"," ++ t ++ ")"]) etag
-                 ++ proxySettings
-                 ++ ["Try {",
-                     "$wc.DownloadFile("++ escape (show uri) ++ "," ++ escape destPath ++ ")",
-                     "} Catch {Write-Error $_; Exit(5);}",
-                     "Write-Host \"200\"",
-                     "Write-Host $wc.ResponseHeaders.Item(\"ETag\")",
-                     "Exit"]
-      withTempFile (takeDirectory destPath) "psScript.ps1" $ \tmpFile tmpHandle -> do
-         hPutStr tmpHandle script
-         hClose tmpHandle
-         foo <- getProgramInvocationOutputAndErrors verbosity (programInvocation prog ["-InputFormat","None","-File",tmpFile])
-         putStrLn $ show foo
-         parseResponse (fst foo)
-
     posthttp = noPostYet
 
+    posthttpfile verbosity uri' path auth =
+        withTempFile (takeDirectory path)
+                     (takeFileName path) $ \tmpFile tmpHandle ->
+        withTempFile (takeDirectory path)
+                     "psScript.ps1" $ \tmpScriptFile tmpScriptHandle -> do
+          (body, boundary) <- generateMultipartBody path
+          BS.hPut tmpHandle body
+          hClose tmpHandle
+
+          fullPath <- canonicalizePath tmpFile
+          hPutStr tmpScriptHandle (script fullPath boundary)
+          hClose tmpScriptHandle
+          let args = ["-InputFormat", "None", "-File", tmpScriptFile]
+          resp <- getProgramInvocationOutput verbosity
+                    (programInvocation prog args)
+          parseResponse resp
+      where
+        script fullPath boundary = 
+          concatMap (++";\n") $
+            [ "$wc = new-object system.net.webclient"
+            , "$wc.Headers.Add(\"user-agent\","++escape userAgent++")"
+            , "$wc.Headers.Add(\"Content-type\"," ++
+                              "\"multipart/form-data; " ++
+                              "boundary="++boundary++"\")" ]
+         ++ [ "$wc.Credentials = new-object System.Net.NetworkCredential("
+              ++ escape uname ++ "," ++ escape passwd ++ ",\"\")"
+            | (uname,passwd) <- maybeToList auth ]
+         ++ [ "Try {"
+            , "$bytes = [System.IO.File]::ReadAllBytes("++escape fullPath++")"
+            , "$wc.UploadData("++ escape (show uri) ++ ",$bytes)"
+            , "} Catch {Write-Error $_; Exit(1);}"
+            , "Write-Host \"200\""
+            , "Exit" ]
 
-    puthttpfile uri' path auth = withTempFile (takeDirectory path) (takeFileName path) $ \tmpFile tmpHandle -> do
-      boundary <- genBoundary
-      body <- generateMultipartBody (ByteString.pack boundary) path
-      ByteString.hPut tmpHandle body
-      hClose tmpHandle
-      fullPath <- canonicalizePath tmpFile
-      _proxyInfo <- proxy verbosity
-      let
         uri = uriToSecure uri'
         escape x = show x
-        proxySettings = [] --TODO extract real settings from proxyInfo
 
-        parseResponse x = case readMay . unlines . take 1 . lines $ trim x of
+        parseResponse x = case readMaybe . unlines . take 1 . lines $ trim x of
           Just i -> return (i, x) -- TODO extract real etag
           Nothing -> statusParseFail uri x
 
-        script = unlines . map (++";") $
-                 ["$wc = new-object system.net.webclient",
-                  "$wc.Headers.Add(\"user-agent\","++escape userAgent++")",
-                  "$wc.Headers.Add(\"Content-type\","++"\"multipart/form-data; boundary="++boundary++"\")"]
-                 ++ authSettings
-                 ++ proxySettings
-                 ++ ["Try {",
-                     "$bytes = [System.IO.File]::ReadAllBytes("++escape fullPath++")",
-                     "$wc.UploadData("++ escape (show uri) ++ ",$bytes)",
-                     "} Catch {Write-Error $_; Exit(1);}",
-                     "Write-Host \"200\"",
-                     "Exit"]
-        authSettings = case auth of Just (u,p) -> ["$wc.Credentials = new-object System.Net.NetworkCredential("++escape u ++ "," ++ escape p ++ ",\"\")"]; Nothing -> []
-
-      withTempFile (takeDirectory path) "psScript.ps1" $ \tmpScriptFile tmpScriptHandle -> do
-         hPutStr tmpScriptHandle script
-         hClose tmpScriptHandle
-         foo <- getProgramInvocationOutputAndErrors verbosity (programInvocation prog ["-InputFormat","None","-File",tmpScriptFile])
-         putStrLn $ show foo
-         parseResponse (fst foo)
-
-plainHttpTransport :: Verbosity -> HttpTransport
-plainHttpTransport verbosity = HttpTransport gethttp posthttp puthttpfile
-  where gethttp uri etag destPath =
-          processGetResult destPath . snd =<< cabalBrowse (request
-            Request{ rqURI     = uri
-                   , rqMethod  = GET
-                   , rqHeaders = Header HdrUserAgent userAgent
-                                 : maybe [] (\t -> [Header HdrIfNoneMatch t]) etag
-                   , rqBody    = ByteString.empty })
-
-        processGetResult destPath resp = do
-            when (code==200) $ writeFileAtomic destPath $ rspBody resp
-            return (code, etag)
-          where code = case rspCode (resp) of (a,b,c) -> a*100 + b*10 + c
-                etag = lookupHeader HdrETag (rspHeaders resp)
-
-        posthttp = noPostYet
-
-        puthttpfile uri path auth = do
-          boundary <- genBoundary
-          body <- generateMultipartBody (ByteString.pack boundary) path
-          let authorize = do
-                setAllowBasicAuth False
-                setAuthorityGen (\_ _ -> return auth)
-          processPutResult . snd <$> cabalBrowse (authorize >> request Request {
-                         rqURI = uri,
-                         rqMethod = POST,
-                         rqHeaders = [Header HdrContentType ("multipart/form-data; boundary="++boundary),
-                                      Header HdrContentLength (show (ByteString.length body)),
-                                      Header HdrAccept ("text/plain")],
-                         rqBody = body
-                        })
-
-        processPutResult resp = (code, rspReason resp)
-          where code = case rspCode (resp) of (a,b,c) -> a*100 + b*10 + c
-
-        cabalBrowse act = do
-          p <- proxy verbosity
-          Exception.handleJust
-                (guard . isDoesNotExistError)
-                (const . die $ "Couldn't establish HTTP connection. "
-                 ++ "Possible cause: HTTP proxy server is down.") $
-                browse $ do
-                  setProxy p
-                  setErrHandler (warn verbosity . ("http error: "++))
-                  setOutHandler (debug verbosity)
-                  act
 
-downloadURI :: HttpTransport
-            -> Verbosity
-            -> URI      -- ^ What to download
-            -> FilePath -- ^ Where to put it
-            -> IO DownloadResult
-downloadURI _transport verbosity uri path | uriScheme uri == "file:" = do
-  copyFileVerbose verbosity (uriPath uri) path
-  return (FileDownloaded path)
-  -- Can we store the hash of the file so we can safely return path when the
-  -- hash matches to avoid unnecessary computation?
+------------------------------------------------------------------------------
+-- The builtin plain HttpTransport
+--
 
-downloadURI transport verbosity uri path = withTempFileName (takeDirectory path) (takeFileName path) $ \tmpFile -> do
-  let etagPath = path <.> "etag"
-  targetExists   <- doesFileExist path
-  etagPathExists <- doesFileExist etagPath
-  -- In rare cases the target file doesn't exist, but the etag does.
-  etag <- if targetExists && etagPathExists
-            then Just <$> readFile etagPath
-            else return Nothing
-
-  result <- getHttp transport uri etag tmpFile
-
-  -- Only write the etag if we get a 200 response code.
-  -- A 304 still sends us an etag header.
-  case result of
-    (200, Just newEtag) -> writeFile etagPath newEtag
-    _ -> return ()
-
-  case fst result of
-    200 -> do
-        info verbosity ("Downloaded to " ++ path)
-        renameFile tmpFile path
-        return (FileDownloaded path)
-    304 -> do
-        notice verbosity "Skipping download: Local and remote files match."
-        return FileAlreadyInCache
-    errCode ->  die $ "Failed to download " ++ show uri ++ " : HTTP code " ++ show errCode
-
--- Utility function for legacy support.
-isOldHackageURI :: URI -> Bool
-isOldHackageURI uri
-    = case uriAuthority uri of
-        Just (URIAuth {uriRegName = "hackage.haskell.org"}) ->
-            FilePath.Posix.splitDirectories (uriPath uri) == ["/","packages","archive"]
-        _ -> False
+plainHttpTransport :: HttpTransport
+plainHttpTransport =
+    HttpTransport gethttp posthttp posthttpfile
+  where
+    gethttp verbosity uri etag destPath = do
+      let req = Request{
+                  rqURI     = uri,
+                  rqMethod  = GET,
+                  rqHeaders = [ Header HdrIfNoneMatch t
+                              | t <- maybeToList etag ],
+                  rqBody    = BS.empty
+                }
+      (_, resp) <- cabalBrowse verbosity Nothing (request req)
+      let code  = convertRspCode (rspCode resp)
+          etag' = lookupHeader HdrETag (rspHeaders resp)
+      when (code==200) $
+        writeFileAtomic destPath $ rspBody resp
+      return (code, etag')
+
+    posthttp = noPostYet
 
--- Gets us the temp file name but gives us more control over the file itself.
+    posthttpfile verbosity uri path auth = do
+      (body, boundary) <- generateMultipartBody path
+      let headers = [ Header HdrContentType
+                             ("multipart/form-data; boundary="++boundary)
+                    , Header HdrContentLength (show (BS.length body))
+                    , Header HdrAccept ("text/plain")
+                    ]
+          req = Request {
+                  rqURI     = uri,
+                  rqMethod  = POST,
+                  rqHeaders = headers,
+                  rqBody    = body
+                }
+      (_, resp) <- cabalBrowse verbosity auth (request req)
+      return (convertRspCode (rspCode resp), rspErrorString resp)
+
+    convertRspCode (a,b,c) = a*100 + b*10 + c
+
+    rspErrorString resp =
+      case lookupHeader HdrContentType (rspHeaders resp) of
+        Just contenttype
+           | takeWhile (/= ';') contenttype == "text/plain"
+          -> BS.unpack (rspBody resp)
+        _ -> rspReason resp
+
+    cabalBrowse verbosity auth act = do
+      p <- fixupEmptyProxy <$> fetchProxy True
+      Exception.handleJust
+        (guard . isDoesNotExistError)
+        (const . die $ "Couldn't establish HTTP connection. "
+                    ++ "Possible cause: HTTP proxy server is down.") $
+        browse $ do
+          setProxy p
+          setErrHandler (warn verbosity . ("http error: "++))
+          setOutHandler (debug verbosity)
+          setUserAgent  userAgent
+          setAllowBasicAuth False
+          setAuthorityGen (\_ _ -> return auth)
+          act
+
+    fixupEmptyProxy (Proxy uri _) | null uri = NoProxy
+    fixupEmptyProxy p = p
+
+
+------------------------------------------------------------------------------
+-- Common stuff used by multiple transport impls
+--
 
-withTempFileName :: FilePath
-             -> String
-             -> (FilePath -> IO a) -> IO a
-withTempFileName tmpDir template action =
-  Exception.bracket
-    (openTempFile tmpDir template)
-    (\(name, _) -> (`when` removeFile name) =<< doesFileExist name)
-    (\(name, h) -> hClose h >> action name)
+userAgent :: String
+userAgent = concat [ "cabal-install/", display Paths_cabal_install.version
+                   , " (", display buildOS, "; ", display buildArch, ")"
+                   ]
+
+uriToSecure :: URI -> URI
+uriToSecure x | uriScheme x == "http:" = x {uriScheme = "https:"} 
+              | otherwise = x
 
+-- Trim
+trim :: String -> String
+trim = f . f
+      where f = reverse . dropWhile isSpace
+
+
+------------------------------------------------------------------------------
 -- Multipart stuff partially taken from cgi package.
+--
+
+generateMultipartBody :: FilePath -> IO (BS.ByteString, String)
+generateMultipartBody path = do
+    content  <- BS.readFile path
+    boundary <- genBoundary
+    let !body = formatBody content (BS.pack boundary)
+    return (body, boundary)
+  where
+    formatBody content boundary =
+        BS.concat $
+        [ crlf, dd, boundary, crlf ]
+     ++ [ BS.pack (show header) | header <- headers ]
+     ++ [ crlf
+        , content
+        , crlf, dd, boundary, dd, crlf ]
+
+    headers =
+      [ Header (HdrCustom "Content-disposition")
+               ("form-data; name=package; " ++
+                "filename=\"" ++ takeFileName path ++ "\"")
+      , Header HdrContentType "application/x-gzip"
+      ]
+
+    crlf = BS.pack "\r\n"
+    dd   = BS.pack "--"
 
 genBoundary :: IO String
-genBoundary = do i <- randomRIO (0x10000000000000,0xFFFFFFFFFFFFFF) :: IO Integer
-                 return $ showHex i ""
-
-generateMultipartBody :: ByteString.ByteString -> FilePath -> IO ByteString.ByteString
-generateMultipartBody boundary path = do
-  pkg <- ByteString.readFile path
-  let
-    crlf = ByteString.pack "\r\n"
-    dd = ByteString.pack "--"
-    printOneMultiPart (hs, c) = printBodyPart (hs,c) ++ [crlf, dd, boundary, dd, crlf]
-    printBodyPart (hs, c) = [crlf, dd, boundary, crlf] ++ map (ByteString.pack . show) hs ++ [crlf, c]
-    formData = ( [Header (HdrCustom "Content-disposition") $
-                   "form-data; name=package; filename=\""++takeFileName path++"\"",
-                   Header HdrContentType "application/x-gzip"],
-                   pkg)
-    body = ByteString.concat $ printOneMultiPart formData
-  return body
-
--- This should go back in the main program machinery. We need the errors explicitly because wget writes its results to stderr for no good reason.
-
-getProgramInvocationOutputAndErrors :: Verbosity -> ProgramInvocation -> IO (String, String)
+genBoundary = do
+    i <- randomRIO (0x10000000000000,0xFFFFFFFFFFFFFF) :: IO Integer
+    return $ showHex i ""
+
+------------------------------------------------------------------------------
+-- Compat utils
+
+-- TODO: This is only here temporarily so we can release without also requiring
+-- the latest Cabal lib. The function is also included in Cabal now.
+
+getProgramInvocationOutputAndErrors :: Verbosity -> ProgramInvocation
+                                    -> IO (String, String, ExitCode)
 getProgramInvocationOutputAndErrors verbosity
   ProgramInvocation {
     progInvokePath  = path,
@@ -408,9 +602,7 @@ getProgramInvocationOutputAndErrors verbosity
                                     path args
                                     mcwd menv
                                     input utf8
-    when (exitCode /= ExitSuccess) $
-      die $ "'" ++ path ++ "' exited with an error:\n" ++ errors ++ "\n" ++ decode output
-    return (decode output, errors)
+    return (decode output, decode errors, exitCode)
   where
     input =
       case minputStr of
diff --git a/cabal-install/Distribution/Client/Setup.hs b/cabal-install/Distribution/Client/Setup.hs
index 75626fa8910e964164abe6a548bae1bc6260b9e1..a11ddcb7f4915b4ca8ce4e28e283629c74e9317f 100644
--- a/cabal-install/Distribution/Client/Setup.hs
+++ b/cabal-install/Distribution/Client/Setup.hs
@@ -294,7 +294,7 @@ globalCommand commands = CommandUI {
          trueArg
 
       ,option [] ["http-transport"]
-         "Set a transport for http(s) requests. Accepts 'curl', 'wget', 'powershell', and 'insecure-http'. (default: 'curl')"
+         "Set a transport for http(s) requests. Accepts 'curl', 'wget', 'powershell', and 'plain-http'. (default: 'curl')"
          globalConfigFile (\v flags -> flags { globalHttpTransport = v })
          (reqArgFlag "HttpTransport")
 
diff --git a/cabal-install/Distribution/Client/Upload.hs b/cabal-install/Distribution/Client/Upload.hs
index 33c16d34868fe867c0a4228ce05958401032a0d2..d0ced9fed04ea9077a36357b83b728ccfc2eb3bc 100644
--- a/cabal-install/Distribution/Client/Upload.hs
+++ b/cabal-install/Distribution/Client/Upload.hs
@@ -97,10 +97,10 @@ check transport verbosity paths = do
 handlePackage :: HttpTransport -> Verbosity -> URI -> Auth
               -> FilePath -> IO ()
 handlePackage transport verbosity uri auth path =
-  do resp <- putHttpFile transport uri path auth
+  do resp <- postHttpFile transport verbosity uri path auth
      case resp of
        (200,_)     -> do notice verbosity "Ok"
-       (code,err)  -> do notice verbosity $ "Error: " ++ path ++ ": "
-                                     ++ show code ++ " "
+       (code,err)  -> do notice verbosity $ "Error uploading " ++ path ++ ": "
+                                     ++ "http code " ++ show code ++ "\n"
                                      ++ err
 
diff --git a/cabal-install/Distribution/Client/Utils.hs b/cabal-install/Distribution/Client/Utils.hs
index 60703efe32db41c4fb1095d85d5d8410a7b5f336..901540d893029644862bf88a189d5c7ebf1a1ea1 100644
--- a/cabal-install/Distribution/Client/Utils.hs
+++ b/cabal-install/Distribution/Client/Utils.hs
@@ -2,8 +2,10 @@
 
 module Distribution.Client.Utils ( MergeResult(..)
                                  , mergeBy, duplicates, duplicatesBy
+                                 , readMaybe
                                  , inDir, determineNumJobs, numberOfProcessors
                                  , removeExistingFile
+                                 , withTempFileName
                                  , makeAbsoluteToCwd, filePathToByteString
                                  , byteStringToFilePath, tryCanonicalizePath
                                  , canonicalizePathNoThrow
@@ -24,20 +26,24 @@ import Data.Bits
          ( (.|.), shiftL, shiftR )
 import Data.Char
          ( ord, chr )
+#if MIN_VERSION_base(4,6,0)
+import Text.Read
+         ( readMaybe )
+#endif
 import Data.List
          ( isPrefixOf, sortBy, groupBy )
 import Data.Word
          ( Word8, Word32)
 import Foreign.C.Types ( CInt(..) )
 import qualified Control.Exception as Exception
-         ( finally )
+         ( finally, bracket )
 import System.Directory
          ( canonicalizePath, doesFileExist, getCurrentDirectory
          , removeFile, setCurrentDirectory )
 import System.FilePath
          ( (</>), isAbsolute )
 import System.IO
-         ( Handle
+         ( Handle, hClose, openTempFile
 #if MIN_VERSION_base(4,4,0)
          , hGetEncoding, hSetEncoding
 #endif
@@ -87,6 +93,14 @@ duplicatesBy cmp = filter moreThanOne . groupBy eq . sortBy cmp
     moreThanOne (_:_:_) = True
     moreThanOne _       = False
 
+#if !MIN_VERSION_base(4,6,0)
+-- | An implementation of readMaybe, for compatability with older base versions.
+readMaybe :: Read a => String -> Maybe a
+readMaybe s = case reads s of
+                [(x,"")] -> Just x
+                _        -> Nothing
+#endif
+
 -- | Like 'removeFile', but does not throw an exception when the file does not
 -- exist.
 removeExistingFile :: FilePath -> IO ()
@@ -95,6 +109,19 @@ removeExistingFile path = do
   when exists $
     removeFile path
 
+-- | A variant of 'withTempFile' that only gives us the file name, and while
+-- it will clean up the file afterwards, it's lenient if the file is
+-- moved\/deleted.
+--
+withTempFileName :: FilePath
+                 -> String
+                 -> (FilePath -> IO a) -> IO a
+withTempFileName tmpDir template action =
+  Exception.bracket
+    (openTempFile tmpDir template)
+    (\(name, _) -> removeExistingFile name)
+    (\(name, h) -> hClose h >> action name)
+
 -- | Executes the action in the specified directory.
 inDir :: Maybe FilePath -> IO a -> IO a
 inDir Nothing m = m