[banshee] Add a BaseHttpServer class; use it in DAAP
- From: Gabriel Burt <gburt src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [banshee] Add a BaseHttpServer class; use it in DAAP
- Date: Fri, 6 Nov 2009 04:27:12 +0000 (UTC)
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]