[banshee] Add a BaseHttpServer class; use it in DAAP



commit 83af7b75c184e11f269e8d64d1482bb28d4bb6c6
Author: Neil Loknath <neil loknath gmail com>
Date:   Thu Nov 5 20:21:18 2009 -0800

    Add a BaseHttpServer class; use it in DAAP
    
    This reduces duplication if any other extension needs to implement an
    HTTP server.
    
    Signed-off-by: Bertrand Lorentz <bertrand lorentz gmail com>
    Signed-off-by: Gabriel Burt <gabriel burt gmail com>

 .../Banshee.Services/Banshee.Web/BaseHttpServer.cs |  324 ++++++++++++++++++++
 src/Core/Banshee.Services/Makefile.am              |    1 +
 .../Banshee.Daap/DaapProxyWebServer.cs             |  189 +-----------
 3 files changed, 337 insertions(+), 177 deletions(-)
---
diff --git a/src/Core/Banshee.Services/Banshee.Web/BaseHttpServer.cs b/src/Core/Banshee.Services/Banshee.Web/BaseHttpServer.cs
new file mode 100644
index 0000000..6cf238b
--- /dev/null
+++ b/src/Core/Banshee.Services/Banshee.Web/BaseHttpServer.cs
@@ -0,0 +1,324 @@
+//
+// BaseWebServer.cs
+//
+// Author:
+//   Aaron Bockover <aaron aaronbock net>
+//   James Wilcox   <snorp snorp net>
+//   Neil Loknath   <neil loknath gmail com
+//
+// Copyright (C) 2005-2006 Novell, Inc.
+// Copyright (C) 2009 Neil Loknath
+//
+// 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.Text;
+using System.Web;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Banshee.Web
+{
+    public abstract class BaseHttpServer
+    {
+        protected Socket server;
+
+        protected readonly ArrayList clients = new ArrayList();
+
+        public BaseHttpServer (EndPoint endpoint, string name)
+        {
+            this.end_point = endpoint;
+            this.name = name;
+        }
+
+        public BaseHttpServer (EndPoint endpoint, string name, int chunk_length) : this (endpoint, name)
+        {
+            this.chunk_length = chunk_length;
+        }
+
+        private string name = "Banshee Web Server";
+        public string Name {
+            get { return name; }
+        }
+
+        private EndPoint end_point = new IPEndPoint (IPAddress.Any, 80);
+        protected EndPoint EndPoint {
+            get { return end_point; }
+            set {
+                if (value == null) {
+                    throw new ArgumentNullException ("end_point");
+                }
+                if (running) {
+                    throw new InvalidOperationException ("Cannot set EndPoint while running.");
+                }
+                end_point = value; 
+            }
+        }
+
+        private bool running;
+        public bool Running {
+            get { return running; }
+            protected set { running = value; }
+        }
+
+        private int chunk_length = 8192;
+        public int ChunkLength {
+            get { return chunk_length; }
+        }
+
+        public void Start ()
+        {
+            Start (10);
+        }
+        
+        public virtual void Start (int backlog) 
+        {
+            if (backlog < 0) {
+                throw new ArgumentOutOfRangeException ("backlog");
+            }
+            
+            if (running) {
+                return;
+            }
+            
+            server = new Socket (this.EndPoint.AddressFamily, SocketType.Stream, ProtocolType.IP);
+            server.Bind (this.EndPoint);
+            
+            server.Listen (backlog);
+
+            running = true;
+            Thread thread = new Thread (ServerLoop);
+            thread.Name = this.Name;
+            thread.IsBackground = true;
+            thread.Start ();
+        }
+
+        public virtual void Stop () 
+        {
+            running = false;
+            
+            if (server != null) {
+                server.Close ();
+                server = null;
+            }
+
+            foreach (Socket client in (ArrayList)clients.Clone ()) {
+                client.Close ();
+            }
+        }
+        
+        private void ServerLoop ()
+        {
+            while (true) {
+                try {
+                    if (!running) {
+                        break;
+                    }
+                    
+                    Socket client = server.Accept ();
+                    clients.Add (client);
+                    ThreadPool.QueueUserWorkItem (HandleConnection, client);
+                } catch (SocketException) {
+                    break;
+                }
+            }
+        }
+        
+        private void HandleConnection (object o) 
+        {
+            Socket client = (Socket) o;
+
+            try {
+                while (HandleRequest(client));
+            } catch (IOException) {
+            } catch (Exception e) {
+                Hyena.Log.Exception (e);
+            } finally {
+                clients.Remove (client);
+                client.Close ();
+            }
+        }
+
+        protected virtual long ParseRangeRequest (string line)
+        {
+            long offset = 0;
+            if (String.IsNullOrEmpty (line)) {
+                return offset;
+            }
+
+            string [] split_line = line.Split (' ', '=', '-');
+            foreach (string word in split_line) {
+                if (long.TryParse (word, out offset)) {
+                    return offset;
+                }
+            }
+
+            return offset;
+        }
+        
+        protected virtual bool HandleRequest (Socket client) 
+        {
+            if (client == null || !client.Connected) {
+                return false;
+            }
+            
+            bool keep_connection = true;
+            
+            using (StreamReader reader = new StreamReader (new NetworkStream (client, false))) {
+                string request_line = reader.ReadLine ();
+                
+                if (request_line == null) {
+                    return false;
+                }
+
+                List <string> request_headers = new List <string> ();
+                string line = null;
+                
+                do {
+                    line = reader.ReadLine ();
+                    if (line.ToLower () == "connection: close") {
+                        keep_connection = false;
+                    }
+                    request_headers.Add (line);
+                } while (line != String.Empty && line != null);
+
+                string [] split_request_line = request_line.Split ();
+                
+                if (split_request_line.Length < 3) {
+                    WriteResponse (client, HttpStatusCode.BadRequest, "Bad Request");
+                    return keep_connection;
+                } else {
+                    try {
+                        HandleValidRequest (client, split_request_line, request_headers.ToArray () );
+                    } catch (IOException) {
+                        keep_connection = false;
+                    } catch (Exception e) {
+                        keep_connection = false;
+                        Console.Error.WriteLine("Trouble handling request {0}: {1}", split_request_line[1], e);
+                    }
+                }
+            }
+
+            return keep_connection;
+        }
+
+        protected abstract void HandleValidRequest(Socket client, string [] split_request, string [] request_headers);
+            
+        protected void WriteResponse (Socket client, HttpStatusCode code, string body) 
+        {
+            WriteResponse (client, code, Encoding.UTF8.GetBytes (body));
+        }
+        
+        protected virtual void WriteResponse (Socket client, HttpStatusCode code, byte [] body) 
+        {
+            if (client == null || !client.Connected) {
+                return;
+            }
+            else if (body == null) {
+                throw new ArgumentNullException ("body");
+            }
+            
+            StringBuilder headers = new StringBuilder ();
+            headers.AppendFormat ("HTTP/1.1 {0} {1}\r\n", (int) code, code.ToString ());
+            headers.AppendFormat ("Content-Length: {0}\r\n", body.Length);
+            headers.Append ("Content-Type: text/html\r\n");
+            headers.Append ("Connection: close\r\n");
+            headers.Append ("\r\n");
+            
+            using (BinaryWriter writer = new BinaryWriter (new NetworkStream (client, false))) {
+                writer.Write (Encoding.UTF8.GetBytes (headers.ToString ()));
+                writer.Write (body);
+            }
+            
+            client.Close ();
+        }
+
+        protected void WriteResponseStream (Socket client, Stream response, long length, string filename)
+        {
+            WriteResponseStream (client, response, length, filename, 0);
+        }
+        
+        protected virtual void WriteResponseStream (Socket client, Stream response, long length, string filename, long offset)
+        {
+            if (client == null || !client.Connected) {
+                return;
+            }
+            if (response == null) {
+                throw new ArgumentNullException ("response");
+            }
+            if (length < 1) {
+                throw new ArgumentOutOfRangeException ("length", "Must be > 0");
+            }
+            if (offset < 0) {
+                throw new ArgumentOutOfRangeException ("offset", "Must be positive.");
+            }
+
+            using (BinaryWriter writer = new BinaryWriter (new NetworkStream (client, false))) {
+                StringBuilder headers = new StringBuilder ();
+
+                if (offset > 0) {
+                    headers.Append ("HTTP/1.1 206 Partial Content\r\n");
+                    headers.AppendFormat ("Content-Range: {0}-{1}\r\n", offset, offset + length);
+                } else {
+                    headers.Append ("HTTP/1.1 200 OK\r\n");
+                }
+
+                if (length > 0) {
+                    headers.AppendFormat ("Content-Length: {0}\r\n", length);
+                }
+                
+                if (filename != null) {
+                    headers.AppendFormat ("Content-Disposition: attachment; filename=\"{0}\"\r\n",
+                        filename.Replace ("\"", "\\\""));
+                }
+                
+                headers.Append ("Connection: close\r\n");
+                headers.Append ("\r\n");
+                
+                writer.Write (Encoding.UTF8.GetBytes (headers.ToString ()));
+                    
+                using (BinaryReader reader = new BinaryReader (response)) {
+                    while (true) {
+                        byte [] buffer = reader.ReadBytes (ChunkLength);
+                        if (buffer == null) {
+                            break;
+                        }
+                        
+                        writer.Write(buffer);
+                        
+                        if (buffer.Length < ChunkLength) {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        protected static string Escape (string input)
+        {
+            return String.IsNullOrEmpty (input) ? "" : System.Web.HttpUtility.HtmlEncode (input);
+        }
+    }
+}
diff --git a/src/Core/Banshee.Services/Makefile.am b/src/Core/Banshee.Services/Makefile.am
index 875dd00..5109342 100644
--- a/src/Core/Banshee.Services/Makefile.am
+++ b/src/Core/Banshee.Services/Makefile.am
@@ -211,6 +211,7 @@ SOURCES =  \
 	Banshee.Sources/SourceMessage.cs \
 	Banshee.Sources/SourceSortType.cs \
 	Banshee.Streaming/RadioTrackInfo.cs \
+	Banshee.Web/BaseHttpServer.cs \
 	Banshee.Web/Browser.cs \
 	Banshee.Web/HttpRequest.cs
 
diff --git a/src/Extensions/Banshee.Daap/Banshee.Daap/DaapProxyWebServer.cs b/src/Extensions/Banshee.Daap/Banshee.Daap/DaapProxyWebServer.cs
index 550ab4c..fd9e970 100644
--- a/src/Extensions/Banshee.Daap/Banshee.Daap/DaapProxyWebServer.cs
+++ b/src/Extensions/Banshee.Daap/Banshee.Daap/DaapProxyWebServer.cs
@@ -3,8 +3,10 @@
  *  DaapProxyWebServer.cs
  *
  *  Copyright (C) 2005-2006 Novell, Inc.
+ *  Copyright (C) 2009 Neil Loknath
  *  Written by Aaron Bockover <aaron aaronbock net>
  *             James Wilcox <snorp snorp net>
+ *             Neil Loknath <neil loknath gmail com>
  ****************************************************************************/
 
 /*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
@@ -37,54 +39,30 @@ using System.Net.Sockets;
 using System.Threading;
 using System.Collections;
 
+using Banshee.Web;
+
 using DAAP = Daap;
 
 namespace Banshee.Daap
 {
-    internal class DaapProxyWebServer
+    internal class DaapProxyWebServer : BaseHttpServer
     {
-        private const int ChunkLength = 8192;
-
         private ushort port;
-        private Socket server;
-        private bool running;
-        private ArrayList clients = new ArrayList();
         private ArrayList databases = new ArrayList();
       
-        public DaapProxyWebServer() 
+        public DaapProxyWebServer() : base (new IPEndPoint(IPAddress.Any, 8089), "DAAP Proxy")
         {
         }
 
-        public void Start() 
+        public override void Start (int backlog) 
         {
-            server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
             try {
-                server.Bind(new IPEndPoint(IPAddress.Any, 8089));
+                base.Start (backlog);
             } catch (System.Net.Sockets.SocketException) {
-                server.Bind(new IPEndPoint(IPAddress.Any, 0));
+                EndPoint = new IPEndPoint(IPAddress.Any, 0);
+                base.Start (backlog);
             }
             port = (ushort)(server.LocalEndPoint as IPEndPoint).Port;
-            server.Listen(10);
-
-            running = true;
-            Thread thread = new Thread(ServerLoop);
-            thread.Name = "DAAP Proxy";
-            thread.IsBackground = true;
-            thread.Start();
-        }
-
-        public void Stop() 
-        {
-            running = false;
-            
-            if(server != null) {
-                server.Close();
-                server = null;
-            }
-
-            foreach(Socket client in (ArrayList)clients.Clone()) {
-                client.Close();
-            }
         }
         
         public void RegisterDatabase(DAAP.Database database)
@@ -96,84 +74,8 @@ namespace Banshee.Daap
         {
             databases.Remove(database);
         }
-        
-        private void ServerLoop()
-        {
-            while(true) {
-                try {
-                    if(!running) {
-                        break;
-                    }
-                    
-                    Socket client = server.Accept();
-                    clients.Add(client);
-                    ThreadPool.QueueUserWorkItem(HandleConnection, client);
-                } catch(SocketException) {
-                    break;
-                }
-            }
-        }
-        
-        private void HandleConnection(object o) 
-        {
-            Socket client = (Socket)o;
 
-            try {
-                while(HandleRequest(client));
-            } catch(IOException) {
-            } catch(Exception e) {
-                Hyena.Log.Exception (e);
-            } finally {
-                clients.Remove(client);
-                client.Close();
-            }
-        }
-        
-        private bool HandleRequest(Socket client) 
-        {
-            if(!client.Connected) {
-                return false;
-            }
-            
-            bool keep_connection = true;
-            
-            using(StreamReader reader = new StreamReader(new NetworkStream(client, false))) {
-                string request = reader.ReadLine();
-                
-                if(request == null) {
-                    return false;
-                }
-                
-                string line = null;
-                
-                do {
-                    line = reader.ReadLine();
-                    if(line.ToLower() == "connection: close") {
-                        keep_connection = false;
-                    }
-                } while(line != String.Empty && line != null);
-                
-                string [] split_request = request.Split();
-                
-                if(split_request.Length < 3) {
-                    WriteResponse(client, HttpStatusCode.BadRequest, "Bad Request");
-                    return keep_connection;
-                } else {
-                    try {
-                        HandleValidRequest(client, split_request);
-                    } catch(IOException) {
-                        keep_connection = false;
-                    } catch(Exception e) {
-                        keep_connection = false;
-                        Console.Error.WriteLine("Trouble handling request {0}: {1}", split_request[1], e);
-                    }
-                }
-            }
-
-            return keep_connection;
-        }
-
-        private void HandleValidRequest(Socket client, string [] split_request)
+        protected override void HandleValidRequest(Socket client, string [] split_request, string [] body_request)
         {        
             if(split_request[1].StartsWith("/")) {
                split_request[1] = split_request[1].Substring(1);
@@ -291,73 +193,6 @@ namespace Banshee.Daap
             client.Close();
         }
 
-        private void WriteResponse(Socket client, HttpStatusCode code, string body) 
-        {
-            WriteResponse(client, code, Encoding.UTF8.GetBytes(body));
-        }
-        
-        private void WriteResponse(Socket client, HttpStatusCode code, byte [] body) 
-        {
-            if(!client.Connected) {
-                return;
-            }
-            
-            string headers = String.Empty;
-            headers += String.Format("HTTP/1.1 {0} {1}\r\n", (int)code, code.ToString());
-            headers += String.Format("Content-Length: {0}\r\n", body.Length);
-            headers += "Content-Type: text/html\r\n";
-            headers += "Connection: close\r\n";
-            headers += "\r\n";
-            
-            using(BinaryWriter writer = new BinaryWriter(new NetworkStream(client, false))) {
-                writer.Write(Encoding.UTF8.GetBytes(headers));
-                writer.Write(body);
-            }
-            
-            client.Close();
-        }
-
-        private void WriteResponseStream(Socket client, Stream response, long length, string filename)
-        {
-            using(BinaryWriter writer = new BinaryWriter(new NetworkStream(client, false))) {
-                string headers = "HTTP/1.1 200 OK\r\n";
-
-                if(length > 0) {
-                    headers += String.Format("Content-Length: {0}\r\n", length);
-                }
-                
-                if(filename != null) {
-                    headers += String.Format("Content-Disposition: attachment; filename=\"{0}\"\r\n",
-                        filename.Replace("\"", "\\\""));
-                }
-                
-                headers += "Connection: close\r\n";
-                headers += "\r\n";
-                
-                writer.Write(Encoding.UTF8.GetBytes(headers));
-
-                using(BinaryReader reader = new BinaryReader(response)) {
-                    while(true) {
-                        byte [] buffer = reader.ReadBytes(ChunkLength);
-                        if(buffer == null) {
-                            break;
-                        }
-                        
-                        writer.Write(buffer);
-                        
-                        if(buffer.Length < ChunkLength) {
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-
-        private static string Escape (string input)
-        {
-            return String.IsNullOrEmpty (input) ? "" : System.Web.HttpUtility.HtmlEncode (input);
-        }
-        
         private static string GetHtmlHeader(string title)
         {
             return String.Format("<html><head><title>{0} - Banshee DAAP Browser</title></head><body><h1>{0}</h1>", 
@@ -367,7 +202,7 @@ namespace Banshee.Daap
         private static string GetHtmlFooter()
         {
             return String.Format("<hr /><address>Generated on {0} by " + 
-                "Banshee DAAP Plugin (<a href=\"http://banshee-project.org\";>http://banshee-project.org</a>)",
+                "Banshee DAAP Extension (<a href=\"http://banshee-project.org\";>http://banshee-project.org</a>)",
                 DateTime.Now.ToString());
         }
 



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