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