diff options
| author | Dan Winship <danw@gnome.org> | 2014-05-02 09:13:27 -0400 |
|---|---|---|
| committer | Dan Winship <danw@gnome.org> | 2014-05-02 09:13:27 -0400 |
| commit | 2e28875a3ba7e0302cb181a85b7681d6b32ffd1b (patch) | |
| tree | 66ff8de75af2f26c91f55c2e22a4c1be3be22c75 | |
| parent | d17d1baeb000ce398f61d58a813a69a8bd7cef08 (diff) | |
| parent | b067c1aa35e04e2e424625683e04f02f81e0ae93 (diff) | |
| download | libsoup-2e28875a3ba7e0302cb181a85b7681d6b32ffd1b.tar.gz | |
soup-server: add new-and-improved APIs
https://bugzilla.gnome.org/show_bug.cgi?id=522519
https://bugzilla.gnome.org/show_bug.cgi?id=561547
https://bugzilla.gnome.org/show_bug.cgi?id=621138
44 files changed, 2140 insertions, 644 deletions
diff --git a/docs/reference/libsoup-2.4-docs.sgml b/docs/reference/libsoup-2.4-docs.sgml index c0c8a05c..ba33142a 100644 --- a/docs/reference/libsoup-2.4-docs.sgml +++ b/docs/reference/libsoup-2.4-docs.sgml @@ -11,8 +11,8 @@ <xi:include href="build-howto.xml"/> <xi:include href="client-howto.xml"/> <xi:include href="request-howto.xml"/> - <xi:include href="server-howto.xml"/> <xi:include href="session-porting.xml"/> + <xi:include href="server-howto.xml"/> </chapter> <chapter> @@ -35,6 +35,7 @@ <xi:include href="xml/soup-request-file.xml"/> <xi:include href="xml/soup-request-data.xml"/> <xi:include href="xml/soup-server.xml"/> + <xi:include href="xml/soup-server-deprecated.xml"/> <xi:include href="xml/soup-session.xml"/> <xi:include href="xml/soup-session-async.xml"/> <xi:include href="xml/soup-session-sync.xml"/> diff --git a/docs/reference/libsoup-2.4-sections.txt b/docs/reference/libsoup-2.4-sections.txt index 03d35d4b..11f650fb 100644 --- a/docs/reference/libsoup-2.4-sections.txt +++ b/docs/reference/libsoup-2.4-sections.txt @@ -216,25 +216,28 @@ SoupKnownStatusCode <TITLE>SoupServer</TITLE> SoupServer soup_server_new -soup_server_is_https -soup_server_get_port -soup_server_get_listener -soup_server_run -soup_server_run_async -soup_server_quit +soup_server_set_ssl_cert_file +<SUBSECTION> +SoupServerListenOptions +soup_server_listen +soup_server_listen_all +soup_server_listen_local +soup_server_listen_socket +soup_server_listen_fd soup_server_disconnect -soup_server_get_async_context +soup_server_is_https <SUBSECTION> SoupServerCallback soup_server_add_handler soup_server_remove_handler <SUBSECTION> SoupClientContext -soup_client_context_get_socket -soup_client_context_get_address +soup_client_context_get_local_address +soup_client_context_get_remote_address soup_client_context_get_host soup_client_context_get_auth_domain soup_client_context_get_auth_user +soup_client_context_get_gsocket <SUBSECTION> soup_server_add_auth_domain soup_server_remove_auth_domain @@ -242,12 +245,7 @@ soup_server_remove_auth_domain soup_server_pause_message soup_server_unpause_message <SUBSECTION> -SOUP_SERVER_PORT -SOUP_SERVER_INTERFACE -SOUP_SERVER_SSL_CERT_FILE -SOUP_SERVER_SSL_KEY_FILE SOUP_SERVER_TLS_CERTIFICATE -SOUP_SERVER_ASYNC_CONTEXT SOUP_SERVER_RAW_PATHS SOUP_SERVER_SERVER_HEADER SOUP_SERVER_HTTP_ALIASES @@ -266,6 +264,26 @@ soup_client_context_get_type </SECTION> <SECTION> +<FILE>soup-server-deprecated</FILE> +<TITLE>SoupServer deprecated API</TITLE> +soup_server_get_port +soup_server_get_listener +soup_server_run +soup_server_run_async +soup_server_quit +soup_server_get_async_context +<SUBSECTION> +soup_client_context_get_socket +soup_client_context_get_address +<SUBSECTION> +SOUP_SERVER_PORT +SOUP_SERVER_INTERFACE +SOUP_SERVER_SSL_CERT_FILE +SOUP_SERVER_SSL_KEY_FILE +SOUP_SERVER_ASYNC_CONTEXT +</SECTION> + +<SECTION> <FILE>soup-auth-domain</FILE> <TITLE>SoupAuthDomain</TITLE> SoupAuthDomain diff --git a/docs/reference/server-howto.xml b/docs/reference/server-howto.xml index 05fb0c05..336a2af4 100644 --- a/docs/reference/server-howto.xml +++ b/docs/reference/server-howto.xml @@ -3,17 +3,17 @@ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"> <refentry id="libsoup-server-howto"> <refmeta> -<refentrytitle>Soup Server Basics</refentrytitle> +<refentrytitle>libsoup Server Basics</refentrytitle> <manvolnum>3</manvolnum> <refmiscinfo>LIBSOUP Library</refmiscinfo> </refmeta> <refnamediv> -<refname>Soup Server Basics</refname><refpurpose>Server-side tutorial</refpurpose> +<refname>libsoup Server Basics</refname><refpurpose>Server-side tutorial</refpurpose> </refnamediv> <refsect2> -<title>Creating a SoupSession</title> +<title>Creating a SoupServer</title> <para> As with the client API, there is a single object that will encapsulate @@ -25,61 +25,45 @@ linkend="SoupServer"><type>SoupServer</type></link>. You create the server with <link linkend="soup-server-new"><function>soup_server_new</function></link>, and as with the <type>SoupSession</type> constructor, you can specify -various additional options: +a few additional options: </para> <variablelist> <varlistentry> - <term><link linkend="SOUP-SERVER-PORT:CAPS"><literal>SOUP_SERVER_PORT</literal></link></term> + <term><link linkend="SOUP-SERVER-TLS-CERTIFICATE:CAPS"><literal>SOUP_SERVER_TLS_CERTIFICATE</literal></link></term> <listitem><para> - The TCP port to listen on. If <literal>0</literal> (or - left unspecified), some unused port will be selected for - you. (You can find out what port by calling <link - linkend="soup-server-get-port"><function>soup_server_get_port</function></link>. + A <link + linkend="GTlsCertificate"><type>GTlsCertificate</type></link> + (containing a private key) that will be used when handling + HTTPS requests on the server. </para></listitem> </varlistentry> <varlistentry> - <term><link linkend="SOUP-SERVER-INTERFACE:CAPS"><literal>SOUP_SERVER_INTERFACE</literal></link></term> - <listitem><para> - A <link linkend="SoupAddress"><type>SoupAddress</type></link>, - specifying the IP address of the network interface to run - the server on. If <literal>NULL</literal> (or left - unspecified), the server will listen on all interfaces. - </para></listitem> - </varlistentry> - <varlistentry> - <term><link linkend="SOUP-SERVER-SSL-CERT-FILE:CAPS"><literal>SOUP_SERVER_SSL_CERT_FILE</literal></link></term> - <listitem><para> - Points to a file containing an SSL certificate to use. If - this is set, then the server will speak HTTPS; otherwise - it will speak HTTP. - </para></listitem> - </varlistentry> - <varlistentry> - <term><link linkend="SOUP-SERVER-SSL-KEY-FILE:CAPS"><literal>SOUP_SERVER_SSL_KEY_FILE</literal></link></term> + <term><link linkend="SOUP-SERVER-RAW-PATHS:CAPS"><literal>SOUP_SERVER_RAW_PATHS</literal></link></term> <listitem><para> - Points to a file containing the private key for the - <literal>SOUP_SERVER_SSL_CERT_FILE</literal>. (It may - point to the same file.) + Set this to <literal>TRUE</literal> if you don't want + <application>libsoup</application> to decode %-encoding + in the Request-URI. (Eg, because you need to treat + <literal>"/foo/bar"</literal> and + <literal>"/foo%2Fbar"</literal> as different paths. </para></listitem> </varlistentry> <varlistentry> - <term><link linkend="SOUP-SERVER-ASYNC-CONTEXT:CAPS"><literal>SOUP_SERVER_ASYNC_CONTEXT</literal></link></term> + <term><link linkend="SOUP-SERVER-SERVER-HEADER:CAPS"><literal>SOUP_SERVER_SERVER_HEADER</literal></link></term> <listitem><para> - A <link linkend="GMainContext"><type>GMainContext</type></link> which - the server will use for asynchronous operations. This can - be set if you want to use a SoupServer in a thread - other than the main thread. + Allows you to set a Server header string that will be sent + on all responses. </para></listitem> </varlistentry> <varlistentry> - <term><link linkend="SOUP-SERVER-RAW-PATHS:CAPS"><literal>SOUP_SERVER_RAW_PATHS</literal></link></term> + <term><link linkend="SOUP-SERVER-HTTP-ALIASES:CAPS"><literal>SOUP_SERVER_HTTP_ALIASES</literal></link> + and <link linkend="SOUP-SERVER-HTTPS-ALIASES:CAPS"><literal>SOUP_SERVER_HTTPS_ALIASES</literal></link></term> <listitem><para> - Set this to <literal>TRUE</literal> if you don't want - <application>libsoup</application> to decode %-encoding - in the Request-URI. (Eg, because you need to treat - <literal>"/foo/bar"</literal> and - <literal>"/foo%2Fbar"</literal> as different paths. + Allow you to tell the server to recognize additional URI + schemes as aliases for "<literal>http</literal>" or + <literal>https</literal>. You can set this if you are + serving URIs with schemes like "<literal>dav</literal>" or + "<literal>webcal</literal>". </para></listitem> </varlistentry> </variablelist> @@ -87,6 +71,90 @@ various additional options: </refsect2> <refsect2> +<title>Adding Listening Sockets</title> + +<para> + To tell the server where to listen, call <link + linkend="soup-server-listen"><function>soup_server_listen</function></link> + (to listen on a specific <link + linkend="GSocketAddress"><type>GSocketAddress</type></link>), <link + linkend="soup-server-listen-all"><function>soup_server_listen_all</function></link> + (to listen on a given port on all network interfaces), or <link + linkend="soup-server-listen-local"><function>soup_server_listen_local</function></link> + (to listen to a given port on the loopback interface only). You can + call any of these functions multiple times, to set up multiple + listening sockets. +</para> + +<para> + To set up an HTTPS server, you must first either set the <link + linkend="SOUP-SERVER-TLS-CERTIFICATE:CAPS"><literal>SOUP_SERVER_TLS_CERTIFICATE</literal></link> + property, or else call <link + linkend="soup-server-set-ssl-cert-file"><function>soup_server_set_ssl_cert_file</function></link>. + After that you can pass the <link + linkend="SOUP-SERVER-LISTEN-HTTPS:CAPS"><literal>SOUP_SERVER_LISTEN_HTTPS</literal></link> + option to <link + linkend="soup-server-listen"><function>soup_server_listen</function></link>, + etc. +</para> + +<para> + By default, servers listen for both IPv4 and IPv6 connections; if + you don't want this, use the <link + linkend="SOUP-SERVER-LISTEN-IPV4-ONLY:CAPS"><literal>SOUP_SERVER_LISTEN_IPV4_ONLY</literal></link> + or <link + linkend="SOUP-SERVER-LISTEN-IPV6-ONLY:CAPS"><literal>SOUP_SERVER_LISTEN_IPV6_ONLY</literal></link> + options. +</para> + +<para> + The server runs asynchronously, in the thread-default + <link linkend="GMainContext"><type>GMainContext</type></link> of the + thread in which the "listen" calls were made. +</para> +</refsect2> + +<refsect2 id="soup-server-old-api"> +<title>The Old SoupServer Listening API</title> + +<para> +<link +linkend="soup-server-listen"><function>soup_server_listen</function></link>, +etc, are available only in <application>libsoup</application> 2.46 and +later. In earlier versions, there was a simpler API, in which a server +could only listen on a single port, determined at construct time +either by passing the <link +linkend="SOUP-SERVER-INTERFACE:CAPS"><literal>SOUP_SERVER_INTERFACE</literal></link> +property (to specify a <link +linkend="SoupAddress"><type>SoupAddress</type></link> to listen on), +or the <link +linkend="SOUP-SERVER-PORT:CAPS"><literal>SOUP_SERVER_PORT</literal></link> +property (to specify a port to listen on, on all interfaces). The <link +linkend="SOUP-SERVER-SSL-CERT-FILE:CAPS"><literal>SOUP_SERVER_SSL_CERT_FILE</literal></link> +and <link +linkend="SOUP-SERVER-SSL-KEY-FILE:CAPS"><literal>SOUP_SERVER_SSL_KEY_FILE</literal></link> +properties could be used to create an HTTP server. +</para> + +<para> +When using this API, if <link +linkend="SoupServer"><type>SoupServer</type></link> is unable to bind +the listening socket, or unable to read the provided certificate or +key files, then it will return <literal>NULL</literal> from its +constructor (with no further indication of what exactly went wrong). +</para> + +<para> +Additionally, when using this API, it is necessary to call <link +linkend="soup-server-run"><function>soup_server_run</function></link> +or <link +linkend="soup-server-run-async"><function>soup_server_run_async</function></link> +to start the server after creating it. +</para> + +</refsect2> + +<refsect2> <title>Adding Handlers</title> <para> diff --git a/examples/simple-httpd.c b/examples/simple-httpd.c index 71ff874c..f3be82e1 100644 --- a/examples/simple-httpd.c +++ b/examples/simple-httpd.c @@ -107,6 +107,7 @@ do_get (SoupServer *server, SoupMessage *msg, const char *path) soup_message_set_response (msg, "text/html", SOUP_MEMORY_TAKE, listing->str, listing->len); + soup_message_set_status (msg, SOUP_STATUS_OK); g_string_free (listing, FALSE); return; } @@ -213,22 +214,19 @@ quit (int sig) exit (0); } -static int port, ssl_port; -static const char *ssl_cert_file, *ssl_key_file; +static int port; +static const char *tls_cert_file, *tls_key_file; static GOptionEntry entries[] = { { "cert-file", 'c', 0, - G_OPTION_ARG_STRING, &ssl_cert_file, + G_OPTION_ARG_STRING, &tls_cert_file, "Use FILE as the TLS certificate file", "FILE" }, { "key-file", 'k', 0, - G_OPTION_ARG_STRING, &ssl_key_file, + G_OPTION_ARG_STRING, &tls_key_file, "Use FILE as the TLS private key file", "FILE" }, { "port", 'p', 0, G_OPTION_ARG_INT, &port, "Port to listen on", NULL }, - { "ssl-port", 's', 0, - G_OPTION_ARG_INT, &port, - "Port to listen on for TLS traffic", NULL }, { NULL } }; @@ -237,7 +235,10 @@ main (int argc, char **argv) { GOptionContext *opts; GMainLoop *loop; - SoupServer *server, *ssl_server; + SoupServer *server; + GSList *uris, *u; + char *str; + GTlsCertificate *cert; GError *error = NULL; opts = g_option_context_new (NULL); @@ -258,36 +259,35 @@ main (int argc, char **argv) signal (SIGINT, quit); - server = soup_server_new (SOUP_SERVER_PORT, port, - SOUP_SERVER_SERVER_HEADER, "simple-httpd ", - NULL); - if (!server) { - g_printerr ("Unable to bind to server port %d\n", port); - exit (1); + if (tls_cert_file && tls_key_file) { + cert = g_tls_certificate_new_from_files (tls_cert_file, tls_key_file, &error); + if (error) { + g_printerr ("Unable to create server: %s\n", error->message); + exit (1); + } + server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "simple-httpd ", + SOUP_SERVER_TLS_CERTIFICATE, cert, + NULL); + g_object_unref (cert); + + soup_server_listen_all (server, port, SOUP_SERVER_LISTEN_HTTPS, &error); + } else { + server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "simple-httpd ", + NULL); + soup_server_listen_all (server, port, 0, &error); } + soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - g_print ("\nStarting Server on port %d\n", - soup_server_get_port (server)); - soup_server_run_async (server); - - if (ssl_cert_file && ssl_key_file) { - ssl_server = soup_server_new ( - SOUP_SERVER_PORT, ssl_port, - SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file, - SOUP_SERVER_SSL_KEY_FILE, ssl_key_file, - NULL); - - if (!ssl_server) { - g_printerr ("Unable to bind to SSL server port %d\n", ssl_port); - exit (1); - } - soup_server_add_handler (ssl_server, NULL, - server_callback, NULL, NULL); - g_print ("Starting SSL Server on port %d\n", - soup_server_get_port (ssl_server)); - soup_server_run_async (ssl_server); + + uris = soup_server_get_uris (server); + for (u = uris; u; u = u->next) { + str = soup_uri_to_string (u->data, FALSE); + g_print ("Listening on %s\n", str); + g_free (str); + soup_uri_free (u->data); } + g_slist_free (uris); g_print ("\nWaiting for requests...\n"); diff --git a/examples/simple-proxy.c b/examples/simple-proxy.c index db8c9f96..08bd847c 100644 --- a/examples/simple-proxy.c +++ b/examples/simple-proxy.c @@ -141,6 +141,8 @@ main (int argc, char **argv) { GOptionContext *opts; GMainLoop *loop; + GSList *uris, *u; + char *str; GError *error = NULL; opts = g_option_context_new (NULL); @@ -162,12 +164,7 @@ main (int argc, char **argv) signal (SIGINT, quit); - server = soup_server_new (SOUP_SERVER_PORT, port, - NULL); - if (!server) { - g_printerr ("Unable to bind to server port %d\n", port); - exit (1); - } + server = g_object_new (SOUP_TYPE_SERVER, NULL); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); if (require_auth) { @@ -182,9 +179,20 @@ main (int argc, char **argv) g_object_unref (auth_domain); } - g_print ("\nStarting proxy on port %d\n", - soup_server_get_port (server)); - soup_server_run_async (server); + soup_server_listen_all (server, port, 0, &error); + if (error) { + g_printerr ("Unable to create server: %s\n", error->message); + exit (1); + } + + uris = soup_server_get_uris (server); + for (u = uris; u; u = u->next) { + str = soup_uri_to_string (u->data, FALSE); + g_print ("Listening on %s\n", str); + g_free (str); + soup_uri_free (u->data); + } + g_slist_free (uris); session = soup_session_async_new (); diff --git a/libsoup/libsoup-2.4.sym b/libsoup/libsoup-2.4.sym index be4cd7f4..d1b83889 100644 --- a/libsoup/libsoup-2.4.sym +++ b/libsoup/libsoup-2.4.sym @@ -87,7 +87,10 @@ soup_check_version soup_client_context_get_address soup_client_context_get_auth_domain soup_client_context_get_auth_user +soup_client_context_get_gsocket soup_client_context_get_host +soup_client_context_get_local_address +soup_client_context_get_remote_address soup_client_context_get_socket soup_client_context_get_type soup_connection_state_get_type @@ -344,9 +347,18 @@ soup_server_add_handler soup_server_disconnect soup_server_get_async_context soup_server_get_listener +soup_server_get_listeners +soup_server_get_gsocket soup_server_get_port soup_server_get_type +soup_server_get_uris soup_server_is_https +soup_server_listen +soup_server_listen_all +soup_server_listen_fd +soup_server_listen_local +soup_server_listen_socket +soup_server_listen_options_get_type soup_server_new soup_server_pause_message soup_server_quit diff --git a/libsoup/soup-address.c b/libsoup/soup-address.c index b2d1647a..ecbd9a7f 100644 --- a/libsoup/soup-address.c +++ b/libsoup/soup-address.c @@ -384,6 +384,17 @@ soup_address_new_from_sockaddr (struct sockaddr *sa, int len) NULL); } +SoupAddress * +soup_address_new_from_gsockaddr (GSocketAddress *addr) +{ + struct sockaddr_storage sa; + + g_socket_address_to_native (addr, &sa, sizeof (sa), NULL); + return g_object_new (SOUP_TYPE_ADDRESS, + SOUP_ADDRESS_SOCKADDR, &sa, + NULL); +} + /** * SoupAddressFamily: * @SOUP_ADDRESS_FAMILY_INVALID: an invalid %SoupAddress diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h index 0796ba00..9860deea 100644 --- a/libsoup/soup-message-private.h +++ b/libsoup/soup-message-private.h @@ -69,6 +69,7 @@ void soup_message_send_request (SoupMessageQueueItem *item, gpointer user_data); void soup_message_read_request (SoupMessage *msg, SoupSocket *sock, + gboolean use_thread_context, SoupMessageCompletionFn completion_cb, gpointer user_data); diff --git a/libsoup/soup-message-server-io.c b/libsoup/soup-message-server-io.c index f70e9a9d..00272f33 100644 --- a/libsoup/soup-message-server-io.c +++ b/libsoup/soup-message-server-io.c @@ -253,17 +253,22 @@ get_response_headers (SoupMessage *msg, GString *headers, void soup_message_read_request (SoupMessage *msg, SoupSocket *sock, + gboolean use_thread_context, SoupMessageCompletionFn completion_cb, gpointer user_data) { GMainContext *async_context; GIOStream *iostream; - g_object_get (sock, - SOUP_SOCKET_ASYNC_CONTEXT, &async_context, - NULL); - if (!async_context) - async_context = g_main_context_ref (g_main_context_default ()); + if (use_thread_context) + async_context = g_main_context_ref_thread_default (); + else { + g_object_get (sock, + SOUP_SOCKET_ASYNC_CONTEXT, &async_context, + NULL); + if (!async_context) + async_context = g_main_context_ref (g_main_context_default ()); + } iostream = soup_socket_get_iostream (sock); @@ -272,6 +277,5 @@ soup_message_read_request (SoupMessage *msg, parse_request_headers, sock, completion_cb, user_data); - if (async_context) - g_main_context_unref (async_context); + g_main_context_unref (async_context); } diff --git a/libsoup/soup-misc-private.h b/libsoup/soup-misc-private.h index 94aa71d1..78265330 100644 --- a/libsoup/soup-misc-private.h +++ b/libsoup/soup-misc-private.h @@ -40,4 +40,6 @@ guint soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs, SoupRange **ranges, int *length); +SoupAddress *soup_address_new_from_gsockaddr (GSocketAddress *addr); + #endif /* SOUP_MISC_PRIVATE_H */ diff --git a/libsoup/soup-server.c b/libsoup/soup-server.c index 18aefba5..13d6e5e5 100644 --- a/libsoup/soup-server.c +++ b/libsoup/soup-server.c @@ -11,11 +11,14 @@ #include <string.h> +#include <glib/gi18n-lib.h> + #include "soup-server.h" #include "soup.h" #include "soup-message-private.h" #include "soup-misc-private.h" #include "soup-path-map.h" +#include "soup-socket-private.h" /** * SECTION:soup-server @@ -23,6 +26,12 @@ * @see_also: #SoupAuthDomain * * #SoupServer implements a simple HTTP server. + * + * (The following documentation describes the current #SoupServer API, + * available in <application>libsoup</application> 2.48 and later. See + * the section "<link linkend="soup-server-old-api">The Old SoupServer + * Listening API</link>" in the server how-to documentation for + * details on the older #SoupServer API.) * * To begin, create a server using soup_server_new(). Add at least one * handler by calling soup_server_add_handler(); the handler will be @@ -44,16 +53,28 @@ * Additional processing options are available via #SoupServer's * signals; Connect to #SoupServer::request-started to be notified * every time a new request is being processed. (This gives you a - * chance to connect to the #SoupMessage "got-" signals in case you - * want to do processing before the body has been fully read.) + * chance to connect to the #SoupMessage "<literal>got-</literal>" + * signals in case you want to do processing before the body has been + * fully read.) * - * Once the server is set up, start it processing connections by - * calling soup_server_run_async() or soup_server_run(). #SoupServer - * runs via the glib main loop; if you need to have a server that runs - * in another thread (or merely isn't bound to the default main loop), - * create a #GMainContext for it to use, and set that via the - * #SOUP_SERVER_ASYNC_CONTEXT property. - **/ + * If you want to process https connections in addition to (or instead + * of) http connections, you can either set the + * %SOUP_SERVER_TLS_CERTIFICATE property when creating the server, or + * else call soup_server_set_ssl_certificate() after creating it. + * + * Once the server is set up, make one or more calls to + * soup_server_listen(), soup_server_listen_local(), or + * soup_server_listen_all() to tell it where to listen for + * connections. (All ports on a #SoupServer use the same handlers; if + * you need to handle some ports differently, such as returning + * different data for http and https, you'll need to create multiple + * #SoupServers, or else check the passed-in URI in the handler + * function.). + * + * #SoupServer will begin processing connections as soon as you return + * to (or start) the main loop for the current thread-default + * #GMainContext. + */ G_DEFINE_TYPE (SoupServer, soup_server, G_TYPE_OBJECT) @@ -74,6 +95,9 @@ struct SoupClientContext { SoupAuthDomain *auth_domain; char *auth_user; + GSocketAddress *remote_addr; + GSocketAddress *local_addr; + int ref_count; }; @@ -86,28 +110,30 @@ typedef struct { } SoupServerHandler; typedef struct { - SoupAddress *iface; - guint port; + GSList *listeners; + GSList *clients; char *ssl_cert_file, *ssl_key_file; - GTlsCertificate *ssl_cert; + GTlsCertificate *tls_cert; char *server_header; + GMainContext *async_context; GMainLoop *loop; - SoupSocket *listen_sock; - GSList *clients; - gboolean raw_paths; SoupPathMap *handlers; SoupServerHandler *default_handler; GSList *auth_domains; - GMainContext *async_context; - char **http_aliases, **https_aliases; + + SoupAddress *legacy_iface; + int legacy_port; + + gboolean disposed; + } SoupServerPrivate; #define SOUP_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SERVER, SoupServerPrivate)) @@ -120,6 +146,8 @@ enum { PROP_INTERFACE, PROP_SSL_CERT_FILE, PROP_SSL_KEY_FILE, + PROP_TLS_CERT_FILE, + PROP_TLS_KEY_FILE, PROP_TLS_CERTIFICATE, PROP_ASYNC_CONTEXT, PROP_RAW_PATHS, @@ -150,6 +178,20 @@ soup_server_init (SoupServer *server) priv->http_aliases = g_new (char *, 2); priv->http_aliases[0] = (char *)g_intern_string ("*"); priv->http_aliases[1] = NULL; + + priv->legacy_port = -1; +} + +static void +soup_server_dispose (GObject *object) +{ + SoupServer *server = SOUP_SERVER (object); + SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server); + + priv->disposed = TRUE; + soup_server_disconnect (server); + + G_OBJECT_CLASS (soup_server_parent_class)->finalize (object); } static void @@ -158,16 +200,14 @@ soup_server_finalize (GObject *object) SoupServer *server = SOUP_SERVER (object); SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server); - g_clear_object (&priv->iface); + g_clear_object (&priv->legacy_iface); g_free (priv->ssl_cert_file); g_free (priv->ssl_key_file); - g_clear_object (&priv->ssl_cert); + g_clear_object (&priv->tls_cert); g_free (priv->server_header); - g_clear_object (&priv->listen_sock); - while (priv->clients) { SoupClientContext *client = priv->clients->data; SoupSocket *sock = g_object_ref (client->sock); @@ -206,6 +246,42 @@ soup_server_finalize (GObject *object) G_OBJECT_CLASS (soup_server_parent_class)->finalize (object); } +static gboolean +soup_server_ensure_listening (SoupServer *server) +{ + SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server); + SoupSocket *listener; + + if (priv->listeners) + return TRUE; + + if (!priv->legacy_iface) { + priv->legacy_iface = + soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4, + priv->legacy_port); + } + + listener = soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, priv->legacy_iface, + SOUP_SOCKET_SSL_CREDENTIALS, priv->tls_cert, + SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context, + NULL); + if (!soup_socket_listen (listener)) { + g_object_unref (listener); + return FALSE; + } + + /* Re-resolve the interface address, in particular in case + * the passed-in address had SOUP_ADDRESS_ANY_PORT. + */ + g_object_unref (priv->legacy_iface); + priv->legacy_iface = soup_socket_get_local_address (listener); + g_object_ref (priv->legacy_iface); + priv->legacy_port = soup_address_get_port (priv->legacy_iface); + + priv->listeners = g_slist_prepend (priv->listeners, listener); + return TRUE; +} + static GObject * soup_server_constructor (GType type, guint n_construct_properties, @@ -213,27 +289,25 @@ soup_server_constructor (GType type, { GObject *server; SoupServerPrivate *priv; + gboolean legacy_port_set; - server = G_OBJECT_CLASS (soup_server_parent_class)->constructor ( - type, n_construct_properties, construct_properties); - if (!server) - return NULL; + server = G_OBJECT_CLASS (soup_server_parent_class)-> + constructor (type, n_construct_properties, construct_properties); priv = SOUP_SERVER_GET_PRIVATE (server); - if (!priv->iface) { - priv->iface = - soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4, - priv->port); - } - + /* For backward compatibility, we have to process the + * :ssl-cert-file, :ssl-key-file, :interface, and :port + * properties now, and return NULL if they are + * invalid/unsatisfiable. + */ if (priv->ssl_cert_file && priv->ssl_key_file) { GError *error = NULL; - if (priv->ssl_cert) - g_object_unref (priv->ssl_cert); - priv->ssl_cert = g_tls_certificate_new_from_files (priv->ssl_cert_file, priv->ssl_key_file, &error); - if (!priv->ssl_cert) { - g_warning ("Could not read SSL certificate from '%s': %s", + if (priv->tls_cert) + g_object_unref (priv->tls_cert); + priv->tls_cert = g_tls_certificate_new_from_files (priv->ssl_cert_file, priv->ssl_key_file, &error); + if (!priv->tls_cert) { + g_warning ("Could not read TLS certificate from '%s': %s", priv->ssl_cert_file, error->message); g_error_free (error); g_object_unref (server); @@ -241,23 +315,33 @@ soup_server_constructor (GType type, } } - priv->listen_sock = - soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, priv->iface, - SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_cert, - SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context, - NULL); - if (!soup_socket_listen (priv->listen_sock)) { - g_object_unref (server); - return NULL; + if (priv->legacy_port != -1) + legacy_port_set = TRUE; + else { + legacy_port_set = FALSE; + priv->legacy_port = 0; } - /* Re-resolve the interface address, in particular in case - * the passed-in address had SOUP_ADDRESS_ANY_PORT. - */ - g_object_unref (priv->iface); - priv->iface = soup_socket_get_local_address (priv->listen_sock); - g_object_ref (priv->iface); - priv->port = soup_address_get_port (priv->iface); + if (legacy_port_set || priv->legacy_iface) { + if (!soup_server_ensure_listening (SOUP_SERVER (server))) { + g_object_unref (server); + return NULL; + } + } else { + /* If neither port nor iface was specified, then + * either: (a) the caller is planning to use the new + * listen APIs, so we don't have to do anything now, + * or (b) the caller is using the legacy APIs but + * wants the default values for interface and port + * (address 0.0.0.0, port 0), in which case a later + * call to soup_server_ensure_listening() will set it + * up just-in-time; we don't have to worry about it + * failing in that case, because it can't (unless you + * have no IPv4 addresses configured [even localhost], + * or there are already listeners on all 65,535 ports. + * We assume neither of these will happen.) + */ + } return server; } @@ -294,27 +378,28 @@ soup_server_set_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_PORT: - priv->port = g_value_get_uint (value); + if (g_value_get_uint (value) != 0) + priv->legacy_port = g_value_get_uint (value); break; case PROP_INTERFACE: - if (priv->iface) - g_object_unref (priv->iface); - priv->iface = g_value_get_object (value); - if (priv->iface) - g_object_ref (priv->iface); + if (priv->legacy_iface) + g_object_unref (priv->legacy_iface); + priv->legacy_iface = g_value_get_object (value); + if (priv->legacy_iface) + g_object_ref (priv->legacy_iface); break; case PROP_SSL_CERT_FILE: - priv->ssl_cert_file = - g_strdup (g_value_get_string (value)); + g_free (priv->ssl_cert_file); + priv->ssl_cert_file = g_value_dup_string (value); break; case PROP_SSL_KEY_FILE: - priv->ssl_key_file = - g_strdup (g_value_get_string (value)); + g_free (priv->ssl_key_file); + priv->ssl_key_file = g_value_dup_string (value); break; case PROP_TLS_CERTIFICATE: - if (priv->ssl_cert) - g_object_unref (priv->ssl_cert); - priv->ssl_cert = g_value_dup_object (value); + if (priv->tls_cert) + g_object_unref (priv->tls_cert); + priv->tls_cert = g_value_dup_object (value); break; case PROP_ASYNC_CONTEXT: priv->async_context = g_value_get_pointer (value); @@ -355,14 +440,17 @@ static void soup_server_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (object); + SoupServer *server = SOUP_SERVER (object); + SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server); switch (prop_id) { case PROP_PORT: - g_value_set_uint (value, priv->port); + soup_server_ensure_listening (server); + g_value_set_uint (value, priv->legacy_port > 0 ? priv->legacy_port : 0); break; case PROP_INTERFACE: - g_value_set_object (value, priv->iface); + soup_server_ensure_listening (server); + g_value_set_object (value, priv->legacy_iface); break; case PROP_SSL_CERT_FILE: g_value_set_string (value, priv->ssl_cert_file); @@ -371,7 +459,7 @@ soup_server_get_property (GObject *object, guint prop_id, g_value_set_string (value, priv->ssl_key_file); break; case PROP_TLS_CERTIFICATE: - g_value_set_object (value, priv->ssl_cert); + g_value_set_object (value, priv->tls_cert); break; case PROP_ASYNC_CONTEXT: g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL); @@ -403,6 +491,7 @@ soup_server_class_init (SoupServerClass *server_class) /* virtual method override */ object_class->constructor = soup_server_constructor; + object_class->dispose = soup_server_dispose; object_class->finalize = soup_server_finalize; object_class->set_property = soup_server_set_property; object_class->get_property = soup_server_get_property; @@ -514,92 +603,143 @@ soup_server_class_init (SoupServerClass *server_class) /* properties */ /** + * SoupServer:port: + * + * The port the server is listening on, if you are using the + * old #SoupServer API. (This will not be set if you use + * soup_server_listen(), etc.) + * + * Deprecated: #SoupServers can listen on multiple interfaces + * at once now. Use soup_server_listen(), etc, to listen on a + * port, and soup_server_get_uris() to see what ports are + * being listened on. + */ + /** * SOUP_SERVER_PORT: * - * Alias for the #SoupServer:port property. (The port the - * server listens on.) + * Alias for the deprecated #SoupServer:port property, qv. + * + * Deprecated: #SoupServers can listen on multiple interfaces + * at once now. Use soup_server_listen(), etc, to listen on a + * port, and soup_server_get_uris() to see what ports are + * being listened on. **/ g_object_class_install_property ( object_class, PROP_PORT, g_param_spec_uint (SOUP_SERVER_PORT, "Port", - "Port to listen on", + "Port to listen on (Deprecated)", 0, 65536, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_DEPRECATED)); + /** + * SoupServer:interface: + * + * The address of the network interface the server is + * listening on, if you are using the old #SoupServer API. + * (This will not be set if you use soup_server_listen(), + * etc.) + * + * Deprecated: #SoupServers can listen on multiple interfaces + * at once now. Use soup_server_listen(), etc, to listen on an + * interface, and soup_server_get_uris() to see what addresses + * are being listened on. + */ /** * SOUP_SERVER_INTERFACE: * - * Alias for the #SoupServer:interface property. (The address - * of the network interface the server listens on.) + * Alias for the #SoupServer:interface property, qv. + * + * Deprecated: #SoupServers can listen on multiple interfaces + * at once now. Use soup_server_listen(), etc, to listen on an + * interface, and soup_server_get_uris() to see what addresses + * are being listened on. **/ g_object_class_install_property ( object_class, PROP_INTERFACE, g_param_spec_object (SOUP_SERVER_INTERFACE, "Interface", - "Address of interface to listen on", + "Address of interface to listen on (Deprecated)", SOUP_TYPE_ADDRESS, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_DEPRECATED)); /** * SOUP_SERVER_SSL_CERT_FILE: * * Alias for the #SoupServer:ssl-cert-file property, qv. + * + * Deprecated: use #SoupServer:tls-certificate or + * soup_server_set_ssl_certificate(). */ /** * SoupServer:ssl-cert-file: * - * Path to a file containing a PEM-encoded certificate. If - * this and #SoupServer:ssl-key-file are both set, then the - * server will speak https rather than plain http. + * Path to a file containing a PEM-encoded certificate. * - * Alternatively, you can use #SoupServer:tls-certificate - * to provide an arbitrary #GTlsCertificate. + * If you set this property and #SoupServer:ssl-key-file at + * construct time, then soup_server_new() will try to read the + * files; if it cannot, it will return %NULL, with no explicit + * indication of what went wrong (and logging a warning with + * newer versions of glib, since returning %NULL from a + * constructor is illegal). + * + * Deprecated: use #SoupServer:tls-certificate or + * soup_server_set_ssl_certificate(). */ g_object_class_install_property ( object_class, PROP_SSL_CERT_FILE, g_param_spec_string (SOUP_SERVER_SSL_CERT_FILE, - "SSL certificate file", - "File containing server SSL certificate", + "TLS (aka SSL) certificate file", + "File containing server TLS (aka SSL) certificate", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); /** * SOUP_SERVER_SSL_KEY_FILE: * * Alias for the #SoupServer:ssl-key-file property, qv. + * + * Deprecated: use #SoupServer:tls-certificate or + * soup_server_set_ssl_certificate(). */ /** * SoupServer:ssl-key-file: * - * Path to a file containing a PEM-encoded private key. If - * this and #SoupServer:ssl-key-file are both set, then the - * server will speak https rather than plain http. Note that - * you are allowed to set them to the same value, if you have - * a single file containing both the certificate and the key. + * Path to a file containing a PEM-encoded private key. See + * #SoupServer:ssl-cert-file for more information about how this + * is used. * - * Alternatively, you can use #SoupServer:tls-certificate - * to provide an arbitrary #GTlsCertificate. + * Deprecated: use #SoupServer:tls-certificate or + * soup_server_set_ssl_certificate(). */ g_object_class_install_property ( object_class, PROP_SSL_KEY_FILE, g_param_spec_string (SOUP_SERVER_SSL_KEY_FILE, - "SSL key file", - "File containing server SSL key", + "TLS (aka SSL) key file", + "File containing server TLS (aka SSL) key", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); /** * SOUP_SERVER_TLS_CERTIFICATE: * * Alias for the #SoupServer:tls-certificate property, qv. + * + * Since: 2.38 */ /** * SoupServer:tls-certificate: * * A #GTlsCertificate that has a #GTlsCertificate:private-key - * set. If this is set, then the server will speak https - * rather than plain http. + * set. If this is set, then the server will be able to speak + * https in addition to (or instead of) plain http. * - * Alternatively, you can use #SoupServer:ssl-cert-file and - * #SoupServer:ssl-key-file properties, to have #SoupServer - * read in a a certificate from a file. + * Alternatively, you can call soup_server_set_ssl_cert_file() + * to have #SoupServer read in a a certificate from a file. + * + * Since: 2.38 */ g_object_class_install_property ( object_class, PROP_TLS_CERTIFICATE, @@ -609,17 +749,33 @@ soup_server_class_init (SoupServerClass *server_class) G_TYPE_TLS_CERTIFICATE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** + * SoupServer:async-context: + * + * The server's #GMainContext, if you are using the old API. + * Servers created using soup_server_listen() will listen on + * the #GMainContext that was the thread-default context at + * the time soup_server_listen() was called. + * + * Deprecated: The new API uses the thread-default #GMainContext + * rather than having an explicitly-specified one. + */ + /** * SOUP_SERVER_ASYNC_CONTEXT: * - * Alias for the #SoupServer:async-context property. (The - * server's #GMainContext.) + * Alias for the deprecated #SoupServer:async-context + * property, qv. + * + * Deprecated: The new API uses the thread-default #GMainContext + * rather than having an explicitly-specified one. **/ g_object_class_install_property ( object_class, PROP_ASYNC_CONTEXT, g_param_spec_pointer (SOUP_SERVER_ASYNC_CONTEXT, "Async GMainContext", "The GMainContext to dispatch async I/O in", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_DEPRECATED)); /** * SOUP_SERVER_RAW_PATHS: * @@ -744,9 +900,11 @@ soup_server_class_init (SoupServerClass *server_class) * @optname1: name of first property to set * @...: value of @optname1, followed by additional property/value pairs * - * Creates a new #SoupServer. + * Creates a new #SoupServer. This is exactly equivalent to calling + * g_object_new() and specifying %SOUP_TYPE_SERVER as the type. * - * Return value: a new #SoupServer + * Return value: a new #SoupServer. If you are using certain legacy + * properties, this may also return %NULL if an error occurs. **/ SoupServer * soup_server_new (const char *optname1, ...) @@ -766,32 +924,86 @@ soup_server_new (const char *optname1, ...) * soup_server_get_port: * @server: a #SoupServer * - * Gets the TCP port that @server is listening on. This is most useful - * when you did not request a specific port (or explicitly requested - * %SOUP_ADDRESS_ANY_PORT). + * Gets the TCP port that @server is listening on, if you are using + * the old API. * * Return value: the port @server is listening on. + * + * Deprecated: If you are using soup_server_listen(), etc, then use + * soup_server_get_uris() to get a list of all listening addresses. **/ guint soup_server_get_port (SoupServer *server) { + SoupServerPrivate *priv; + g_return_val_if_fail (SOUP_IS_SERVER (server), 0); + priv = SOUP_SERVER_GET_PRIVATE (server); + + soup_server_ensure_listening (server); + g_return_val_if_fail (priv->legacy_iface != NULL, 0); + + return priv->legacy_port; +} + +/** + * soup_server_set_ssl_cert_file: + * @server: a #SoupServer + * @ssl_cert_file: path to a file containing a PEM-encoded SSL/TLS + * certificate. + * @ssl_key_file: path to a file containing a PEM-encoded private key. + * @error: return location for a #GError + * + * Sets @server up to do https, using the SSL/TLS certificate + * specified by @ssl_cert_file and @ssl_key_file (which may point to + * the same file). + * + * Alternatively, you can set the #SoupServer:tls-certificate property + * at construction time, if you already have a #GTlsCertificate. + * + * Return value: success or failure. + * + * Since: 2.48 + */ +gboolean +soup_server_set_ssl_cert_file (SoupServer *server, + const char *ssl_cert_file, + const char *ssl_key_file, + GError **error) +{ + SoupServerPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE); + priv = SOUP_SERVER_GET_PRIVATE (server); - return SOUP_SERVER_GET_PRIVATE (server)->port; + if (priv->tls_cert) + g_object_unref (priv->tls_cert); + priv->tls_cert = g_tls_certificate_new_from_files (priv->ssl_cert_file, + priv->ssl_key_file, + error); + return priv->tls_cert != NULL; } /** * soup_server_is_https: * @server: a #SoupServer * - * Checks whether @server is running plain http or https. + * Checks whether @server is capable of https. * - * In order for a server to run https, you must set the - * %SOUP_SERVER_SSL_CERT_FILE and %SOUP_SERVER_SSL_KEY_FILE properties - * or %SOUP_SERVER_TLS_CERTIFICATE property to provide it with an SSL + * In order for a server to run https, you must call + * soup_server_set_ssl_cert_file(), or set the + * #SoupServer:tls-certificate property, to provide it with a * certificate to use. * - * Return value: %TRUE if @server is serving https. + * If you are using the deprecated single-listener APIs, then a return + * value of %TRUE indicates that the #SoupServer serves https + * exclusively. If you are using soup_server_listen(), etc, then a + * %TRUE return value merely indicates that the server is + * <emphasis>able</emphasis> to do https, regardless of whether it + * actually currently is or not. Use soup_server_get_uris() to see if + * it currently has any https listeners. + * + * Return value: %TRUE if @server is configured to serve https. **/ gboolean soup_server_is_https (SoupServer *server) @@ -801,18 +1013,23 @@ soup_server_is_https (SoupServer *server) g_return_val_if_fail (SOUP_IS_SERVER (server), 0); priv = SOUP_SERVER_GET_PRIVATE (server); - return priv->ssl_cert != NULL; + return priv->tls_cert != NULL; } /** * soup_server_get_listener: * @server: a #SoupServer * - * Gets @server's listening socket. You should treat this as - * read-only; writing to it or modifiying it may cause @server to - * malfunction. + * Gets @server's listening socket, if you are using the old API. + * + * You should treat this socket as read-only; writing to it or + * modifiying it may cause @server to malfunction. * * Return value: (transfer none): the listening socket. + * + * Deprecated: If you are using soup_server_listen(), etc, then use + * soup_server_get_listeners() to get a list of all listening sockets, + * but note that that function returns #GSockets, not #SoupSockets. **/ SoupSocket * soup_server_get_listener (SoupServer *server) @@ -822,7 +1039,45 @@ soup_server_get_listener (SoupServer *server) g_return_val_if_fail (SOUP_IS_SERVER (server), NULL); priv = SOUP_SERVER_GET_PRIVATE (server); - return priv->listen_sock; + soup_server_ensure_listening (server); + g_return_val_if_fail (priv->legacy_iface != NULL, NULL); + + return priv->listeners ? priv->listeners->data : NULL; +} + +/** + * soup_server_get_listeners: + * @server: a #SoupServer + * + * Gets @server's list of listening sockets. + * + * You should treat these sockets as read-only; writing to or + * modifiying any of these sockets may cause @server to malfunction. + * + * (Beware that in contrast to the old soup_server_get_listener(), this + * function returns #GSockets, not #SoupSockets.) + * + * Return value: (transfer container) (element-type Gio.Socket): a + * list of listening sockets. + **/ +GSList * +soup_server_get_listeners (SoupServer *server) +{ + SoupServerPrivate *priv; + GSList *listeners, *iter; + + g_return_val_if_fail (SOUP_IS_SERVER (server), NULL); + priv = SOUP_SERVER_GET_PRIVATE (server); + + listeners = NULL; + for (iter = priv->listeners; iter; iter = iter->next) + listeners = g_slist_prepend (listeners, soup_socket_get_gsocket (iter->data)); + + /* priv->listeners has the sockets in reverse order from how + * they were added, so listeners now has them back in the + * original order. + */ + return listeners; } static void start_request (SoupServer *, SoupClientContext *); @@ -842,14 +1097,11 @@ soup_client_context_new (SoupServer *server, SoupSocket *sock) static void soup_client_context_cleanup (SoupClientContext *client) { - if (client->auth_domain) { - g_object_unref (client->auth_domain); - client->auth_domain = NULL; - } - if (client->auth_user) { - g_free (client->auth_user); - client->auth_user = NULL; - } + g_clear_object (&client->auth_domain); + g_clear_pointer (&client->auth_user, g_free); + g_clear_object (&client->remote_addr); + g_clear_object (&client->local_addr); + client->msg = NULL; } @@ -929,8 +1181,8 @@ got_headers (SoupMessage *msg, SoupClientContext *client) char *auth_user; uri = soup_message_get_uri (msg); - if ((soup_server_is_https (server) && !soup_uri_is_https (uri, priv->https_aliases)) || - (!soup_server_is_https (server) && !soup_uri_is_http (uri, priv->http_aliases))) { + if ((soup_socket_is_ssl (client->sock) && !soup_uri_is_https (uri, priv->https_aliases)) || + (!soup_socket_is_ssl (client->sock) && !soup_uri_is_http (uri, priv->http_aliases))) { soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST); return; } @@ -1056,12 +1308,9 @@ start_request (SoupServer *server, SoupClientContext *client) g_object_ref (client->sock); - if (priv->async_context) - g_main_context_push_thread_default (priv->async_context); soup_message_read_request (msg, client->sock, + priv->legacy_iface == NULL, request_finished, client); - if (priv->async_context) - g_main_context_pop_thread_default (priv->async_context); } static void @@ -1075,7 +1324,7 @@ socket_disconnected (SoupSocket *sock, SoupClientContext *client) } static void -new_connection (SoupSocket *listner, SoupSocket *sock, gpointer user_data) +new_connection (SoupSocket *listener, SoupSocket *sock, gpointer user_data) { SoupServer *server = user_data; SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server); @@ -1092,24 +1341,33 @@ new_connection (SoupSocket *listner, SoupSocket *sock, gpointer user_data) * soup_server_run_async: * @server: a #SoupServer * - * Starts @server, causing it to listen for and process incoming - * connections. + * Starts @server, if you are using the old API, causing it to listen + * for and process incoming connections. * - * The server actually runs in @server's #GMainContext. It will not - * actually perform any processing unless the appropriate main loop is - * running. In the simple case where you did not set the server's + * The server runs in @server's #GMainContext. It will not actually + * perform any processing unless the appropriate main loop is running. + * In the simple case where you did not set the server's * %SOUP_SERVER_ASYNC_CONTEXT property, this means the server will run * whenever the glib main loop is running. + * + * Deprecated: When using soup_server_listen(), etc, the server will + * always listen for connections, and will process them whenever the + * thread-default #GMainContext is running. **/ void soup_server_run_async (SoupServer *server) { SoupServerPrivate *priv; + SoupSocket *listener; g_return_if_fail (SOUP_IS_SERVER (server)); priv = SOUP_SERVER_GET_PRIVATE (server); - if (!priv->listen_sock) { + soup_server_ensure_listening (server); + + g_return_if_fail (priv->legacy_iface != NULL); + + if (!priv->listeners) { if (priv->loop) { g_main_loop_unref (priv->loop); priv->loop = NULL; @@ -1117,21 +1375,26 @@ soup_server_run_async (SoupServer *server) return; } - g_signal_connect (priv->listen_sock, "new_connection", + listener = priv->listeners->data; + g_signal_connect (listener, "new_connection", G_CALLBACK (new_connection), server); return; - } /** * soup_server_run: * @server: a #SoupServer * - * Starts @server, causing it to listen for and process incoming - * connections. Unlike soup_server_run_async(), this creates a - * #GMainLoop and runs it, and it will not return until someone calls - * soup_server_quit() to stop the server. + * Starts @server, if you are using the old API, causing it to listen + * for and process incoming connections. Unlike + * soup_server_run_async(), this creates a #GMainLoop and runs it, and + * it will not return until someone calls soup_server_quit() to stop + * the server. + * + * Deprecated: When using soup_server_listen(), etc, the server will + * always listen for connections, and will process them whenever the + * thread-default #GMainContext is running. **/ void soup_server_run (SoupServer *server) @@ -1143,7 +1406,9 @@ soup_server_run (SoupServer *server) if (!priv->loop) { priv->loop = g_main_loop_new (priv->async_context, TRUE); + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; soup_server_run_async (server); + G_GNUC_END_IGNORE_DEPRECATIONS; } if (priv->loop) @@ -1154,21 +1419,34 @@ soup_server_run (SoupServer *server) * soup_server_quit: * @server: a #SoupServer * - * Stops processing for @server. Call this to clean up after - * soup_server_run_async(), or to terminate a call to soup_server_run(). + * Stops processing for @server, if you are using the old API. Call + * this to clean up after soup_server_run_async(), or to terminate a + * call to soup_server_run(). + * + * Note that messages currently in progress will continue to be + * handled, if the main loop associated with the server is resumed or + * kept running. * * @server is still in a working state after this call; you can start * and stop a server as many times as you want. + * + * Deprecated: When using soup_server_listen(), etc, the server will + * always listen for connections, and will process them whenever the + * thread-default #GMainContext is running. **/ void soup_server_quit (SoupServer *server) { SoupServerPrivate *priv; + SoupSocket *listener; g_return_if_fail (SOUP_IS_SERVER (server)); priv = SOUP_SERVER_GET_PRIVATE (server); + g_return_if_fail (priv->legacy_iface != NULL); + g_return_if_fail (priv->listeners != NULL); - g_signal_handlers_disconnect_by_func (priv->listen_sock, + listener = priv->listeners->data; + g_signal_handlers_disconnect_by_func (listener, G_CALLBACK (new_connection), server); if (priv->loop) @@ -1179,43 +1457,517 @@ soup_server_quit (SoupServer *server) * soup_server_disconnect: * @server: a #SoupServer * - * Stops processing for @server and closes its socket. This implies - * the effects of soup_server_quit(), but additionally closes the - * listening socket. Note that messages currently in progress will - * continue to be handled, if the main loop associated with the - * server is resumed or kept running. + * Closes and frees @server's listening sockets. If you are using the + * old #SoupServer APIs, this also includes the effect of + * soup_server_quit(). + * + * Note that if there are currently requests in progress on @server, + * that they will continue to be processed if @server's #GMainContext + * is still running. * - * After calling this function, @server is no longer functional, so it - * has nearly the same effect as destroying @server entirely. The - * function is thus useful mainly for language bindings without - * explicit control over object lifetime. + * You can call soup_server_listen(), etc, after calling this function + * if you want to start listening again. **/ void soup_server_disconnect (SoupServer *server) { SoupServerPrivate *priv; + GSList *listeners, *iter; + SoupSocket *listener; g_return_if_fail (SOUP_IS_SERVER (server)); priv = SOUP_SERVER_GET_PRIVATE (server); - soup_server_quit (server); + if (priv->legacy_iface) { + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + soup_server_quit (server); + G_GNUC_END_IGNORE_DEPRECATIONS; + } + + listeners = priv->listeners; + priv->listeners = NULL; + for (iter = listeners; iter; iter = iter->next) { + listener = iter->data; + soup_socket_disconnect (listener); + g_object_unref (listener); + } + g_slist_free (listeners); +} + +/** + * SoupServerListenOptions: + * @SOUP_SERVER_LISTEN_HTTPS: Listen for https connections rather + * than plain http. + * @SOUP_SERVER_LISTEN_IPV4_ONLY: Only listen on IPv4 interfaces. + * @SOUP_SERVER_LISTEN_IPV6_ONLY: Only listen on IPv6 interfaces. + * + * Options to pass to soup_server_listen(), etc. + * + * %SOUP_SERVER_LISTEN_IPV4_ONLY and %SOUP_SERVER_LISTEN_IPV6_ONLY + * only make sense with soup_server_listen_all() and + * soup_server_listen_local(), not plain soup_server_listen() (which + * simply listens on whatever kind of socket you give it). And you + * cannot specify both of them in a single call. + * + * Since: 2.48 + */ + +static gboolean +soup_server_listen_internal (SoupServer *server, SoupSocket *listener, + SoupServerListenOptions options, + GError **error) +{ + SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server); + gboolean is_listening; + + if (options & SOUP_SERVER_LISTEN_HTTPS) { + if (!priv->tls_cert) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Can't create a TLS server without a TLS certificate")); + return FALSE; + } + + g_object_set (G_OBJECT (listener), + SOUP_SOCKET_SSL_CREDENTIALS, priv->tls_cert, + NULL); + } + + g_object_get (G_OBJECT (listener), + SOUP_SOCKET_IS_SERVER, &is_listening, + NULL); + if (!is_listening) { + if (!soup_socket_listen (listener)) { + SoupAddress *saddr = soup_socket_get_local_address (listener); + + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Could not listen on address %s, port %d"), + soup_address_get_physical (saddr), + soup_address_get_port (saddr)); + return FALSE; + } + } + + g_signal_connect (listener, "new_connection", + G_CALLBACK (new_connection), server); + + /* Note: soup_server_listen_ipv4_ipv6() below relies on the + * fact that this does g_slist_prepend(). + */ + priv->listeners = g_slist_prepend (priv->listeners, g_object_ref (listener)); + return TRUE; +} + +/** + * soup_server_listen: + * @server: a #SoupServer + * @address: the address of the interface to listen on + * @options: listening options for this server + * @error: return location for a #GError + * + * This attempts to set up @server to listen for connections on + * @address. + * + * If @options includes %SOUP_SERVER_LISTEN_HTTPS, and @server has + * been configured for TLS, then @server will listen for https + * connections on this port. Otherwise it will listen for plain http. + * + * You may call this method (along with the other "listen" methods) + * any number of times on a server, if you want to listen on multiple + * ports, or set up both http and https service. + * + * After calling this method, @server will begin accepting and + * processing connections as soon as the appropriate #GMainContext is + * run. + * + * Note that #SoupServer never makes use of dual IPv4/IPv6 sockets; if + * @address is an IPv6 address, it will only accept IPv6 connections. + * You must configure IPv4 listening separately. + * + * Return value: %TRUE on success, %FALSE if @address could not be + * bound or any other error occurred (in which case @error will be + * set). + * + * Since: 2.48 + **/ +gboolean +soup_server_listen (SoupServer *server, GSocketAddress *address, + SoupServerListenOptions options, + GError **error) +{ + SoupServerPrivate *priv; + SoupSocket *listener; + SoupAddress *saddr; + gboolean success; + + g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE); + g_return_val_if_fail (!(options & SOUP_SERVER_LISTEN_IPV4_ONLY) && + !(options & SOUP_SERVER_LISTEN_IPV6_ONLY), FALSE); + + priv = SOUP_SERVER_GET_PRIVATE (server); + g_return_val_if_fail (priv->disposed == FALSE, FALSE); + + saddr = soup_address_new_from_gsockaddr (address); + listener = soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, saddr, + SOUP_SOCKET_USE_THREAD_CONTEXT, TRUE, + SOUP_SOCKET_IPV6_ONLY, TRUE, + NULL); + + success = soup_server_listen_internal (server, listener, options, error); + g_object_unref (listener); + g_object_unref (saddr); + + return success; +} + +static gboolean +soup_server_listen_ipv4_ipv6 (SoupServer *server, + GInetAddress *iaddr4, + GInetAddress *iaddr6, + guint port, + SoupServerListenOptions options, + GError **error) +{ + SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server); + GSocketAddress *addr4, *addr6; + GError *my_error = NULL; + SoupSocket *v4sock; + guint v4port; + + g_return_val_if_fail (iaddr4 != NULL || iaddr6 != NULL, FALSE); + + options &= ~(SOUP_SERVER_LISTEN_IPV4_ONLY | SOUP_SERVER_LISTEN_IPV6_ONLY); + + try_again: + if (iaddr4) { + addr4 = g_inet_socket_address_new (iaddr4, port); + if (!soup_server_listen (server, addr4, options, error)) { + g_object_unref (addr4); + return FALSE; + } + g_object_unref (addr4); + + v4sock = priv->listeners->data; + v4port = soup_address_get_port (soup_socket_get_local_address (v4sock)); + } else { + v4sock = NULL; + v4port = port; + } + + if (!iaddr6) + return TRUE; + + addr6 = g_inet_socket_address_new (iaddr6, v4port); + if (soup_server_listen (server, addr6, options, &my_error)) { + g_object_unref (addr6); + return TRUE; + } + g_object_unref (addr6); + + if (v4sock && g_error_matches (my_error, G_IO_ERROR, +#if GLIB_CHECK_VERSION (2, 41, 0) + G_IO_ERROR_NOT_SUPPORTED +#else + G_IO_ERROR_FAILED +#endif + )) { + /* No IPv6 support, but IPV6_ONLY wasn't specified, so just + * ignore the failure. + */ + g_error_free (my_error); + return TRUE; + } + + if (v4sock) { + priv->listeners = g_slist_remove (priv->listeners, v4sock); + soup_socket_disconnect (v4sock); + g_object_unref (v4sock); + } + + if (port == 0 && g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE)) { + /* The randomly-assigned IPv4 port was in use on the IPv6 side... Try again */ + g_clear_error (&my_error); + goto try_again; + } + + g_propagate_error (error, my_error); + return FALSE; +} + +/** + * soup_server_listen_all: + * @server: a #SoupServer + * @port: the port to listen on, or 0 + * @options: listening options for this server + * @error: return location for a #GError + * + * This attempts to set up @server to listen for connections on all + * interfaces on the system. (That is, it listens on the addresses + * <literal>0.0.0.0</literal> and/or <literal>::</literal>, depending + * on whether @options includes %SOUP_SERVER_LISTEN_IPV4_ONLY, + * %SOUP_SERVER_LISTEN_IPV6_ONLY, or neither.) If @port is specified, + * @server will listen on that port. If it is 0, @server will find an + * unused port to listen on. (In that case, you can use + * soup_server_get_uris() to find out what port it ended up choosing.) + * + * See soup_server_listen() for more details. + * + * Return value: %TRUE on success, %FALSE if @port could not be bound + * or any other error occurred (in which case @error will be set). + * + * Since: 2.48 + **/ +gboolean +soup_server_listen_all (SoupServer *server, guint port, + SoupServerListenOptions options, + GError **error) +{ + GInetAddress *iaddr4, *iaddr6; + gboolean success; + + g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE); + g_return_val_if_fail (!(options & SOUP_SERVER_LISTEN_IPV4_ONLY) || + !(options & SOUP_SERVER_LISTEN_IPV6_ONLY), FALSE); + + if (options & SOUP_SERVER_LISTEN_IPV6_ONLY) + iaddr4 = NULL; + else + iaddr4 = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); + + if (options & SOUP_SERVER_LISTEN_IPV4_ONLY) + iaddr6 = NULL; + else + iaddr6 = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6); + + success = soup_server_listen_ipv4_ipv6 (server, iaddr4, iaddr6, + port, options, error); + + g_clear_object (&iaddr4); + g_clear_object (&iaddr6); + + return success; +} + +/** + * soup_server_listen_local: + * @server: a #SoupServer + * @port: the port to listen on, or 0 + * @options: listening options for this server + * @error: return location for a #GError + * + * This attempts to set up @server to listen for connections on + * "localhost" (that is, <literal>127.0.0.1</literal> and/or + * <literal>::1</literal>, depending on whether @options includes + * %SOUP_SERVER_LISTEN_IPV4_ONLY, %SOUP_SERVER_LISTEN_IPV6_ONLY, or + * neither). If @port is specified, @server will listen on that port. + * If it is 0, @server will find an unused port to listen on. (In that + * case, you can use soup_server_get_uris() to find out what port it + * ended up choosing.) + * + * See soup_server_listen() for more details. + * + * Return value: %TRUE on success, %FALSE if @port could not be bound + * or any other error occurred (in which case @error will be set). + * + * Since: 2.48 + **/ +gboolean +soup_server_listen_local (SoupServer *server, guint port, + SoupServerListenOptions options, + GError **error) +{ + GInetAddress *iaddr4, *iaddr6; + gboolean success; + + g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE); + g_return_val_if_fail (!(options & SOUP_SERVER_LISTEN_IPV4_ONLY) || + !(options & SOUP_SERVER_LISTEN_IPV6_ONLY), FALSE); + + if (options & SOUP_SERVER_LISTEN_IPV6_ONLY) + iaddr4 = NULL; + else + iaddr4 = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); + + if (options & SOUP_SERVER_LISTEN_IPV4_ONLY) + iaddr6 = NULL; + else + iaddr6 = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV6); + + success = soup_server_listen_ipv4_ipv6 (server, iaddr4, iaddr6, + port, options, error); + + g_clear_object (&iaddr4); + g_clear_object (&iaddr6); + + return success; +} + +/** + * soup_server_listen_socket: + * @server: a #SoupServer + * @socket: a listening #GSocket + * @options: listening options for this server + * @error: return location for a #GError + * + * This attempts to set up @server to listen for connections on + * @socket. + * + * See soup_server_listen() for more details. + * + * Return value: %TRUE on success, %FALSE if an error occurred (in + * which case @error will be set). + * + * Since: 2.48 + **/ +gboolean +soup_server_listen_socket (SoupServer *server, GSocket *socket, + SoupServerListenOptions options, + GError **error) +{ + SoupServerPrivate *priv; + SoupSocket *listener; + gboolean success; + + g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE); + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + g_return_val_if_fail (!(options & SOUP_SERVER_LISTEN_IPV4_ONLY) && + !(options & SOUP_SERVER_LISTEN_IPV6_ONLY), FALSE); + + priv = SOUP_SERVER_GET_PRIVATE (server); + g_return_val_if_fail (priv->disposed == FALSE, FALSE); + + listener = g_initable_new (SOUP_TYPE_SOCKET, NULL, error, + SOUP_SOCKET_GSOCKET, socket, + SOUP_SOCKET_USE_THREAD_CONTEXT, TRUE, + SOUP_SOCKET_IPV6_ONLY, TRUE, + NULL); + if (!listener) + return FALSE; + + success = soup_server_listen_internal (server, listener, options, error); + g_object_unref (listener); + + return success; +} + +/** + * soup_server_listen_fd: + * @server: a #SoupServer + * @fd: the file descriptor of a listening socket + * @options: listening options for this server + * @error: return location for a #GError + * + * This attempts to set up @server to listen for connections on + * @fd. + * + * See soup_server_listen() for more details. + * + * Note that @server will close @fd when you free it or call + * soup_server_disconnect(). + * + * Return value: %TRUE on success, %FALSE if an error occurred (in + * which case @error will be set). + * + * Since: 2.48 + **/ +gboolean +soup_server_listen_fd (SoupServer *server, int fd, + SoupServerListenOptions options, + GError **error) +{ + SoupServerPrivate *priv; + SoupSocket *listener; + gboolean success; + + g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE); + g_return_val_if_fail (!(options & SOUP_SERVER_LISTEN_IPV4_ONLY) && + !(options & SOUP_SERVER_LISTEN_IPV6_ONLY), FALSE); + + priv = SOUP_SERVER_GET_PRIVATE (server); + g_return_val_if_fail (priv->disposed == FALSE, FALSE); + + listener = g_initable_new (SOUP_TYPE_SOCKET, NULL, error, + SOUP_SOCKET_FD, fd, + SOUP_SOCKET_USE_THREAD_CONTEXT, TRUE, + SOUP_SOCKET_IPV6_ONLY, TRUE, + NULL); + if (!listener) + return FALSE; + + success = soup_server_listen_internal (server, listener, options, error); + g_object_unref (listener); - if (priv->listen_sock) { - soup_socket_disconnect (priv->listen_sock); - g_object_unref (priv->listen_sock); - priv->listen_sock = NULL; + return success; +} + +/** + * soup_server_get_uris: + * @server: a #SoupServer + * + * Gets a list of URIs corresponding to the interfaces @server is + * listening on. These will contain IP addresses, not hostnames, and + * will also indicate whether the given listener is http or https. + * + * Note that if you used soup_server_listen_all(), the returned URIs + * will use the addresses <literal>0.0.0.0</literal> and + * <literal>::</literal>, rather than actually returning separate URIs + * for each interface on the system. + * + * Return value: (transfer full) (element-type Soup.URI): a list of + * #SoupURIs, which you must free when you are done with it. + * + * Since: 2.48 + */ +GSList * +soup_server_get_uris (SoupServer *server) +{ + SoupServerPrivate *priv; + GSList *uris, *l; + SoupSocket *listener; + SoupAddress *addr; + SoupURI *uri; + gpointer creds; + + g_return_val_if_fail (SOUP_IS_SERVER (server), NULL); + priv = SOUP_SERVER_GET_PRIVATE (server); + + for (l = priv->listeners, uris = NULL; l; l = l->next) { + listener = l->data; + addr = soup_socket_get_local_address (listener); + g_object_get (G_OBJECT (listener), SOUP_SOCKET_SSL_CREDENTIALS, &creds, NULL); + + uri = soup_uri_new (NULL); + soup_uri_set_scheme (uri, creds ? "https" : "http"); + soup_uri_set_host (uri, soup_address_get_physical (addr)); + soup_uri_set_port (uri, soup_address_get_port (addr)); + soup_uri_set_path (uri, "/"); + + uris = g_slist_prepend (uris, uri); } + + return uris; } /** * soup_server_get_async_context: * @server: a #SoupServer * - * Gets @server's async_context. This does not add a ref to the - * context, so you will need to ref it yourself if you want it to - * outlive its server. + * Gets @server's async_context, if you are using the old API. (With + * the new API, the server runs in the thread's thread-default + * #GMainContext, regardless of what this method returns.) * - * Return value: (transfer none): @server's #GMainContext, which may be %NULL + * This does not add a ref to the context, so you will need to ref it + * yourself if you want it to outlive its server. + * + * Return value: (transfer none): @server's #GMainContext, which may + * be %NULL + * + * Deprecated: If you are using soup_server_listen(), etc, then + * the server listens on the thread-default #GMainContext, and this + * property is ignored. **/ GMainContext * soup_server_get_async_context (SoupServer *server) @@ -1237,9 +1989,9 @@ soup_server_get_async_context (SoupServer *server) * soup_client_context_get_auth_user() to determine if HTTP * authentication was used successfully. * - * soup_client_context_get_address() and/or + * soup_client_context_get_remote_address() and/or * soup_client_context_get_host() can be used to get information for - * logging or debugging purposes. soup_client_context_get_socket() may + * logging or debugging purposes. soup_client_context_get_gsocket() may * also be of use in some situations (eg, tracking when multiple * requests are made on the same connection). **/ @@ -1261,6 +2013,9 @@ G_DEFINE_BOXED_TYPE (SoupClientContext, soup_client_context, soup_client_context * * Return value: (transfer none): the #SoupSocket that @client is * associated with. + * + * Deprecated: use soup_client_context_get_gsocket(), which returns + * a #GSocket. **/ SoupSocket * soup_client_context_get_socket (SoupClientContext *client) @@ -1271,6 +2026,32 @@ soup_client_context_get_socket (SoupClientContext *client) } /** + * soup_client_context_get_gsocket: + * @client: a #SoupClientContext + * + * Retrieves the #GSocket that @client is associated with. + * + * If you are using this method to observe when multiple requests are + * made on the same persistent HTTP connection (eg, as the ntlm-test + * test program does), you will need to pay attention to socket + * destruction as well (eg, by using weak references), so that you do + * not get fooled when the allocator reuses the memory address of a + * previously-destroyed socket to represent a new socket. + * + * Return value: (transfer none): the #GSocket that @client is + * associated with. + * + * Since: 2.48 + **/ +GSocket * +soup_client_context_get_gsocket (SoupClientContext *client) +{ + g_return_val_if_fail (client != NULL, NULL); + + return soup_socket_get_gsocket (client->sock); +} + +/** * soup_client_context_get_address: * @client: a #SoupClientContext * @@ -1279,6 +2060,9 @@ soup_client_context_get_socket (SoupClientContext *client) * * Return value: (transfer none): the #SoupAddress associated with the * remote end of a connection. + * + * Deprecated: Use soup_client_context_get_remote_address(), which returns + * a #GSocketAddress. **/ SoupAddress * soup_client_context_get_address (SoupClientContext *client) @@ -1289,13 +2073,55 @@ soup_client_context_get_address (SoupClientContext *client) } /** + * soup_client_context_get_remote_address: + * @client: a #SoupClientContext + * + * Retrieves the #GSocketAddress associated with the remote end + * of a connection. + * + * Return value: (transfer none): the #GSocketAddress associated with + * the remote end of a connection. + * + * Since: 2.48 + **/ +GSocketAddress * +soup_client_context_get_remote_address (SoupClientContext *client) +{ + g_return_val_if_fail (client != NULL, NULL); + + if (!client->remote_addr) + client->remote_addr = g_socket_get_remote_address (soup_client_context_get_gsocket (client), NULL); + return client->remote_addr; +} + +/** + * soup_client_context_get_local_address: + * @client: a #SoupClientContext + * + * Retrieves the #GSocketAddress associated with the local end + * of a connection. + * + * Return value: (transfer none): the #GSocketAddress associated with + * the local end of a connection. + * + * Since: 2.48 + **/ +GSocketAddress * +soup_client_context_get_local_address (SoupClientContext *client) +{ + g_return_val_if_fail (client != NULL, NULL); + + if (!client->local_addr) + client->local_addr = g_socket_get_local_address (soup_client_context_get_gsocket (client), NULL); + return client->local_addr; +} + +/** * soup_client_context_get_host: * @client: a #SoupClientContext * * Retrieves the IP address associated with the remote end of a - * connection. (If you want the actual hostname, you'll have to call - * soup_client_context_get_address() and then call the appropriate - * #SoupAddress method to resolve it.) + * connection. * * Return value: the IP address associated with the remote end of a * connection. @@ -1305,7 +2131,9 @@ soup_client_context_get_host (SoupClientContext *client) { SoupAddress *address; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; address = soup_client_context_get_address (client); + G_GNUC_END_IGNORE_DEPRECATIONS; return soup_address_get_physical (address); } diff --git a/libsoup/soup-server.h b/libsoup/soup-server.h index 0d09322e..79f6002c 100644 --- a/libsoup/soup-server.h +++ b/libsoup/soup-server.h @@ -22,6 +22,12 @@ typedef struct SoupClientContext SoupClientContext; GType soup_client_context_get_type (void); #define SOUP_TYPE_CLIENT_CONTEXT (soup_client_context_get_type ()) +typedef enum { + SOUP_SERVER_LISTEN_HTTPS = (1 << 0), + SOUP_SERVER_LISTEN_IPV4_ONLY = (1 << 1), + SOUP_SERVER_LISTEN_IPV6_ONLY = (1 << 2) +} SoupServerListenOptions; + struct _SoupServer { GObject parent; @@ -56,61 +62,118 @@ typedef void (*SoupServerCallback) (SoupServer *server, SoupClientContext *client, gpointer user_data); -#define SOUP_SERVER_PORT "port" -#define SOUP_SERVER_INTERFACE "interface" -#define SOUP_SERVER_SSL_CERT_FILE "ssl-cert-file" -#define SOUP_SERVER_SSL_KEY_FILE "ssl-key-file" #define SOUP_SERVER_TLS_CERTIFICATE "tls-certificate" -#define SOUP_SERVER_ASYNC_CONTEXT "async-context" #define SOUP_SERVER_RAW_PATHS "raw-paths" #define SOUP_SERVER_SERVER_HEADER "server-header" #define SOUP_SERVER_HTTP_ALIASES "http-aliases" #define SOUP_SERVER_HTTPS_ALIASES "https-aliases" -SoupServer *soup_server_new (const char *optname1, - ...) G_GNUC_NULL_TERMINATED; - -gboolean soup_server_is_https (SoupServer *server); -guint soup_server_get_port (SoupServer *server); - -SoupSocket *soup_server_get_listener (SoupServer *server); - -void soup_server_run (SoupServer *server); -void soup_server_run_async (SoupServer *server); -void soup_server_quit (SoupServer *server); -void soup_server_disconnect (SoupServer *server); +SoupServer *soup_server_new (const char *optname1, + ...) G_GNUC_NULL_TERMINATED; + +SOUP_AVAILABLE_IN_2_48 +gboolean soup_server_set_ssl_cert_file (SoupServer *server, + const char *ssl_cert_file, + const char *ssl_key_file, + GError **error); +gboolean soup_server_is_https (SoupServer *server); + +SOUP_AVAILABLE_IN_2_48 +gboolean soup_server_listen (SoupServer *server, + GSocketAddress *interface, + SoupServerListenOptions options, + GError **error); +SOUP_AVAILABLE_IN_2_48 +gboolean soup_server_listen_all (SoupServer *server, + guint port, + SoupServerListenOptions options, + GError **error); +SOUP_AVAILABLE_IN_2_48 +gboolean soup_server_listen_local (SoupServer *server, + guint port, + SoupServerListenOptions options, + GError **error); +SOUP_AVAILABLE_IN_2_48 +gboolean soup_server_listen_socket (SoupServer *server, + GSocket *socket, + SoupServerListenOptions options, + GError **error); +SOUP_AVAILABLE_IN_2_48 +gboolean soup_server_listen_fd (SoupServer *server, + int fd, + SoupServerListenOptions options, + GError **error); +SOUP_AVAILABLE_IN_2_48 +GSList *soup_server_get_uris (SoupServer *server); +SOUP_AVAILABLE_IN_2_48 +GSList *soup_server_get_listeners (SoupServer *server); + +void soup_server_disconnect (SoupServer *server); -GMainContext *soup_server_get_async_context (SoupServer *server); /* Handlers and auth */ -void soup_server_add_handler (SoupServer *server, - const char *path, - SoupServerCallback callback, - gpointer user_data, - GDestroyNotify destroy); -void soup_server_remove_handler (SoupServer *server, - const char *path); +void soup_server_add_handler (SoupServer *server, + const char *path, + SoupServerCallback callback, + gpointer user_data, + GDestroyNotify destroy); +void soup_server_remove_handler (SoupServer *server, + const char *path); -void soup_server_add_auth_domain (SoupServer *server, - SoupAuthDomain *auth_domain); -void soup_server_remove_auth_domain (SoupServer *server, - SoupAuthDomain *auth_domain); +void soup_server_add_auth_domain (SoupServer *server, + SoupAuthDomain *auth_domain); +void soup_server_remove_auth_domain (SoupServer *server, + SoupAuthDomain *auth_domain); /* I/O */ -void soup_server_pause_message (SoupServer *server, - SoupMessage *msg); -void soup_server_unpause_message (SoupServer *server, - SoupMessage *msg); +void soup_server_pause_message (SoupServer *server, + SoupMessage *msg); +void soup_server_unpause_message (SoupServer *server, + SoupMessage *msg); /* Client context */ -SoupSocket *soup_client_context_get_socket (SoupClientContext *client); -SoupAddress *soup_client_context_get_address (SoupClientContext *client); -const char *soup_client_context_get_host (SoupClientContext *client); -SoupAuthDomain *soup_client_context_get_auth_domain (SoupClientContext *client); -const char *soup_client_context_get_auth_user (SoupClientContext *client); +SOUP_AVAILABLE_IN_2_48 +GSocket *soup_client_context_get_gsocket (SoupClientContext *client); +SOUP_AVAILABLE_IN_2_48 +GSocketAddress *soup_client_context_get_local_address (SoupClientContext *client); +SOUP_AVAILABLE_IN_2_48 +GSocketAddress *soup_client_context_get_remote_address (SoupClientContext *client); +const char *soup_client_context_get_host (SoupClientContext *client); +SoupAuthDomain *soup_client_context_get_auth_domain (SoupClientContext *client); +const char *soup_client_context_get_auth_user (SoupClientContext *client); + + +/* Legacy API */ + +#define SOUP_SERVER_PORT "port" +#define SOUP_SERVER_INTERFACE "interface" +#define SOUP_SERVER_ASYNC_CONTEXT "async-context" +#define SOUP_SERVER_SSL_CERT_FILE "ssl-cert-file" +#define SOUP_SERVER_SSL_KEY_FILE "ssl-key-file" + +SOUP_DEPRECATED_IN_2_48 +guint soup_server_get_port (SoupServer *server); + +SOUP_DEPRECATED_IN_2_48 +SoupSocket *soup_server_get_listener (SoupServer *server); + +SOUP_DEPRECATED_IN_2_48 +GMainContext *soup_server_get_async_context (SoupServer *server); + +SOUP_DEPRECATED_IN_2_48 +void soup_server_run (SoupServer *server); +SOUP_DEPRECATED_IN_2_48 +void soup_server_run_async (SoupServer *server); +SOUP_DEPRECATED_IN_2_48 +void soup_server_quit (SoupServer *server); + +SOUP_DEPRECATED_IN_2_48 +SoupAddress *soup_client_context_get_address (SoupClientContext *client); +SOUP_DEPRECATED_IN_2_48 +SoupSocket *soup_client_context_get_socket (SoupClientContext *client); G_END_DECLS diff --git a/libsoup/soup-socket-private.h b/libsoup/soup-socket-private.h index 159dd241..55a37739 100644 --- a/libsoup/soup-socket-private.h +++ b/libsoup/soup-socket-private.h @@ -12,6 +12,7 @@ #define SOUP_SOCKET_CLOSE_ON_DISPOSE "close-on-dispose" #define SOUP_SOCKET_FD "fd" #define SOUP_SOCKET_GSOCKET "gsocket" +#define SOUP_SOCKET_IPV6_ONLY "ipv6-only" gboolean soup_socket_connect_sync_internal (SoupSocket *sock, GCancellable *cancellable, diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c index b309169f..61329a91 100644 --- a/libsoup/soup-socket.c +++ b/libsoup/soup-socket.c @@ -55,6 +55,7 @@ enum { PROP_LOCAL_ADDRESS, PROP_REMOTE_ADDRESS, PROP_NON_BLOCKING, + PROP_IPV6_ONLY, PROP_IS_SERVER, PROP_SSL_CREDENTIALS, PROP_SSL_STRICT, @@ -81,6 +82,7 @@ typedef struct { GProxyResolver *proxy_resolver; guint non_blocking:1; + guint ipv6_only:1; guint is_server:1; guint ssl:1; guint ssl_strict:1; @@ -274,6 +276,9 @@ soup_socket_set_property (GObject *object, guint prop_id, case PROP_NON_BLOCKING: priv->non_blocking = g_value_get_boolean (value); break; + case PROP_IPV6_ONLY: + priv->ipv6_only = g_value_get_boolean (value); + break; case PROP_SSL_CREDENTIALS: priv->ssl_creds = g_value_get_pointer (value); if (priv->ssl_creds) @@ -353,6 +358,9 @@ soup_socket_get_property (GObject *object, guint prop_id, case PROP_NON_BLOCKING: g_value_set_boolean (value, priv->non_blocking); break; + case PROP_IPV6_ONLY: + g_value_set_boolean (value, priv->ipv6_only); + break; case PROP_IS_SERVER: g_value_set_boolean (value, priv->is_server); break; @@ -581,6 +589,13 @@ soup_socket_class_init (SoupSocketClass *socket_class) "Whether or not the socket uses non-blocking I/O", TRUE, G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, PROP_IPV6_ONLY, + g_param_spec_boolean (SOUP_SOCKET_IPV6_ONLY, + "IPv6 only", + "IPv6 only", + FALSE, + G_PARAM_READWRITE)); /** * SOUP_SOCKET_IS_SERVER: * @@ -1206,6 +1221,17 @@ soup_socket_listen (SoupSocket *sock) goto cant_listen; finish_socket_setup (sock); +#if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) + if (priv->ipv6_only) { + int fd, v6_only; + + fd = g_socket_get_fd (priv->gsock); + v6_only = TRUE; + setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, + &v6_only, sizeof (v6_only)); + } +#endif + /* Bind */ if (!g_socket_bind (priv->gsock, addr, TRUE, NULL)) goto cant_listen; diff --git a/po/POTFILES.in b/po/POTFILES.in index 23a19a5e..ece9e952 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -5,6 +5,7 @@ libsoup/soup-message-client-io.c libsoup/soup-message-io.c libsoup/soup-message-server-io.c libsoup/soup-request.c +libsoup/soup-server.c libsoup/soup-session.c libsoup/soup-socket.c libsoup/soup-tld.c diff --git a/tests/auth-test.c b/tests/auth-test.c index 8ed5cead..f69501e9 100644 --- a/tests/auth-test.c +++ b/tests/auth-test.c @@ -864,12 +864,10 @@ do_select_auth_test (void) * side of this scenario correctly, because we test it against * curl in server-auth-test. */ - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - - uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (uri, soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); basic_auth_domain = soup_auth_domain_basic_new ( SOUP_AUTH_DOMAIN_REALM, "auth-test", @@ -1012,12 +1010,12 @@ do_auth_close_test (void) SoupURI *uri; AuthCloseData acd; - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - uri = soup_uri_new ("http://127.0.0.1/close"); - soup_uri_set_port (uri, soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); + soup_uri_set_path (uri, "/close"); basic_auth_domain = soup_auth_domain_basic_new ( SOUP_AUTH_DOMAIN_REALM, "auth-test", @@ -1128,9 +1126,7 @@ do_disappearing_auth_test (void) server = soup_test_server_new (FALSE); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - - uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (uri, soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); auth_domain = soup_auth_domain_basic_new ( SOUP_AUTH_DOMAIN_REALM, "auth-test", diff --git a/tests/cache-test.c b/tests/cache-test.c index 3478f377..ce811dff 100644 --- a/tests/cache-test.c +++ b/tests/cache-test.c @@ -641,8 +641,7 @@ main (int argc, char **argv) server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_test_add_data_func ("/cache/basics", base_uri, do_basics_test); g_test_add_data_func ("/cache/cancellation", base_uri, do_cancel_test); diff --git a/tests/chunk-io-test.c b/tests/chunk-io-test.c index 1e53eef1..4746ea6c 100644 --- a/tests/chunk-io-test.c +++ b/tests/chunk-io-test.c @@ -10,7 +10,6 @@ force_io_streams_init (void) { SoupServer *server; SoupSession *session; - guint port; SoupURI *base_uri; SoupMessage *msg; @@ -20,10 +19,7 @@ force_io_streams_init (void) */ server = soup_test_server_new (TRUE); - port = soup_server_get_port (server); - - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, port); + base_uri = soup_test_server_get_uri (server, "http", NULL); session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); msg = soup_message_new_from_uri ("POST", base_uri); diff --git a/tests/chunk-test.c b/tests/chunk-test.c index 864f8620..e1dbb88d 100644 --- a/tests/chunk-test.c +++ b/tests/chunk-test.c @@ -427,21 +427,17 @@ main (int argc, char **argv) { GMainLoop *loop; SoupServer *server; - guint port; int ret; test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - port = soup_server_get_port (server); loop = g_main_loop_new (NULL, TRUE); - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, port); - + base_uri = soup_test_server_get_uri (server, "http", NULL); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); g_test_add_data_func ("/chunks/request/unstreamed", GINT_TO_POINTER (0), do_request_test); diff --git a/tests/coding-test.c b/tests/coding-test.c index 0445f632..de2949be 100644 --- a/tests/coding-test.c +++ b/tests/coding-test.c @@ -486,10 +486,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_test_add ("/coding/message/plain", CodingTestData, GINT_TO_POINTER (CODING_TEST_NO_DECODER), diff --git a/tests/connection-test.c b/tests/connection-test.c index 8f8c74be..d953a771 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -51,10 +51,12 @@ timeout_request_started (SoupServer *server, SoupMessage *msg, SoupClientContext *client, gpointer user_data) { SoupSocket *sock; - GMainContext *context = soup_server_get_async_context (server); + GMainContext *context = g_main_context_get_thread_default (); guint readable; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; sock = soup_client_context_get_socket (client); + G_GNUC_END_IGNORE_DEPRECATIONS; readable = g_signal_connect (sock, "readable", G_CALLBACK (timeout_socket), NULL); while (soup_socket_is_connected (sock)) @@ -130,7 +132,9 @@ server_callback (SoupServer *server, SoupMessage *msg, * the declared Content-Length. Instead, we * forcibly close the socket at that point. */ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; sock = soup_client_context_get_socket (context); + G_GNUC_END_IGNORE_DEPRECATIONS; g_signal_connect (msg, "wrote-chunk", G_CALLBACK (close_socket), sock); } else if (no_close) { @@ -148,7 +152,9 @@ server_callback (SoupServer *server, SoupMessage *msg, if (!strcmp (path, "/timeout-persistent")) { SoupSocket *sock; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; sock = soup_client_context_get_socket (context); + G_GNUC_END_IGNORE_DEPRECATIONS; setup_timeout_persistent (server, sock); } @@ -849,10 +855,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); apache_init (); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, "http", NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_test_add_func ("/connection/content-length-framing", do_content_length_framing_test); g_test_add_func ("/connection/persistent-connection-timeout", do_persistent_connection_timeout_test); diff --git a/tests/context-test.c b/tests/context-test.c index 727c63ba..d20a78d8 100644 --- a/tests/context-test.c +++ b/tests/context-test.c @@ -65,7 +65,7 @@ server_callback (SoupServer *server, SoupMessage *msg, sd->server = server; sd->msg = msg; sd->timeout = soup_add_timeout ( - soup_server_get_async_context (server), + g_main_context_get_thread_default (), 200, add_body_chunk, sd); g_signal_connect (msg, "finished", G_CALLBACK (request_failed), sd); @@ -329,14 +329,16 @@ int main (int argc, char **argv) { SoupServer *server; + SoupURI *uri; int ret; test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); + base_uri = soup_uri_to_string (uri, FALSE); + soup_uri_free (uri); g_test_add_data_func ("/context/blocking/explicit", GINT_TO_POINTER (FALSE), do_test1); g_test_add_data_func ("/context/blocking/thread-default", GINT_TO_POINTER (TRUE), do_test1); diff --git a/tests/continue-test.c b/tests/continue-test.c index b6a5805f..5e0c6603 100644 --- a/tests/continue-test.c +++ b/tests/continue-test.c @@ -10,7 +10,7 @@ #define MAX_POST_LENGTH (sizeof (SHORT_BODY)) -static int port; +static SoupURI *base_uri; static GSList *events; static void @@ -54,17 +54,20 @@ do_message (const char *path, gboolean long_body, SoupSession *session; SoupMessage *msg; const char *body; - char *uri; + SoupURI *uri; va_list ap; const char *expected_event; char *actual_event; int expected_status, actual_status; - uri = g_strdup_printf ("http://%s127.0.0.1:%d/%s", - auth ? "user:pass@" : "", - port, path); - msg = soup_message_new ("POST", uri); - g_free (uri); + uri = soup_uri_copy (base_uri); + if (auth) { + soup_uri_set_user (uri, "user"); + soup_uri_set_password (uri, "pass"); + } + soup_uri_set_path (uri, path); + msg = soup_message_new_from_uri ("POST", uri); + soup_uri_free (uri); body = long_body ? LONG_BODY : SHORT_BODY; soup_message_set_request (msg, "text/plain", SOUP_MEMORY_STATIC, @@ -148,7 +151,7 @@ do_message (const char *path, gboolean long_body, static void do_test_unauth_short_noexpect_nopass (void) { - do_message ("unauth", FALSE, FALSE, FALSE, + do_message ("/unauth", FALSE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -165,7 +168,7 @@ do_test_unauth_short_noexpect_nopass (void) static void do_test_unauth_long_noexpect_nopass (void) { - do_message ("unauth", TRUE, FALSE, FALSE, + do_message ("/unauth", TRUE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -182,7 +185,7 @@ do_test_unauth_long_noexpect_nopass (void) static void do_test_unauth_short_expect_nopass (void) { - do_message ("unauth", FALSE, TRUE, FALSE, + do_message ("/unauth", FALSE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", "server-wrote_informational", SOUP_STATUS_CONTINUE, @@ -201,7 +204,7 @@ do_test_unauth_short_expect_nopass (void) static void do_test_unauth_long_expect_nopass (void) { - do_message ("unauth", TRUE, TRUE, FALSE, + do_message ("/unauth", TRUE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE, @@ -216,7 +219,7 @@ do_test_unauth_long_expect_nopass (void) static void do_test_auth_short_noexpect_nopass (void) { - do_message ("auth", FALSE, FALSE, FALSE, + do_message ("/auth", FALSE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -233,7 +236,7 @@ do_test_auth_short_noexpect_nopass (void) static void do_test_auth_long_noexpect_nopass (void) { - do_message ("auth", TRUE, FALSE, FALSE, + do_message ("/auth", TRUE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -250,7 +253,7 @@ do_test_auth_long_noexpect_nopass (void) static void do_test_auth_short_expect_nopass (void) { - do_message ("auth", FALSE, TRUE, FALSE, + do_message ("/auth", FALSE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED, @@ -265,7 +268,7 @@ do_test_auth_short_expect_nopass (void) static void do_test_auth_long_expect_nopass (void) { - do_message ("auth", TRUE, TRUE, FALSE, + do_message ("/auth", TRUE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED, @@ -280,7 +283,7 @@ do_test_auth_long_expect_nopass (void) static void do_test_auth_short_noexpect_pass (void) { - do_message ("auth", FALSE, FALSE, TRUE, + do_message ("/auth", FALSE, FALSE, TRUE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -306,7 +309,7 @@ do_test_auth_short_noexpect_pass (void) static void do_test_auth_long_noexpect_pass (void) { - do_message ("auth", TRUE, FALSE, TRUE, + do_message ("/auth", TRUE, FALSE, TRUE, "client-wrote_headers", "client-wrote_body", "server-got_headers", @@ -332,7 +335,7 @@ do_test_auth_long_noexpect_pass (void) static void do_test_auth_short_expect_pass (void) { - do_message ("auth", FALSE, TRUE, TRUE, + do_message ("/auth", FALSE, TRUE, TRUE, "client-wrote_headers", "server-got_headers", "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED, @@ -358,7 +361,7 @@ do_test_auth_short_expect_pass (void) static void do_test_auth_long_expect_pass (void) { - do_message ("auth", TRUE, TRUE, TRUE, + do_message ("/auth", TRUE, TRUE, TRUE, "client-wrote_headers", "server-got_headers", "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED, @@ -452,7 +455,7 @@ setup_server (void) SoupServer *server; SoupAuthDomain *auth_domain; - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); g_signal_connect (server, "request-started", G_CALLBACK (request_started), NULL); @@ -481,7 +484,7 @@ main (int argc, char **argv) test_init (argc, argv, NULL); server = setup_server (); - port = soup_server_get_port (server); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_test_add_func ("/continue/unauth_short_noexpect_nopass", do_test_unauth_short_noexpect_nopass); g_test_add_func ("/continue/unauth_long_noexpect_nopass", do_test_unauth_long_noexpect_nopass); @@ -499,6 +502,8 @@ main (int argc, char **argv) ret = g_test_run (); soup_test_server_quit_unref (server); + soup_uri_free (base_uri); + test_cleanup (); return ret; diff --git a/tests/cookies-test.c b/tests/cookies-test.c index 12529d81..479bcfe7 100644 --- a/tests/cookies-test.c +++ b/tests/cookies-test.c @@ -167,16 +167,19 @@ do_cookies_parsing_test (void) int main (int argc, char **argv) { + SoupURI *server_uri; int ret; test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); + server_uri = soup_test_server_get_uri (server, "http", NULL); + first_party_uri = soup_uri_new (first_party); third_party_uri = soup_uri_new (third_party); - soup_uri_set_port (first_party_uri, soup_server_get_port (server)); - soup_uri_set_port (third_party_uri, soup_server_get_port (server)); + soup_uri_set_port (first_party_uri, server_uri->port); + soup_uri_set_port (third_party_uri, server_uri->port); g_test_add_func ("/cookies/accept-policy", do_cookies_accept_policy_test); g_test_add_func ("/cookies/parsing", do_cookies_parsing_test); @@ -185,6 +188,7 @@ main (int argc, char **argv) soup_uri_free (first_party_uri); soup_uri_free (third_party_uri); + soup_uri_free (server_uri); soup_test_server_quit_unref (server); test_cleanup (); diff --git a/tests/forms-test.c b/tests/forms-test.c index 3915b019..349932bf 100644 --- a/tests/forms-test.c +++ b/tests/forms-test.c @@ -420,41 +420,43 @@ main (int argc, char **argv) { GMainLoop *loop; SoupServer *server; - guint port; - char *uri_str; + SoupURI *base_uri, *uri; int ret = 0; test_init (argc, argv, no_test_entry); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, "/hello", hello_callback, NULL, NULL); soup_server_add_handler (server, "/md5", md5_callback, NULL, NULL); - port = soup_server_get_port (server); + base_uri = soup_test_server_get_uri (server, "http", NULL); loop = g_main_loop_new (NULL, TRUE); if (run_tests) { - uri_str = g_strdup_printf ("http://127.0.0.1:%u/hello", port); - g_test_add_data_func_full ("/forms/hello", uri_str, do_hello_tests, g_free); + uri = soup_uri_new_with_base (base_uri, "/hello"); + g_test_add_data_func_full ("/forms/hello", soup_uri_to_string (uri, FALSE), do_hello_tests, g_free); + soup_uri_free (uri); - uri_str = g_strdup_printf ("http://127.0.0.1:%u/md5", port); - g_test_add_data_func_full ("/forms/md5/curl", g_strdup (uri_str), do_md5_test_curl, g_free); - g_test_add_data_func_full ("/forms/md5/libsoup", g_strdup (uri_str), do_md5_test_libsoup, g_free); - g_free (uri_str); + uri = soup_uri_new_with_base (base_uri, "/md5"); + g_test_add_data_func_full ("/forms/md5/curl", soup_uri_to_string (uri, FALSE), do_md5_test_curl, g_free); + g_test_add_data_func_full ("/forms/md5/libsoup", soup_uri_to_string (uri, FALSE), do_md5_test_libsoup, g_free); + soup_uri_free (uri); g_test_add_func ("/forms/decode", do_form_decode_test); ret = g_test_run (); } else { - g_print ("Listening on port %d\n", port); + g_print ("Listening on port %d\n", base_uri->port); g_main_loop_run (loop); } g_main_loop_unref (loop); soup_test_server_quit_unref (server); + soup_uri_free (base_uri); + if (run_tests) test_cleanup (); return ret; diff --git a/tests/misc-test.c b/tests/misc-test.c index 00559a03..acf42c39 100644 --- a/tests/misc-test.c +++ b/tests/misc-test.c @@ -73,7 +73,7 @@ server_callback (SoupServer *server, SoupMessage *msg, if (!strcmp (path, "/slow")) { soup_server_pause_message (server, msg); g_object_set_data (G_OBJECT (msg), "server", server); - soup_add_timeout (soup_server_get_async_context (server), + soup_add_timeout (g_main_context_get_thread_default (), 1000, timeout_finish_message, msg); } @@ -151,35 +151,28 @@ static void do_callback_unref_test (void) { SoupServer *bad_server; - SoupAddress *addr; SoupSession *session; SoupMessage *one, *two; GMainLoop *loop; - char *bad_uri; + SoupURI *bad_uri; g_test_bug ("533473"); /* Get a guaranteed-bad URI */ - addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT); - soup_address_resolve_sync (addr, NULL); - bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr, - NULL); - g_object_unref (addr); - - bad_uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (bad_server)); - g_object_unref (bad_server); + bad_server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); + bad_uri = soup_test_server_get_uri (bad_server, "http", NULL); + soup_test_server_quit_unref (bad_server); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session); loop = g_main_loop_new (NULL, TRUE); - one = soup_message_new ("GET", bad_uri); + one = soup_message_new_from_uri ("GET", bad_uri); g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one); - two = soup_message_new ("GET", bad_uri); + two = soup_message_new_from_uri ("GET", bad_uri); g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two); - g_free (bad_uri); + soup_uri_free (bad_uri); soup_session_queue_message (session, one, cu_one_completed, loop); soup_session_queue_message (session, two, cu_two_completed, loop); @@ -245,22 +238,15 @@ static void do_callback_unref_req_test (void) { SoupServer *bad_server; - SoupAddress *addr; SoupSession *session; SoupRequest *one, *two; GMainLoop *loop; - char *bad_uri; + SoupURI *bad_uri; /* Get a guaranteed-bad URI */ - addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT); - soup_address_resolve_sync (addr, NULL); - bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr, - NULL); - g_object_unref (addr); - - bad_uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (bad_server)); - g_object_unref (bad_server); + bad_server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); + bad_uri = soup_test_server_get_uri (bad_server, "http", NULL); + soup_test_server_quit_unref (bad_server); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, @@ -269,11 +255,11 @@ do_callback_unref_req_test (void) loop = g_main_loop_new (NULL, TRUE); - one = soup_session_request (session, bad_uri, NULL); + one = soup_session_request_uri (session, bad_uri, NULL); g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one); - two = soup_session_request (session, bad_uri, NULL); + two = soup_session_request_uri (session, bad_uri, NULL); g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two); - g_free (bad_uri); + soup_uri_free (bad_uri); soup_request_send_async (one, NULL, cur_one_completed, session); g_object_unref (one); @@ -922,10 +908,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, "http", NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); auth_domain = soup_auth_domain_basic_new ( SOUP_AUTH_DOMAIN_REALM, "misc-test", @@ -936,10 +921,9 @@ main (int argc, char **argv) g_object_unref (auth_domain); if (tls_available) { - ssl_server = soup_test_server_new_ssl (TRUE); + ssl_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (ssl_server, NULL, server_callback, "https", NULL); - ssl_base_uri = soup_uri_new ("https://127.0.0.1/"); - soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server)); + ssl_base_uri = soup_test_server_get_uri (ssl_server, "https", "127.0.0.1"); } g_test_add_func ("/misc/host", do_host_test); diff --git a/tests/multipart-test.c b/tests/multipart-test.c index e057412e..488865ae 100644 --- a/tests/multipart-test.c +++ b/tests/multipart-test.c @@ -486,10 +486,9 @@ main (int argc, char **argv) buffer = g_malloc (READ_BUFFER_SIZE); - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); base_uri_string = soup_uri_to_string (base_uri, FALSE); /* FIXME: I had to raise the number of connections allowed here, otherwise I diff --git a/tests/no-ssl-test.c b/tests/no-ssl-test.c index 82532c74..54887f1f 100644 --- a/tests/no-ssl-test.c +++ b/tests/no-ssl-test.c @@ -3,14 +3,14 @@ #include "test-utils.h" static void -do_ssl_test_for_session (SoupSession *session, const char *uri) +do_ssl_test_for_session (SoupSession *session, SoupURI *uri) { SoupMessage *msg; GTlsCertificate *cert = NULL; GTlsCertificateFlags flags; gboolean is_https; - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, SOUP_STATUS_SSL_FAILED); @@ -24,8 +24,9 @@ do_ssl_test_for_session (SoupSession *session, const char *uri) } static void -do_ssl_tests (gconstpointer uri) +do_ssl_tests (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; g_test_bug ("700518"); @@ -121,7 +122,8 @@ int main (int argc, char **argv) { SoupServer *server; - char *uri; + SoupURI *uri; + guint port; int ret; /* Force this test to use the dummy TLS backend */ @@ -135,15 +137,17 @@ main (int argc, char **argv) */ server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - uri = g_strdup_printf ("https://127.0.0.1:%u/", - soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); + port = uri->port; + soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS); + soup_uri_set_port (uri, port); g_test_add_func ("/no-ssl/session-properties", do_session_property_tests); g_test_add_data_func ("/no-ssl/request-error", uri, do_ssl_tests); ret = g_test_run (); - g_free (uri); + soup_uri_free (uri); soup_test_server_quit_unref (server); test_cleanup (); diff --git a/tests/ntlm-test.c b/tests/ntlm-test.c index 24a0f2e4..c6d53804 100644 --- a/tests/ntlm-test.c +++ b/tests/ntlm-test.c @@ -44,7 +44,7 @@ server_callback (SoupServer *server, SoupMessage *msg, SoupClientContext *client, gpointer data) { GHashTable *connections = data; - SoupSocket *socket; + GSocket *socket; const char *auth; NTLMServerState state, required_user = 0; gboolean auth_required, not_found = FALSE; @@ -70,7 +70,7 @@ server_callback (SoupServer *server, SoupMessage *msg, if (strstr (path, "/404")) not_found = TRUE; - socket = soup_client_context_get_socket (client); + socket = soup_client_context_get_gsocket (client); state = GPOINTER_TO_INT (g_hash_table_lookup (connections, socket)); auth = soup_message_headers_get_one (msg->request_headers, "Authorization"); @@ -597,13 +597,12 @@ main (int argc, char **argv) test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); connections = g_hash_table_new (NULL, NULL); soup_server_add_handler (server, NULL, server_callback, connections, NULL); - uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (uri, soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); for (i = 0; i < G_N_ELEMENTS (ntlm_tests); i++) g_test_add_data_func (ntlm_tests[i].name, &ntlm_tests[i], do_ntlm_test); @@ -611,8 +610,6 @@ main (int argc, char **argv) ret = g_test_run (); - soup_uri_free (uri); - soup_test_server_quit_unref (server); test_cleanup (); g_hash_table_destroy (connections); diff --git a/tests/proxy-test.c b/tests/proxy-test.c index 4b6679b3..e898a0bf 100644 --- a/tests/proxy-test.c +++ b/tests/proxy-test.c @@ -338,10 +338,9 @@ main (int argc, char **argv) g_simple_proxy_resolver_new (proxies[i], (char **) ignore_hosts); } - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); for (i = 0; i < ntests; i++) { path = g_strdup_printf ("/proxy/async/%s", tests[i].explanation); diff --git a/tests/range-test.c b/tests/range-test.c index c23ba462..06cde772 100644 --- a/tests/range-test.c +++ b/tests/range-test.c @@ -361,16 +361,18 @@ do_libsoup_range_test (void) { SoupSession *session; SoupServer *server; - char *base_uri; + SoupURI *base_uri; + char *base_uri_str; session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - base_uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (server)); - do_range_test (session, base_uri, TRUE, TRUE); - g_free (base_uri); + base_uri = soup_test_server_get_uri (server, "http", NULL); + base_uri_str = soup_uri_to_string (base_uri, FALSE); + do_range_test (session, base_uri_str, TRUE, TRUE); + soup_uri_free (base_uri); + g_free (base_uri_str); soup_test_server_quit_unref (server); soup_test_session_abort_unref (session); diff --git a/tests/redirect-test.c b/tests/redirect-test.c index ad8dabaa..6b165e82 100644 --- a/tests/redirect-test.c +++ b/tests/redirect-test.c @@ -395,24 +395,24 @@ main (int argc, char **argv) { GMainLoop *loop; SoupServer *server, *server2; - guint port; + SoupURI *uri2; char *path; int n, ret; test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - port = soup_server_get_port (server); - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, port); + base_uri = soup_test_server_get_uri (server, "http", NULL); - server2 = soup_test_server_new (TRUE); + server2 = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server2, NULL, server2_callback, NULL, NULL); - server2_uri = g_strdup_printf ("http://127.0.0.1:%d/on-server2", - soup_server_get_port (server2)); + uri2 = soup_test_server_get_uri (server2, "http", NULL); + soup_uri_set_path (uri2, "/on-server2"); + server2_uri = soup_uri_to_string (uri2, FALSE); + soup_uri_free (uri2); loop = g_main_loop_new (NULL, TRUE); @@ -422,8 +422,8 @@ main (int argc, char **argv) sync_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); for (n = 0; n < n_tests; n++) { - path = g_strdup_printf ("/redirect/async/msg/%d-%s-%d", n - , tests[n].requests[0].method, + path = g_strdup_printf ("/redirect/async/msg/%d-%s-%d", n, + tests[n].requests[0].method, tests[n].requests[0].status_code); g_test_add_data_func (path, &tests[n], do_async_msg_api_test); g_free (path); diff --git a/tests/requester-test.c b/tests/requester-test.c index 39b30bd5..506bb7f5 100644 --- a/tests/requester-test.c +++ b/tests/requester-test.c @@ -279,7 +279,7 @@ do_async_test (SoupSession *session, SoupURI *uri, } static void -do_test_for_thread_and_context (SoupSession *session, const char *base_uri) +do_test_for_thread_and_context (SoupSession *session, SoupURI *base_uri) { SoupRequester *requester; SoupURI *uri; @@ -292,39 +292,33 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri) soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER); debug_printf (1, " basic test\n"); - uri = soup_uri_new (base_uri); - do_async_test (session, uri, test_sent, + do_async_test (session, base_uri, test_sent, SOUP_STATUS_OK, response, TRUE, FALSE); - soup_uri_free (uri); debug_printf (1, " chunked test\n"); - uri = soup_uri_new (base_uri); - soup_uri_set_path (uri, "/chunked"); + uri = soup_uri_new_with_base (base_uri, "/chunked"); do_async_test (session, uri, test_sent, SOUP_STATUS_OK, response, TRUE, FALSE); soup_uri_free (uri); debug_printf (1, " auth test\n"); - uri = soup_uri_new (base_uri); - soup_uri_set_path (uri, "/auth"); + uri = soup_uri_new_with_base (base_uri, "/auth"); do_async_test (session, uri, auth_test_sent, SOUP_STATUS_UNAUTHORIZED, auth_response, TRUE, FALSE); soup_uri_free (uri); debug_printf (1, " non-persistent test\n"); - uri = soup_uri_new (base_uri); - soup_uri_set_path (uri, "/non-persistent"); + uri = soup_uri_new_with_base (base_uri, "/non-persistent"); do_async_test (session, uri, test_sent, SOUP_STATUS_OK, response, FALSE, FALSE); soup_uri_free (uri); debug_printf (1, " cancellation test\n"); - uri = soup_uri_new (base_uri); - soup_uri_set_path (uri, "/"); + uri = soup_uri_new_with_base (base_uri, "/"); do_async_test (session, uri, test_sent, SOUP_STATUS_FORBIDDEN, NULL, FALSE, TRUE); @@ -332,8 +326,9 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri) } static void -do_simple_plain_test (gconstpointer uri) +do_simple_plain_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; g_test_bug ("653707"); @@ -344,8 +339,9 @@ do_simple_plain_test (gconstpointer uri) } static void -do_simple_async_test (gconstpointer uri) +do_simple_async_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; g_test_bug ("653707"); @@ -358,7 +354,7 @@ do_simple_async_test (gconstpointer uri) } static void -do_test_with_context_and_type (const char *uri, gboolean plain_session) +do_test_with_context_and_type (SoupURI *uri, gboolean plain_session) { GMainContext *async_context; SoupSession *session; @@ -381,14 +377,18 @@ do_test_with_context_and_type (const char *uri, gboolean plain_session) } static void -do_async_test_with_context (gconstpointer uri) +do_async_test_with_context (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; + do_test_with_context_and_type (uri, FALSE); } static void -do_plain_test_with_context (gconstpointer uri) +do_plain_test_with_context (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; + do_test_with_context_and_type (uri, TRUE); } @@ -407,8 +407,9 @@ plain_test_thread (gpointer uri) } static void -do_async_test_in_thread (gconstpointer uri) +do_async_test_in_thread (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; GThread *thread; thread = g_thread_new ("do_async_test_in_thread", @@ -418,8 +419,9 @@ do_async_test_in_thread (gconstpointer uri) } static void -do_plain_test_in_thread (gconstpointer uri) +do_plain_test_in_thread (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; GThread *thread; thread = g_thread_new ("do_plain_test_in_thread", @@ -504,7 +506,7 @@ do_sync_request (SoupSession *session, SoupRequest *request, } static void -do_sync_tests_for_session (SoupSession *session, const char *uri_string) +do_sync_tests_for_session (SoupSession *session, SoupURI *base_uri) { SoupRequester *requester; SoupRequest *request; @@ -512,7 +514,7 @@ do_sync_tests_for_session (SoupSession *session, const char *uri_string) requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER)); - uri = soup_uri_new (uri_string); + uri = soup_uri_copy (base_uri); debug_printf (1, " basic test\n"); if (requester) @@ -572,8 +574,9 @@ do_sync_tests_for_session (SoupSession *session, const char *uri_string) } static void -do_plain_sync_test (gconstpointer uri) +do_plain_sync_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); @@ -582,8 +585,9 @@ do_plain_sync_test (gconstpointer uri) } static void -do_sync_sync_test (gconstpointer uri) +do_sync_sync_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; SoupRequester *requester; @@ -751,16 +755,16 @@ do_close_test_for_session (SoupSession *session, } static void -do_async_close_test (gconstpointer uri) +do_async_close_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; SoupURI *slow_uri; g_test_bug ("695652"); g_test_bug ("711260"); - slow_uri = soup_uri_new (uri); - soup_uri_set_path (slow_uri, "/slow"); + slow_uri = soup_uri_new_with_base ((SoupURI *)uri, "/slow"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, @@ -772,18 +776,17 @@ do_async_close_test (gconstpointer uri) } static void -do_sync_close_test (gconstpointer uri) +do_sync_close_test (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; SoupURI *slow_uri; g_test_bug ("695652"); g_test_bug ("711260"); - slow_uri = soup_uri_new (uri); - soup_uri_set_path (slow_uri, "/slow"); + slow_uri = soup_uri_new_with_base ((SoupURI *)uri, "/slow"); - debug_printf (1, " SoupSessionSync\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); @@ -796,7 +799,7 @@ do_sync_close_test (gconstpointer uri) int main (int argc, char **argv) { - char *uri; + SoupURI *uri; int ret; test_init (argc, argv, NULL); @@ -806,10 +809,11 @@ main (int argc, char **argv) AUTH_HTML_BODY, strlen (AUTH_HTML_BODY)); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - uri = g_strdup_printf ("http://127.0.0.1:%u/foo", soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); + soup_uri_set_path (uri, "/foo"); g_test_add_data_func ("/requester/simple/SoupSession", uri, do_simple_plain_test); g_test_add_data_func ("/requester/simple/SoupSessionAsync", uri, do_simple_async_test); @@ -826,7 +830,8 @@ main (int argc, char **argv) ret = g_test_run (); - g_free (uri); + soup_uri_free (uri); + soup_buffer_free (response); soup_buffer_free (auth_response); soup_test_server_quit_unref (server); diff --git a/tests/server-auth-test.c b/tests/server-auth-test.c index f386f526..ee3f57b4 100644 --- a/tests/server-auth-test.c +++ b/tests/server-auth-test.c @@ -282,7 +282,7 @@ main (int argc, char **argv) test_init (argc, argv, no_test_entry); - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); g_signal_connect (server, "request_started", G_CALLBACK (request_started_callback), NULL); soup_server_add_handler (server, NULL, @@ -310,12 +310,10 @@ main (int argc, char **argv) loop = g_main_loop_new (NULL, TRUE); + base_uri = soup_test_server_get_uri (server, "http", NULL); if (run_tests) { int i; - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); - for (i = 0; i < 16; i++) { char *path; const char *authtypes; @@ -344,13 +342,12 @@ main (int argc, char **argv) } ret = g_test_run (); - - soup_uri_free (base_uri); } else { - g_print ("Listening on port %d\n", soup_server_get_port (server)); + g_print ("Listening on port %d\n", base_uri->port); g_main_loop_run (loop); ret = 0; } + soup_uri_free (base_uri); g_main_loop_unref (loop); soup_test_server_quit_unref (server); diff --git a/tests/server-test.c b/tests/server-test.c index 0c980908..25b02c2f 100644 --- a/tests/server-test.c +++ b/tests/server-test.c @@ -5,7 +5,9 @@ #include "test-utils.h" -SoupServer *server, *ssl_server; +#include <gio/gnetworking.h> + +SoupServer *server; SoupURI *base_uri, *ssl_base_uri; static void @@ -220,10 +222,13 @@ ipv6_server_callback (SoupServer *server, SoupMessage *msg, SoupClientContext *context, gpointer data) { const char *host; + GSocketAddress *addr; char expected_host[128]; + addr = soup_client_context_get_local_address (context); g_snprintf (expected_host, sizeof (expected_host), - "[::1]:%d", soup_server_get_port (server)); + "[::1]:%d", + g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr))); host = soup_message_headers_get_one (msg->request_headers, "Host"); g_assert_cmpstr (host, ==, expected_host); @@ -239,27 +244,26 @@ do_ipv6_test (void) { SoupServer *ipv6_server; SoupURI *ipv6_uri; - SoupAddress *ipv6_addr; SoupSession *session; SoupMessage *msg; + GError *error = NULL; g_test_bug ("666399"); - ipv6_addr = soup_address_new ("::1", SOUP_ADDRESS_ANY_PORT); - soup_address_resolve_sync (ipv6_addr, NULL); - ipv6_server = soup_server_new (SOUP_SERVER_INTERFACE, ipv6_addr, - NULL); - g_object_unref (ipv6_addr); - if (!ipv6_server) { - debug_printf (1, " skipping due to lack of IPv6 support\n"); + ipv6_server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + soup_server_add_handler (ipv6_server, NULL, ipv6_server_callback, NULL, NULL); + + if (!soup_server_listen_local (ipv6_server, 0, + SOUP_SERVER_LISTEN_IPV6_ONLY, + &error)) { +#if GLIB_CHECK_VERSION (2, 41, 0) + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); +#endif + g_test_skip ("no IPv6 support"); return; } - soup_server_add_handler (ipv6_server, NULL, ipv6_server_callback, NULL, NULL); - soup_server_run_async (ipv6_server); - - ipv6_uri = soup_uri_new ("http://[::1]/"); - soup_uri_set_port (ipv6_uri, soup_server_get_port (ipv6_server)); + ipv6_uri = soup_test_server_get_uri (ipv6_server, "http", "::1"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); @@ -281,6 +285,311 @@ do_ipv6_test (void) soup_test_server_quit_unref (ipv6_server); } +static void +multi_server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + GSocketAddress *addr; + GInetSocketAddress *iaddr; + SoupURI *uri; + char *uristr, *addrstr; + + addr = soup_client_context_get_local_address (context); + iaddr = G_INET_SOCKET_ADDRESS (addr); + + uri = soup_message_get_uri (msg); + uristr = soup_uri_to_string (uri, FALSE); + + addrstr = g_inet_address_to_string (g_inet_socket_address_get_address (iaddr)); + g_assert_cmpstr (addrstr, ==, uri->host); + g_free (addrstr); + + g_assert_cmpint (g_inet_socket_address_get_port (iaddr), ==, uri->port); + + // FIXME ssl + + soup_message_set_response (msg, "text/plain", + SOUP_MEMORY_TAKE, uristr, strlen (uristr)); + soup_message_set_status (msg, SOUP_STATUS_OK); +} + +static void +do_multi_test (SoupServer *server, SoupURI *uri1, SoupURI *uri2) +{ + char *uristr; + SoupSession *session; + SoupMessage *msg; + + soup_server_add_handler (server, NULL, multi_server_callback, NULL, NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + + uristr = soup_uri_to_string (uri1, FALSE); + msg = soup_message_new ("GET", uristr); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_assert_cmpstr (msg->response_body->data, ==, uristr); + g_object_unref (msg); + g_free (uristr); + + uristr = soup_uri_to_string (uri2, FALSE); + msg = soup_message_new ("GET", uristr); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_assert_cmpstr (msg->response_body->data, ==, uristr); + g_object_unref (msg); + g_free (uristr); + + soup_test_session_abort_unref (session); + + soup_test_server_quit_unref (server); + soup_uri_free (uri1); + soup_uri_free (uri2); +} + +static void +do_multi_port_test (void) +{ + SoupServer *server; + GSList *uris; + SoupURI *uri1, *uri2; + GError *error = NULL; + + server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + + if (!soup_server_listen_local (server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)) { + g_assert_no_error (error); + g_error_free (error); + return; + } + if (!soup_server_listen_local (server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)) { + g_assert_no_error (error); + g_error_free (error); + return; + } + + uris = soup_server_get_uris (server); + g_assert_cmpint (g_slist_length (uris), ==, 2); + uri1 = uris->data; + uri2 = uris->next->data; + g_slist_free (uris); + + g_assert_cmpint (uri1->port, !=, uri2->port); + + do_multi_test (server, uri1, uri2); +} + +static void +do_multi_scheme_test (void) +{ + SoupServer *server; + GSList *uris; + SoupURI *uri1, *uri2; + GError *error = NULL; + + SOUP_TEST_SKIP_IF_NO_TLS; + + server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + + if (!soup_server_listen_local (server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)) { + g_assert_no_error (error); + g_error_free (error); + return; + } + if (!soup_server_listen_local (server, 0, + SOUP_SERVER_LISTEN_IPV4_ONLY | SOUP_SERVER_LISTEN_HTTPS, + &error)) { + g_assert_no_error (error); + g_error_free (error); + return; + } + + uris = soup_server_get_uris (server); + g_assert_cmpint (g_slist_length (uris), ==, 2); + uri1 = uris->data; + uri2 = uris->next->data; + g_slist_free (uris); + + g_assert_cmpstr (uri1->scheme, !=, uri2->scheme); + + do_multi_test (server, uri1, uri2); +} + +static void +do_multi_family_test (void) +{ + SoupServer *server; + GSList *uris; + SoupURI *uri1, *uri2; + GError *error = NULL; + + SOUP_TEST_SKIP_IF_NO_TLS; + + server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + + if (!soup_server_listen_local (server, 0, 0, &error)) { + g_assert_no_error (error); + g_error_free (error); + return; + } + + uris = soup_server_get_uris (server); + if (g_slist_length (uris) == 1) { + gboolean ipv6_works; + + /* No IPv6? Double-check */ + ipv6_works = soup_server_listen_local (server, 0, + SOUP_SERVER_LISTEN_IPV6_ONLY, + NULL); + if (ipv6_works) + g_assert_false (ipv6_works); + else + g_test_skip ("no IPv6 support"); + return; + } + + g_assert_cmpint (g_slist_length (uris), ==, 2); + uri1 = uris->data; + uri2 = uris->next->data; + g_slist_free (uris); + + g_assert_cmpstr (uri1->host, !=, uri2->host); + g_assert_cmpint (uri1->port, ==, uri2->port); + + do_multi_test (server, uri1, uri2); +} + +static void +do_gsocket_import_test (void) +{ + GSocket *gsock; + GSocketAddress *gaddr; + SoupServer *server; + GSList *listeners; + SoupURI *uri; + SoupSession *session; + SoupMessage *msg; + GError *error = NULL; + + gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + + gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", 0); + g_socket_bind (gsock, gaddr, TRUE, &error); + g_object_unref (gaddr); + g_assert_no_error (error); + g_socket_listen (gsock, &error); + g_assert_no_error (error); + + gaddr = g_socket_get_local_address (gsock, &error); + g_assert_no_error (error); + + server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + soup_server_add_handler (server, NULL, server_callback, NULL, NULL); + + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 0); + g_slist_free (listeners); + + soup_server_listen_socket (server, gsock, 0, &error); + g_assert_no_error (error); + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 1); + g_slist_free (listeners); + + uri = soup_test_server_get_uri (server, "http", "127.0.0.1"); + g_assert_nonnull (uri); + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 1); + g_slist_free (listeners); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + msg = soup_message_new_from_uri ("GET", uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + + soup_test_session_abort_unref (session); + + soup_uri_free (uri); + soup_test_server_quit_unref (server); + + g_assert_false (g_socket_is_connected (gsock)); + g_object_unref (gsock); +} + +static void +do_fd_import_test (void) +{ + GSocket *gsock; + GSocketAddress *gaddr; + SoupServer *server; + GSList *listeners; + SoupURI *uri; + SoupSession *session; + SoupMessage *msg; + int type; + GError *error = NULL; + + gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + + gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", 0); + g_socket_bind (gsock, gaddr, TRUE, &error); + g_object_unref (gaddr); + g_assert_no_error (error); + g_socket_listen (gsock, &error); + g_assert_no_error (error); + + gaddr = g_socket_get_local_address (gsock, &error); + g_assert_no_error (error); + + server = soup_test_server_new (SOUP_TEST_SERVER_NO_DEFAULT_LISTENER); + soup_server_add_handler (server, NULL, server_callback, NULL, NULL); + + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 0); + g_slist_free (listeners); + + soup_server_listen_fd (server, g_socket_get_fd (gsock), 0, &error); + g_assert_no_error (error); + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 1); + g_slist_free (listeners); + + uri = soup_test_server_get_uri (server, "http", "127.0.0.1"); + g_assert_nonnull (uri); + listeners = soup_server_get_listeners (server); + g_assert_cmpint (g_slist_length (listeners), ==, 1); + g_slist_free (listeners); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + msg = soup_message_new_from_uri ("GET", uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + + soup_test_session_abort_unref (session); + + soup_uri_free (uri); + soup_test_server_quit_unref (server); + + /* @server should have closed our socket, although @gsock doesn't + * know this. + */ + g_socket_get_option (gsock, SOL_SOCKET, SO_TYPE, &type, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + g_object_unref (gsock); +} + int main (int argc, char **argv) { @@ -290,21 +599,17 @@ main (int argc, char **argv) test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_object_set (G_OBJECT (server), SOUP_SERVER_HTTP_ALIASES, http_aliases, NULL); if (tls_available) { - ssl_server = soup_test_server_new_ssl (TRUE); - soup_server_add_handler (ssl_server, NULL, server_callback, NULL, NULL); - ssl_base_uri = soup_uri_new ("https://127.0.0.1/"); - soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server)); - g_object_set (G_OBJECT (ssl_server), + ssl_base_uri = soup_test_server_get_uri (server, "https", NULL); + g_object_set (G_OBJECT (server), SOUP_SERVER_HTTPS_ALIASES, https_aliases, NULL); } @@ -313,16 +618,19 @@ main (int argc, char **argv) g_test_add_func ("/server/aliases", do_server_aliases_test); g_test_add_func ("/server/..-in-path", do_dot_dot_test); g_test_add_func ("/server/ipv6", do_ipv6_test); + g_test_add_func ("/server/multi/port", do_multi_port_test); + g_test_add_func ("/server/multi/scheme", do_multi_scheme_test); + g_test_add_func ("/server/multi/family", do_multi_family_test); + g_test_add_func ("/server/import/gsocket", do_gsocket_import_test); + g_test_add_func ("/server/import/fd", do_fd_import_test); ret = g_test_run (); soup_uri_free (base_uri); soup_test_server_quit_unref (server); - if (tls_available) { + if (tls_available) soup_uri_free (ssl_base_uri); - soup_test_server_quit_unref (ssl_server); - } test_cleanup (); return ret; diff --git a/tests/session-test.c b/tests/session-test.c index 15072058..84fe35b4 100644 --- a/tests/session-test.c +++ b/tests/session-test.c @@ -25,7 +25,7 @@ server_handler (SoupServer *server, gpointer user_data) { if (!strcmp (path, "/request-timeout")) { - GMainContext *context = soup_server_get_async_context (server); + GMainContext *context = g_main_context_get_thread_default (); GSource *timer; timer = g_timeout_source_new (100); @@ -57,8 +57,7 @@ cancel_message_cb (SoupMessage *msg, gpointer session) } static void -do_test_for_session (SoupSession *session, - const char *uri, +do_test_for_session (SoupSession *session, SoupURI *uri, gboolean queue_is_async, gboolean send_is_blocking, gboolean cancel_is_immediate) @@ -66,17 +65,17 @@ do_test_for_session (SoupSession *session, SoupMessage *msg; gboolean finished, local_timeout; guint timeout_id; - char *timeout_uri; + SoupURI *timeout_uri; debug_printf (1, " queue_message\n"); debug_printf (2, " requesting timeout\n"); - timeout_uri = g_strdup_printf ("%s/request-timeout", uri); - msg = soup_message_new ("GET", timeout_uri); - g_free (timeout_uri); + timeout_uri = soup_uri_new_with_base (uri, "/request-timeout"); + msg = soup_message_new_from_uri ("GET", timeout_uri); + soup_uri_free (timeout_uri); soup_session_send_message (session, msg); g_object_unref (msg); - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); server_processed_message = timeout = finished = FALSE; soup_session_queue_message (session, msg, finished_cb, &finished); while (!timeout) @@ -98,7 +97,7 @@ do_test_for_session (SoupSession *session, } debug_printf (1, " send_message\n"); - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); server_processed_message = local_timeout = FALSE; timeout_id = g_idle_add_full (G_PRIORITY_HIGH, timeout_cb, &local_timeout, NULL); soup_session_send_message (session, msg); @@ -120,7 +119,7 @@ do_test_for_session (SoupSession *session, return; debug_printf (1, " cancel_message\n"); - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); g_object_ref (msg); finished = FALSE; soup_session_queue_message (session, msg, finished_cb, &finished); @@ -146,8 +145,9 @@ do_test_for_session (SoupSession *session, } static void -do_plain_tests (gconstpointer uri) +do_plain_tests (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); @@ -156,8 +156,9 @@ do_plain_tests (gconstpointer uri) } static void -do_async_tests (gconstpointer uri) +do_async_tests (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); @@ -166,8 +167,9 @@ do_async_tests (gconstpointer uri) } static void -do_sync_tests (gconstpointer uri) +do_sync_tests (gconstpointer data) { + SoupURI *uri = (SoupURI *)data; SoupSession *session; session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); @@ -194,7 +196,7 @@ priority_test_finished_cb (SoupSession *session, SoupMessage *msg, gpointer user static void do_priority_tests (gconstpointer data) { - const char *uri = data; + SoupURI *uri = (SoupURI *)data; SoupSession *session; int i, finished_count = 0; SoupMessagePriority priorities[] = @@ -212,12 +214,14 @@ do_priority_tests (gconstpointer data) expected_priorities[2] = SOUP_MESSAGE_PRIORITY_LOW; for (i = 0; i < 3; i++) { - char *msg_uri; + SoupURI *msg_uri; SoupMessage *msg; + char buf[5]; - msg_uri = g_strdup_printf ("%s/%d", uri, i); - msg = soup_message_new ("GET", uri); - g_free (msg_uri); + g_snprintf (buf, sizeof (buf), "%d", i); + msg_uri = soup_uri_new_with_base (uri, buf); + msg = soup_message_new_from_uri ("GET", msg_uri); + soup_uri_free (msg_uri); soup_message_set_priority (msg, priorities[i]); soup_session_queue_message (session, msg, priority_test_finished_cb, &finished_count); @@ -368,16 +372,14 @@ int main (int argc, char **argv) { SoupServer *server; - char *uri, *timeout_uri; + SoupURI *uri; int ret; test_init (argc, argv, NULL); server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - uri = g_strdup_printf ("http://127.0.0.1:%u", - soup_server_get_port (server)); - timeout_uri = g_strdup_printf ("%s/request-timeout", uri); + uri = soup_test_server_get_uri (server, "http", NULL); g_test_add_data_func ("/session/SoupSession", uri, do_plain_tests); g_test_add_data_func ("/session/SoupSessionAsync", uri, do_async_tests); @@ -387,8 +389,7 @@ main (int argc, char **argv) ret = g_test_run (); - g_free (uri); - g_free (timeout_uri); + soup_uri_free (uri); soup_test_server_quit_unref (server); test_cleanup (); diff --git a/tests/sniffing-test.c b/tests/sniffing-test.c index 32fad9fc..1e8680bf 100644 --- a/tests/sniffing-test.c +++ b/tests/sniffing-test.c @@ -440,10 +440,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, diff --git a/tests/socket-test.c b/tests/socket-test.c index 821b1a22..cdeace23 100644 --- a/tests/socket-test.c +++ b/tests/socket-test.c @@ -114,6 +114,7 @@ static void do_socket_from_fd_client_test (void) { SoupServer *server; + SoupURI *uri; GSocket *gsock; SoupSocket *sock; SoupAddress *local, *remote; @@ -122,7 +123,8 @@ do_socket_from_fd_client_test (void) int type; GError *error = NULL; - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); + uri = soup_test_server_get_uri (server, "http", "127.0.0.1"); gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, @@ -130,7 +132,7 @@ do_socket_from_fd_client_test (void) &error); g_assert_no_error (error); - gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", soup_server_get_port (server)); + gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", uri->port); g_socket_connect (gsock, gaddr, NULL, &error); g_object_unref (gaddr); g_assert_no_error (error); @@ -158,7 +160,7 @@ do_socket_from_fd_client_test (void) g_assert_cmpstr (soup_address_get_physical (local), ==, "127.0.0.1"); g_assert_cmpint (soup_address_get_port (local), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr))); g_assert_cmpstr (soup_address_get_physical (remote), ==, "127.0.0.1"); - g_assert_cmpint (soup_address_get_port (remote), ==, soup_server_get_port (server)); + g_assert_cmpint (soup_address_get_port (remote), ==, uri->port); g_object_unref (local); g_object_unref (remote); diff --git a/tests/ssl-test.c b/tests/ssl-test.c index e6bbb615..989c48d3 100644 --- a/tests/ssl-test.c +++ b/tests/ssl-test.c @@ -2,16 +2,16 @@ #include "test-utils.h" -static char *uri; +SoupURI *uri; static void -do_properties_test_for_session (SoupSession *session, const char *uri) +do_properties_test_for_session (SoupSession *session) { SoupMessage *msg; GTlsCertificate *cert; GTlsCertificateFlags flags; - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, SOUP_STATUS_OK); @@ -39,7 +39,7 @@ do_async_properties_tests (void) SOUP_SESSION_SSL_CA_FILE, "/dev/null", SOUP_SESSION_SSL_STRICT, FALSE, NULL); - do_properties_test_for_session (session, uri); + do_properties_test_for_session (session); soup_test_session_abort_unref (session); } @@ -55,7 +55,7 @@ do_sync_properties_tests (void) SOUP_SESSION_SSL_CA_FILE, "/dev/null", SOUP_SESSION_SSL_STRICT, FALSE, NULL); - do_properties_test_for_session (session, uri); + do_properties_test_for_session (session); soup_test_session_abort_unref (session); } @@ -109,7 +109,7 @@ do_strictness_test (gconstpointer data) NULL); } - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, test->expected_status); @@ -127,6 +127,7 @@ do_strictness_test (gconstpointer data) debug_printf (1, " tls error flags: 0x%x\n", flags); g_object_unref (msg); + soup_test_session_abort_unref (session); } @@ -257,11 +258,11 @@ main (int argc, char **argv) test_init (argc, argv, NULL); if (tls_available) { - server = soup_test_server_new_ssl (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - uri = g_strdup_printf ("https://127.0.0.1:%u/", - soup_server_get_port (server)); - } + uri = soup_test_server_get_uri (server, "https", "127.0.0.1"); + } else + uri = NULL; g_test_add_func ("/ssl/session-properties", do_session_property_tests); g_test_add_func ("/ssl/message-properties/async", do_async_properties_tests); @@ -276,7 +277,7 @@ main (int argc, char **argv) ret = g_test_run (); if (tls_available) { - g_free (uri); + soup_uri_free (uri); soup_test_server_quit_unref (server); } diff --git a/tests/streaming-test.c b/tests/streaming-test.c index 8d8c02ef..bd4a5194 100644 --- a/tests/streaming-test.c +++ b/tests/streaming-test.c @@ -138,7 +138,6 @@ main (int argc, char **argv) { GMainLoop *loop; SoupServer *server; - guint port; SoupURI *base_uri; int ret; @@ -149,15 +148,13 @@ main (int argc, char **argv) (guchar *)full_response->data, full_response->length); - server = soup_test_server_new (FALSE); + server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); - port = soup_server_get_port (server); loop = g_main_loop_new (NULL, TRUE); - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, port); + base_uri = soup_test_server_get_uri (server, "http", NULL); g_test_add_data_func ("/streaming/chunked", base_uri, do_chunked_test); g_test_add_data_func ("/streaming/content-length", base_uri, do_content_length_test); diff --git a/tests/test-utils.c b/tests/test-utils.c index bc160aea..8fc70249 100644 --- a/tests/test-utils.c +++ b/tests/test-utils.c @@ -262,79 +262,217 @@ soup_test_session_abort_unref (SoupSession *session) g_object_unref (session); } -static gpointer run_server_thread (gpointer user_data); +static void +server_listen (SoupServer *server) +{ + GError *error = NULL; + SoupServerListenOptions options = + GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (server), "listen-options")); + + soup_server_listen_local (server, 0, options, &error); + if (error) { + g_printerr ("Unable to create server: %s\n", error->message); + exit (1); + } +} + +static GMutex server_start_mutex; +static GCond server_start_cond; + +static gpointer +run_server_thread (gpointer user_data) +{ + SoupServer *server = user_data; + GMainContext *context; + GMainLoop *loop; + + context = g_main_context_new (); + g_main_context_push_thread_default (context); + loop = g_main_loop_new (context, FALSE); + g_object_set_data (G_OBJECT (server), "GMainLoop", loop); + + server_listen (server); + + g_mutex_lock (&server_start_mutex); + g_cond_signal (&server_start_cond); + g_mutex_unlock (&server_start_mutex); + + g_main_loop_run (loop); + g_main_loop_unref (loop); -static SoupServer * -test_server_new (gboolean in_own_thread, gboolean ssl) + soup_server_disconnect (server); + + g_main_context_pop_thread_default (context); + g_main_context_unref (context); + + return NULL; +} + +SoupServer * +soup_test_server_new (SoupTestServerOptions options) { SoupServer *server; - GMainContext *async_context; - char *ssl_cert_file, *ssl_key_file; - SoupAddress *addr; + GTlsCertificate *cert = NULL; + GError *error = NULL; - async_context = in_own_thread ? g_main_context_new () : NULL; + if (tls_available) { + char *ssl_cert_file, *ssl_key_file; - if (ssl) { ssl_cert_file = g_test_build_filename (G_TEST_DIST, "test-cert.pem", NULL); ssl_key_file = g_test_build_filename (G_TEST_DIST, "test-key.pem", NULL); - } else - ssl_cert_file = ssl_key_file = NULL; - - addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT); - soup_address_resolve_sync (addr, NULL); + cert = g_tls_certificate_new_from_files (ssl_cert_file, + ssl_key_file, + &error); + g_free (ssl_cert_file); + g_free (ssl_key_file); + if (error) { + g_printerr ("Unable to create server: %s\n", error->message); + exit (1); + } + } - server = soup_server_new (SOUP_SERVER_INTERFACE, addr, - SOUP_SERVER_ASYNC_CONTEXT, async_context, - SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file, - SOUP_SERVER_SSL_KEY_FILE, ssl_key_file, + server = soup_server_new (SOUP_SERVER_TLS_CERTIFICATE, cert, NULL); - g_object_unref (addr); - if (async_context) - g_main_context_unref (async_context); - g_free (ssl_cert_file); - g_free (ssl_key_file); - - if (!server) { - g_printerr ("Unable to create server\n"); - exit (1); - } + g_clear_object (&cert); + + g_object_set_data (G_OBJECT (server), "options", GUINT_TO_POINTER (options)); - if (in_own_thread) { + if (options & SOUP_TEST_SERVER_IN_THREAD) { GThread *thread; + g_mutex_lock (&server_start_mutex); + thread = g_thread_new ("server_thread", run_server_thread, server); g_object_set_data (G_OBJECT (server), "thread", thread); - } else - soup_server_run_async (server); + + if (!(options & SOUP_TEST_SERVER_NO_DEFAULT_LISTENER)) { + /* We have to call soup_server_listen() from the server's + * thread, but want to be sure we don't return from here + * until it happens, hence the locking. + */ + g_cond_wait (&server_start_cond, &server_start_mutex); + g_mutex_unlock (&server_start_mutex); + } + } else if (!(options & SOUP_TEST_SERVER_NO_DEFAULT_LISTENER)) + server_listen (server); return server; } -SoupServer * -soup_test_server_new (gboolean in_own_thread) +static SoupURI * +find_server_uri (SoupServer *server, const char *scheme, const char *host) { - return test_server_new (in_own_thread, FALSE); + GSList *uris, *u; + SoupURI *uri, *ret_uri = NULL; + + uris = soup_server_get_uris (server); + for (u = uris; u; u = u->next) { + uri = u->data; + + if (scheme && strcmp (uri->scheme, scheme) != 0) + continue; + if (host && strcmp (uri->host, host) != 0) + continue; + + ret_uri = soup_uri_copy (uri); + break; + } + g_slist_free_full (uris, (GDestroyNotify)soup_uri_free); + + return ret_uri; } -SoupServer * -soup_test_server_new_ssl (gboolean in_own_thread) +static SoupURI * +add_listener (SoupServer *server, const char *scheme, const char *host) { - return test_server_new (in_own_thread, TRUE); + SoupServerListenOptions options = 0; + GError *error = NULL; + + if (!g_strcmp0 (scheme, SOUP_URI_SCHEME_HTTPS)) + options |= SOUP_SERVER_LISTEN_HTTPS; + if (!g_strcmp0 (host, "127.0.0.1")) + options |= SOUP_SERVER_LISTEN_IPV4_ONLY; + else if (!g_strcmp0 (host, "::1")) + options |= SOUP_SERVER_LISTEN_IPV6_ONLY; + + soup_server_listen_local (server, 0, options, &error); + g_assert_no_error (error); + + return find_server_uri (server, scheme, host); } -static gpointer -run_server_thread (gpointer user_data) +typedef struct { + GMutex mutex; + GCond cond; + + SoupServer *server; + const char *scheme; + const char *host; + + SoupURI *uri; +} AddListenerData; + +static gboolean +add_listener_in_thread (gpointer user_data) { - SoupServer *server = user_data; + AddListenerData *data = user_data; - soup_server_run (server); - return NULL; + data->uri = add_listener (data->server, data->scheme, data->host); + g_mutex_lock (&data->mutex); + g_cond_signal (&data->cond); + g_mutex_unlock (&data->mutex); + + return FALSE; +} + +SoupURI * +soup_test_server_get_uri (SoupServer *server, + const char *scheme, + const char *host) +{ + SoupURI *uri; + GMainLoop *loop; + + uri = find_server_uri (server, scheme, host); + if (uri) + return uri; + + /* Need to add a new listener */ + uri = soup_uri_new (NULL); + soup_uri_set_scheme (uri, scheme); + soup_uri_set_host (uri, host); + + loop = g_object_get_data (G_OBJECT (server), "GMainLoop"); + if (loop) { + GMainContext *context = g_main_loop_get_context (loop); + AddListenerData data; + + g_mutex_init (&data.mutex); + g_cond_init (&data.cond); + data.server = server; + data.scheme = scheme; + data.host = host; + data.uri = NULL; + + g_mutex_lock (&data.mutex); + soup_add_completion (context, add_listener_in_thread, &data); + + while (!data.uri) + g_cond_wait (&data.cond, &data.mutex); + + g_mutex_clear (&data.mutex); + g_cond_clear (&data.cond); + uri = data.uri; + } else + uri = add_listener (server, scheme, host); + + return uri; } static gboolean -idle_quit_server (gpointer server) +idle_quit_server (gpointer loop) { - soup_server_quit (server); + g_main_loop_quit (loop); return FALSE; } @@ -345,11 +483,15 @@ soup_test_server_quit_unref (SoupServer *server) thread = g_object_get_data (G_OBJECT (server), "thread"); if (thread) { - soup_add_completion (soup_server_get_async_context (server), - idle_quit_server, server); + GMainLoop *loop; + GMainContext *context; + + loop = g_object_get_data (G_OBJECT (server), "GMainLoop"); + context = g_main_loop_get_context (loop); + soup_add_completion (context, idle_quit_server, loop); g_thread_join (thread); } else - soup_server_quit (server); + soup_server_disconnect (server); g_assert_cmpint (G_OBJECT (server)->ref_count, ==, 1); g_object_unref (server); diff --git a/tests/test-utils.h b/tests/test-utils.h index 03637dcf..43ef5a75 100644 --- a/tests/test-utils.h +++ b/tests/test-utils.h @@ -55,9 +55,17 @@ typedef enum { SoupSession *soup_test_session_new (GType type, ...); void soup_test_session_abort_unref (SoupSession *session); -SoupServer *soup_test_server_new (gboolean in_own_thread); -SoupServer *soup_test_server_new_ssl (gboolean in_own_thread); -void soup_test_server_quit_unref (SoupServer *server); +typedef enum { + SOUP_TEST_SERVER_DEFAULT = 0, + SOUP_TEST_SERVER_IN_THREAD = (1 << 0), + SOUP_TEST_SERVER_NO_DEFAULT_LISTENER = (1 << 1) +} SoupTestServerOptions; + +SoupServer *soup_test_server_new (SoupTestServerOptions options); +SoupURI *soup_test_server_get_uri (SoupServer *server, + const char *scheme, + const char *host); +void soup_test_server_quit_unref (SoupServer *server); GInputStream *soup_test_request_send (SoupRequest *req, GCancellable *cancellable, diff --git a/tests/timeout-test.c b/tests/timeout-test.c index 81fb4331..afb2c591 100644 --- a/tests/timeout-test.c +++ b/tests/timeout-test.c @@ -22,7 +22,7 @@ request_started_cb (SoupSession *session, SoupMessage *msg, } static void -do_message_to_session (SoupSession *session, const char *uri, +do_message_to_session (SoupSession *session, SoupURI *uri, const char *comment, guint expected_status) { SoupMessage *msg; @@ -30,7 +30,7 @@ do_message_to_session (SoupSession *session, const char *uri, if (comment) debug_printf (1, " msg %s\n", comment); - msg = soup_message_new ("GET", uri); + msg = soup_message_new_from_uri ("GET", uri); g_signal_connect (msg, "finished", G_CALLBACK (message_finished), &finished); @@ -51,8 +51,8 @@ static void do_msg_tests_for_session (SoupSession *timeout_session, SoupSession *idle_session, SoupSession *plain_session, - const char *fast_uri, - const char *slow_uri) + SoupURI *fast_uri, + SoupURI *slow_uri) { SoupSocket *ret, *idle_first, *idle_second; SoupSocket *plain_first, *plain_second; @@ -100,7 +100,7 @@ do_msg_tests_for_session (SoupSession *timeout_session, } static void -do_request_to_session (SoupSession *session, const char *uri, +do_request_to_session (SoupSession *session, SoupURI *uri, const char *comment, gboolean expect_timeout) { SoupRequest *req; @@ -110,7 +110,7 @@ do_request_to_session (SoupSession *session, const char *uri, gboolean finished = FALSE; debug_printf (1, " req %s\n", comment); - req = soup_session_request (session, uri, NULL); + req = soup_session_request_uri (session, uri, NULL); msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); g_signal_connect (msg, "finished", @@ -149,8 +149,8 @@ static void do_req_tests_for_session (SoupSession *timeout_session, SoupSession *idle_session, SoupSession *plain_session, - const char *fast_uri, - const char *slow_uri) + SoupURI *fast_uri, + SoupURI *slow_uri) { SoupSocket *ret, *idle_first, *idle_second; SoupSocket *plain_first, *plain_second; @@ -201,11 +201,11 @@ static void do_async_timeout_tests (gconstpointer data) { SoupSession *timeout_session, *idle_session, *plain_session; - const char *fast_uri = data; - const char *slow_uri = g_build_path ("/", fast_uri, "slow", NULL); + SoupURI *fast_uri = (SoupURI *)data; + SoupURI *slow_uri = soup_uri_new_with_base (fast_uri, "/slow"); gboolean extra_slow; - if (g_str_has_prefix (fast_uri, "https")) { + if (fast_uri->scheme == SOUP_URI_SCHEME_HTTPS) { SOUP_TEST_SKIP_IF_NO_TLS; extra_slow = slow_https; @@ -236,17 +236,19 @@ do_async_timeout_tests (gconstpointer data) soup_test_session_abort_unref (timeout_session); soup_test_session_abort_unref (idle_session); soup_test_session_abort_unref (plain_session); + + soup_uri_free (slow_uri); } static void do_sync_timeout_tests (gconstpointer data) { SoupSession *timeout_session, *plain_session; - const char *fast_uri = data; - const char *slow_uri = g_build_path ("/", fast_uri, "slow", NULL); + SoupURI *fast_uri = (SoupURI *)data; + SoupURI *slow_uri = soup_uri_new_with_base (fast_uri, "/slow"); gboolean extra_slow; - if (g_str_has_prefix (fast_uri, "https")) { + if (fast_uri->scheme == SOUP_URI_SCHEME_HTTPS) { SOUP_TEST_SKIP_IF_NO_TLS; extra_slow = slow_https; @@ -290,7 +292,7 @@ server_handler (SoupServer *server, if (!strcmp (path, "/slow")) { soup_server_pause_message (server, msg); g_object_set_data (G_OBJECT (msg), "server", server); - soup_add_timeout (soup_server_get_async_context (server), + soup_add_timeout (g_main_context_get_thread_default (), 4000, timeout_finish_message, msg); } } @@ -299,24 +301,22 @@ int main (int argc, char **argv) { SoupServer *server, *https_server = NULL; - char *uri, *https_uri = NULL; + SoupURI *uri, *https_uri = NULL; int ret; test_init (argc, argv, NULL); server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (server)); + uri = soup_test_server_get_uri (server, "http", NULL); if (tls_available) { SoupSession *test_session; gint64 start, end; - https_server = soup_test_server_new_ssl (TRUE); + https_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (https_server, NULL, server_handler, NULL, NULL); - https_uri = g_strdup_printf ("https://127.0.0.1:%u/", - soup_server_get_port (https_server)); + https_uri = soup_test_server_get_uri (server, "https", "127.0.0.1"); /* The 1-second timeouts are too fast for some machines... */ test_session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); @@ -333,7 +333,7 @@ main (int argc, char **argv) slow_https = FALSE; } } else - https_uri = g_strdup ("https://fail."); + https_uri = soup_uri_new ("https://fail."); g_test_add_data_func ("/timeout/http/async", uri, do_async_timeout_tests); g_test_add_data_func ("/timeout/http/sync", uri, do_sync_timeout_tests); @@ -342,8 +342,8 @@ main (int argc, char **argv) ret = g_test_run (); - g_free (uri); - g_free (https_uri); + soup_uri_free (uri); + soup_uri_free (https_uri); soup_test_server_quit_unref (server); if (https_server) soup_test_server_quit_unref (https_server); diff --git a/tests/xmlrpc-server-test.c b/tests/xmlrpc-server-test.c index bfeb200a..bd8341de 100644 --- a/tests/xmlrpc-server-test.c +++ b/tests/xmlrpc-server-test.c @@ -310,15 +310,17 @@ int main (int argc, char **argv) { SoupServer *server; + SoupURI *server_uri; int ret; test_init (argc, argv, no_test_entry); - server = soup_test_server_new (run_tests); + server = soup_test_server_new (run_tests ? SOUP_TEST_SERVER_IN_THREAD : SOUP_TEST_SERVER_DEFAULT); soup_server_add_handler (server, "/xmlrpc-server.php", server_callback, NULL, NULL); - uri = g_strdup_printf ("http://127.0.0.1:%u/xmlrpc-server.php", - soup_server_get_port (server)); + server_uri = soup_test_server_get_uri (server, "http", NULL); + soup_uri_set_path (server_uri, "/xmlrpc-server.php"); + uri = soup_uri_to_string (server_uri, FALSE); if (run_tests) { char *out, **tests, *path; @@ -363,6 +365,7 @@ main (int argc, char **argv) } soup_test_server_quit_unref (server); + soup_uri_free (server_uri); g_free (uri); if (run_tests) test_cleanup (); |
