tests/check/Makefile.am: Disable some more elements in the state test.
[platform/upstream/gstreamer.git] / tests / check / elements / souphttpsrc.c
1 /* GStreamer unit tests for the souphttpsrc element
2  * Copyright (C) 2006-2007 Tim-Philipp Müller <tim centricular net>
3  * Copyright (C) 2008 Wouter Cloetens <wouter@mind.be>
4  * Copyright (C) 2001-2003, Ximian, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #include <glib.h>
27 #include <glib/gprintf.h>
28 #include <libsoup/soup-address.h>
29 #include <libsoup/soup-message.h>
30 #include <libsoup/soup-server.h>
31 #include <gst/check/gstcheck.h>
32
33 static int http_port = 0, https_port = 0;
34 gboolean redirect = TRUE;
35 static const char **cookies = NULL;
36
37 static int run_server (int *http_port, int *https_port);
38
39
40 static void
41 handoff_cb (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
42     GstBuffer ** p_outbuf)
43 {
44   GST_LOG ("handoff, buf = %p", buf);
45   if (*p_outbuf == NULL)
46     *p_outbuf = gst_buffer_ref (buf);
47 }
48
49 int
50 run_test (const char *format, ...)
51 {
52   GstStateChangeReturn ret;
53   GstElement *pipe, *src, *sink;
54   GstBuffer *buf = NULL;
55   GstMessage *msg;
56   gchar *url;
57   va_list args;
58   int rc = -1;
59
60   pipe = gst_pipeline_new (NULL);
61
62   src = gst_element_factory_make ("souphttpsrc", NULL);
63   fail_unless (src != NULL);
64
65   sink = gst_element_factory_make ("fakesink", NULL);
66   fail_unless (sink != NULL);
67
68   gst_bin_add (GST_BIN (pipe), src);
69   gst_bin_add (GST_BIN (pipe), sink);
70   fail_unless (gst_element_link (src, sink));
71
72   if (http_port == 0) {
73     GST_DEBUG ("failed to start soup http server");
74   }
75   fail_unless (http_port != 0);
76   va_start (args, format);
77   g_vasprintf (&url, format, args);
78   va_end (args);
79   fail_unless (url != NULL);
80   g_object_set (src, "location", url, NULL);
81   g_free (url);
82
83   g_object_set (src, "automatic-redirect", redirect, NULL);
84   if (cookies != NULL)
85     g_object_set (src, "cookies", cookies, NULL);
86   g_object_set (sink, "signal-handoffs", TRUE, NULL);
87   g_signal_connect (sink, "preroll-handoff", G_CALLBACK (handoff_cb), &buf);
88
89   ret = gst_element_set_state (pipe, GST_STATE_PAUSED);
90   if (ret != GST_STATE_CHANGE_ASYNC) {
91     GST_DEBUG ("failed to start up soup http src, ret = %d", ret);
92     goto done;
93   }
94
95   gst_element_set_state (pipe, GST_STATE_PLAYING);
96   msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
97       GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
98   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
99     gchar *debug = NULL;
100     GError *err = NULL;
101
102     gst_message_parse_error (msg, &err, &debug);
103     GST_INFO ("error: %s", err->message);
104     if (g_str_has_suffix (err->message, "Not Found"))
105       rc = 404;
106     else if (g_str_has_suffix (err->message, "Forbidden"))
107       rc = 403;
108     else if (g_str_has_suffix (err->message, "Found"))
109       rc = 302;
110     GST_INFO ("debug: %s", debug);
111     g_error_free (err);
112     g_free (debug);
113     gst_message_unref (msg);
114     goto done;
115   }
116   gst_message_unref (msg);
117
118   /* don't wait for more than 10 seconds */
119   ret = gst_element_get_state (pipe, NULL, NULL, 10 * GST_SECOND);
120   GST_LOG ("ret = %u", ret);
121
122   if (buf == NULL) {
123     /* we want to test the buffer offset, nothing else; if there's a failure
124      * it might be for lots of reasons (no network connection, whatever), we're
125      * not interested in those */
126     GST_DEBUG ("didn't manage to get data within 10 seconds, skipping test");
127     goto done;
128   }
129
130   GST_DEBUG ("buffer offset = %" G_GUINT64_FORMAT, GST_BUFFER_OFFSET (buf));
131
132   /* first buffer should have a 0 offset */
133   fail_unless (GST_BUFFER_OFFSET (buf) == 0);
134   gst_buffer_unref (buf);
135   rc = 0;
136
137 done:
138
139   gst_element_set_state (pipe, GST_STATE_NULL);
140   gst_object_unref (pipe);
141   return rc;
142 }
143
144 GST_START_TEST (test_first_buffer_has_offset)
145 {
146   fail_unless (run_test ("http://127.0.0.1:%d/", http_port) == 0);
147 }
148
149 GST_END_TEST;
150
151 GST_START_TEST (test_not_found)
152 {
153   fail_unless (run_test ("http://127.0.0.1:%d/404", http_port) == 404);
154 }
155
156 GST_END_TEST;
157
158 GST_START_TEST (test_forbidden)
159 {
160   fail_unless (run_test ("http://127.0.0.1:%d/403", http_port) == 403);
161 }
162
163 GST_END_TEST;
164
165 GST_START_TEST (test_redirect_no)
166 {
167   redirect = FALSE;
168   fail_unless (run_test ("http://127.0.0.1:%d/302", http_port) == 302);
169 }
170
171 GST_END_TEST;
172
173 GST_START_TEST (test_redirect_yes)
174 {
175   redirect = TRUE;
176   fail_unless (run_test ("http://127.0.0.1:%d/302", http_port) == 0);
177 }
178
179 GST_END_TEST;
180
181 GST_START_TEST (test_https)
182 {
183   if (!https_port)
184     GST_INFO ("Failed to start an HTTPS server; let's just skip this test.");
185   else
186     fail_unless (run_test ("https://127.0.0.1:%d/", https_port) == 0);
187 }
188
189 GST_END_TEST;
190
191 GST_START_TEST (test_cookies)
192 {
193   static const char *biscotti[] = { "delacre=yummie", "koekje=lu", NULL };
194   int rc;
195
196   cookies = biscotti;
197   rc = run_test ("http://127.0.0.1:%d/", http_port);
198   cookies = NULL;
199   fail_unless (rc == 0);
200 }
201
202 GST_END_TEST;
203
204 static gboolean icy_caps = FALSE;
205
206 static void
207 got_buffer (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
208     gpointer user_data)
209 {
210   GstStructure *s;
211
212   /* Caps can be anything if we don't except icy caps */
213   if (!icy_caps)
214     return;
215
216   /* Otherwise they _must_ be "application/x-icy" */
217   fail_unless (GST_BUFFER_CAPS (buf) != NULL);
218   s = gst_caps_get_structure (GST_BUFFER_CAPS (buf), 0);
219   fail_unless_equals_string (gst_structure_get_name (s), "application/x-icy");
220 }
221
222 GST_START_TEST (test_icy_stream)
223 {
224   GstElement *pipe, *src, *sink;
225   GstMessage *msg;
226
227   pipe = gst_pipeline_new (NULL);
228
229   src = gst_element_factory_make ("souphttpsrc", NULL);
230   fail_unless (src != NULL);
231   g_object_set (src, "iradio-mode", TRUE, NULL);
232
233   sink = gst_element_factory_make ("fakesink", NULL);
234   fail_unless (sink != NULL);
235   g_object_set (sink, "signal-handoffs", TRUE, NULL);
236   g_signal_connect (sink, "handoff", G_CALLBACK (got_buffer), NULL);
237
238   gst_bin_add (GST_BIN (pipe), src);
239   gst_bin_add (GST_BIN (pipe), sink);
240   fail_unless (gst_element_link (src, sink));
241
242   /* First try Virgin Radio Ogg stream, to see if there's connectivity and all
243    * (which is an attempt to work around the completely horrid error reporting
244    * and that we can't distinguish different types of failures here). */
245
246   g_object_set (src, "location", "http://ogg2.smgradio.com/vr32.ogg", NULL);
247   g_object_set (src, "num-buffers", 1, NULL);
248   icy_caps = FALSE;
249   gst_element_set_state (pipe, GST_STATE_PLAYING);
250
251   msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
252       GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
253   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
254     GST_INFO ("looks like there's no net connectivity or sgmradio.com is "
255         "down. In any case, let's just skip this test");
256     gst_message_unref (msg);
257     goto done;
258   }
259   gst_message_unref (msg);
260   msg = NULL;
261   gst_element_set_state (pipe, GST_STATE_NULL);
262
263   /* Now, if the ogg stream works, the mp3 shoutcast stream should work as
264    * well (time will tell if that's true) */
265
266   /* Virgin Radio 32kbps mp3 shoutcast stream */
267   g_object_set (src, "location", "http://mp3-vr-32.smgradio.com:80/", NULL);
268
269
270   /* EOS after the first buffer */
271   g_object_set (src, "num-buffers", 1, NULL);
272   icy_caps = TRUE;
273   gst_element_set_state (pipe, GST_STATE_PLAYING);
274   msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
275       GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
276
277   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) {
278     GST_DEBUG ("success, we're done here");
279     gst_message_unref (msg);
280     goto done;
281   }
282
283   {
284     GError *err = NULL;
285
286     gst_message_parse_error (msg, &err, NULL);
287     gst_message_unref (msg);
288     g_error ("Error with ICY mp3 shoutcast stream: %s", err->message);
289     g_error_free (err);
290   }
291
292 done:
293   icy_caps = FALSE;
294
295   gst_element_set_state (pipe, GST_STATE_NULL);
296   gst_object_unref (pipe);
297 }
298
299 GST_END_TEST;
300
301 static Suite *
302 souphttpsrc_suite (void)
303 {
304   g_type_init ();
305   g_thread_init (NULL);
306
307   Suite *s = suite_create ("souphttpsrc");
308   TCase *tc_chain = tcase_create ("general");
309   TCase *tc_internet = tcase_create ("internet");
310
311   suite_add_tcase (s, tc_chain);
312   run_server (&http_port, &https_port);
313   tcase_add_test (tc_chain, test_first_buffer_has_offset);
314   tcase_add_test (tc_chain, test_https);
315   tcase_add_test (tc_chain, test_redirect_yes);
316   tcase_add_test (tc_chain, test_redirect_no);
317   tcase_add_test (tc_chain, test_not_found);
318   tcase_add_test (tc_chain, test_forbidden);
319   tcase_add_test (tc_chain, test_cookies);
320
321   suite_add_tcase (s, tc_internet);
322   tcase_set_timeout (tc_internet, 250);
323   tcase_add_test (tc_internet, test_icy_stream);
324
325   return s;
326 }
327
328 GST_CHECK_MAIN (souphttpsrc);
329
330 static void
331 do_get (SoupMessage * msg, const char *path)
332 {
333   char *uri;
334   int buflen = 4096;
335   SoupKnownStatusCode status = SOUP_STATUS_OK;
336
337   uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
338   GST_DEBUG ("request: \"%s\"", uri);
339
340   if (!strcmp (path, "/301"))
341     status = SOUP_STATUS_MOVED_PERMANENTLY;
342   else if (!strcmp (path, "/302"))
343     status = SOUP_STATUS_MOVED_TEMPORARILY;
344   else if (!strcmp (path, "/307"))
345     status = SOUP_STATUS_TEMPORARY_REDIRECT;
346   else if (!strcmp (path, "/403"))
347     status = SOUP_STATUS_FORBIDDEN;
348   else if (!strcmp (path, "/404"))
349     status = SOUP_STATUS_NOT_FOUND;
350
351   if (SOUP_STATUS_IS_REDIRECTION (status)) {
352     char *redir_uri;
353
354     redir_uri = g_strdup_printf ("%s-redirected", uri);
355     soup_message_headers_append (msg->response_headers, "Location", redir_uri);
356     g_free (redir_uri);
357   }
358   if (status != SOUP_STATUS_OK)
359     goto leave;
360
361   if (msg->method == SOUP_METHOD_GET) {
362     char *buf;
363
364     buf = g_malloc (buflen);
365     memset (buf, 0, buflen);
366     soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE,
367         buf, buflen);
368   } else {                      /* msg->method == SOUP_METHOD_HEAD */
369
370     char *length;
371
372     /* We could just use the same code for both GET and
373      * HEAD. But we'll optimize and avoid the extra
374      * malloc.
375      */
376     length = g_strdup_printf ("%lu", (gulong) buflen);
377     soup_message_headers_append (msg->response_headers,
378         "Content-Length", length);
379     g_free (length);
380   }
381
382 leave:
383   soup_message_set_status (msg, status);
384   g_free (uri);
385 }
386
387 static void
388 print_header (const char *name, const char *value, gpointer data)
389 {
390   GST_DEBUG ("header: %s: %s", name, value);
391 }
392
393 static void
394 server_callback (SoupServer * server, SoupMessage * msg,
395     const char *path, GHashTable * query,
396     SoupClientContext * context, gpointer data)
397 {
398   GST_DEBUG ("%s %s HTTP/1.%d", msg->method, path,
399       soup_message_get_http_version (msg));
400   soup_message_headers_foreach (msg->request_headers, print_header, NULL);
401   if (msg->request_body->length)
402     GST_DEBUG ("%s", msg->request_body->data);
403
404   if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
405     do_get (msg, path);
406   else
407     soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
408
409   GST_DEBUG ("  -> %d %s", msg->status_code, msg->reason_phrase);
410 }
411
412 int
413 run_server (int *http_port, int *https_port)
414 {
415   SoupServer *server, *ssl_server;
416   int port = SOUP_ADDRESS_ANY_PORT;
417   int ssl_port = SOUP_ADDRESS_ANY_PORT;
418   const char *ssl_cert_file = G_STRINGIFY (CHECKDATA_DIR) "/test-cert.pem";
419   const char *ssl_key_file = G_STRINGIFY (CHECKDATA_DIR) "/test-key.pem";
420   static int server_running = 0;
421
422   if (server_running)
423     return 0;
424   server_running = 1;
425
426   *http_port = *https_port = 0;
427
428   server = soup_server_new (SOUP_SERVER_PORT, port, NULL);
429   if (!server) {
430     GST_DEBUG ("Unable to bind to server port %d", port);
431     return 1;
432   }
433   *http_port = soup_server_get_port (server);
434   GST_INFO ("HTTP server listening on port %d", *http_port);
435   soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
436   soup_server_run_async (server);
437
438   if (ssl_cert_file && ssl_key_file) {
439     ssl_server = soup_server_new (SOUP_SERVER_PORT, ssl_port,
440         SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
441         SOUP_SERVER_SSL_KEY_FILE, ssl_key_file, NULL);
442
443     if (!ssl_server) {
444       GST_DEBUG ("Unable to bind to SSL server port %d", ssl_port);
445       return 1;
446     }
447     *https_port = soup_server_get_port (ssl_server);
448     GST_INFO ("HTTPS server listening on port %d", *https_port);
449     soup_server_add_handler (ssl_server, NULL, server_callback, NULL, NULL);
450     soup_server_run_async (ssl_server);
451   }
452
453   return 0;
454 }