summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2012-11-18 20:06:28 -0400
committerGravatar Joey Hess <joey@kitenet.net>2012-11-18 20:30:05 -0400
commit6aa68125b6d476f0ad9372a0f0fbaa5c118e7096 (patch)
treebaadc0f86773f86903239e242559df8553bd41fa
parentac71b499ac6d53408cfce19a1ddd00bfa4b2645f (diff)
upload progress bars for webdav!
-rw-r--r--Crypto.hs8
-rw-r--r--Remote/WebDAV.hs23
-rw-r--r--Utility/Observed.hs43
-rw-r--r--doc/design/assistant/progressbars.mdwn3
4 files changed, 66 insertions, 11 deletions
diff --git a/Crypto.hs b/Crypto.hs
index fe6c6d5cb..99b17ce02 100644
--- a/Crypto.hs
+++ b/Crypto.hs
@@ -19,6 +19,7 @@ module Crypto (
decryptCipher,
encryptKey,
feedFile,
+ feedFileMetered,
feedBytes,
readBytes,
encrypt,
@@ -36,6 +37,8 @@ import Common.Annex
import qualified Utility.Gpg as Gpg
import Types.Key
import Types.Crypto
+import Types.Remote
+import Utility.Observed
{- The first half of a Cipher is used for HMAC; the remainder
- is used as the GPG symmetric encryption passphrase.
@@ -122,6 +125,11 @@ type Reader a = Handle -> IO a
feedFile :: FilePath -> Feeder
feedFile f h = L.hPut h =<< L.readFile f
+feedFileMetered :: FilePath -> MeterUpdate -> Feeder
+feedFileMetered f m to = withBinaryFile f ReadMode $ \h -> do
+ b <- hGetContentsObserved h $ m . toInteger
+ L.hPut to b
+
feedBytes :: L.ByteString -> Feeder
feedBytes = flip L.hPut
diff --git a/Remote/WebDAV.hs b/Remote/WebDAV.hs
index ed7b82b64..2dce15499 100644
--- a/Remote/WebDAV.hs
+++ b/Remote/WebDAV.hs
@@ -31,6 +31,7 @@ import Remote.Helper.Encryptable
import Remote.Helper.Chunked
import Crypto
import Creds
+import Utility.Observed
type DavUrl = String
type DavUser = B8.ByteString
@@ -84,17 +85,21 @@ webdavSetup u c = do
setRemoteCredPair c' (davCreds u)
store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
-store r k _f _p = davAction r False $ \(baseurl, user, pass) -> do
- let url = davLocation baseurl k
- f <- inRepo $ gitAnnexLocation k
- liftIO $ storeHelper r url user pass =<< L.readFile f
+store r k _f p = metered (Just p) k $ \meterupdate ->
+ davAction r False $ \(baseurl, user, pass) -> do
+ let url = davLocation baseurl k
+ f <- inRepo $ gitAnnexLocation k
+ liftIO $ withBinaryFile f ReadMode $ \h -> do
+ b <- hGetContentsObserved h $ meterupdate . toInteger
+ storeHelper r url user pass b
storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
-storeEncrypted r (cipher, enck) k _p = davAction r False $ \(baseurl, user, pass) -> do
- let url = davLocation baseurl enck
- f <- inRepo $ gitAnnexLocation k
- liftIO $ encrypt cipher (feedFile f) $
- readBytes $ storeHelper r url user pass
+storeEncrypted r (cipher, enck) k p = metered (Just p) k $ \meterupdate ->
+ davAction r False $ \(baseurl, user, pass) -> do
+ let url = davLocation baseurl enck
+ f <- inRepo $ gitAnnexLocation k
+ liftIO $ encrypt cipher (feedFileMetered f meterupdate) $
+ readBytes $ storeHelper r url user pass
storeHelper :: Remote -> DavUrl -> DavUser -> DavPass -> L.ByteString -> IO Bool
storeHelper r urlbase user pass b = catchBoolIO $ do
diff --git a/Utility/Observed.hs b/Utility/Observed.hs
new file mode 100644
index 000000000..3ee973429
--- /dev/null
+++ b/Utility/Observed.hs
@@ -0,0 +1,43 @@
+module Utility.Observed where
+
+import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString as S
+import System.IO
+import System.IO.Unsafe
+import Foreign.Storable (Storable(sizeOf))
+
+{- This is like L.hGetContents, but after each chunk is read, an action
+ - is run to observe the size of the chunk.
+ -
+ - Note that the observer is run in unsafeInterleaveIO, which means that
+ - it can be run at any time. It's even possible for observers to run out
+ - of order, as different parts of the ByteString are consumed.
+ -
+ - All the usual caveats about using unsafeInterleaveIO apply to the observers,
+ - so use caution.
+ -}
+hGetContentsObserved :: Handle -> (Int -> IO ()) -> IO L.ByteString
+hGetContentsObserved h observe = lazyRead
+ where
+ lazyRead = unsafeInterleaveIO loop
+
+ loop = do
+ c <- S.hGetSome h defaultChunkSize
+ if S.null c
+ then do
+ hClose h
+ return $ L.empty
+ else do
+ observe $ S.length c
+ {- unsafeInterleaveIO causes this to be
+ - deferred until the data is read from the
+ - ByteString. -}
+ cs <- lazyRead
+ return $ L.append (L.fromChunks [c]) cs
+
+{- Same default chunk size Lazy ByteStrings use. -}
+defaultChunkSize :: Int
+defaultChunkSize = 32 * k - chunkOverhead
+ where
+ k = 1024
+ chunkOverhead = 2 * sizeOf (undefined :: Int) -- GHC specific
diff --git a/doc/design/assistant/progressbars.mdwn b/doc/design/assistant/progressbars.mdwn
index 6228cb7f8..37dfe6f8c 100644
--- a/doc/design/assistant/progressbars.mdwn
+++ b/doc/design/assistant/progressbars.mdwn
@@ -10,7 +10,6 @@ This is one of those potentially hidden but time consuming problems.
## downloads
* Watch temp file as it's coming in and use its size.
- This is the only option for some special remotes (ie, non-rsync).
Can either poll every .5 seconds or so to check file size, or
could use inotify. **done**
@@ -23,7 +22,7 @@ the MeterUpdate callback as the upload progresses.
* rsync: **done**
* directory: **done**
* web: Not applicable; does not upload
-* webdav: TODO
+* webdav: **done**
* S3: TODO
* bup: TODO
* hook: Would require the hook interface to somehow do this, which seems