2 * Copyright (C) 2010 Nokia Corporation.
4 * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
5 * <zeeshan.ali@nokia.com>
7 * This file is part of Rygel.
9 * Rygel is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * Rygel is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 public errordomain Rygel.TestError {
32 public errordomain Rygel.ClientHacksError {
36 public class Rygel.ClientHacks {
37 public static ClientHacks create (Message? message) throws Error {
38 throw new ClientHacksError.NA ("");
41 public void apply (MediaItem item) {
45 public class Rygel.HTTPPostTest : GLib.Object {
46 protected HTTPServer server;
47 protected HTTPClient client;
49 private bool server_done;
50 private bool client_done;
52 private MainLoop main_loop;
56 public static int main (string[] args) {
58 var test = new HTTPPostTest ();
61 } catch (TestError.SKIP error) {
63 } catch (Error error) {
64 critical ("%s", error.message);
72 public HTTPPostTest () throws Error {
73 this.server = new HTTPServer ();
74 this.client = new HTTPClient (this.server.context,
76 this.main_loop = new MainLoop (null, false);
79 public virtual void run () throws Error {
81 var file = File.new_for_uri (MediaItem.URI);
82 FileUtils.remove (file.get_path ());
83 Timeout.add_seconds (3, this.on_timeout);
84 this.server.message_received.connect (this.on_message_received);
85 this.client.completed.connect (this.on_client_completed);
87 this.client.run.begin ();
89 this.main_loop.run ();
91 if (this.error != null) {
96 private HTTPRequest create_request (Soup.Message msg) throws Error {
97 return new HTTPPost (this.server,
98 this.server.context.server,
102 private void on_client_completed (StateMachine client) {
103 this.client_done = true;
104 this.check_and_exit.begin ();
107 private void on_message_received (HTTPServer server,
109 this.handle_client_message.begin (msg);
112 private async void handle_client_message (Soup.Message msg) {
114 var request = this.create_request (msg);
116 yield request.run ();
118 assert ((request as HTTPPost).item != null);
120 this.server_done = true;
121 this.check_and_exit.begin ();
122 } catch (Error error) {
124 this.main_loop.quit ();
130 private bool on_timeout () {
131 this.error = new TestError.TIMEOUT ("Timeout");
132 this.main_loop.quit ();
137 private async void check_and_exit () {
138 if (!(this.server_done && this.client_done)) {
143 var file = this.server.root_container.item.file;
144 var stream = yield file.read_async (Priority.HIGH, null);
145 var buffer = new uint8[HTTPClient.LENGTH];
147 yield stream.read_async (buffer, Priority.HIGH, null);
149 for (var i = 0; i < HTTPClient.LENGTH; i++) {
150 assert (buffer[i] == this.client.content[i]);
152 } catch (Error error) {
156 this.main_loop.quit ();
160 public class Rygel.HTTPServer : GLib.Object {
161 private const string SERVER_PATH = "/RygelHTTPServer/Rygel/Test";
162 public string path_root {
168 public MediaContainer root_container;
169 public GUPnP.Context context;
173 var item_uri = new HTTPItemURI (this.root_container.item,
176 return item_uri.to_string ();
180 public signal void message_received (Soup.Message message);
182 public HTTPServer () throws TestError {
184 this.context = new GUPnP.Context (null, "lo", 0);
185 } catch (Error error) {
186 throw new TestError.SKIP ("Network context not available");
189 assert (this.context != null);
190 assert (this.context.host_ip != null);
191 assert (this.context.port > 0);
193 context.server.request_started.connect (this.on_request_started);
195 this.root_container = new MediaContainer ();
198 private void on_request_started (Soup.Server server,
200 Soup.ClientContext client) {
201 msg.got_headers.connect (this.on_got_headers);
204 private void on_got_headers (Soup.Message msg) {
205 this.message_received (msg);
208 public Transcoder get_transcoder (string target) throws Error {
209 if (target == "MP3") {
210 return new Transcoder ("mp3");
212 throw new HTTPRequestError.NOT_FOUND (
213 "No transcoder available for target format '%s'",
218 public class Rygel.HTTPClient : GLib.Object, StateMachine {
219 public const size_t LENGTH = 1024;
221 public uint8[] content;
223 public GUPnP.Context context;
224 public Soup.Message msg;
226 public Cancellable cancellable { get; set; }
228 public HTTPClient (GUPnP.Context context,
230 this.context = context;
231 this.content = new uint8[1024];
233 this.msg = new Soup.Message ("POST", uri);
234 assert (this.msg != null);
237 public async void run () {
238 SourceFunc run_continue = run.callback;
240 this.msg.request_body.append (MemoryUse.COPY, content);
242 this.context.session.queue_message (this.msg, (session, msg) => {
252 public class Rygel.MediaContainer : Rygel.MediaObject {
253 public const string ITEM_ID = "TestItem";
255 public signal void container_updated (MediaContainer container);
257 public string id = "TesContainer";
258 public MediaItem item;
260 private FileMonitor monitor;
262 public MediaContainer () {
263 this.item = new MediaItem (ITEM_ID, this);
265 this.monitor = this.item.file.monitor_file (FileMonitorFlags.NONE);
266 this.monitor.changed.connect (this.on_file_changed);
269 public async MediaObject? find_object (string item_id,
270 Cancellable? cancellable)
272 SourceFunc find_object_continue = find_object.callback;
274 find_object_continue ();
281 if (item_id == ITEM_ID) {
288 public void on_file_changed (FileMonitor monitor,
291 FileMonitorEvent event_type) {
292 this.item.place_holder = false;
293 this.container_updated (this);
297 public class Rygel.MediaItem : Rygel.MediaObject {
298 public const string URI = "file:///tmp/rygel-upload-test.wav";
300 public weak MediaContainer parent;
303 public long size = 1024;
304 public long duration = 1024;
305 public ArrayList<string> uris = new ArrayList<string> ();
307 public bool place_holder = true;
311 public MediaItem.for_visual_item () {}
313 public MediaItem (string id, MediaContainer parent) {
315 this.parent = parent;
317 this.file = File.new_for_uri (URI);
322 this.file.delete (null);
323 } catch (GLib.Error error) {
324 assert_not_reached ();
328 public async File? get_writable (Cancellable? cancellable) throws Error {
329 SourceFunc get_writable_continue = get_writable.callback;
332 get_writable_continue ();
343 internal class Rygel.HTTPResponse : Rygel.StateMachine, GLib.Object {
344 public Cancellable cancellable { get; set; }
346 private Soup.Message msg;
347 private Soup.Server server;
349 public HTTPResponse (HTTPPost get_request) {
350 this.msg = get_request.msg;
351 this.server = get_request.server;
354 public async void run () {
355 SourceFunc run_continue = run.callback;
365 this.msg.set_status (Soup.KnownStatusCode.OK);
366 this.server.unpause_message (msg);
372 public class Rygel.ItemRemovalQueue: GLib.Object {
373 public static ItemRemovalQueue get_default () {
374 return new ItemRemovalQueue ();
377 public bool dequeue (MediaItem item) {
381 public async void remove_now (MediaItem item, Cancellable? cancellable) {
382 Idle.add (remove_now.callback);
388 public class Rygel.MediaObject : GLib.Object {
389 public string mime_type = "";
392 public class Rygel.Thumbnail : GLib.Object {
393 public string file_extension;
396 public class Rygel.VisualItem : Rygel.MediaItem {
397 public ArrayList<Thumbnail> thumbnails = new ArrayList<Thumbnail> ();
399 public VisualItem () {
400 base.for_visual_item();
404 private class Rygel.Subtitle : GLib.Object {
405 public string caption_type;
408 private class Rygel.VideoItem : Rygel.VisualItem {
409 public ArrayList<Subtitle> subtitles = new ArrayList<Subtitle> ();
412 public errordomain Rygel.ContentDirectoryError {
416 public class Rygel.Transcoder : GLib.Object {
417 public string extension { get; protected set; }
419 public Transcoder (string extension) {
420 this.extension = extension;