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 * Copyright (C) 2021 Igalia S.L.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <stdlib.h>
28
29 #include <glib.h>
30 #include <glib/gprintf.h>
31
32 #include <libsoup/soup.h>
33 #include <gst/check/gstcheck.h>
34
35 #if ! SOUP_CHECK_VERSION(3, 0, 0)
36 #if !defined(SOUP_MINOR_VERSION) || SOUP_MINOR_VERSION < 44
37 #define SoupStatus SoupKnownStatusCode
38 #endif
39 #endif
40
41 #if SOUP_CHECK_VERSION(3, 0, 0)
42
43 #define SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK "auth-callback"
44 #define SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK "auth-callback"
45
46 #define gst_soup_uri_free g_uri_unref
47 #define gst_soup_uri_to_string(x) g_uri_to_string_partial(x, G_URI_HIDE_PASSWORD)
48 #define gst_soup_uri_get_port(x) g_uri_get_port(x)
49
50 #else
51
52 #define gst_soup_uri_free soup_uri_free
53 #define gst_soup_uri_to_string(x) soup_uri_to_string(x, FALSE)
54 #define gst_soup_uri_get_port(x) soup_uri_get_port(x)
55
56 #define SoupServerMessage SoupMessage
57
58 #define soup_server_message_get_method(x) (x->method)
59 #define soup_server_message_get_http_version(x) soup_message_get_http_version(x)
60 #define soup_server_message_get_status(x) (x->status_code)
61 #define soup_server_message_set_status(x, s, r) soup_message_set_status(x, s)
62 #define soup_server_message_get_reason_phrase(x) (x->reason_phrase)
63 #define soup_server_message_get_uri(x) soup_message_get_uri(x)
64 #define soup_server_message_get_request_headers(x) (x->request_headers)
65 #define soup_server_message_get_response_headers(x) (x->response_headers)
66 #define soup_server_message_get_request_body(x) (x->request_body)
67 #define soup_server_message_get_response_body(x) (x->response_body)
68
69 #endif
70
71 gboolean redirect = TRUE;
72
73 static const char **cookies = NULL;
74
75 /* Variables for authentication tests */
76 static const char *user_id = NULL;
77 static const char *user_pw = NULL;
78 static const char *good_user = "good_user";
79 static const char *bad_user = "bad_user";
80 static const char *good_pw = "good_pw";
81 static const char *bad_pw = "bad_pw";
82 static const char *realm = "SOUPHTTPSRC_REALM";
83 static const char *basic_auth_path = "/basic_auth";
84 static const char *digest_auth_path = "/digest_auth";
85
86 static const char *ssl_cert_file = GST_TEST_FILES_PATH "/test-cert.pem";
87 static const char *ssl_key_file = GST_TEST_FILES_PATH "/test-key.pem";
88
89 static guint get_port_from_server (SoupServer * server);
90 static SoupServer *run_server (gboolean use_https);
91
92 static void
handoff_cb(GstElement * fakesink,GstBuffer * buf,GstPad * pad,GstBuffer ** p_outbuf)93 handoff_cb (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
94 GstBuffer ** p_outbuf)
95 {
96 GST_LOG ("handoff, buf = %p", buf);
97 if (*p_outbuf == NULL)
98 *p_outbuf = gst_buffer_ref (buf);
99 }
100
101 static gboolean
basic_auth_cb(SoupAuthDomain * domain,SoupMessage * msg,const char * username,const char * password,gpointer user_data)102 basic_auth_cb (SoupAuthDomain * domain, SoupMessage * msg,
103 const char *username, const char *password, gpointer user_data)
104 {
105 /* There is only one good login for testing */
106 return (strcmp (username, good_user) == 0)
107 && (strcmp (password, good_pw) == 0);
108 }
109
110
111 static char *
digest_auth_cb(SoupAuthDomain * domain,SoupMessage * msg,const char * username,gpointer user_data)112 digest_auth_cb (SoupAuthDomain * domain, SoupMessage * msg,
113 const char *username, gpointer user_data)
114 {
115 /* There is only one good login for testing */
116 if (strcmp (username, good_user) == 0)
117 return soup_auth_domain_digest_encode_password (good_user, realm, good_pw);
118 return NULL;
119 }
120
121 static gboolean
run_test(gboolean use_https,const gchar * path,gint expected)122 run_test (gboolean use_https, const gchar * path, gint expected)
123 {
124 GstStateChangeReturn ret;
125 GstElement *pipe, *src, *sink;
126 GstBuffer *buf = NULL;
127 GstMessage *msg;
128 gchar *url;
129 gboolean res = FALSE;
130 SoupServer *server;
131 guint port;
132
133 server = run_server (use_https);
134 if (server == NULL) {
135 g_print ("Failed to start up %s server", use_https ? "HTTPS" : "HTTP");
136 /* skip this test */
137 return TRUE;
138 }
139
140 pipe = gst_pipeline_new (NULL);
141
142 src = gst_element_factory_make ("souphttpsrc", NULL);
143 fail_unless (src != NULL);
144
145 sink = gst_element_factory_make ("fakesink", NULL);
146 fail_unless (sink != NULL);
147
148 gst_bin_add (GST_BIN (pipe), src);
149 gst_bin_add (GST_BIN (pipe), sink);
150 fail_unless (gst_element_link (src, sink));
151
152 port = get_port_from_server (server);
153 url = g_strdup_printf ("%s://127.0.0.1:%u%s",
154 use_https ? "https" : "http", port, path);
155 fail_unless (url != NULL);
156 g_object_set (src, "location", url, NULL);
157 g_free (url);
158
159 if (use_https) {
160 GTlsDatabase *tlsdb;
161 GError *error = NULL;
162 gchar *path;
163
164 /* GTlsFileDatabase needs an absolute path. Using a relative one
165 * causes a warning from GLib-Net followed by a segfault in GnuTLS */
166 if (g_path_is_absolute (ssl_cert_file)) {
167 path = g_strdup (ssl_cert_file);
168 } else {
169 gchar *cwd = g_get_current_dir ();
170 path = g_build_filename (cwd, ssl_cert_file, NULL);
171 g_free (cwd);
172 }
173
174 tlsdb = g_tls_file_database_new (path, &error);
175 fail_unless (tlsdb, "Failed to load certificate: %s", error->message);
176
177 g_object_set (src, "tls-database", tlsdb, NULL);
178
179 g_object_unref (tlsdb);
180 g_free (path);
181 }
182
183 g_object_set (src, "automatic-redirect", redirect, NULL);
184 if (cookies != NULL)
185 g_object_set (src, "cookies", cookies, NULL);
186 g_object_set (sink, "signal-handoffs", TRUE, NULL);
187 g_signal_connect (sink, "preroll-handoff", G_CALLBACK (handoff_cb), &buf);
188
189 if (user_id != NULL)
190 g_object_set (src, "user-id", user_id, NULL);
191 if (user_pw != NULL)
192 g_object_set (src, "user-pw", user_pw, NULL);
193
194 ret = gst_element_set_state (pipe, GST_STATE_PAUSED);
195 if (ret != GST_STATE_CHANGE_ASYNC) {
196 GST_DEBUG ("failed to start up soup http src, ret = %d", ret);
197 goto done;
198 }
199
200 gst_element_set_state (pipe, GST_STATE_PLAYING);
201 msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
202 GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
203 if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
204 gchar *debug = NULL;
205 GError *err = NULL;
206 gint rc = -1;
207
208 gst_message_parse_error (msg, &err, &debug);
209 GST_INFO ("error: %s", err->message);
210 if (g_str_has_suffix (err->message, "Not Found"))
211 rc = 404;
212 else if (g_str_has_suffix (err->message, "Forbidden"))
213 rc = 403;
214 else if (g_str_has_suffix (err->message, "Unauthorized"))
215 rc = 401;
216 else if (g_str_has_suffix (err->message, "Found"))
217 rc = 302;
218 GST_INFO ("debug: %s", debug);
219 /* should not've gotten any output in case of a 40x error. Wait a bit
220 * to give the streaming thread a chance to push out a buffer and trigger
221 * our callback before shutting down the pipeline */
222 g_usleep (G_USEC_PER_SEC / 2);
223 fail_unless (buf == NULL);
224 g_error_free (err);
225 g_free (debug);
226 gst_message_unref (msg);
227 GST_DEBUG ("Got HTTP error %u, expected %u", rc, expected);
228 res = (rc == expected);
229 goto done;
230 }
231 gst_message_unref (msg);
232
233 /* don't wait for more than 10 seconds */
234 ret = gst_element_get_state (pipe, NULL, NULL, 10 * GST_SECOND);
235 GST_LOG ("ret = %u", ret);
236
237 if (buf == NULL) {
238 /* we want to test the buffer offset, nothing else; if there's a failure
239 * it might be for lots of reasons (no network connection, whatever), we're
240 * not interested in those */
241 GST_DEBUG ("didn't manage to get data within 10 seconds, skipping test");
242 res = TRUE;
243 goto done;
244 }
245
246 GST_DEBUG ("buffer offset = %" G_GUINT64_FORMAT, GST_BUFFER_OFFSET (buf));
247
248 /* first buffer should have a 0 offset */
249 fail_unless (GST_BUFFER_OFFSET (buf) == 0);
250 gst_buffer_unref (buf);
251 res = (expected == 0);
252
253 done:
254
255 gst_element_set_state (pipe, GST_STATE_NULL);
256 gst_object_unref (pipe);
257 gst_object_unref (server);
258 return res;
259 }
260
GST_START_TEST(test_first_buffer_has_offset)261 GST_START_TEST (test_first_buffer_has_offset)
262 {
263 fail_unless (run_test (FALSE, "/", 0));
264 }
265
266 GST_END_TEST;
267
GST_START_TEST(test_not_found)268 GST_START_TEST (test_not_found)
269 {
270 fail_unless (run_test (FALSE, "/404", 404));
271 fail_unless (run_test (FALSE, "/404-with-data", 404));
272 }
273
274 GST_END_TEST;
275
GST_START_TEST(test_forbidden)276 GST_START_TEST (test_forbidden)
277 {
278 fail_unless (run_test (FALSE, "/403", 403));
279 }
280
281 GST_END_TEST;
282
GST_START_TEST(test_redirect_no)283 GST_START_TEST (test_redirect_no)
284 {
285 redirect = FALSE;
286 fail_unless (run_test (FALSE, "/302", 302));
287 }
288
289 GST_END_TEST;
290
GST_START_TEST(test_redirect_yes)291 GST_START_TEST (test_redirect_yes)
292 {
293 redirect = TRUE;
294 fail_unless (run_test (FALSE, "/302", 0));
295 }
296
297 GST_END_TEST;
298
GST_START_TEST(test_https)299 GST_START_TEST (test_https)
300 {
301 fail_unless (run_test (TRUE, "/", 0));
302 }
303
304 GST_END_TEST;
305
GST_START_TEST(test_cookies)306 GST_START_TEST (test_cookies)
307 {
308 static const char *biscotti[] = { "delacre=yummie", "koekje=lu", NULL };
309 gboolean res;
310
311 cookies = biscotti;
312 res = run_test (FALSE, "/", 0);
313 cookies = NULL;
314 fail_unless (res);
315 }
316
317 GST_END_TEST;
318
GST_START_TEST(test_good_user_basic_auth)319 GST_START_TEST (test_good_user_basic_auth)
320 {
321 gboolean res;
322
323 user_id = good_user;
324 user_pw = good_pw;
325 res = run_test (FALSE, basic_auth_path, 0);
326 GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res);
327 user_id = user_pw = NULL;
328 fail_unless (res);
329 }
330
331 GST_END_TEST;
332
GST_START_TEST(test_bad_user_basic_auth)333 GST_START_TEST (test_bad_user_basic_auth)
334 {
335 gboolean res;
336
337 user_id = bad_user;
338 user_pw = good_pw;
339 res = run_test (FALSE, basic_auth_path, 401);
340 GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res);
341 user_id = user_pw = NULL;
342 fail_unless (res);
343 }
344
345 GST_END_TEST;
346
GST_START_TEST(test_bad_password_basic_auth)347 GST_START_TEST (test_bad_password_basic_auth)
348 {
349 gboolean res;
350
351 user_id = good_user;
352 user_pw = bad_pw;
353 res = run_test (FALSE, basic_auth_path, 401);
354 GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res);
355 user_id = user_pw = NULL;
356 fail_unless (res);
357 }
358
359 GST_END_TEST;
360
GST_START_TEST(test_good_user_digest_auth)361 GST_START_TEST (test_good_user_digest_auth)
362 {
363 gboolean res;
364
365 user_id = good_user;
366 user_pw = good_pw;
367 res = run_test (FALSE, digest_auth_path, 0);
368 GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res);
369 user_id = user_pw = NULL;
370 fail_unless (res);
371 }
372
373 GST_END_TEST;
374
GST_START_TEST(test_bad_user_digest_auth)375 GST_START_TEST (test_bad_user_digest_auth)
376 {
377 gboolean res;
378
379 user_id = bad_user;
380 user_pw = good_pw;
381 res = run_test (FALSE, digest_auth_path, 401);
382 GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res);
383 user_id = user_pw = NULL;
384 fail_unless (res);
385 }
386
387 GST_END_TEST;
388
GST_START_TEST(test_bad_password_digest_auth)389 GST_START_TEST (test_bad_password_digest_auth)
390 {
391 gboolean res;
392
393 user_id = good_user;
394 user_pw = bad_pw;
395 res = run_test (FALSE, digest_auth_path, 401);
396 GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res);
397 user_id = user_pw = NULL;
398 fail_unless (res);
399 }
400
401 GST_END_TEST;
402
403 static gboolean icy_caps = FALSE;
404
405 static void
got_buffer(GstElement * fakesink,GstBuffer * buf,GstPad * pad,gpointer user_data)406 got_buffer (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
407 gpointer user_data)
408 {
409 GstStructure *s;
410 GstCaps *caps;
411
412 /* Caps can be anything if we don't except icy caps */
413 if (!icy_caps)
414 return;
415
416 /* Otherwise they _must_ be "application/x-icy" */
417 caps = gst_pad_get_current_caps (pad);
418 fail_unless (caps != NULL);
419 s = gst_caps_get_structure (caps, 0);
420 fail_unless_equals_string (gst_structure_get_name (s), "application/x-icy");
421 gst_caps_unref (caps);
422 }
423
GST_START_TEST(test_icy_stream)424 GST_START_TEST (test_icy_stream)
425 {
426 GstElement *pipe, *src, *sink;
427
428 GstMessage *msg;
429
430 pipe = gst_pipeline_new (NULL);
431
432 src = gst_element_factory_make ("souphttpsrc", NULL);
433 fail_unless (src != NULL);
434
435 sink = gst_element_factory_make ("fakesink", NULL);
436 fail_unless (sink != NULL);
437 g_object_set (sink, "signal-handoffs", TRUE, NULL);
438 g_signal_connect (sink, "handoff", G_CALLBACK (got_buffer), NULL);
439
440 gst_bin_add (GST_BIN (pipe), src);
441 gst_bin_add (GST_BIN (pipe), sink);
442 fail_unless (gst_element_link (src, sink));
443
444 /* Radionomy Hot40Music shoutcast stream */
445 g_object_set (src, "location",
446 "http://streaming.radionomy.com:80/Hot40Music", NULL);
447
448 /* EOS after the first buffer */
449 g_object_set (src, "num-buffers", 1, NULL);
450 icy_caps = TRUE;
451 gst_element_set_state (pipe, GST_STATE_PLAYING);
452 msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
453 GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
454
455 switch (GST_MESSAGE_TYPE (msg)) {
456 case GST_MESSAGE_EOS:
457 GST_DEBUG ("success, we're done here");
458 gst_message_unref (msg);
459 break;
460 case GST_MESSAGE_ERROR:{
461 GError *err = NULL;
462
463 gst_message_parse_error (msg, &err, NULL);
464 GST_INFO ("Error with ICY mp3 shoutcast stream: %s", err->message);
465 gst_message_unref (msg);
466 g_clear_error (&err);
467 break;
468 }
469 default:
470 break;
471 }
472
473 icy_caps = FALSE;
474
475 gst_element_set_state (pipe, GST_STATE_NULL);
476 gst_object_unref (pipe);
477 }
478
479 GST_END_TEST;
480
481 static Suite *
souphttpsrc_suite(void)482 souphttpsrc_suite (void)
483 {
484 TCase *tc_chain, *tc_internet;
485 Suite *s;
486
487 /* we don't support exceptions from the proxy, so just unset the environment
488 * variable - in case it's set in the test environment it would otherwise
489 * prevent us from connecting to localhost (like jenkins.qa.ubuntu.com) */
490 g_unsetenv ("http_proxy");
491
492 s = suite_create ("souphttpsrc");
493 tc_chain = tcase_create ("general");
494 tc_internet = tcase_create ("internet");
495
496 suite_add_tcase (s, tc_chain);
497 tcase_add_test (tc_chain, test_first_buffer_has_offset);
498 tcase_add_test (tc_chain, test_redirect_yes);
499 tcase_add_test (tc_chain, test_redirect_no);
500 tcase_add_test (tc_chain, test_not_found);
501 tcase_add_test (tc_chain, test_forbidden);
502 tcase_add_test (tc_chain, test_cookies);
503 tcase_add_test (tc_chain, test_good_user_basic_auth);
504 tcase_add_test (tc_chain, test_bad_user_basic_auth);
505 tcase_add_test (tc_chain, test_bad_password_basic_auth);
506 tcase_add_test (tc_chain, test_good_user_digest_auth);
507 tcase_add_test (tc_chain, test_bad_user_digest_auth);
508 tcase_add_test (tc_chain, test_bad_password_digest_auth);
509 tcase_add_test (tc_chain, test_https);
510
511 suite_add_tcase (s, tc_internet);
512 tcase_set_timeout (tc_internet, 250);
513 tcase_add_test (tc_internet, test_icy_stream);
514
515 return s;
516 }
517
518 GST_CHECK_MAIN (souphttpsrc);
519
520 static void
do_get(SoupServerMessage * msg,const char * path)521 do_get (SoupServerMessage * msg, const char *path)
522 {
523 gboolean send_error_doc = FALSE;
524 char *uri;
525
526 int buflen = 4096;
527
528 SoupStatus status = SOUP_STATUS_OK;
529
530 uri = gst_soup_uri_to_string (soup_server_message_get_uri (msg));
531 GST_DEBUG ("request: \"%s\"", uri);
532
533 if (!strcmp (path, "/301"))
534 status = SOUP_STATUS_MOVED_PERMANENTLY;
535 else if (!strcmp (path, "/302"))
536 status = SOUP_STATUS_MOVED_TEMPORARILY;
537 else if (!strcmp (path, "/307"))
538 status = SOUP_STATUS_TEMPORARY_REDIRECT;
539 else if (!strcmp (path, "/403"))
540 status = SOUP_STATUS_FORBIDDEN;
541 else if (!strcmp (path, "/404"))
542 status = SOUP_STATUS_NOT_FOUND;
543 else if (!strcmp (path, "/404-with-data")) {
544 status = SOUP_STATUS_NOT_FOUND;
545 send_error_doc = TRUE;
546 }
547
548 if (SOUP_STATUS_IS_REDIRECTION (status)) {
549 char *redir_uri;
550
551 redir_uri = g_strdup_printf ("%s-redirected", uri);
552 soup_message_headers_append (soup_server_message_get_response_headers (msg),
553 "Location", redir_uri);
554 g_free (redir_uri);
555 }
556 if (status != (SoupStatus) SOUP_STATUS_OK && !send_error_doc)
557 goto leave;
558
559 if (soup_server_message_get_method (msg) == SOUP_METHOD_GET) {
560 char *buf;
561
562 buf = g_malloc (buflen);
563 memset (buf, 0, buflen);
564 soup_message_body_append (soup_server_message_get_response_body (msg),
565 SOUP_MEMORY_TAKE, buf, buflen);
566 } else { /* method == SOUP_METHOD_HEAD */
567
568 char *length;
569
570 /* We could just use the same code for both GET and
571 * HEAD. But we'll optimize and avoid the extra
572 * malloc.
573 */
574 length = g_strdup_printf ("%lu", (gulong) buflen);
575 soup_message_headers_append (soup_server_message_get_response_headers (msg),
576 "Content-Length", length);
577 g_free (length);
578 }
579
580 leave:
581 soup_server_message_set_status (msg, status, NULL);
582 g_free (uri);
583 }
584
585 static void
print_header(const char * name,const char * value,gpointer data)586 print_header (const char *name, const char *value, gpointer data)
587 {
588 GST_DEBUG ("header: %s: %s", name, value);
589 }
590
591 static void
server_callback(SoupServer * server,SoupServerMessage * msg,const char * path,GHashTable * query,SoupClientContext * context,gpointer data)592 server_callback (SoupServer * server, SoupServerMessage * msg,
593 const char *path, GHashTable * query,
594 #if !SOUP_CHECK_VERSION(3, 0, 0)
595 SoupClientContext * context,
596 #endif
597 gpointer data)
598 {
599 const char *method = soup_server_message_get_method (msg);
600
601 GST_DEBUG ("%s %s HTTP/1.%d", method, path,
602 soup_server_message_get_http_version (msg));
603 soup_message_headers_foreach (soup_server_message_get_request_headers (msg),
604 print_header, NULL);
605 if (soup_server_message_get_request_body (msg)->length)
606 GST_DEBUG ("%s", soup_server_message_get_request_body (msg)->data);
607
608 if (method == SOUP_METHOD_GET || method == SOUP_METHOD_HEAD)
609 do_get (msg, path);
610 else
611 soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
612
613 GST_DEBUG (" -> %d %s", soup_server_message_get_status (msg),
614 soup_server_message_get_reason_phrase (msg));
615 }
616
617 static guint
get_port_from_server(SoupServer * server)618 get_port_from_server (SoupServer * server)
619 {
620 GSList *uris;
621 guint port;
622
623 uris = soup_server_get_uris (server);
624 g_assert (g_slist_length (uris) == 1);
625 port = gst_soup_uri_get_port (uris->data);
626 g_slist_free_full (uris, (GDestroyNotify) gst_soup_uri_free);
627
628 return port;
629 }
630
631 static SoupServer *
run_server(gboolean use_https)632 run_server (gboolean use_https)
633 {
634 SoupServer *server = soup_server_new (NULL, NULL);
635 SoupServerListenOptions listen_flags = 0;
636 guint port;
637
638
639 if (use_https) {
640 GTlsBackend *backend = g_tls_backend_get_default ();
641 GError *err = NULL;
642
643 if (backend == NULL || !g_tls_backend_supports_tls (backend)) {
644 GST_INFO ("No TLS support");
645 g_object_unref (server);
646 return NULL;
647 }
648 #if SOUP_CHECK_VERSION(3, 0, 0)
649 {
650 GTlsCertificate *cert =
651 g_tls_certificate_new_from_files (ssl_cert_file, ssl_key_file,
652 &err);
653 if (!cert) {
654 GST_INFO ("Failed to load certificate: %s", err->message);
655 g_error_free (err);
656 return NULL;
657 }
658 soup_server_set_tls_certificate (server, cert);
659 g_object_unref (cert);
660 }
661 #else
662 if (!soup_server_set_ssl_cert_file (server, ssl_cert_file, ssl_key_file,
663 &err)) {
664 GST_INFO ("Failed to load certificate: %s", err->message);
665 g_object_unref (server);
666 g_error_free (err);
667 return NULL;
668 }
669 #endif
670
671 listen_flags |= SOUP_SERVER_LISTEN_HTTPS;
672 }
673
674 soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
675
676 {
677 SoupAuthDomain *domain;
678
679 domain = soup_auth_domain_basic_new ("realm", realm,
680 SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_cb, NULL);
681 soup_auth_domain_add_path (domain, basic_auth_path);
682 soup_server_add_auth_domain (server, domain);
683 g_object_unref (domain);
684
685 domain = soup_auth_domain_digest_new ("realm", realm,
686 SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, digest_auth_cb, NULL);
687 soup_auth_domain_add_path (domain, digest_auth_path);
688 soup_server_add_auth_domain (server, domain);
689 g_object_unref (domain);
690 }
691
692 {
693 GSocketAddress *address;
694 GError *err = NULL;
695
696 address = g_inet_socket_address_new_from_string ("0.0.0.0", 0);
697 soup_server_listen (server, address, listen_flags, &err);
698 g_object_unref (address);
699
700 if (err) {
701 GST_ERROR ("Failed to start %s server: %s",
702 use_https ? "HTTPS" : "HTTP", err->message);
703 g_object_unref (server);
704 g_error_free (err);
705 return NULL;
706 }
707 }
708
709 port = get_port_from_server (server);
710 GST_DEBUG ("%s server listening on port %u", use_https ? "HTTPS" : "HTTP",
711 port);
712
713 /* check if we can connect to our local http server */
714 {
715 GSocketConnection *conn;
716 GSocketClient *client;
717
718 client = g_socket_client_new ();
719 g_socket_client_set_timeout (client, 2);
720 conn =
721 g_socket_client_connect_to_host (client, "127.0.0.1", port, NULL, NULL);
722 if (conn == NULL) {
723 GST_INFO ("Couldn't connect to 127.0.0.1:%u", port);
724 g_object_unref (client);
725 g_object_unref (server);
726 return NULL;
727 }
728
729 g_object_unref (conn);
730 g_object_unref (client);
731 }
732
733 return server;
734 }
735