[libsoup/wip/server-api: 5/7] soup-server: add new-and-improved APIs
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/wip/server-api: 5/7] soup-server: add new-and-improved APIs
- Date: Mon, 17 Mar 2014 16:24:21 +0000 (UTC)
commit 9c00b827c594a25cd8b211a431ef3b269eb3685c
Author: Dan Winship <danw gnome org>
Date: Sun Mar 9 10:02:55 2014 -0400
soup-server: add new-and-improved APIs
Add new APIs to SoupServer, allowing multiple listening sockets on a
server, and listening on an existing fd or GSocket, and with better
error handling.
FIXME bug numbers
docs/reference/libsoup-2.4-docs.sgml | 3 +-
docs/reference/libsoup-2.4-sections.txt | 46 +-
docs/reference/server-howto.xml | 150 +++--
examples/simple-httpd.c | 68 +-
examples/simple-proxy.c | 26 +-
libsoup/libsoup-2.4.sym | 12 +
libsoup/soup-address.c | 11 +
libsoup/soup-message-private.h | 1 +
libsoup/soup-message-server-io.c | 18 +-
libsoup/soup-misc-private.h | 2 +
libsoup/soup-server.c | 1155 ++++++++++++++++++++++++++-----
libsoup/soup-server.h | 131 +++-
libsoup/soup-socket-private.h | 1 +
libsoup/soup-socket.c | 26 +
po/POTFILES.in | 1 +
15 files changed, 1336 insertions(+), 315 deletions(-)
---
diff --git a/docs/reference/libsoup-2.4-docs.sgml b/docs/reference/libsoup-2.4-docs.sgml
index c0c8a05..ba33142 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 03d35d4..11f650f 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 05fb0c0..c8d333a 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.48 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 71ff874..f3be82e 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 db8c9f9..08bd847 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 be4cd7f..d1b8388 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 b2d1647..ecbd9a7 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 35cc988..c29d73e 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -68,6 +68,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 f70e9a9..00272f3 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 94aa71d..7826533 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 07d801d..f56a592 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.
+ *
+ * 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).
*
- * 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_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 call soup_server_set_ssl_cert_file()
+ * to have #SoupServer read in a a certificate from a file.
*
- * 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.
+ * 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 SOUP_SERVER_GET_PRIVATE (server)->port;
+ 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);
+
+ 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;
}
@@ -927,8 +1179,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;
}
@@ -1054,12 +1306,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
@@ -1073,7 +1322,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);
@@ -1090,24 +1339,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;
@@ -1115,21 +1373,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)
@@ -1141,7 +1404,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)
@@ -1152,21 +1417,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)
@@ -1177,43 +1455,502 @@ 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);
+ }
- if (priv->listen_sock) {
- soup_socket_disconnect (priv->listen_sock);
- g_object_unref (priv->listen_sock);
- priv->listen_sock = 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() above 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;
+
+ options &= ~(SOUP_SERVER_LISTEN_IPV4_ONLY | SOUP_SERVER_LISTEN_IPV6_ONLY);
+
+ 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) {
+ 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);
+
+ /* FIXME: this isn't right. Need to translate EAFNOSUPPORT */
+ if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
+ g_propagate_error (error, my_error);
+
+ /* Close the IPv4 socket if IPv6 failed */
+ if (v4sock) {
+ priv->listeners = g_slist_remove (priv->listeners, v4sock);
+ soup_socket_disconnect (v4sock);
+ g_object_unref (v4sock);
+ }
+
+ return FALSE;
+ }
+
+ g_error_free (my_error);
+ }
+
+ return TRUE;
+}
+
+/**
+ * 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);
+
+ 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. (FIXME: is this the best choice?)
+ *
+ * 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.)
+ *
+ * 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
+ * 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)
@@ -1235,9 +1972,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).
**/
@@ -1259,6 +1996,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)
@@ -1269,6 +2009,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
*
@@ -1277,6 +2043,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)
@@ -1287,13 +2056,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.
@@ -1303,7 +2114,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 0d09322..5a5f07b 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,110 @@ 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);
+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);
+
+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);
-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);
-
-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"
+
+gboolean soup_server_is_https (SoupServer *server);
+guint soup_server_get_port (SoupServer *server);
+
+SoupSocket *soup_server_get_listener (SoupServer *server);
+
+GMainContext *soup_server_get_async_context (SoupServer *server);
+
+void soup_server_run (SoupServer *server);
+void soup_server_run_async (SoupServer *server);
+void soup_server_quit (SoupServer *server);
+
+SoupAddress *soup_client_context_get_address (SoupClientContext *client);
+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 f661cc5..41b542c 100644
--- a/libsoup/soup-socket-private.h
+++ b/libsoup/soup-socket-private.h
@@ -13,6 +13,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 9ddef6d..cc3e125 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,
@@ -82,6 +83,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;
@@ -273,6 +275,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);
break;
@@ -329,6 +334,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;
@@ -559,6 +567,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:
*
@@ -1191,6 +1206,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 23a19a5..ece9e95 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
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]