tizen 2.0 init
[framework/multimedia/gst-plugins-good0.10.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 <stdlib.h>
27
28 #include <glib.h>
29 #include <glib/gprintf.h>
30 #include <libsoup/soup-address.h>
31 #include <libsoup/soup-message.h>
32 #include <libsoup/soup-misc.h>
33 #include <libsoup/soup-server.h>
34 #include <libsoup/soup-auth-domain.h>
35 #include <libsoup/soup-auth-domain-basic.h>
36 #include <libsoup/soup-auth-domain-digest.h>
37 #include <gst/check/gstcheck.h>
38
39 static guint http_port = 0, https_port = 0;
40
41 gboolean redirect = TRUE;
42
43 static const char **cookies = NULL;
44
45 /* Variables for authentication tests */
46 static const char *user_id = NULL;
47 static const char *user_pw = NULL;
48 static const char *good_user = "good_user";
49 static const char *bad_user = "bad_user";
50 static const char *good_pw = "good_pw";
51 static const char *bad_pw = "bad_pw";
52 static const char *realm = "SOUPHTTPSRC_REALM";
53 static const char *basic_auth_path = "/basic_auth";
54 static const char *digest_auth_path = "/digest_auth";
55
56 static int run_server (guint * http_port, guint * https_port);
57 static void stop_server (void);
58
59 static void
60 handoff_cb (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
61     GstBuffer ** p_outbuf)
62 {
63   GST_LOG ("handoff, buf = %p", buf);
64   if (*p_outbuf == NULL)
65     *p_outbuf = gst_buffer_ref (buf);
66 }
67
68 static gboolean
69 basic_auth_cb (SoupAuthDomain * domain, SoupMessage * msg,
70     const char *username, const char *password, gpointer user_data)
71 {
72   /* There is only one good login for testing */
73   return (strcmp (username, good_user) == 0)
74       && (strcmp (password, good_pw) == 0);
75 }
76
77
78 static char *
79 digest_auth_cb (SoupAuthDomain * domain, SoupMessage * msg,
80     const char *username, gpointer user_data)
81 {
82   /* There is only one good login for testing */
83   if (strcmp (username, good_user) == 0)
84     return soup_auth_domain_digest_encode_password (good_user, realm, good_pw);
85   return NULL;
86 }
87
88 static int
89 run_test (const char *format, ...)
90 {
91   GstStateChangeReturn ret;
92
93   GstElement *pipe, *src, *sink;
94
95   GstBuffer *buf = NULL;
96
97   GstMessage *msg;
98
99   gchar *url;
100
101   va_list args;
102
103   int rc = -1;
104
105   pipe = gst_pipeline_new (NULL);
106
107   src = gst_element_factory_make ("souphttpsrc", NULL);
108   fail_unless (src != NULL);
109
110   sink = gst_element_factory_make ("fakesink", NULL);
111   fail_unless (sink != NULL);
112
113   gst_bin_add (GST_BIN (pipe), src);
114   gst_bin_add (GST_BIN (pipe), sink);
115   fail_unless (gst_element_link (src, sink));
116
117   if (http_port == 0) {
118     GST_DEBUG ("failed to start soup http server");
119   }
120   fail_unless (http_port != 0);
121   va_start (args, format);
122   g_vasprintf (&url, format, args);
123   va_end (args);
124   fail_unless (url != NULL);
125   g_object_set (src, "location", url, NULL);
126   g_free (url);
127
128   g_object_set (src, "automatic-redirect", redirect, NULL);
129   if (cookies != NULL)
130     g_object_set (src, "cookies", cookies, NULL);
131   g_object_set (sink, "signal-handoffs", TRUE, NULL);
132   g_signal_connect (sink, "preroll-handoff", G_CALLBACK (handoff_cb), &buf);
133
134   if (user_id != NULL)
135     g_object_set (src, "user-id", user_id, NULL);
136   if (user_pw != NULL)
137     g_object_set (src, "user-pw", user_pw, NULL);
138
139   ret = gst_element_set_state (pipe, GST_STATE_PAUSED);
140   if (ret != GST_STATE_CHANGE_ASYNC) {
141     GST_DEBUG ("failed to start up soup http src, ret = %d", ret);
142     goto done;
143   }
144
145   gst_element_set_state (pipe, GST_STATE_PLAYING);
146   msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
147       GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
148   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
149     gchar *debug = NULL;
150
151     GError *err = NULL;
152
153     gst_message_parse_error (msg, &err, &debug);
154     GST_INFO ("error: %s", err->message);
155     if (g_str_has_suffix (err->message, "Not Found"))
156       rc = 404;
157     else if (g_str_has_suffix (err->message, "Forbidden"))
158       rc = 403;
159     else if (g_str_has_suffix (err->message, "Unauthorized"))
160       rc = 401;
161     else if (g_str_has_suffix (err->message, "Found"))
162       rc = 302;
163     GST_INFO ("debug: %s", debug);
164     g_error_free (err);
165     g_free (debug);
166     gst_message_unref (msg);
167     goto done;
168   }
169   gst_message_unref (msg);
170
171   /* don't wait for more than 10 seconds */
172   ret = gst_element_get_state (pipe, NULL, NULL, 10 * GST_SECOND);
173   GST_LOG ("ret = %u", ret);
174
175   if (buf == NULL) {
176     /* we want to test the buffer offset, nothing else; if there's a failure
177      * it might be for lots of reasons (no network connection, whatever), we're
178      * not interested in those */
179     GST_DEBUG ("didn't manage to get data within 10 seconds, skipping test");
180     goto done;
181   }
182
183   GST_DEBUG ("buffer offset = %" G_GUINT64_FORMAT, GST_BUFFER_OFFSET (buf));
184
185   /* first buffer should have a 0 offset */
186   fail_unless (GST_BUFFER_OFFSET (buf) == 0);
187   gst_buffer_unref (buf);
188   rc = 0;
189
190 done:
191
192   gst_element_set_state (pipe, GST_STATE_NULL);
193   gst_object_unref (pipe);
194   return rc;
195 }
196
197 GST_START_TEST (test_first_buffer_has_offset)
198 {
199   fail_unless (run_test ("http://127.0.0.1:%u/", http_port) == 0);
200 }
201
202 GST_END_TEST;
203
204 GST_START_TEST (test_not_found)
205 {
206   fail_unless (run_test ("http://127.0.0.1:%u/404", http_port) == 404);
207 }
208
209 GST_END_TEST;
210
211 GST_START_TEST (test_forbidden)
212 {
213   fail_unless (run_test ("http://127.0.0.1:%u/403", http_port) == 403);
214 }
215
216 GST_END_TEST;
217
218 GST_START_TEST (test_redirect_no)
219 {
220   redirect = FALSE;
221   fail_unless (run_test ("http://127.0.0.1:%u/302", http_port) == 302);
222 }
223
224 GST_END_TEST;
225
226 GST_START_TEST (test_redirect_yes)
227 {
228   redirect = TRUE;
229   fail_unless (run_test ("http://127.0.0.1:%u/302", http_port) == 0);
230 }
231
232 GST_END_TEST;
233
234 GST_START_TEST (test_https)
235 {
236   if (!https_port)
237     GST_INFO ("Failed to start an HTTPS server; let's just skip this test.");
238   else
239     fail_unless (run_test ("https://127.0.0.1:%u/", https_port) == 0);
240 }
241
242 GST_END_TEST;
243
244 GST_START_TEST (test_cookies)
245 {
246   static const char *biscotti[] = { "delacre=yummie", "koekje=lu", NULL };
247   int rc;
248
249   cookies = biscotti;
250   rc = run_test ("http://127.0.0.1:%u/", http_port);
251   cookies = NULL;
252   fail_unless (rc == 0);
253 }
254
255 GST_END_TEST;
256
257 GST_START_TEST (test_good_user_basic_auth)
258 {
259   int res;
260
261   user_id = good_user;
262   user_pw = good_pw;
263   res = run_test ("http://127.0.0.1:%u%s", http_port, basic_auth_path);
264   GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res);
265   user_id = user_pw = NULL;
266   fail_unless (res == 0);
267 }
268
269 GST_END_TEST;
270
271 GST_START_TEST (test_bad_user_basic_auth)
272 {
273   int res;
274
275   user_id = bad_user;
276   user_pw = good_pw;
277   res = run_test ("http://127.0.0.1:%u%s", http_port, basic_auth_path);
278   GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res);
279   user_id = user_pw = NULL;
280   fail_unless (res == 401);
281 }
282
283 GST_END_TEST;
284
285 GST_START_TEST (test_bad_password_basic_auth)
286 {
287   int res;
288
289   user_id = good_user;
290   user_pw = bad_pw;
291   res = run_test ("http://127.0.0.1:%u%s", http_port, basic_auth_path);
292   GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res);
293   user_id = user_pw = NULL;
294   fail_unless (res == 401);
295 }
296
297 GST_END_TEST;
298
299 GST_START_TEST (test_good_user_digest_auth)
300 {
301   int res;
302
303   user_id = good_user;
304   user_pw = good_pw;
305   res = run_test ("http://127.0.0.1:%u%s", http_port, digest_auth_path);
306   GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res);
307   user_id = user_pw = NULL;
308   fail_unless (res == 0);
309 }
310
311 GST_END_TEST;
312
313 GST_START_TEST (test_bad_user_digest_auth)
314 {
315   int res;
316
317   user_id = bad_user;
318   user_pw = good_pw;
319   res = run_test ("http://127.0.0.1:%u%s", http_port, digest_auth_path);
320   GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res);
321   user_id = user_pw = NULL;
322   fail_unless (res == 401);
323 }
324
325 GST_END_TEST;
326
327 GST_START_TEST (test_bad_password_digest_auth)
328 {
329   int res;
330
331   user_id = good_user;
332   user_pw = bad_pw;
333   res = run_test ("http://127.0.0.1:%u%s", http_port, digest_auth_path);
334   GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res);
335   user_id = user_pw = NULL;
336   fail_unless (res == 401);
337 }
338
339 GST_END_TEST;
340
341 static gboolean icy_caps = FALSE;
342
343 static void
344 got_buffer (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
345     gpointer user_data)
346 {
347   GstStructure *s;
348
349   /* Caps can be anything if we don't except icy caps */
350   if (!icy_caps)
351     return;
352
353   /* Otherwise they _must_ be "application/x-icy" */
354   fail_unless (GST_BUFFER_CAPS (buf) != NULL);
355   s = gst_caps_get_structure (GST_BUFFER_CAPS (buf), 0);
356   fail_unless_equals_string (gst_structure_get_name (s), "application/x-icy");
357 }
358
359 GST_START_TEST (test_icy_stream)
360 {
361   GstElement *pipe, *src, *sink;
362
363   GstMessage *msg;
364
365   pipe = gst_pipeline_new (NULL);
366
367   src = gst_element_factory_make ("souphttpsrc", NULL);
368   fail_unless (src != NULL);
369   g_object_set (src, "iradio-mode", TRUE, NULL);
370
371   sink = gst_element_factory_make ("fakesink", NULL);
372   fail_unless (sink != NULL);
373   g_object_set (sink, "signal-handoffs", TRUE, NULL);
374   g_signal_connect (sink, "handoff", G_CALLBACK (got_buffer), NULL);
375
376   gst_bin_add (GST_BIN (pipe), src);
377   gst_bin_add (GST_BIN (pipe), sink);
378   fail_unless (gst_element_link (src, sink));
379
380   /* First try Virgin Radio Ogg stream, to see if there's connectivity and all
381    * (which is an attempt to work around the completely horrid error reporting
382    * and that we can't distinguish different types of failures here). */
383
384   g_object_set (src, "location", "http://ogg2.smgradio.com/vr32.ogg", NULL);
385   g_object_set (src, "num-buffers", 1, NULL);
386   icy_caps = FALSE;
387   gst_element_set_state (pipe, GST_STATE_PLAYING);
388
389   msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
390       GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
391   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
392     GST_INFO ("looks like there's no net connectivity or sgmradio.com is "
393         "down. In any case, let's just skip this test");
394     gst_message_unref (msg);
395     goto done;
396   }
397   gst_message_unref (msg);
398   msg = NULL;
399   gst_element_set_state (pipe, GST_STATE_NULL);
400
401   /* Now, if the ogg stream works, the mp3 shoutcast stream should work as
402    * well (time will tell if that's true) */
403
404   /* Virgin Radio 32kbps mp3 shoutcast stream */
405   g_object_set (src, "location", "http://mp3-vr-32.smgradio.com:80/", NULL);
406
407
408   /* EOS after the first buffer */
409   g_object_set (src, "num-buffers", 1, NULL);
410   icy_caps = TRUE;
411   gst_element_set_state (pipe, GST_STATE_PLAYING);
412   msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
413       GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
414
415   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) {
416     GST_DEBUG ("success, we're done here");
417     gst_message_unref (msg);
418     goto done;
419   }
420
421   {
422     GError *err = NULL;
423
424     gst_message_parse_error (msg, &err, NULL);
425     gst_message_unref (msg);
426     g_error ("Error with ICY mp3 shoutcast stream: %s", err->message);
427     g_error_free (err);
428   }
429
430 done:
431   icy_caps = FALSE;
432
433   gst_element_set_state (pipe, GST_STATE_NULL);
434   gst_object_unref (pipe);
435 }
436
437 GST_END_TEST;
438
439 static Suite *
440 souphttpsrc_suite (void)
441 {
442   Suite *s;
443
444   TCase *tc_chain, *tc_internet;
445
446   g_type_init ();
447
448 #if !GLIB_CHECK_VERSION (2, 31, 0)
449   if (!g_thread_supported ())
450     g_thread_init (NULL);
451 #endif
452
453   s = suite_create ("souphttpsrc");
454   tc_chain = tcase_create ("general");
455   tc_internet = tcase_create ("internet");
456
457   suite_add_tcase (s, tc_chain);
458   run_server (&http_port, &https_port);
459   atexit (stop_server);
460   tcase_add_test (tc_chain, test_first_buffer_has_offset);
461   tcase_add_test (tc_chain, test_redirect_yes);
462   tcase_add_test (tc_chain, test_redirect_no);
463   tcase_add_test (tc_chain, test_not_found);
464   tcase_add_test (tc_chain, test_forbidden);
465   tcase_add_test (tc_chain, test_cookies);
466   tcase_add_test (tc_chain, test_good_user_basic_auth);
467   tcase_add_test (tc_chain, test_bad_user_basic_auth);
468   tcase_add_test (tc_chain, test_bad_password_basic_auth);
469   tcase_add_test (tc_chain, test_good_user_digest_auth);
470   tcase_add_test (tc_chain, test_bad_user_digest_auth);
471   tcase_add_test (tc_chain, test_bad_password_digest_auth);
472   if (soup_ssl_supported)
473     tcase_add_test (tc_chain, test_https);
474
475   suite_add_tcase (s, tc_internet);
476   tcase_set_timeout (tc_internet, 250);
477   tcase_add_test (tc_internet, test_icy_stream);
478
479   return s;
480 }
481
482 GST_CHECK_MAIN (souphttpsrc);
483
484 static void
485 do_get (SoupMessage * msg, const char *path)
486 {
487   char *uri;
488
489   int buflen = 4096;
490
491   SoupKnownStatusCode status = SOUP_STATUS_OK;
492
493   uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
494   GST_DEBUG ("request: \"%s\"", uri);
495
496   if (!strcmp (path, "/301"))
497     status = SOUP_STATUS_MOVED_PERMANENTLY;
498   else if (!strcmp (path, "/302"))
499     status = SOUP_STATUS_MOVED_TEMPORARILY;
500   else if (!strcmp (path, "/307"))
501     status = SOUP_STATUS_TEMPORARY_REDIRECT;
502   else if (!strcmp (path, "/403"))
503     status = SOUP_STATUS_FORBIDDEN;
504   else if (!strcmp (path, "/404"))
505     status = SOUP_STATUS_NOT_FOUND;
506
507   if (SOUP_STATUS_IS_REDIRECTION (status)) {
508     char *redir_uri;
509
510     redir_uri = g_strdup_printf ("%s-redirected", uri);
511     soup_message_headers_append (msg->response_headers, "Location", redir_uri);
512     g_free (redir_uri);
513   }
514   if (status != SOUP_STATUS_OK)
515     goto leave;
516
517   if (msg->method == SOUP_METHOD_GET) {
518     char *buf;
519
520     buf = g_malloc (buflen);
521     memset (buf, 0, buflen);
522     soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE,
523         buf, buflen);
524   } else {                      /* msg->method == SOUP_METHOD_HEAD */
525
526     char *length;
527
528     /* We could just use the same code for both GET and
529      * HEAD. But we'll optimize and avoid the extra
530      * malloc.
531      */
532     length = g_strdup_printf ("%lu", (gulong) buflen);
533     soup_message_headers_append (msg->response_headers,
534         "Content-Length", length);
535     g_free (length);
536   }
537
538 leave:
539   soup_message_set_status (msg, status);
540   g_free (uri);
541 }
542
543 static void
544 print_header (const char *name, const char *value, gpointer data)
545 {
546   GST_DEBUG ("header: %s: %s", name, value);
547 }
548
549 static void
550 server_callback (SoupServer * server, SoupMessage * msg,
551     const char *path, GHashTable * query,
552     SoupClientContext * context, gpointer data)
553 {
554   GST_DEBUG ("%s %s HTTP/1.%d", msg->method, path,
555       soup_message_get_http_version (msg));
556   soup_message_headers_foreach (msg->request_headers, print_header, NULL);
557   if (msg->request_body->length)
558     GST_DEBUG ("%s", msg->request_body->data);
559
560   if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
561     do_get (msg, path);
562   else
563     soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
564
565   GST_DEBUG ("  -> %d %s", msg->status_code, msg->reason_phrase);
566 }
567
568 static SoupServer *server;      /* NULL */
569 static SoupServer *ssl_server;  /* NULL */
570
571 int
572 run_server (guint * http_port, guint * https_port)
573 {
574   guint port = SOUP_ADDRESS_ANY_PORT;
575   guint ssl_port = SOUP_ADDRESS_ANY_PORT;
576   const char *ssl_cert_file = GST_TEST_FILES_PATH "/test-cert.pem";
577   const char *ssl_key_file = GST_TEST_FILES_PATH "/test-key.pem";
578   static int server_running = 0;
579
580   SoupAuthDomain *domain = NULL;
581
582   if (server_running)
583     return 0;
584   server_running = 1;
585
586   *http_port = *https_port = 0;
587
588   server = soup_server_new (SOUP_SERVER_PORT, port, NULL);
589   if (!server) {
590     GST_DEBUG ("Unable to bind to server port %u", port);
591     return 1;
592   }
593   *http_port = soup_server_get_port (server);
594   GST_INFO ("HTTP server listening on port %u", *http_port);
595   soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
596   domain = soup_auth_domain_basic_new (SOUP_AUTH_DOMAIN_REALM, realm,
597       SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_cb,
598       SOUP_AUTH_DOMAIN_ADD_PATH, basic_auth_path, NULL);
599   soup_server_add_auth_domain (server, domain);
600   g_object_unref (domain);
601   domain = soup_auth_domain_digest_new (SOUP_AUTH_DOMAIN_REALM, realm,
602       SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, digest_auth_cb,
603       SOUP_AUTH_DOMAIN_ADD_PATH, digest_auth_path, NULL);
604   soup_server_add_auth_domain (server, domain);
605   g_object_unref (domain);
606   soup_server_run_async (server);
607
608   if (ssl_cert_file && ssl_key_file) {
609     ssl_server = soup_server_new (SOUP_SERVER_PORT, ssl_port,
610         SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
611         SOUP_SERVER_SSL_KEY_FILE, ssl_key_file, NULL);
612
613     if (!ssl_server) {
614       GST_DEBUG ("Unable to bind to SSL server port %u", ssl_port);
615       return 1;
616     }
617     *https_port = soup_server_get_port (ssl_server);
618     GST_INFO ("HTTPS server listening on port %u", *https_port);
619     soup_server_add_handler (ssl_server, NULL, server_callback, NULL, NULL);
620     soup_server_run_async (ssl_server);
621   }
622
623   return 0;
624 }
625
626 static void
627 stop_server (void)
628 {
629   GST_INFO ("cleaning up");
630
631   if (server) {
632     g_object_unref (server);
633     server = NULL;
634   }
635   if (ssl_server) {
636     g_object_unref (ssl_server);
637     ssl_server = NULL;
638   }
639 }