• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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