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.
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.
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.
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.
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>
33 static int http_port = 0, https_port = 0;
34 gboolean redirect = TRUE;
35 static const char **cookies = NULL;
37 static int run_server (int *http_port, int *https_port);
41 handoff_cb (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
42 GstBuffer ** p_outbuf)
44 GST_LOG ("handoff, buf = %p", buf);
45 if (*p_outbuf == NULL)
46 *p_outbuf = gst_buffer_ref (buf);
50 run_test (const char *format, ...)
52 GstStateChangeReturn ret;
53 GstElement *pipe, *src, *sink;
54 GstBuffer *buf = NULL;
60 pipe = gst_pipeline_new (NULL);
62 src = gst_element_factory_make ("souphttpsrc", NULL);
63 fail_unless (src != NULL);
65 sink = gst_element_factory_make ("fakesink", NULL);
66 fail_unless (sink != NULL);
68 gst_bin_add (GST_BIN (pipe), src);
69 gst_bin_add (GST_BIN (pipe), sink);
70 fail_unless (gst_element_link (src, sink));
73 GST_DEBUG ("failed to start soup http server");
75 fail_unless (http_port != 0);
76 va_start (args, format);
77 g_vasprintf (&url, format, args);
79 fail_unless (url != NULL);
80 g_object_set (src, "location", url, NULL);
83 g_object_set (src, "automatic-redirect", redirect, 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);
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);
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) {
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"))
106 else if (g_str_has_suffix (err->message, "Forbidden"))
108 else if (g_str_has_suffix (err->message, "Found"))
110 GST_INFO ("debug: %s", debug);
113 gst_message_unref (msg);
116 gst_message_unref (msg);
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);
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");
130 GST_DEBUG ("buffer offset = %" G_GUINT64_FORMAT, GST_BUFFER_OFFSET (buf));
132 /* first buffer should have a 0 offset */
133 fail_unless (GST_BUFFER_OFFSET (buf) == 0);
134 gst_buffer_unref (buf);
139 gst_element_set_state (pipe, GST_STATE_NULL);
140 gst_object_unref (pipe);
144 GST_START_TEST (test_first_buffer_has_offset)
146 fail_unless (run_test ("http://127.0.0.1:%d/", http_port) == 0);
151 GST_START_TEST (test_not_found)
153 fail_unless (run_test ("http://127.0.0.1:%d/404", http_port) == 404);
158 GST_START_TEST (test_forbidden)
160 fail_unless (run_test ("http://127.0.0.1:%d/403", http_port) == 403);
165 GST_START_TEST (test_redirect_no)
168 fail_unless (run_test ("http://127.0.0.1:%d/302", http_port) == 302);
173 GST_START_TEST (test_redirect_yes)
176 fail_unless (run_test ("http://127.0.0.1:%d/302", http_port) == 0);
181 GST_START_TEST (test_https)
184 GST_INFO ("Failed to start an HTTPS server; let's just skip this test.");
186 fail_unless (run_test ("https://127.0.0.1:%d/", https_port) == 0);
191 GST_START_TEST (test_cookies)
193 static const char *biscotti[] = { "delacre=yummie", "koekje=lu", NULL };
197 rc = run_test ("http://127.0.0.1:%d/", http_port);
199 fail_unless (rc == 0);
204 static gboolean icy_caps = FALSE;
207 got_buffer (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
212 /* Caps can be anything if we don't except icy caps */
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");
222 GST_START_TEST (test_icy_stream)
224 GstElement *pipe, *src, *sink;
227 pipe = gst_pipeline_new (NULL);
229 src = gst_element_factory_make ("souphttpsrc", NULL);
230 fail_unless (src != NULL);
231 g_object_set (src, "iradio-mode", TRUE, NULL);
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);
238 gst_bin_add (GST_BIN (pipe), src);
239 gst_bin_add (GST_BIN (pipe), sink);
240 fail_unless (gst_element_link (src, sink));
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). */
246 g_object_set (src, "location", "http://ogg2.smgradio.com/vr32.ogg", NULL);
247 g_object_set (src, "num-buffers", 1, NULL);
249 gst_element_set_state (pipe, GST_STATE_PLAYING);
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);
259 gst_message_unref (msg);
261 gst_element_set_state (pipe, GST_STATE_NULL);
263 /* Now, if the ogg stream works, the mp3 shoutcast stream should work as
264 * well (time will tell if that's true) */
266 /* Virgin Radio 32kbps mp3 shoutcast stream */
267 g_object_set (src, "location", "http://mp3-vr-32.smgradio.com:80/", NULL);
270 /* EOS after the first buffer */
271 g_object_set (src, "num-buffers", 1, NULL);
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);
277 if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) {
278 GST_DEBUG ("success, we're done here");
279 gst_message_unref (msg);
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);
295 gst_element_set_state (pipe, GST_STATE_NULL);
296 gst_object_unref (pipe);
302 souphttpsrc_suite (void)
305 g_thread_init (NULL);
307 Suite *s = suite_create ("souphttpsrc");
308 TCase *tc_chain = tcase_create ("general");
309 TCase *tc_internet = tcase_create ("internet");
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);
321 suite_add_tcase (s, tc_internet);
322 tcase_set_timeout (tc_internet, 250);
323 tcase_add_test (tc_internet, test_icy_stream);
328 GST_CHECK_MAIN (souphttpsrc);
331 do_get (SoupMessage * msg, const char *path)
335 SoupKnownStatusCode status = SOUP_STATUS_OK;
337 uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
338 GST_DEBUG ("request: \"%s\"", uri);
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;
351 if (SOUP_STATUS_IS_REDIRECTION (status)) {
354 redir_uri = g_strdup_printf ("%s-redirected", uri);
355 soup_message_headers_append (msg->response_headers, "Location", redir_uri);
358 if (status != SOUP_STATUS_OK)
361 if (msg->method == SOUP_METHOD_GET) {
364 buf = g_malloc (buflen);
365 memset (buf, 0, buflen);
366 soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE,
368 } else { /* msg->method == SOUP_METHOD_HEAD */
372 /* We could just use the same code for both GET and
373 * HEAD. But we'll optimize and avoid the extra
376 length = g_strdup_printf ("%lu", (gulong) buflen);
377 soup_message_headers_append (msg->response_headers,
378 "Content-Length", length);
383 soup_message_set_status (msg, status);
388 print_header (const char *name, const char *value, gpointer data)
390 GST_DEBUG ("header: %s: %s", name, value);
394 server_callback (SoupServer * server, SoupMessage * msg,
395 const char *path, GHashTable * query,
396 SoupClientContext * context, gpointer data)
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);
404 if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
407 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
409 GST_DEBUG (" -> %d %s", msg->status_code, msg->reason_phrase);
413 run_server (int *http_port, int *https_port)
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;
426 *http_port = *https_port = 0;
428 server = soup_server_new (SOUP_SERVER_PORT, port, NULL);
430 GST_DEBUG ("Unable to bind to server port %d", port);
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);
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);
444 GST_DEBUG ("Unable to bind to SSL server port %d", ssl_port);
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);