[rygel] core: Refactor: request handling into separate interface



commit 598a9588351c04d1379f97c9a16aa93e60d0378a
Author: James Henstridge <james jamesh id au>
Date:   Mon Aug 3 21:06:51 2009 +0800

    core: Refactor: request handling into separate interface
    
    Factor out the request handling code into an HTTPRequestHandler interface.
    The interface is implemented by Rygel.Transcoder and an "identity request
    handler" used to serve items as is.

 src/rygel/Makefile.am                         |    2 +
 src/rygel/rygel-http-request-handler.vala     |   37 +++++++
 src/rygel/rygel-http-request.vala             |  131 ++++--------------------
 src/rygel/rygel-identity-request-handler.vala |   78 +++++++++++++++
 src/rygel/rygel-media-item.vala               |   10 ++-
 src/rygel/rygel-seek.vala                     |   13 ++-
 src/rygel/rygel-transcoder.vala               |   27 +++++-
 7 files changed, 182 insertions(+), 116 deletions(-)
---
diff --git a/src/rygel/Makefile.am b/src/rygel/Makefile.am
index ef9466d..fa29b50 100644
--- a/src/rygel/Makefile.am
+++ b/src/rygel/Makefile.am
@@ -51,6 +51,8 @@ VAPI_SOURCE_FILES = rygel-configuration.vala \
 		    rygel-http-server.vala \
 		    rygel-state-machine.vala \
 		    rygel-http-request.vala \
+		    rygel-http-request-handler.vala \
+		    rygel-identity-request-handler.vala \
 		    rygel-seek.vala \
 		    rygel-http-response.vala \
 		    rygel-live-response.vala \
diff --git a/src/rygel/rygel-http-request-handler.vala b/src/rygel/rygel-http-request-handler.vala
new file mode 100644
index 0000000..b5fcdfa
--- /dev/null
+++ b/src/rygel/rygel-http-request-handler.vala
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008, 2009 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+using Rygel;
+
+/**
+ * HTTP request handler interface.
+ */
+internal interface Rygel.HTTPRequestHandler: GLib.Object {
+    // Add response headers.
+    public abstract void add_response_headers (HTTPRequest request)
+        throws HTTPRequestError;
+    // Create an HTTPResponse object that will render the body.
+    public abstract HTTPResponse render_body (HTTPRequest request)
+        throws HTTPRequestError;
+}
diff --git a/src/rygel/rygel-http-request.vala b/src/rygel/rygel-http-request.vala
index 5f4de08..481f25b 100644
--- a/src/rygel/rygel-http-request.vala
+++ b/src/rygel/rygel-http-request.vala
@@ -40,17 +40,18 @@ internal errordomain Rygel.HTTPRequestError {
 internal class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
     private unowned HTTPServer http_server;
     private MediaContainer root_container;
-    private Soup.Server server;
-    private Soup.Message msg;
+    public Soup.Server server;
+    public Soup.Message msg;
     private HashTable<string,string>? query;
 
     private HTTPResponse response;
 
     private string item_id;
-    private Transcoder transcoder;
-    private MediaItem item;
-    private Seek byte_range;
-    private Seek time_range;
+    public MediaItem item;
+    public Seek byte_range;
+    public Seek time_range;
+
+    private HTTPRequestHandler request_handler;
 
     private Cancellable cancellable;
 
@@ -81,7 +82,7 @@ internal class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
             this.item_id = this.query.lookup ("itemid");
             var transcode_target = this.query.lookup ("transcode");
             if (transcode_target != null) {
-                this.transcoder = this.http_server.get_transcoder (
+                this.request_handler = this.http_server.get_transcoder (
                                                     transcode_target);
             }
         }
@@ -91,36 +92,16 @@ internal class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
             return;
         }
 
+        if (this.request_handler == null) {
+            this.request_handler = new IdentityRequestHandler ();
+        }
+
         // Fetch the requested item
         this.root_container.find_object (this.item_id,
                                          null,
                                          this.on_item_found);
     }
 
-    private void stream_from_gst_source (owned Element src) throws Error {
-        var response = new LiveResponse (this.server,
-                                         this.msg,
-                                         "RygelLiveResponse",
-                                         src,
-                                         this.time_range);
-        this.response = response;
-        response.completed += on_response_completed;
-
-        response.run (this.cancellable);
-    }
-
-    private void serve_uri (string uri, size_t size) {
-        var response = new SeekableResponse (this.server,
-                                             this.msg,
-                                             uri,
-                                             this.byte_range,
-                                             size);
-        this.response = response;
-        response.completed += on_response_completed;
-
-        response.run (this.cancellable);
-    }
-
     private void on_response_completed (HTTPResponse response) {
         this.end (Soup.KnownStatusCode.NONE);
     }
@@ -129,93 +110,25 @@ internal class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
         try {
             this.byte_range = Seek.from_byte_range(this.msg);
             this.time_range = Seek.from_time_range(this.msg);
-        } catch (Error error) {
-            this.handle_error (error);
-            return;
-        }
-
-        // Add headers
-        this.add_item_headers ();
-
-        if (this.msg.method == "HEAD") {
-            // Only headers requested, no need to send contents
-            this.server.unpause_message (this.msg);
-            this.end (Soup.KnownStatusCode.OK);
-            return;
-        }
-
-        if (this.item.size > 0 && this.transcoder == null) {
-            this.handle_interactive_item ();
-        } else {
-            this.handle_streaming_item ();
-        }
-    }
 
-    private void add_item_headers () {
-        if (this.transcoder != null) {
-            this.msg.response_headers.append ("Content-Type",
-                                              this.transcoder.mime_type);
-            this.time_range.add_response_header(this.msg);
-            return;
-        }
+            // Add headers
+            this.request_handler.add_response_headers (this);
 
-        if (this.item.mime_type != null) {
-            this.msg.response_headers.append ("Content-Type",
-                                              this.item.mime_type);
-        }
-
-        if (this.item.size >= 0) {
-            this.msg.response_headers.set_content_length (this.item.size);
-        }
-
-        if (this.item.size > 0) {
-            Seek seek;
-
-            if (this.byte_range != null) {
-                seek = this.byte_range;
-            } else {
-                seek = new Seek (Format.BYTES, 0, this.item.size - 1);
+            if (this.msg.method == "HEAD") {
+                // Only headers requested, no need to send contents
+                this.server.unpause_message (this.msg);
+                this.end (Soup.KnownStatusCode.OK);
+                return;
             }
 
-            seek.add_response_header (this.msg, this.item.size);
-        }
-    }
-
-    private void handle_streaming_item () {
-        Element src = null;
-
-        src = this.item.create_stream_source ();
-
-        if (src == null) {
-            this.handle_error (new HTTPRequestError.NOT_FOUND ("Not Found"));
-            return;
-        }
-
-        try {
-            if (this.transcoder != null) {
-                src = this.transcoder.create_source (this.item, src);
-            }
-
-            // Then start the gst stream
-            this.stream_from_gst_source (src);
+            this.response = this.request_handler.render_body (this);
+            this.response.completed += on_response_completed;
+            this.response.run (this.cancellable);
         } catch (Error error) {
             this.handle_error (error);
-            return;
         }
     }
 
-    private void handle_interactive_item () {
-        if (this.item.uris.size == 0) {
-            var error = new HTTPRequestError.NOT_FOUND (
-                                "Requested item '%s' didn't provide a URI\n",
-                                this.item.id);
-            this.handle_error (error);
-            return;
-        }
-
-        this.serve_uri (this.item.uris.get (0), this.item.size);
-    }
-
     private void on_item_found (GLib.Object source_object,
                                 AsyncResult res) {
         var container = (MediaContainer) source_object;
diff --git a/src/rygel/rygel-identity-request-handler.vala b/src/rygel/rygel-identity-request-handler.vala
new file mode 100644
index 0000000..acd62bb
--- /dev/null
+++ b/src/rygel/rygel-identity-request-handler.vala
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008, 2009 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+// An HTTP request handler that passes the item content through as is.
+internal class Rygel.IdentityRequestHandler : GLib.Object,
+                                              Rygel.HTTPRequestHandler {
+
+    public IdentityRequestHandler () {}
+
+    public virtual void add_response_headers (HTTPRequest request)
+            throws HTTPRequestError {
+        weak MediaItem item = request.item;
+
+        request.msg.response_headers.append ("Content-Type", item.mime_type);
+        if (item.size >= 0) {
+            request.msg.response_headers.set_content_length (item.size);
+        }
+        if (item.should_stream ()) {
+            if (request.time_range != null) {
+                request.time_range.add_response_header (request.msg);
+            }
+        } else {
+            request.msg.response_headers.append ("Accept-Ranges", "bytes");
+            if (request.byte_range != null) {
+                request.byte_range.add_response_header (request.msg, item.size);
+            }
+        }
+    }
+
+    public virtual HTTPResponse render_body (HTTPRequest request)
+            throws HTTPRequestError {
+        weak MediaItem item = request.item;
+
+        if (item.should_stream ()) {
+            Gst.Element src = item.create_stream_source ();
+
+            if (src == null) {
+                throw new HTTPRequestError.NOT_FOUND ("Not found");
+            }
+            return new LiveResponse (request.server,
+                                     request.msg,
+                                     "RygelLiveResponse",
+                                     src,
+                                     request.time_range);
+        } else {
+            if (item.uris.size == 0) {
+                throw new HTTPRequestError.NOT_FOUND (
+                        "Requested item '%s' didn't provide a URI\n",
+                        item.id);
+            }
+            return new SeekableResponse (request.server,
+                                         request.msg,
+                                         item.uris.get (0),
+                                         request.byte_range,
+                                         item.size);
+        }
+    }
+}
diff --git a/src/rygel/rygel-media-item.vala b/src/rygel/rygel-media-item.vala
index 75ee87b..f179d16 100644
--- a/src/rygel/rygel-media-item.vala
+++ b/src/rygel/rygel-media-item.vala
@@ -101,6 +101,14 @@ public class Rygel.MediaItem : MediaObject {
                (int) transcoder2.get_distance (this);
     }
 
+    // Return true if item should be streamed as a live response with
+    // time based seeking, or false to serve directly with byte range
+    // seeking.
+    public virtual bool should_stream () {
+        // Simple heuristic: if we know the size, serve directly.
+        return this.size <= 0;
+    }
+
     internal void add_resources (DIDLLiteItem didl_item) throws Error {
         foreach (var uri in this.uris) {
             this.add_resource (didl_item, uri, null);
@@ -143,7 +151,7 @@ public class Rygel.MediaItem : MediaObject {
             protocol_info.dlna_flags |= DLNAFlags.STREAMING_TRANSFER_MODE;
         }
 
-        if (res.size > 0) {
+        if (!this.should_stream ()) {
             protocol_info.dlna_operation = DLNAOperation.RANGE;
             protocol_info.dlna_flags |= DLNAFlags.BACKGROUND_TRANSFER_MODE;
         }
diff --git a/src/rygel/rygel-seek.vala b/src/rygel/rygel-seek.vala
index 7962489..3c42195 100644
--- a/src/rygel/rygel-seek.vala
+++ b/src/rygel/rygel-seek.vala
@@ -151,19 +151,22 @@ internal class Rygel.Seek : GLib.Object {
         } else {
             // Content-Range: bytes START_BYTE-STOP_BYTE/TOTAL_LENGTH
             value = "bytes " + this.start.to_string() + "-";
-            if (this.stop >= 0) {
-                int64 end_point = this.stop;
 
-                if (length > 0) {
-                    end_point = int64.min(end_point, length - 1);
+            int64 end_point = this.stop;
+            if (length > 0) {
+                if (end_point >= 0) {
+                    end_point = int64.max (end_point, length - 1);
+                } else {
+                    end_point = length - 1;
                 }
+            }
+            if (end_point >= 0)
                 value += end_point.to_string();
             }
             if (length > 0) {
                 value += "/" + length.to_string();
             }
             msg.response_headers.append ("Content-Range", value);
-            msg.response_headers.append ("Accept-Ranges", "bytes");
         }
     }
 }
diff --git a/src/rygel/rygel-transcoder.vala b/src/rygel/rygel-transcoder.vala
index f826ba7..ed9451e 100644
--- a/src/rygel/rygel-transcoder.vala
+++ b/src/rygel/rygel-transcoder.vala
@@ -29,7 +29,7 @@ using Gee;
  * The base Transcoder class. Each implementation derives from it and must
  * at least implement create_source method.
  */
-internal abstract class Rygel.Transcoder : GLib.Object {
+internal abstract class Rygel.Transcoder : GLib.Object, HTTPRequestHandler {
     public string mime_type { get; protected set; }
     public string dlna_profile { get; protected set; }
 
@@ -103,5 +103,30 @@ internal abstract class Rygel.Transcoder : GLib.Object {
 
         return g_content_type_is_a (content_type1, content_type2);
     }
+
+    public virtual void add_response_headers (HTTPRequest request)
+            throws HTTPRequestError {
+        request.msg.response_headers.append ("Content-Type", this.mime_type);
+        if (request.time_range != null) {
+            request.time_range.add_response_header(request.msg);
+        }
+    }
+
+    public virtual HTTPResponse render_body (HTTPRequest request)
+            throws HTTPRequestError {
+        weak MediaItem item = request.item;
+        Element src = item.create_stream_source ();
+
+        if (src == null) {
+            throw new HTTPRequestError.NOT_FOUND ("Not found");
+        }
+
+        src = this.create_source (item, src);
+        return new LiveResponse (request.server,
+                                 request.msg,
+                                 "RygelLiveResponse",
+                                 src,
+                                 request.time_range);
+    }
 }
 



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