* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// This module contains the common code between the test cases for
-// HTTPResponse subclasses.
using Soup;
public errordomain Rygel.TestError {
TIMEOUT
}
-public abstract class Rygel.HTTPResponseTest : GLib.Object {
- public const long MAX_BYTES = 1024;
+public errordomain Rygel.HTTPRequestError {
+ NOT_FOUND = Soup.Status.NOT_FOUND
+}
+
+public class Rygel.HTTPResponseTest : GLib.Object {
+ public const long MAX_BYTES = 102400;
protected HTTPServer server;
protected HTTPClient client;
private bool server_done;
private bool client_done;
+ private MediaItem item;
+
private MainLoop main_loop;
protected Cancellable cancellable;
private Error error;
+ public static int main (string[] args) {
+ try {
+ var test = new HTTPResponseTest.complete ();
+ test.run ();
+
+ test = new HTTPResponseTest.abort ();
+ test.run ();
+ } catch (TestError.SKIP error) {
+ return error.code;
+ } catch (Error error) {
+ critical ("%s", error.message);
+
+ return -1;
+ }
+
+ return 0;
+ }
+
public HTTPResponseTest (Cancellable? cancellable = null) throws Error {
this.cancellable = cancellable;
public HTTPResponseTest.complete () throws Error {
this ();
+
+ this.item = new MediaItem.fixed_size ();
}
public HTTPResponseTest.abort () throws Error {
this (new Cancellable ());
+
+ this.item = new MediaItem ();
}
public virtual void run () throws Error {
}
}
- internal abstract HTTPResponse create_response (Soup.Message msg)
- throws Error;
+ private HTTPResponse create_response (Soup.Message msg) throws Error {
+ var seek = null as HTTPSeek;
+
+ if (!this.item.is_live_stream ()) {
+ seek = new HTTPByteSeek (0, MAX_BYTES - 1, this.item.size);
+ msg.response_headers.set_content_length (seek.length);
+ }
+
+ var request = new HTTPGet (this.server.context.server,
+ msg,
+ this.item,
+ seek,
+ this.cancellable);
+ var handler = new HTTPGetHandler (this.cancellable);
+ var src = this.item.create_stream_source ();
+
+ return new HTTPResponse (request, handler, src);
+ }
private void on_client_completed (StateMachine client) {
if (this.server_done) {
if (active) {
this.cancellable = new Cancellable ();
- this.cancellable.cancelled += this.on_cancelled;
+ this.cancellable.cancelled.connect (this.on_cancelled);
}
}
if (bytes_received >= this.total_bytes &&
this.cancellable != null) {
- bytes_received = bytes_received.clamp (0, this.total_bytes);
this.cancellable.cancel ();
}
});
this.context.session.queue_message (this.msg, (session, msg) => {
- assert (cancellable == null || bytes_received == this.total_bytes);
+ assert (cancellable != null || bytes_received == this.total_bytes);
run_continue ();
});
private void on_cancelled (Cancellable cancellable) {
this.context.session.cancel_message (this.msg,
- KnownStatusCode.CANCELLED);
+ Status.CANCELLED);
this.completed ();
}
}
+
+public class Rygel.HTTPSeek : GLib.Object {
+ public int64 start { get; private set; }
+ public int64 stop { get; private set; }
+ public int64 length { get; private set; }
+ public int64 total_length { get; private set; }
+
+ public HTTPSeek (int64 start, int64 stop, int64 total_length) {
+ this.start = start;
+ this.stop = stop;
+ this.total_length = total_length;
+
+ this.length = stop - start + 1;
+ }
+}
+
+public class Rygel.HTTPByteSeek : Rygel.HTTPSeek {
+ public HTTPByteSeek (int64 start, int64 stop, int64 total_length) {
+ base (start, stop, total_length);
+ }
+}
+
+public class Rygel.HTTPTimeSeek : Rygel.HTTPSeek {
+ public HTTPTimeSeek (int64 start, int64 stop, int64 total_length) {
+ base (start, stop, total_length);
+ }
+}
+
+public class Rygel.HTTPGet : GLib.Object {
+ public Soup.Server server;
+ public Soup.Message msg;
+
+ public Cancellable cancellable;
+
+ public MediaItem item;
+
+ internal HTTPSeek seek;
+
+ public HTTPGet (Soup.Server server,
+ Soup.Message msg,
+ MediaItem item,
+ HTTPSeek? seek,
+ Cancellable? cancellable) {
+ this.server = server;
+ this.msg = msg;
+ this.item = item;
+ this.seek = seek;
+ this.cancellable = cancellable;
+ this.msg.response_headers.set_encoding (Soup.Encoding.EOF);
+ this.msg.set_status (Soup.Status.OK);
+ }
+}
+
+public class Rygel.HTTPGetHandler : GLib.Object {
+ public Cancellable cancellable;
+
+ public HTTPGetHandler (Cancellable? cancellable) {
+ this.cancellable = cancellable;
+ }
+}
+
+internal class Rygel.TestDataSource : Rygel.DataSource, Object {
+ private long block_size;
+ private long buffers;
+ private uint64 data_sent;
+ private bool frozen;
+
+ public TestDataSource (long block_size, long buffers) {
+ this.block_size = block_size;
+ this.buffers = buffers;
+ this.data_sent = 0;
+ }
+
+ public void start (HTTPSeek? seek) throws Error {
+ Idle.add ( () => {
+ if (frozen) {
+ return false;
+ }
+
+ var data = new uint8[block_size];
+ this.data_sent += block_size;
+ if (this.data_sent > HTTPResponseTest.MAX_BYTES) {
+ this.done ();
+
+ return false;
+ }
+
+ this.data_available (data);
+
+ return true;
+ });
+ }
+
+ public void freeze () {
+ this.frozen = true;
+ }
+
+ public void thaw () {
+ if (!this.frozen) {
+ return;
+ }
+
+ this.frozen = false;
+
+ try {
+ this.start (null);
+ } catch (GLib.Error error) {
+ assert_not_reached ();
+ }
+ }
+
+ public void stop () {
+ this.freeze ();
+ }
+}
+
+public class Rygel.MediaItem {
+ private static const long BLOCK_SIZE = HTTPResponseTest.MAX_BYTES / 16;
+ private static const long MAX_BUFFERS = 25;
+
+ public int64 size {
+ get {
+ return MAX_BUFFERS * BLOCK_SIZE;
+ }
+ }
+
+ private DataSource src;
+ bool is_live = false;
+
+ public MediaItem () {
+ this.src = new TestDataSource (BLOCK_SIZE, MAX_BUFFERS);
+ this.is_live = true;
+ }
+
+ public MediaItem.fixed_size () {
+ this ();
+ }
+
+ public DataSource? create_stream_source () {
+ return this.src;
+ }
+
+ public bool is_live_stream () {
+ return this.is_live;
+ }
+}