Tutorial

Building with libsoup
Creating a Basic Client
Creating a SoupSession
Downloading Into Memory
Efficiently Streaming Data
Using Asynchronously
Advanced Usage
Customizing Session Options
Adding Session Features
Using a proxy
Using the SoupMessage API
Handling authentication
Everything TLS Related
Accepting Invalid or Pinned Certificates
Setting a Custom CA
Using Client Certificates

Building with libsoup

Buildsystem Integration

Like other GNOME libraries, libsoup uses pkg-config to provide compiler options. The package name is "libsoup-3.0".

For example if you use Autotools:

1
2
3
PKG_CHECK_MODULES(LIBSOUP, [libsoup-3.0])
AC_SUBST(LIBSOUP_CFLAGS)
AC_SUBST(LIBSOUP_LIBS)

If you use Meson:

1
libsoup_dep = dependency('libsoup-3.0')

API Availability and Deprecation Warnings

If you want to restrict your program to a particular libsoup version or range of versions, you can define SOUP_VERSION_MIN_REQUIRED and/or SOUP_VERSION_MAX_ALLOWED. For example with Autotools:

1
2
LIBSOUP_CFLAGS="$LIBSOUP_CFLAGS -DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_3_0"
LIBSOUP_CFLAGS="$LIBSOUP_CFLAGS -DSOUP_VERSION_MAX_ALLOWED=SOUP_VERSION_3_2"

Or with Meson:

1
2
3
4
5
add_project_arguments(
  '-DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_2_99',
  '-DSOUP_VERSION_MAX_ALLOWED=SOUP_VERSION_3_0',
  language: 'c'
)

The SOUP_VERSION_MIN_REQUIRED declaration states that the code is not expected to compile on versions of libsoup older than the indicated version, and so the compiler should print warnings if the code uses functions that were deprecated as of that release.

The SOUP_VERSION_MAX_ALLOWED declaration states that the code is expected to compile on versions of libsoup up to the indicated version, and so, when compiling the program against a newer version than that, the compiler should print warnings if the code uses functions that did not yet exist in the max-allowed release.

You can use SOUP_CHECK_VERSION to check the version of libsoup at compile time, to compile different code for different libsoup versions. (If you are setting SOUP_VERSION_MIN_REQUIRED and SOUP_VERSION_MAX_ALLOWED to different versions, as in the example above, then you almost certainly need to be doing this.)

Headers

Code using libsoup should include the header like so:

1
#include <libsoup/soup.h>
<refentry> <refmeta> <refentrytitle>Server Basics</refentrytitle> <manvolnum>3</manvolnum> <refmiscinfo>LIBSOUP Library</refmiscinfo> </refmeta>

<refentrytitle>Server Basics</refentrytitle>

<refname>libsoup Server Basics</refname>

<refsect2> <title>Creating a SoupServer</title> <para> As with the client API, there is a single object that will encapsulate most of your interactions with libsoup. In this case, <type>SoupServer</type>. </para> <para> You create the server with <function>soup_server_new</function>, and as with the <type>SoupSession</type> constructor, you can specify a few additional options: </para> <variablelist> <varlistentry> <term><literal>"tls-certificate"</literal></term> <listitem><para> A <type>GTlsCertificate</type> (containing a private key) that will be used when handling HTTPS requests on the server. </para></listitem> </varlistentry> <varlistentry> <term><literal>"raw-paths"</literal></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. </para></listitem> </varlistentry> <varlistentry> <term><literal>"server-header"</literal></term> <listitem><para> Allows you to set a Server header string that will be sent on all responses. </para></listitem> </varlistentry> </variablelist> </refsect2>
<refsect2> <title>Adding Listening Sockets</title> <para> To tell the server where to listen, call <function>soup_server_listen</function> (to listen on a specific <type>GSocketAddress</type>), <function>soup_server_listen_all</function> (to listen on a given port on all network interfaces), or <function>soup_server_listen_local</function> (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 <literal>SoupServer:tls-certificate</literal> property, or else call <function>soup_server_set_ssl_cert_file</function>. After that you can pass the <literal>SOUP_SERVER_LISTEN_HTTPS</literal> option to <function>soup_server_listen</function>, etc. </para> <para> By default, servers listen for both IPv4 and IPv6 connections; if you don't want this, use the <literal>SOUP_SERVER_LISTEN_IPV4_ONLY</literal> or <literal>SOUP_SERVER_LISTEN_IPV6_ONLY</literal> options. </para> <para> The server runs asynchronously, in the thread-default <type>GMainContext</type> of the thread in which the "listen" calls were made. </para> </refsect2>
<refsect2> <title>Adding Handlers</title> <para> By default, <type>SoupServer</type> returns "404 Not Found" in response to all requests (except ones that it can't parse, which get "400 Bad Request"). To override this behavior, call <function>soup_server_add_handler</function> to set a callback to handle certain URI paths. </para> <informalexample><programlisting> soup_server_add_handler (server, "/foo", server_callback, data, destroy_notify); </programlisting></informalexample> <para> The <literal>"/foo"</literal> indicates the base path for this handler. When a request comes in, if there is a handler registered for exactly the path in the request's <literal>Request-URI</literal>, then that handler will be called. Otherwise <application>libsoup</application> will strip path components one by one until it finds a matching handler. So for example, a request of the form "<literal>GET /foo/bar/baz.html?a=1&b=2 HTTP/1.1</literal>" would look for handlers for "<literal>/foo/bar/baz.html</literal>", "<literal>/foo/bar</literal>", and "<literal>/foo</literal>". If a handler has been registered with a <literal>NULL</literal> base path, then it is used as the default handler for any request that doesn't match any other handler. </para> </refsect2>
<refsect2> <title>Responding to Requests</title> <para> A handler callback looks something like this: </para> <informalexample><programlisting> static void server_callback (SoupServer *server, SoupServerMessage *msg, const char *path, GHashTable *query, gpointer user_data) { ... } </programlisting></informalexample> <para> <literal>msg</literal> is the request that has been received and <literal>user_data</literal> is the data that was passed to <function>soup_server_add_handler</function>. <literal>path</literal> is the path (from <literal>msg</literal>'s URI), and <literal>query</literal> contains the result of parsing the URI query field. (It is <literal>NULL</literal> if there was no query.) </para> <para> By default, <application>libsoup</application> assumes that you have completely finished processing the message when you return from the callback, and that it can therefore begin sending the response. If you are not ready to send a response immediately (eg, you have to contact another server, or wait for data from a database), you must call <function>soup_server_pause_message</function> on the message before returning from the callback. This will delay sending a response until you call <function>soup_server_unpause_message</function>. (You must also connect to the finished signal on the message in this case, so that you can break off processing if the client unexpectedly disconnects before you start sending the data.) </para> <para> To set the response status, call <function>soup_server_message_set_status</function>. If the response requires a body, you must decide whether to use <literal>Content-Length</literal> encoding (the default), or <literal>chunked</literal> encoding. </para> <refsect3> <title>Responding with <literal>Content-Length</literal> Encoding</title> <para> This is the simpler way to set a response body, if you have all of the data available at once. </para> <informalexample><programlisting> static void server_callback (SoupServer *server, SoupServerMessage *msg, const char *path, GHashTable *query, gpointer user_data) { MyServerData *server_data = user_data; const char *mime_type; GByteArray *body; if (soup_server_message_get_method (msg) != SOUP_METHOD_GET) { soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL); return; } /* This is somewhat silly. Presumably your server will do * something more interesting. */ body = g_hash_table_lookup (server_data->bodies, path); mime_type = g_hash_table_lookup (server_data->mime_types, path); if (!body || !mime_type) { soup_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL); return; } soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); soup_server_message_set_response (msg, mime_type, SOUP_MEMORY_COPY, body->data, body->len); } </programlisting></informalexample> </refsect3> <refsect3> <title>Responding with <literal>chunked</literal> Encoding</title> <para> If you want to supply the response body in chunks as it becomes available, use <literal>chunked</literal> encoding instead. In this case, first call <function>soup_message_headers_set_encoding</function> <literal>(msg->response_headers, SOUP_ENCODING_CHUNKED)</literal> to tell <application>libsoup</application> that you'll be using chunked encoding. Then call <function>soup_message_body_append</function> (or <function>soup_message_body_append_bytes</function>) on <literal>msg->response_body</literal> with each chunk of the response body as it becomes available, and call <function>soup_message_body_complete</function> when the response is complete. After each of these calls, you must also call <function>soup_server_unpause_message</function> to cause the chunk to be sent. (You do not normally need to call <function>soup_server_pause_message</function>, because I/O is automatically paused when doing a <literal>chunked</literal> transfer if no chunks are available.) </para> <para> When using chunked encoding, you must also connect to the finished signal on the message, so that you will be notified if the client disconnects between two chunks; <type>SoupServer</type> will unref the message if that happens, so you must stop adding new chunks to the response at that point. (An alternate possibility is to write each new chunk only when the wrote_chunk signal is emitted indicating that the previous one was written successfully.) </para> <para> The <emphasis><literal>simple-proxy</literal></emphasis> example in the <literal>examples/</literal> directory gives an example of using <literal>chunked</literal> encoding. </para> </refsect3> </refsect2>
<refsect2> <title>Handling Authentication</title> <para> To have <type>SoupServer</type> handle HTTP authentication for you, create a <type>SoupAuthDomainBasic</type> or <type>SoupAuthDomainDigest</type>, and pass it to <function>soup_server_add_auth_domain</function>: </para> <informalexample><programlisting> SoupAuthDomain *domain; domain = soup_auth_domain_basic_new ( "realm", "My Realm", "auth-callback", auth_callback, "auth-data", auth_data, "add-path", "/foo", "add-path", "/bar/private", NULL); soup_server_add_auth_domain (server, domain); g_object_unref (domain); </programlisting></informalexample> <para> Then, every request under one of the auth domain's paths will be passed to the <literal>auth_callback</literal> first before being passed to the <literal>server_callback</literal>: </para> <informalexample><programlisting> static gboolean auth_callback (SoupAuthDomain *domain, SoupServerMessage *msg, const char *username, const char *password, gpointer user_data) { MyServerData *server_data = user_data; MyUserData *user; user = my_server_data_lookup_user (server_data, username); if (!user) return FALSE; /* FIXME: Don't do this. Keeping a cleartext password database * is bad. */ return strcmp (password, user->password) == 0; } </programlisting></informalexample> <para> The <type>SoupAuthDomainBasicAuthCallback</type> is given the username and password from the <literal>Authorization</literal> header and must determine, in some server-specific manner, whether or not to accept them. (In this example we compare the password against a cleartext password database, but it would be better to store the password somehow encoded, as in the UNIX password database. Alternatively, you may need to delegate the password check to PAM or some other service.) </para> <para> If you are using Digest authentication, note that <type>SoupAuthDomainDigestAuthCallback</type> works completely differently (since the server doesn't receive the cleartext password from the client in that case, so there's no way to compare it directly). See the documentation for <type>SoupAuthDomainDigest</type> for more details. </para> <para> You can have multiple <type>SoupAuthDomain</type>s attached to a <literal>SoupServer</literal>, either in separate parts of the path hierarchy, or overlapping. (Eg, you might want to accept either Basic or Digest authentication for a given path.) When more than one auth domain covers a given path, the request will be accepted if the user authenticates successfully against <emphasis>any</emphasis> of the domains. </para> <para> If you want to require authentication for some requests under a certain path, but not all of them (eg, you want to authenticate <literal>PUT</literal> requests, but not <literal>GET</literal> requests), use a <type>SoupAuthDomainFilter</type>. </para> </refsect2> </refentry>