[banshee] Added new Hyena.Downloader namespace



commit 9b6e5d68c070e13dc910f6b8ac24ffd12ed3d61e
Author: Aaron Bockover <abockover novell com>
Date:   Wed Jul 7 18:17:54 2010 -0400

    Added new Hyena.Downloader namespace
    
    Provides a basic async HTTP downloader service.

 src/Libraries/Hyena/Hyena.Downloader/Buffer.cs     |   38 +++
 .../Hyena/Hyena.Downloader/DownloadManager.cs      |  140 +++++++++++
 .../Hyena/Hyena.Downloader/HttpDownloader.cs       |  256 ++++++++++++++++++++
 .../Hyena/Hyena.Downloader/HttpDownloaderState.cs  |   44 ++++
 .../Hyena/Hyena.Downloader/HttpFileDownloader.cs   |   85 +++++++
 src/Libraries/Hyena/Hyena.csproj                   |    5 +
 src/Libraries/Hyena/Makefile.am                    |    5 +
 7 files changed, 573 insertions(+), 0 deletions(-)
---
diff --git a/src/Libraries/Hyena/Hyena.Downloader/Buffer.cs b/src/Libraries/Hyena/Hyena.Downloader/Buffer.cs
new file mode 100644
index 0000000..97792b3
--- /dev/null
+++ b/src/Libraries/Hyena/Hyena.Downloader/Buffer.cs
@@ -0,0 +1,38 @@
+// 
+// Buffer.cs
+//  
+// Author:
+//   Aaron Bockover <abockover novell com>
+// 
+// Copyright 2010 Novell, Inc.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+namespace Hyena.Downloader
+{
+    public class Buffer
+    {
+        public long Length { get; set; }
+        public DateTime TimeStamp { get; set; }
+        public byte [] Data { get; set; }
+    }
+}
+
diff --git a/src/Libraries/Hyena/Hyena.Downloader/DownloadManager.cs b/src/Libraries/Hyena/Hyena.Downloader/DownloadManager.cs
new file mode 100644
index 0000000..6d12943
--- /dev/null
+++ b/src/Libraries/Hyena/Hyena.Downloader/DownloadManager.cs
@@ -0,0 +1,140 @@
+// 
+// DownloadManager.cs
+//  
+// Author:
+//   Aaron Bockover <abockover novell com>
+// 
+// Copyright 2010 Novell, Inc.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+namespace Hyena.Downloader
+{
+    public class DownloadManager
+    {
+        private object sync_root = new object ();
+        protected object SyncRoot {
+            get { return sync_root; }
+        }
+
+        private Queue<HttpDownloader> pending_downloaders = new Queue<HttpDownloader> ();
+        private List<HttpDownloader> active_downloaders = new List<HttpDownloader> ();
+
+        protected Queue<HttpDownloader> PendingDownloaders {
+            get { return pending_downloaders; }
+        }
+
+        protected List<HttpDownloader> ActiveDownloaders {
+            get { return active_downloaders; }
+        }
+
+        public event Action<HttpDownloader> Started;
+        public event Action<HttpDownloader> Finished;
+        public event Action<HttpDownloader> Progress;
+        public event Action<HttpDownloader> BufferUpdated;
+
+        public int MaxConcurrentDownloaders { get; set; }
+
+        public int PendingDownloadCount {
+            get { return pending_downloaders.Count; }
+        }
+
+        public int ActiveDownloadCount {
+            get { return active_downloaders.Count; }
+        }
+
+        public int TotalDownloadCount {
+            get { return PendingDownloadCount + ActiveDownloadCount; }
+        }
+
+        public DownloadManager ()
+        {
+            MaxConcurrentDownloaders = 2;
+        }
+
+        public void QueueDownloader (HttpDownloader downloader)
+        {
+            lock (SyncRoot) {
+                pending_downloaders.Enqueue (downloader);
+                Update ();
+            }
+        }
+
+        public void WaitUntilFinished ()
+        {
+            while (TotalDownloadCount > 0);
+        }
+
+        private void Update ()
+        {
+            lock (SyncRoot) {
+                while (pending_downloaders.Count > 0 && active_downloaders.Count < MaxConcurrentDownloaders) {
+                    var downloader = pending_downloaders.Peek ();
+                    downloader.Started += OnDownloaderStarted;
+                    downloader.Finished += OnDownloaderFinished;
+                    downloader.Progress += OnDownloaderProgress;
+                    downloader.BufferUpdated += OnDownloaderBufferUpdated;
+                    active_downloaders.Add (downloader);
+                    pending_downloaders.Dequeue ();
+                    downloader.Start ();
+                }
+            }
+        }
+
+        protected virtual void OnDownloaderStarted (HttpDownloader downloader)
+        {
+            var handler = Started;
+            if (handler != null) {
+                handler (downloader);
+            }
+        }
+
+        protected virtual void OnDownloaderFinished (HttpDownloader downloader)
+        {
+            lock (SyncRoot) {
+                active_downloaders.Remove (downloader);
+                Update ();
+            }
+
+            var handler = Finished;
+            if (handler != null) {
+                handler (downloader);
+            }
+        }
+
+        protected virtual void OnDownloaderProgress (HttpDownloader downloader)
+        {
+            var handler = Progress;
+            if (handler != null) {
+                handler (downloader);
+            }
+        }
+
+        protected virtual void OnDownloaderBufferUpdated (HttpDownloader downloader)
+        {
+            var handler = BufferUpdated;
+            if (handler != null) {
+                handler (downloader);
+            }
+        }
+    }
+}
diff --git a/src/Libraries/Hyena/Hyena.Downloader/HttpDownloader.cs b/src/Libraries/Hyena/Hyena.Downloader/HttpDownloader.cs
new file mode 100644
index 0000000..72a2572
--- /dev/null
+++ b/src/Libraries/Hyena/Hyena.Downloader/HttpDownloader.cs
@@ -0,0 +1,256 @@
+// 
+// HttpDownloader.cs
+//  
+// Author:
+//   Aaron Bockover <abockover novell com>
+// 
+// Copyright 2010 Novell, Inc.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.IO;
+using System.Net;
+
+namespace Hyena.Downloader
+{
+    public class HttpDownloader
+    {
+        private object sync_root = new object ();
+        protected object SyncRoot {
+            get { return sync_root; }
+        }
+
+        private HttpWebRequest request;
+        private HttpWebResponse response;
+        private Stream response_stream;
+        private DateTime last_raised_percent_complete;
+        private IAsyncResult async_begin_result;
+
+        public string UserAgent { get; set; }
+        public Uri Uri { get; set; }
+        public TimeSpan ProgressEventRaiseLimit { get; set; }
+        public HttpDownloaderState State { get; private set; }
+        
+        private int buffer_size = 8192;
+        public int BufferSize {
+            get { return buffer_size; }
+            set {
+                if (value <= 0) {
+                    throw new InvalidOperationException ("Invalid buffer size");
+                }
+                buffer_size = value;
+            }
+        }
+
+        private string name;
+        public string Name {
+            get { return name ?? Path.GetFileName (Uri.UnescapeDataString (Uri.LocalPath)); }
+            set { name = value; }
+        }
+
+        public event Action<HttpDownloader> Started;
+        public event Action<HttpDownloader> Finished;
+        public event Action<HttpDownloader> Progress;
+        public event Action<HttpDownloader> BufferUpdated;
+
+        public HttpDownloader ()
+        {
+            ProgressEventRaiseLimit = TimeSpan.FromSeconds (0.25);
+        }
+
+        public void Start ()
+        {
+            lock (SyncRoot) {
+                if (request != null || async_begin_result != null) {
+                    throw new InvalidOperationException ("HttpDownloader is already active");
+                }
+                
+                State = new HttpDownloaderState () {
+                    Buffer = new Buffer () {
+                        Data = new byte[BufferSize]
+                    }
+                };
+
+                request = CreateRequest ();
+                async_begin_result = request.BeginGetResponse (OnRequestResponse, this);
+
+                State.StartTime = DateTime.Now;
+                State.Working = true;
+                OnStarted ();
+            }
+        }
+
+        public void Abort ()
+        {
+            lock (SyncRoot) {
+                Close ();
+                OnFinished ();
+            }
+        }
+
+        private void Close ()
+        {
+            lock (SyncRoot) {
+                State.FinishTime = DateTime.Now;
+                State.Working = false;
+
+                if (response_stream != null) {
+                    response_stream.Close ();
+                }
+
+                if (response != null) {
+                    response.Close ();
+                }
+
+                response_stream = null;
+                response = null;
+                request = null;
+            }
+        }
+
+        protected virtual HttpWebRequest CreateRequest ()
+        {
+            var request = (HttpWebRequest)WebRequest.Create (Uri);
+            request.Method = "GET";
+            request.AllowAutoRedirect = true;
+            request.UserAgent = UserAgent;
+            request.Timeout = 10000;
+            return request;
+        }
+
+        private void OnRequestResponse (IAsyncResult asyncResult)
+        {
+            lock (SyncRoot) {
+                async_begin_result = null;
+
+                if (request == null) {
+                    return;
+                }
+
+                var raise = false;
+
+                try {
+                    response = (HttpWebResponse)request.EndGetResponse (asyncResult);
+                    if (response.StatusCode != HttpStatusCode.OK) {
+                        State.Success = false;
+                        raise = true;
+                        return;
+                    }
+
+                    State.TotalBytesExpected = response.ContentLength;
+
+                    response_stream = response.GetResponseStream ();
+                    async_begin_result = response_stream.BeginRead (State.Buffer.Data, 0,
+                        State.Buffer.Data.Length, OnResponseRead, this);
+                } catch (Exception e) {
+                    State.FailureException = e;
+                    State.Success = false;
+                    raise = true;
+                } finally {
+                    if (raise) {
+                        Close ();
+                        OnFinished ();
+                    }
+                }
+            }
+        }
+
+        private void OnResponseRead (IAsyncResult asyncResult)
+        {
+            lock (SyncRoot) {
+                async_begin_result = null;
+
+                if (request == null || response == null || response_stream == null) {
+                    return;
+                }
+
+                try {
+                    var now = DateTime.Now;
+
+                    State.Buffer.Length = response_stream.EndRead (asyncResult);
+                    State.Buffer.TimeStamp = now;
+                    State.TotalBytesRead += State.Buffer.Length;
+                    State.TransferRate = State.TotalBytesRead / (now - State.StartTime).TotalSeconds;
+                    State.PercentComplete = (double)State.TotalBytesRead / (double)State.TotalBytesExpected;
+
+                    OnBufferUpdated ();
+
+                    if (State.Buffer.Length <= 0) {
+                        State.Success = true;
+                        Close ();
+                        OnFinished ();
+                        return;
+                    }
+
+                    if (State.PercentComplete >= 1 || last_raised_percent_complete == DateTime.MinValue ||
+                        (now - last_raised_percent_complete >= ProgressEventRaiseLimit)) {
+                        last_raised_percent_complete = now;
+                        OnProgress ();
+                    }
+
+                    async_begin_result = response_stream.BeginRead (State.Buffer.Data, 0,
+                        State.Buffer.Data.Length, OnResponseRead, this);
+                } catch (Exception e) {
+                    State.FailureException = e;
+                    State.Success = false;
+                    Close ();
+                    OnFinished ();
+                }
+            }
+        }
+
+        protected virtual void OnStarted ()
+        {
+            var handler = Started;
+            if (handler != null) {
+                handler (this);
+            }
+        }
+
+        protected virtual void OnBufferUpdated ()
+        {
+            var handler = BufferUpdated;
+            if (handler != null) {
+                handler (this);
+            }
+        }
+
+        protected virtual void OnProgress ()
+        {
+            var handler = Progress;
+            if (handler != null) {
+                handler (this);
+            }
+        }
+
+        protected virtual void OnFinished ()
+        {
+            var handler = Finished;
+            if (handler != null) {
+                handler (this);
+            } 
+        }
+
+        public override string ToString ()
+        {
+            return Name;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Libraries/Hyena/Hyena.Downloader/HttpDownloaderState.cs b/src/Libraries/Hyena/Hyena.Downloader/HttpDownloaderState.cs
new file mode 100644
index 0000000..0cc77b5
--- /dev/null
+++ b/src/Libraries/Hyena/Hyena.Downloader/HttpDownloaderState.cs
@@ -0,0 +1,44 @@
+// 
+// HttpDownloaderState.cs
+//  
+// Author:
+//   Aaron Bockover <abockover novell com>
+// 
+// Copyright 2010 Novell, Inc.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+namespace Hyena.Downloader
+{
+    public class HttpDownloaderState
+    {
+        public DateTime StartTime { get; internal set; }
+        public DateTime FinishTime { get; internal set; }
+        public double PercentComplete { get; internal set; }
+        public double TransferRate { get; internal set; }
+        public Buffer Buffer { get; internal set; }
+        public long TotalBytesRead { get; internal set; }
+        public long TotalBytesExpected { get; internal set; }
+        public bool Success { get; internal set; }
+        public bool Working { get; internal set; }
+        public Exception FailureException { get; internal set; }
+    }
+}
\ No newline at end of file
diff --git a/src/Libraries/Hyena/Hyena.Downloader/HttpFileDownloader.cs b/src/Libraries/Hyena/Hyena.Downloader/HttpFileDownloader.cs
new file mode 100644
index 0000000..6c9e4e1
--- /dev/null
+++ b/src/Libraries/Hyena/Hyena.Downloader/HttpFileDownloader.cs
@@ -0,0 +1,85 @@
+// 
+// HttpFileDownloader.cs
+//  
+// Author:
+//   Aaron Bockover <abockover novell com>
+// 
+// Copyright 2010 Novell, Inc.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.IO;
+
+namespace Hyena.Downloader
+{
+    public class HttpFileDownloader : HttpDownloader
+    {
+        private FileStream file_stream;
+
+        public string TempPathRoot { get; set; }
+        public string FileExtension { get; set; }
+        public string LocalPath { get; private set; }
+
+        public event Action<HttpFileDownloader> FileFinished;
+
+        public HttpFileDownloader ()
+        {
+            TempPathRoot = System.IO.Path.GetTempPath ();
+        }
+        
+        protected override void OnStarted ()
+        {
+            Directory.CreateDirectory (TempPathRoot);
+            LocalPath = Path.Combine (TempPathRoot, CryptoUtil.Md5Encode (Uri.AbsoluteUri));
+            if (!String.IsNullOrEmpty (FileExtension)) {
+                LocalPath += "." + FileExtension;
+            }
+            base.OnStarted ();
+        }
+
+        protected override void OnBufferUpdated ()
+        {
+            if (file_stream == null) {
+                file_stream = new FileStream (LocalPath, FileMode.Create, FileAccess.Write);
+            }
+            file_stream.Write (State.Buffer.Data, 0, (int)State.Buffer.Length);
+            base.OnBufferUpdated ();
+        }
+
+        protected override void OnFinished ()
+        {
+            if (file_stream != null) {
+                file_stream.Close ();
+                file_stream = null;
+                OnFileFinished ();
+            }
+
+            base.OnFinished ();
+        }
+
+        protected virtual void OnFileFinished ()
+        {
+            var handler = FileFinished;
+            if (handler != null) {
+                handler (this);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Libraries/Hyena/Hyena.csproj b/src/Libraries/Hyena/Hyena.csproj
index 7d37213..7798824 100644
--- a/src/Libraries/Hyena/Hyena.csproj
+++ b/src/Libraries/Hyena/Hyena.csproj
@@ -176,6 +176,11 @@
     <Compile Include="Hyena.Json\Serializer.cs" />
     <Compile Include="Hyena\Tests\DateTimeUtilTests.cs" />
     <Compile Include="Hyena.Query\ExactUriStringQueryValue.cs" />
+    <Compile Include="Hyena.Downloader\Buffer.cs" />
+    <Compile Include="Hyena.Downloader\DownloadManager.cs" />
+    <Compile Include="Hyena.Downloader\HttpDownloader.cs" />
+    <Compile Include="Hyena.Downloader\HttpDownloaderState.cs" />
+    <Compile Include="Hyena.Downloader\HttpFileDownloader.cs" />
   </ItemGroup>
   <ItemGroup>
     <Reference Include="System" />
diff --git a/src/Libraries/Hyena/Makefile.am b/src/Libraries/Hyena/Makefile.am
index 6ad4bb3..029e5f8 100644
--- a/src/Libraries/Hyena/Makefile.am
+++ b/src/Libraries/Hyena/Makefile.am
@@ -50,6 +50,11 @@ SOURCES =  \
 	Hyena.Data/ModelSelection.cs \
 	Hyena.Data/PropertyStore.cs \
 	Hyena.Data/SortType.cs \
+	Hyena.Downloader/Buffer.cs \
+	Hyena.Downloader/DownloadManager.cs \
+	Hyena.Downloader/HttpDownloader.cs \
+	Hyena.Downloader/HttpDownloaderState.cs \
+	Hyena.Downloader/HttpFileDownloader.cs \
 	Hyena.Jobs/Job.cs \
 	Hyena.Jobs/JobExtensions.cs \
 	Hyena.Jobs/PriorityHints.cs \



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]