core: Separate class for HTTP GET request
authorZeeshan Ali (Khattak) <zeeshanak@gnome.org>
Thu, 28 Jan 2010 09:41:30 +0000 (11:41 +0200)
committerZeeshan Ali (Khattak) <zeeshanak@gnome.org>
Wed, 3 Feb 2010 13:38:49 +0000 (15:38 +0200)
More generalization so that we can easily implement request and request
handler classes for other HTTP methods (e.g POST). This change also
implies renaming of HTTPRequestHander to HTTPGetHandler.

src/rygel/Makefile.am
src/rygel/rygel-http-byte-seek.vala
src/rygel/rygel-http-get-handler.vala [moved from src/rygel/rygel-http-request-handler.vala with 87% similarity]
src/rygel/rygel-http-get.vala [new file with mode: 0644]
src/rygel/rygel-http-identity-handler.vala
src/rygel/rygel-http-request.vala
src/rygel/rygel-http-server.vala
src/rygel/rygel-http-time-seek.vala
src/rygel/rygel-http-transcode-handler.vala

index 2f72214..50caa70 100644 (file)
@@ -54,7 +54,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-http-get-handler.vala \
+                   rygel-http-get.vala \
                    rygel-http-identity-handler.vala \
                    rygel-http-transcode-handler.vala \
                    rygel-http-seek.vala \
index a1727dc..ad47dae 100644 (file)
@@ -22,7 +22,7 @@
  */
 
 internal class Rygel.HTTPByteSeek : Rygel.HTTPSeek {
-    public HTTPByteSeek (HTTPRequest request) throws HTTPSeekError {
+    public HTTPByteSeek (HTTPGet request) throws HTTPSeekError {
         string range, pos;
         string[] range_tokens;
         int64 start = 0, total_length;
@@ -77,7 +77,7 @@ internal class Rygel.HTTPByteSeek : Rygel.HTTPSeek {
               total_length);
     }
 
-    public static bool needed (HTTPRequest request) {
+    public static bool needed (HTTPGet request) {
         return (request.item.size > 0 &&
                 request.handler is HTTPIdentityHandler) ||
                (request.thumbnail != null && request.thumbnail.size > 0);
similarity index 87%
rename from src/rygel/rygel-http-request-handler.vala
rename to src/rygel/rygel-http-get-handler.vala
index f958c22..fe9c354 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009 Nokia Corporation.
+ * Copyright (C) 2008-2010 Nokia Corporation.
  *
  * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
  *                               <zeeshan.ali@nokia.com>
 using GUPnP;
 
 /**
- * HTTP request handler interface.
+ * HTTP GET request handler interface.
  */
-internal abstract class Rygel.HTTPRequestHandler: GLib.Object {
+internal abstract class Rygel.HTTPGetHandler: GLib.Object {
     public Cancellable cancellable { get; set; }
 
     // Add response headers.
-    public virtual void add_response_headers (HTTPRequest request)
+    public virtual void add_response_headers (HTTPGet request)
                                               throws HTTPRequestError {
         var mode = request.msg.request_headers.get ("transferMode.dlna.org");
         if (mode != null) {
@@ -59,10 +59,10 @@ internal abstract class Rygel.HTTPRequestHandler: GLib.Object {
     }
 
     // Create an HTTPResponse object that will render the body.
-    public abstract HTTPResponse render_body (HTTPRequest request)
+    public abstract HTTPResponse render_body (HTTPGet request)
                                               throws HTTPRequestError;
 
     protected abstract DIDLLiteResource add_resource (DIDLLiteItem didl_item,
-                                                      HTTPRequest  request)
+                                                      HTTPGet      request)
                                                       throws Error;
 }
diff --git a/src/rygel/rygel-http-get.vala b/src/rygel/rygel-http-get.vala
new file mode 100644 (file)
index 0000000..a3596e5
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008-2010 Nokia Corporation.
+ * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
+ *                               <zeeshan.ali@nokia.com>
+ *         Jorn Baayen <jorn.baayen@gmail.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 Gst;
+
+/**
+ * Responsible for handling HTTP GET & HEAD client requests.
+ */
+internal class Rygel.HTTPGet : HTTPRequest {
+    public Thumbnail thumbnail;
+    public HTTPSeek seek;
+
+    private int thumbnail_index;
+
+    public HTTPGetHandler handler;
+
+    public HTTPGet (HTTPServer                http_server,
+                    Soup.Server               server,
+                    Soup.Message              msg,
+                    HashTable<string,string>? query) {
+        base (http_server, server, msg, query);
+
+        this.thumbnail_index = -1;
+    }
+
+    protected override async void handle () {
+        this.server.pause_message (this.msg);
+
+        yield base.handle ();
+
+        var header = this.msg.request_headers.get (
+                                        "getcontentFeatures.dlna.org");
+
+        /* We only entertain 'HEAD' and 'GET' requests */
+        if ((this.msg.method != "HEAD" && this.msg.method != "GET") ||
+            (header != null && header != "1")) {
+            this.handle_error (
+                        new HTTPRequestError.BAD_REQUEST ("Invalid Request"));
+            return;
+        }
+
+        try {
+            if (uri.transcode_target != null) {
+                var transcoder = this.http_server.get_transcoder (
+                                                        uri.transcode_target);
+                this.handler = new HTTPTranscodeHandler (transcoder,
+                                                         this.cancellable);
+            }
+        } catch (Error err) {
+            warning ("Failed to parse query: %s", err.message);
+        }
+
+        if (this.handler == null) {
+            this.handler = new HTTPIdentityHandler (this.cancellable);
+        }
+
+        yield this.handle_item_request ();
+    }
+
+    protected override async void find_item () {
+        yield base.find_item ();
+
+        if (this.uri.thumbnail_index >= 0) {
+            this.thumbnail = this.item.thumbnails.get (
+                                        this.uri.thumbnail_index);
+        }
+    }
+
+    private async void handle_item_request () {
+        try {
+            if (HTTPTimeSeek.needed (this)) {
+                this.seek = new HTTPTimeSeek (this);
+            } else if (HTTPByteSeek.needed (this)) {
+                this.seek = new HTTPByteSeek (this);
+            }
+
+            // Add headers
+            this.handler.add_response_headers (this);
+            debug ("Following HTTP headers appended to response:");
+            this.msg.response_headers.foreach ((name, value) => {
+                    debug ("%s : %s", name, value);
+            });
+
+            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;
+            }
+
+            var response = this.handler.render_body (this);
+
+            yield response.run ();
+
+            this.end (Soup.KnownStatusCode.NONE);
+        } catch (Error error) {
+            this.handle_error (error);
+        }
+    }
+
+    protected override void handle_error (Error error) {
+        this.server.unpause_message (this.msg);
+
+        base.handle_error (error);
+    }
+}
+
index 7152d26..9db8625 100644 (file)
 using GUPnP;
 
 // An HTTP request handler that passes the item content through as is.
-internal class Rygel.HTTPIdentityHandler : Rygel.HTTPRequestHandler {
+internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
 
     public HTTPIdentityHandler (Cancellable? cancellable) {
         this.cancellable = cancellable;
     }
 
-    public override void add_response_headers (HTTPRequest request)
+    public override void add_response_headers (HTTPGet request)
                                                throws HTTPRequestError {
         if (request.thumbnail != null) {
             request.msg.response_headers.append ("Content-Type",
@@ -48,7 +48,7 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPRequestHandler {
         base.add_response_headers (request);
     }
 
-    public override HTTPResponse render_body (HTTPRequest request)
+    public override HTTPResponse render_body (HTTPGet request)
                                               throws HTTPRequestError {
         try {
             return this.render_body_real (request);
@@ -58,7 +58,7 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPRequestHandler {
     }
 
     protected override DIDLLiteResource add_resource (DIDLLiteItem didl_item,
-                                                      HTTPRequest  request)
+                                                      HTTPGet      request)
                                                       throws Error {
         var protocol = request.http_server.get_protocol ();
 
@@ -69,7 +69,7 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPRequestHandler {
         }
     }
 
-    private HTTPResponse render_body_real (HTTPRequest request) throws Error {
+    private HTTPResponse render_body_real (HTTPGet request) throws Error {
         if (request.thumbnail != null) {
             return new SeekableResponse (request.server,
                                          request.msg,
index 4887b7c..afe1dd9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009 Nokia Corporation.
+ * Copyright (C) 2008-2010 Nokia Corporation.
  * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
  *
  * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
@@ -23,8 +23,6 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-using Gst;
-
 internal errordomain Rygel.HTTPRequestError {
     UNACCEPTABLE = Soup.KnownStatusCode.NOT_ACCEPTABLE,
     BAD_REQUEST = Soup.KnownStatusCode.BAD_REQUEST,
@@ -32,9 +30,9 @@ internal errordomain Rygel.HTTPRequestError {
 }
 
 /**
- * Responsible for handling HTTP client requests.
+ * Base class for HTTP client requests.
  */
-internal class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
+internal abstract class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
     public unowned HTTPServer http_server;
     private MediaContainer root_container;
     public Soup.Server server;
@@ -43,13 +41,8 @@ internal class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
 
     public Cancellable cancellable { get; set; }
 
-    private string item_id;
-    private int thumbnail_index;
+    protected HTTPItemURI uri;
     public MediaItem item;
-    public Thumbnail thumbnail;
-    public HTTPSeek seek;
-
-    public HTTPRequestHandler handler;
 
     public HTTPRequest (HTTPServer                http_server,
                         Soup.Server               server,
@@ -61,111 +54,49 @@ internal class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
         this.server = server;
         this.msg = msg;
         this.query = query;
-        this.thumbnail_index = -1;
     }
 
     public async void run () {
-        this.server.pause_message (this.msg);
-
-        var header = this.msg.request_headers.get (
-                                        "getcontentFeatures.dlna.org");
-
-        /* We only entertain 'HEAD' and 'GET' requests */
-        if ((this.msg.method != "HEAD" && this.msg.method != "GET") ||
-            (header != null && header != "1")) {
-            this.handle_error (
-                        new HTTPRequestError.BAD_REQUEST ("Invalid Request"));
-            return;
-        }
-
-        try {
-            var uri = new HTTPItemURI.from_string (this.msg.uri.path,
-                                                   this.http_server.path_root);
-
-            this.item_id = uri.item_id;
-            this.thumbnail_index = uri.thumbnail_index;
-            if (uri.transcode_target != null) {
-                var transcoder = this.http_server.get_transcoder (
-                                                        uri.transcode_target);
-                this.handler = new HTTPTranscodeHandler (transcoder,
-                                                         this.cancellable);
-            }
-        } catch (Error err) {
-            warning ("Failed to parse query: %s", err.message);
-        }
+        yield this.handle ();
+    }
 
+    protected virtual async void handle () {
+        this.uri = new HTTPItemURI.from_string (this.msg.uri.path,
+                                                this.http_server.path_root);
 
-        if (this.item_id == null) {
+        if (this.uri.item_id == null) {
             this.handle_error (new HTTPRequestError.NOT_FOUND ("Not Found"));
+
             return;
         }
 
-        if (this.handler == null) {
-            this.handler = new HTTPIdentityHandler (this.cancellable);
-        }
         yield this.find_item ();
     }
 
-    public async void find_item () {
+    protected virtual async void find_item () {
         // Fetch the requested item
         MediaObject media_object;
         try {
-            media_object = yield this.root_container.find_object (this.item_id,
-                                                                  null);
+            media_object = yield this.root_container.find_object (
+                                        this.uri.item_id,
+                                        null);
         } catch (Error err) {
             this.handle_error (err);
+
             return;
         }
 
         if (media_object == null || !(media_object is MediaItem)) {
             this.handle_error (new HTTPRequestError.NOT_FOUND (
                                         "requested item '%s' not found",
-                                        this.item_id));
+                                        this.uri.item_id));
             return;
         }
 
         this.item = (MediaItem) media_object;
-
-        if (this.thumbnail_index >= 0) {
-            this.thumbnail = this.item.thumbnails.get (this.thumbnail_index);
-        }
-
-        yield this.handle_item_request ();
-    }
-
-    private async void handle_item_request () {
-        try {
-            if (HTTPTimeSeek.needed (this)) {
-                this.seek = new HTTPTimeSeek (this);
-            } else if (HTTPByteSeek.needed (this)) {
-                this.seek = new HTTPByteSeek (this);
-            }
-
-            // Add headers
-            this.handler.add_response_headers (this);
-            debug ("Following HTTP headers appended to response:");
-            this.msg.response_headers.foreach ((name, value) => {
-                    debug ("%s : %s", name, value);
-            });
-
-            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;
-            }
-
-            var response = this.handler.render_body (this);
-
-            yield response.run ();
-
-            this.end (Soup.KnownStatusCode.NONE);
-        } catch (Error error) {
-            this.handle_error (error);
-        }
     }
 
-    private void handle_error (Error error) {
+    protected virtual void handle_error (Error error) {
         warning ("%s", error.message);
 
         uint status;
@@ -175,11 +106,10 @@ internal class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
             status = Soup.KnownStatusCode.NOT_FOUND;
         }
 
-        this.server.unpause_message (this.msg);
         this.end (status);
     }
 
-    public void end (uint status) {
+    protected void end (uint status) {
         if (status != Soup.KnownStatusCode.NONE) {
             this.msg.set_status (status);
         }
index 33ad342..a0cef18 100644 (file)
@@ -169,7 +169,7 @@ internal class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
                 debug ("%s : %s", name, value);
         });
 
-        var request = new HTTPRequest (this, server, msg, query);
+        var request = new HTTPGet (this, server, msg, query);
 
         request.completed += this.on_request_completed;
         this.requests.add (request);
index 970bab6..a855f3c 100644 (file)
@@ -31,7 +31,7 @@ internal class Rygel.HTTPTimeSeek : Rygel.HTTPSeek {
     // and not
     //
     // TimeSeekRange.dlna.org : npt=10:19:25.7-13:23:33.6
-    public HTTPTimeSeek (HTTPRequest request) throws HTTPSeekError {
+    public HTTPTimeSeek (HTTPGet request) throws HTTPSeekError {
         string range, time;
         string[] range_tokens;
         int64 start = 0;
@@ -80,7 +80,7 @@ internal class Rygel.HTTPTimeSeek : Rygel.HTTPSeek {
               duration);
     }
 
-    public static bool needed (HTTPRequest request) {
+    public static bool needed (HTTPGet request) {
         return request.item.duration > 0 &&
                (request.handler is HTTPTranscodeHandler ||
                 (request.thumbnail == null &&
index 149e5e0..1b732de 100644 (file)
@@ -27,7 +27,7 @@ using GUPnP;
 /**
  * The handler for HTTP transcoding requests.
  */
-internal class Rygel.HTTPTranscodeHandler : HTTPRequestHandler {
+internal class Rygel.HTTPTranscodeHandler : HTTPGetHandler {
     private Transcoder transcoder;
 
     public HTTPTranscodeHandler (Transcoder   transcoder,
@@ -36,7 +36,7 @@ internal class Rygel.HTTPTranscodeHandler : HTTPRequestHandler {
         this.cancellable = cancellable;
     }
 
-    public override void add_response_headers (HTTPRequest request)
+    public override void add_response_headers (HTTPGet request)
                                                throws HTTPRequestError {
         request.msg.response_headers.append ("Content-Type",
                                              this.transcoder.mime_type);
@@ -48,7 +48,7 @@ internal class Rygel.HTTPTranscodeHandler : HTTPRequestHandler {
         base.add_response_headers (request);
     }
 
-    public override HTTPResponse render_body (HTTPRequest request)
+    public override HTTPResponse render_body (HTTPGet request)
                                               throws HTTPRequestError {
         var item = request.item;
         var src = item.create_stream_source ();
@@ -71,7 +71,7 @@ internal class Rygel.HTTPTranscodeHandler : HTTPRequestHandler {
     }
 
     protected override DIDLLiteResource add_resource (DIDLLiteItem didl_item,
-                                                      HTTPRequest  request)
+                                                      HTTPGet      request)
                                                       throws Error {
         return this.transcoder.add_resource (didl_item,
                                              request.item,