1 /* GStreamer unit tests for the curlhttpsrc element
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Library General Public
5  * License as published by the Free Software Foundation; either
6  * version 2 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Library General Public License for more details.
12  *
13  * You should have received a copy of the GNU Library General Public
14  * License along with this library; if not, write to the
15  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
16  * Boston, MA 02110-1301, USA.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22 
23 #include <stdlib.h>
24 
25 #include <gio/gio.h>
26 #include <glib.h>
27 #include <glib/gprintf.h>
28 
29 #include <gst/check/gstcheck.h>
30 
31 gboolean redirect = TRUE;
32 
33 static const char **cookies = NULL;
34 
35 typedef struct _GioHttpServer
36 {
37   guint16 port;
38   char *root;
39   GSocketService *service;
40   guint64 delay;
41 } GioHttpServer;
42 
43 typedef struct _HttpRequest
44 {
45   gchar *method;
46   gchar *version;
47   gchar *path;
48   gchar *query;
49 } HttpRequest;
50 
51 static GioHttpServer *run_server (void);
52 static void stop_server (GioHttpServer * server);
53 static guint16 get_port_from_server (GioHttpServer * server);
54 
55 static const gchar *STATUS_OK = "200 OK";
56 static const gchar *STATUS_MOVED_PERMANENTLY = "301 Moved Permanently";
57 static const gchar *STATUS_MOVED_TEMPORARILY = "302 Moved Temporarily";
58 static const gchar *STATUS_TEMPORARY_REDIRECT = "307 Temporary Redirect";
59 static const gchar *STATUS_FORBIDDEN = "403 Forbidden";
60 static const gchar *STATUS_NOT_FOUND = "404 Not Found";
61 
62 static void
do_get(GioHttpServer * server,const HttpRequest * req,GOutputStream * out)63 do_get (GioHttpServer * server, const HttpRequest * req, GOutputStream * out)
64 {
65   gboolean send_error_doc = FALSE;
66   int buflen = 1024;
67   const gchar *status = STATUS_OK;
68   const gchar *content_type = "application/octet-stream";
69   GString *s;
70   char *buf = NULL;
71   gsize written = 0;
72 
73   GST_DEBUG ("%s request: \"%s\"", req->method, req->path);
74 
75   if (!strcmp (req->path, "/301"))
76     status = STATUS_MOVED_PERMANENTLY;
77   else if (!strcmp (req->path, "/302"))
78     status = STATUS_MOVED_TEMPORARILY;
79   else if (!strcmp (req->path, "/307"))
80     status = STATUS_TEMPORARY_REDIRECT;
81   else if (!strcmp (req->path, "/403"))
82     status = STATUS_FORBIDDEN;
83   else if (!strcmp (req->path, "/404"))
84     status = STATUS_NOT_FOUND;
85   else if (!strcmp (req->path, "/404-with-data")) {
86     status = STATUS_NOT_FOUND;
87     send_error_doc = TRUE;
88   }
89   s = g_string_new ("HTTP/");
90   g_string_append_printf (s, "%s %s\r\n", req->version, status);
91 
92   if (g_str_has_prefix (status, "30")) {
93     g_string_append_printf (s, "Location: %s-redirected\r\n", req->path);
94   }
95 
96   if (status == STATUS_OK || send_error_doc) {
97     g_string_append_printf (s, "Content-Type: %s\r\n", content_type);
98     g_string_append_printf (s, "Content-Length: %lu\r\n", (gulong) buflen);
99     if (!g_strcmp0 (req->method, "GET")) {
100       buf = g_malloc (buflen);
101       memset (buf, 0, buflen);
102     }
103   }
104 
105   g_string_append (s, "\r\n");
106   GST_DEBUG ("Response headers: %lu\n%s\n********\n", s->len, s->str);
107   g_output_stream_write_all (out, s->str, s->len, &written, NULL, NULL);
108   fail_if (written != s->len);
109   g_string_free (s, TRUE);
110   if (buf) {
111     g_output_stream_write_all (out, buf, buflen, &written, NULL, NULL);
112     fail_if (written != buflen);
113     g_free (buf);
114   }
115 }
116 
117 static void
send_error(GOutputStream * out,int error_code,const gchar * reason)118 send_error (GOutputStream * out, int error_code, const gchar * reason)
119 {
120   gchar *res;
121 
122   res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n"
123       "<html><head><title>%d %s</title></head>"
124       "<body>%s</body></html>", error_code, reason, error_code, reason, reason);
125   g_output_stream_write_all (out, res, strlen (res), NULL, NULL, NULL);
126   g_free (res);
127 }
128 
129 static gboolean
server_callback(GThreadedSocketService * service,GSocketConnection * connection,GSocketListener * listener,gpointer user_data)130 server_callback (GThreadedSocketService * service,
131     GSocketConnection * connection,
132     GSocketListener * listener, gpointer user_data)
133 {
134   GioHttpServer *server = (GioHttpServer *) user_data;
135   GOutputStream *out;
136   GInputStream *in;
137   GDataInputStream *data = NULL;
138   char *line = NULL, *escaped, *tmp;
139   HttpRequest req;
140 
141   in = g_io_stream_get_input_stream (G_IO_STREAM (connection));
142   out = g_io_stream_get_output_stream (G_IO_STREAM (connection));
143 
144   data = g_data_input_stream_new (in);
145 
146   g_data_input_stream_set_newline_type (data, G_DATA_STREAM_NEWLINE_TYPE_ANY);
147 
148   line = g_data_input_stream_read_line (data, NULL, NULL, NULL);
149 
150   if (line == NULL) {
151     send_error (out, 400, "Invalid request");
152     goto out;
153   }
154 
155   tmp = strchr (line, ' ');
156   if (!tmp) {
157     send_error (out, 400, "Invalid request");
158     goto out;
159   }
160   req.method = line;
161   *tmp = '\0';
162   escaped = tmp + 1;
163 
164   req.version = NULL;
165   tmp = strchr (escaped, ' ');
166   if (tmp != NULL) {
167     *tmp = 0;
168     req.version = tmp + 6;      /* skip "HTTP/" from version field */
169   }
170 
171   req.query = strchr (escaped, '?');
172   if (req.query != NULL) {
173     *req.query = '\0';
174     req.query++;
175   }
176 
177   req.path = g_uri_unescape_string (escaped, NULL);
178 
179   GST_DEBUG ("%s %s HTTP/%s", req.method, req.path, req.version);
180 
181   if (server->delay) {
182     g_usleep (server->delay);
183   }
184   do_get (server, &req, out);
185 
186   g_free (req.path);
187 out:
188   g_free (line);
189   if (data)
190     g_object_unref (data);
191 
192   return TRUE;
193 }
194 
195 static guint16
get_port_from_server(GioHttpServer * server)196 get_port_from_server (GioHttpServer * server)
197 {
198   fail_if (server == NULL);
199   return server->port;
200 }
201 
202 static GioHttpServer *
run_server(void)203 run_server (void)
204 {
205   GioHttpServer *server;
206   GError *error = NULL;
207 
208   server = g_slice_new0 (GioHttpServer);
209   server->service = g_threaded_socket_service_new (10);
210   server->port =
211       g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (server->service),
212       NULL, &error);
213   fail_if (server->port == 0);
214   g_signal_connect (server->service, "run", G_CALLBACK (server_callback),
215       server);
216 
217   GST_DEBUG ("HTTP server listening on port %u", server->port);
218 
219   /* check if we can connect to our local http server */
220   {
221     GSocketConnection *conn;
222     GSocketClient *client;
223 
224     client = g_socket_client_new ();
225     g_socket_client_set_timeout (client, 2);
226     conn =
227         g_socket_client_connect_to_host (client, "127.0.0.1", server->port,
228         NULL, NULL);
229     if (conn == NULL) {
230       GST_INFO ("Couldn't connect to 127.0.0.1:%u", server->port);
231       g_object_unref (client);
232       g_slice_free (GioHttpServer, server);
233       return NULL;
234     }
235 
236     g_object_unref (conn);
237     g_object_unref (client);
238   }
239 
240   return server;
241 }
242 
243 static void
stop_server(GioHttpServer * server)244 stop_server (GioHttpServer * server)
245 {
246   fail_if (server == NULL);
247   GST_DEBUG ("Stopping server...");
248   g_socket_service_stop (server->service);
249   g_socket_listener_close (G_SOCKET_LISTENER (server->service));
250   g_object_unref (server->service);
251   g_slice_free (GioHttpServer, server);
252   GST_DEBUG ("Server stopped");
253 }
254 
255 static void
handoff_cb(GstElement * fakesink,GstBuffer * buf,GstPad * pad,GstBuffer ** p_outbuf)256 handoff_cb (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
257     GstBuffer ** p_outbuf)
258 {
259   GST_LOG ("handoff, buf = %p", buf);
260   if (*p_outbuf == NULL)
261     *p_outbuf = gst_buffer_ref (buf);
262 }
263 
264 static gboolean
run_test(const gchar * path,gint expected_status_code,gboolean has_body,gboolean has_error)265 run_test (const gchar * path, gint expected_status_code,
266     gboolean has_body, gboolean has_error)
267 {
268   GstStateChangeReturn ret;
269   GstElement *pipe, *src, *sink;
270   GstBuffer *buf = NULL;
271   GstMessage *msg;
272   gchar *url;
273   gboolean res = FALSE;
274   GioHttpServer *server;
275   guint port;
276   gboolean done = FALSE;
277 
278   server = run_server ();
279   fail_if (server == NULL, "Failed to start up HTTP server");
280 
281   pipe = gst_pipeline_new (NULL);
282   fail_unless (pipe != NULL);
283 
284   src = gst_element_factory_make ("curlhttpsrc", NULL);
285   fail_unless (src != NULL);
286 
287   sink = gst_element_factory_make ("fakesink", NULL);
288   fail_unless (sink != NULL);
289 
290   gst_bin_add (GST_BIN (pipe), src);
291   gst_bin_add (GST_BIN (pipe), sink);
292   fail_unless (gst_element_link (src, sink));
293 
294   port = get_port_from_server (server);
295   url = g_strdup_printf ("http://127.0.0.1:%u%s", port, path);
296   fail_unless (url != NULL);
297   g_object_set (src, "location", url, NULL);
298   g_free (url);
299 
300   g_object_set (src, "automatic-redirect", redirect, NULL);
301   if (cookies != NULL)
302     g_object_set (src, "cookies", cookies, NULL);
303   g_object_set (sink, "signal-handoffs", TRUE, NULL);
304   /*g_object_set (sink, "dump", TRUE, NULL); */
305   g_signal_connect (sink, "preroll-handoff", G_CALLBACK (handoff_cb), &buf);
306 
307   ret = gst_element_set_state (pipe, GST_STATE_PAUSED);
308   if (ret != GST_STATE_CHANGE_ASYNC) {
309     GST_DEBUG ("failed to start up curl http src, ret = %d", ret);
310     goto done;
311   }
312 
313   gst_element_set_state (pipe, GST_STATE_PLAYING);
314   while (!done) {
315     msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
316         GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
317     GST_DEBUG ("Message: %" GST_PTR_FORMAT, msg);
318     switch (GST_MESSAGE_TYPE (msg)) {
319       case GST_MESSAGE_ERROR:{
320         gchar *debug = NULL;
321         GError *err = NULL;
322         gint rc = -1;
323         const GstStructure *details = NULL;
324 
325         fail_unless (has_error);
326         gst_message_parse_error (msg, &err, &debug);
327         gst_message_parse_error_details (msg, &details);
328         GST_DEBUG ("debug object: %s", debug);
329         GST_DEBUG ("err->message: \"%s\"", err->message);
330         GST_DEBUG ("err->details: %" GST_PTR_FORMAT, details);
331         if (g_str_has_suffix (err->message, "Not Found"))
332           rc = 404;
333         else if (g_str_has_suffix (err->message, "Forbidden"))
334           rc = 403;
335         else if (g_str_has_suffix (err->message, "Unauthorized"))
336           rc = 401;
337         else if (g_str_has_suffix (err->message, "Found"))
338           rc = 302;
339         if (details) {
340           if (gst_structure_has_field_typed (details, "http-status-code",
341                   G_TYPE_UINT)) {
342             guint code = 0;
343             gst_structure_get_uint (details, "http-status-code", &code);
344             rc = code;
345           }
346         }
347         g_error_free (err);
348         g_free (debug);
349         GST_DEBUG ("Got HTTP error %d, expected_status_code %d", rc,
350             expected_status_code);
351         res = (rc == expected_status_code);
352         done = TRUE;
353       }
354         break;
355       case GST_MESSAGE_EOS:
356         if (!has_error)
357           done = TRUE;
358         break;
359       default:
360         fail_if (TRUE, "Unexpected GstMessage");
361         break;
362     }
363     gst_message_unref (msg);
364   }
365 
366   /* don't wait for more than 10 seconds */
367   ret = gst_element_get_state (pipe, NULL, NULL, 10 * GST_SECOND);
368   GST_LOG ("ret = %u", ret);
369 
370   if (buf != NULL) {
371     fail_unless (has_body);
372     /* we want to test the buffer offset, nothing else; if there's a failure
373      * it might be for lots of reasons (no network connection, whatever), we're
374      * not interested in those */
375     GST_DEBUG ("buffer offset = %" G_GUINT64_FORMAT, GST_BUFFER_OFFSET (buf));
376 
377     /* first buffer should have a 0 offset */
378     fail_unless (GST_BUFFER_OFFSET (buf) == 0);
379     gst_buffer_unref (buf);
380   }
381   res = TRUE;
382 
383 done:
384 
385   gst_element_set_state (pipe, GST_STATE_NULL);
386   gst_object_unref (pipe);
387   stop_server (server);
388   return res;
389 }
390 
GST_START_TEST(test_first_buffer_has_offset)391 GST_START_TEST (test_first_buffer_has_offset)
392 {
393   fail_unless (run_test ("/", 200, TRUE, FALSE));
394 }
395 
396 GST_END_TEST;
397 
GST_START_TEST(test_not_found)398 GST_START_TEST (test_not_found)
399 {
400   fail_unless (run_test ("/404", 404, FALSE, TRUE));
401 }
402 
403 GST_END_TEST;
404 
GST_START_TEST(test_not_found_with_data)405 GST_START_TEST (test_not_found_with_data)
406 {
407   fail_unless (run_test ("/404-with-data", 404, TRUE, TRUE));
408 }
409 
410 GST_END_TEST;
411 
GST_START_TEST(test_forbidden)412 GST_START_TEST (test_forbidden)
413 {
414   fail_unless (run_test ("/403", 403, FALSE, TRUE));
415 }
416 
417 GST_END_TEST;
418 
GST_START_TEST(test_redirect_no)419 GST_START_TEST (test_redirect_no)
420 {
421   redirect = FALSE;
422   fail_unless (run_test ("/302", 302, FALSE, FALSE));
423 }
424 
425 GST_END_TEST;
426 
GST_START_TEST(test_redirect_yes)427 GST_START_TEST (test_redirect_yes)
428 {
429   redirect = TRUE;
430   fail_unless (run_test ("/302", 200, TRUE, FALSE));
431 }
432 
433 GST_END_TEST;
434 
GST_START_TEST(test_cookies)435 GST_START_TEST (test_cookies)
436 {
437   static const char *biscotti[] = { "delacre=yummie", "koekje=lu", NULL };
438   gboolean res;
439 
440   cookies = biscotti;
441   res = run_test ("/", 200, TRUE, FALSE);
442   cookies = NULL;
443   fail_unless (res);
444 }
445 
446 GST_END_TEST;
447 
448 typedef struct _HttpSrcTestDownloader
449 {
450   GstElement *bin;
451   GstElement *src;
452   GstElement *sink;
453   GioHttpServer *server;
454   guint count;
455 } HttpSrcTestDownloader;
456 
457 static gboolean
move_element_to_ready(gpointer user_data)458 move_element_to_ready (gpointer user_data)
459 {
460   HttpSrcTestDownloader *tp = (HttpSrcTestDownloader *) user_data;
461 
462   GST_TRACE_OBJECT (tp->bin, "Move bin to READY state");
463   gst_element_set_state (tp->bin, GST_STATE_READY);
464   return G_SOURCE_REMOVE;
465 }
466 
467 static GstPadProbeReturn
src_event_probe(GstPad * pad,GstPadProbeInfo * info,gpointer user_data)468 src_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
469 {
470   HttpSrcTestDownloader *tp = (HttpSrcTestDownloader *) user_data;
471   GstEvent *event;
472 
473   event = gst_pad_probe_info_get_event (info);
474 
475   if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
476     GST_DEBUG_OBJECT (tp->bin, "finished last request");
477     g_idle_add (move_element_to_ready, tp);
478   }
479   return GST_PAD_PROBE_OK;
480 }
481 
482 static void
start_next_download(HttpSrcTestDownloader * tp)483 start_next_download (HttpSrcTestDownloader * tp)
484 {
485   gchar *url;
486 
487   url = g_strdup_printf ("http://127.0.0.1:%u/multi/%s-%u",
488       tp->server->port, GST_ELEMENT_NAME (tp->bin), tp->count);
489   fail_unless (url != NULL);
490   GST_DEBUG_OBJECT (tp->bin, "Start next request for: %s", url);
491   g_object_set (tp->src, "location", url, NULL);
492   g_free (url);
493   fail_unless (gst_element_sync_state_with_parent (tp->bin));
494 }
495 
496 static HttpSrcTestDownloader *
test_curl_http_src_create_downloader(const gchar * name,guint64 delay)497 test_curl_http_src_create_downloader (const gchar * name, guint64 delay)
498 {
499   HttpSrcTestDownloader *tp;
500   gchar *url;
501   GstPad *src_pad;
502 
503   tp = g_slice_new0 (HttpSrcTestDownloader);
504   tp->server = run_server ();
505   fail_if (tp->server == NULL, "Failed to start up HTTP server");
506   tp->server->delay = delay;
507 
508   tp->src = gst_element_factory_make ("curlhttpsrc", NULL);
509   fail_unless (tp->src != NULL);
510 
511   url = g_strdup_printf ("http://127.0.0.1:%u/multi/%s-0", tp->server->port,
512       name);
513   fail_unless (url != NULL);
514   g_object_set (tp->src, "location", url, NULL);
515   g_free (url);
516 
517   src_pad = gst_element_get_static_pad (tp->src, "src");
518   fail_unless (src_pad != NULL);
519   gst_pad_add_probe (src_pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
520       src_event_probe, tp, NULL);
521   gst_object_unref (src_pad);
522 
523   tp->sink = gst_element_factory_make ("fakesink", NULL);
524   fail_unless (tp->sink != NULL);
525 
526   tp->bin = gst_bin_new (name);
527   fail_unless (tp->bin != NULL);
528 
529   gst_bin_add (GST_BIN (tp->bin), tp->src);
530   gst_bin_add (GST_BIN (tp->bin), tp->sink);
531   fail_unless (gst_element_link (tp->src, tp->sink));
532   gst_element_set_locked_state (GST_ELEMENT (tp->bin), TRUE);
533 
534   return tp;
535 }
536 
537 typedef struct _MultipleHttpRequestsContext
538 {
539   GMainLoop *loop;
540   GstElement *pipe;
541   HttpSrcTestDownloader *downloader1;
542   HttpSrcTestDownloader *downloader2;
543   gboolean failed;
544 } MultipleHttpRequestsContext;
545 
546 static gboolean
bus_message(GstBus * bus,GstMessage * msg,gpointer user_data)547 bus_message (GstBus * bus, GstMessage * msg, gpointer user_data)
548 {
549   MultipleHttpRequestsContext *context =
550       (MultipleHttpRequestsContext *) user_data;
551   gchar *debug;
552   GError *err;
553   GstState newstate;
554   GstState pending;
555   const GstStructure *details;
556 
557   GST_TRACE ("Message: %" GST_PTR_FORMAT, msg);
558   switch (GST_MESSAGE_TYPE (msg)) {
559     case GST_MESSAGE_STATE_CHANGED:
560       gst_message_parse_state_changed (msg, NULL, &newstate, &pending);
561       if (newstate == GST_STATE_PLAYING && pending == GST_STATE_VOID_PENDING &&
562           GST_MESSAGE_SRC (msg) == GST_OBJECT (context->pipe)) {
563         GST_DEBUG ("Test ready to start");
564         start_next_download (context->downloader1);
565         start_next_download (context->downloader2);
566       } else if (newstate == GST_STATE_READY
567           && pending == GST_STATE_VOID_PENDING) {
568         if (GST_MESSAGE_SRC (msg) == GST_OBJECT (context->downloader1->bin)) {
569           if (++context->downloader1->count < 20) {
570             start_next_download (context->downloader1);
571           } else {
572             gst_element_set_state (context->downloader1->bin, GST_STATE_NULL);
573             if (context->downloader2->count == 20) {
574               g_main_loop_quit (context->loop);
575             }
576           }
577         } else if (GST_MESSAGE_SRC (msg) ==
578             GST_OBJECT (context->downloader2->bin)) {
579           if (++context->downloader2->count < 20) {
580             start_next_download (context->downloader2);
581           } else {
582             gst_element_set_state (context->downloader2->bin, GST_STATE_NULL);
583             if (context->downloader1->count == 20) {
584               g_main_loop_quit (context->loop);
585             }
586           }
587         }
588       }
589       break;
590     case GST_MESSAGE_ERROR:
591       debug = NULL;
592       err = NULL;
593       details = NULL;
594       gst_message_parse_error (msg, &err, &debug);
595       gst_message_parse_error_details (msg, &details);
596       GST_DEBUG ("err->debug: %s", debug);
597       GST_DEBUG ("err->message: \"%s\"", err->message);
598       GST_DEBUG ("err->details: %" GST_PTR_FORMAT, details);
599       g_error_free (err);
600       g_free (debug);
601       context->failed = TRUE;
602       g_main_loop_quit (context->loop);
603       break;
604     case GST_MESSAGE_EOS:
605       if (context->downloader1->count == 20
606           && context->downloader2->count == 20) {
607         g_main_loop_quit (context->loop);
608       }
609       break;
610     default:
611       break;
612   }
613   return TRUE;
614 }
615 
616 /* test_multiple_http_requests tries to reproduce the way in which
617  * GstAdaptiveDemux makes use of URI source elements. GstAdaptiveDemux
618  * creates a bin with the httpsrc element and a queue element and sets the
619  * locked state of that bin to TRUE, so that it does not follow the state
620  * transitions of its parent. It then moves this bin to the PLAYING state
621  * to start each download and back to READY when the download completes.
622  */
GST_START_TEST(test_multiple_http_requests)623 GST_START_TEST (test_multiple_http_requests)
624 {
625   GstStateChangeReturn ret;
626   MultipleHttpRequestsContext context;
627   guint watch_id;
628   GstBus *bus;
629 
630   context.loop = g_main_loop_new (NULL, FALSE);
631   context.downloader1 =
632       test_curl_http_src_create_downloader ("bin1", 5 * G_USEC_PER_SEC / 1000);
633   fail_unless (context.downloader1 != NULL);
634   context.downloader2 =
635       test_curl_http_src_create_downloader ("bin2", 7 * G_USEC_PER_SEC / 1000);
636   fail_unless (context.downloader2 != NULL);
637 
638   context.pipe = gst_pipeline_new (NULL);
639   fail_unless (context.pipe != NULL);
640 
641   gst_bin_add (GST_BIN_CAST (context.pipe), context.downloader1->bin);
642   gst_bin_add (GST_BIN_CAST (context.pipe), context.downloader2->bin);
643 
644   bus = gst_pipeline_get_bus (GST_PIPELINE (context.pipe));
645   watch_id = gst_bus_add_watch (bus, bus_message, &context);
646   gst_object_unref (bus);
647 
648   GST_DEBUG ("Start pipeline playing");
649   ret = gst_element_set_state (context.pipe, GST_STATE_PLAYING);
650   fail_unless (ret == GST_STATE_CHANGE_ASYNC
651       || ret == GST_STATE_CHANGE_SUCCESS);
652 
653   g_main_loop_run (context.loop);
654   g_source_remove (watch_id);
655   gst_element_set_state (context.pipe, GST_STATE_NULL);
656   gst_object_unref (context.pipe);
657   stop_server (context.downloader1->server);
658   stop_server (context.downloader2->server);
659   g_slice_free (HttpSrcTestDownloader, context.downloader1);
660   g_slice_free (HttpSrcTestDownloader, context.downloader2);
661   g_main_loop_unref (context.loop);
662 }
663 
664 GST_END_TEST;
665 
666 static Suite *
curlhttpsrc_suite(void)667 curlhttpsrc_suite (void)
668 {
669   TCase *tc_chain;
670   Suite *s;
671 
672   /* we don't support exceptions from the proxy, so just unset the environment
673    * variable - in case it's set in the test environment it would otherwise
674    * prevent us from connecting to localhost (like jenkins.qa.ubuntu.com) */
675   g_unsetenv ("http_proxy");
676 
677   s = suite_create ("curlhttpsrc");
678   tc_chain = tcase_create ("general");
679 
680   suite_add_tcase (s, tc_chain);
681   tcase_add_test (tc_chain, test_first_buffer_has_offset);
682   tcase_add_test (tc_chain, test_redirect_yes);
683   tcase_add_test (tc_chain, test_redirect_no);
684   tcase_add_test (tc_chain, test_not_found);
685   tcase_add_test (tc_chain, test_not_found_with_data);
686   tcase_add_test (tc_chain, test_forbidden);
687   tcase_add_test (tc_chain, test_cookies);
688   tcase_add_test (tc_chain, test_multiple_http_requests);
689 
690   return s;
691 }
692 
693 GST_CHECK_MAIN (curlhttpsrc);
694