Blob Blame History Raw
<?xml version="1.0"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" 
               "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
<refentry id="libsoup-server-howto">
<refmeta>
<refentrytitle>libsoup Server Basics</refentrytitle>
<manvolnum>3</manvolnum>
<refmiscinfo>LIBSOUP Library</refmiscinfo>
</refmeta>

<refnamediv>
<refname>libsoup Server Basics</refname><refpurpose>Server-side tutorial</refpurpose>
</refnamediv>

<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, <link
linkend="SoupServer"><type>SoupServer</type></link>.
</para>

<para>
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
a few additional options:
</para>

<variablelist>
    <varlistentry>
	<term><link linkend="SOUP-SERVER-TLS-CERTIFICATE:CAPS"><literal>SOUP_SERVER_TLS_CERTIFICATE</literal></link></term>
	<listitem><para>
	    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-RAW-PATHS:CAPS"><literal>SOUP_SERVER_RAW_PATHS</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.
	</para></listitem>
    </varlistentry>
    <varlistentry>
	<term><link linkend="SOUP-SERVER-SERVER-HEADER:CAPS"><literal>SOUP_SERVER_SERVER_HEADER</literal></link></term>
	<listitem><para>
	    Allows you to set a Server header string that will be sent
	    on all responses.
	</para></listitem>
    </varlistentry>
    <varlistentry>
	<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>
	    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>

</refsect2>

<refsect2>
<title>Adding Listening Sockets</title>

<para>
  To tell the server where to listen, call <link
  linkend="soup-server-listen"><function>soup_server_listen</function></link>
  (to listen on a specific <link
  linkend="GSocketAddress"><type>GSocketAddress</type></link>), <link
  linkend="soup-server-listen-all"><function>soup_server_listen_all</function></link>
  (to listen on a given port on all network interfaces), or <link
  linkend="soup-server-listen-local"><function>soup_server_listen_local</function></link>
  (to listen to a given port on the loopback interface only). You can
  call any of these functions multiple times, to set up multiple
  listening sockets.
</para>

<para>
  To set up an HTTPS server, you must first either set the <link
  linkend="SOUP-SERVER-TLS-CERTIFICATE:CAPS"><literal>SOUP_SERVER_TLS_CERTIFICATE</literal></link>
  property, or else call <link
  linkend="soup-server-set-ssl-cert-file"><function>soup_server_set_ssl_cert_file</function></link>.
  After that you can pass the <link
  linkend="SOUP-SERVER-LISTEN-HTTPS:CAPS"><literal>SOUP_SERVER_LISTEN_HTTPS</literal></link>
  option to <link
  linkend="soup-server-listen"><function>soup_server_listen</function></link>,
  etc.
</para>

<para>
  By default, servers listen for both IPv4 and IPv6 connections; if
  you don't want this, use the <link
  linkend="SOUP-SERVER-LISTEN-IPV4-ONLY:CAPS"><literal>SOUP_SERVER_LISTEN_IPV4_ONLY</literal></link>
  or <link
  linkend="SOUP-SERVER-LISTEN-IPV6-ONLY:CAPS"><literal>SOUP_SERVER_LISTEN_IPV6_ONLY</literal></link>
  options.
</para>

<para>
  The server runs asynchronously, in the thread-default
  <link linkend="GMainContext"><type>GMainContext</type></link> of the
  thread in which the "listen" calls were made.
</para>
</refsect2>

<refsect2 id="soup-server-old-api">
<title>The Old SoupServer Listening API</title>

<para>
<link
linkend="soup-server-listen"><function>soup_server_listen</function></link>,
etc, are available only in <application>libsoup</application> 2.46 and
later. In earlier versions, there was a simpler API, in which a server
could only listen on a single port, determined at construct time
either by passing the <link
linkend="SOUP-SERVER-INTERFACE:CAPS"><literal>SOUP_SERVER_INTERFACE</literal></link>
property (to specify a <link
linkend="SoupAddress"><type>SoupAddress</type></link> to listen on),
or the <link
linkend="SOUP-SERVER-PORT:CAPS"><literal>SOUP_SERVER_PORT</literal></link>
property (to specify a port to listen on, on all interfaces). The <link
linkend="SOUP-SERVER-SSL-CERT-FILE:CAPS"><literal>SOUP_SERVER_SSL_CERT_FILE</literal></link>
and <link
linkend="SOUP-SERVER-SSL-KEY-FILE:CAPS"><literal>SOUP_SERVER_SSL_KEY_FILE</literal></link>
properties could be used to create an HTTP server.
</para>

<para>
When using this API, if <link
linkend="SoupServer"><type>SoupServer</type></link> is unable to bind
the listening socket, or unable to read the provided certificate or
key files, then it will return <literal>NULL</literal> from its
constructor (with no further indication of what exactly went wrong).
</para>

<para>
Additionally, when using this API, it is necessary to call <link
linkend="soup-server-run"><function>soup_server_run</function></link>
or <link
linkend="soup-server-run-async"><function>soup_server_run_async</function></link>
to start the server after creating it.
</para>

</refsect2>

<refsect2>
<title>Adding Handlers</title>

<para>
By default, <link linkend="SoupServer"><type>SoupServer</type></link>
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 <link
linkend="soup-server-add-handler"><function>soup_server_add_handler</function></link>
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&#xA0;/foo/bar/baz.html?a=1&amp;b=2&#xA0;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,
                 SoupMessage       *msg, 
                 const char        *path,
                 GHashTable        *query,
                 SoupClientContext *client,
                 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 <link
linkend="soup-server-add-handler"><function>soup_server_add_handler</function></link>.
<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.) <literal>client</literal> is a <link
linkend="SoupClientContext"><type>SoupClientContext</type></link>,
which contains additional information about the client (including its
IP address, and whether or not it used HTTP authentication).
</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 <link
linkend="soup-server-pause-message"><function>soup_server_pause_message</function></link>
on the message before returning from the callback. This will delay
sending a response until you call <link
linkend="soup-server-unpause-message"><function>soup_server_unpause_message</function></link>.
(You must also connect to the <link
linkend="SoupMessage-finished">finished</link> 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 <link
linkend="soup-message-set-status"><function>soup_message_set_status</function></link>
or <link
linkend="soup-message-set-status-full"><function>soup_message_set_status_full</function></link>.
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,
                 SoupMessage       *msg, 
                 const char        *path,
                 GHashTable        *query,
                 SoupClientContext *client,
                 gpointer           user_data)
{
	MyServerData *server_data = user_data;
	const char *mime_type;
	GByteArray *body;

	if (msg->method != SOUP_METHOD_GET) {
		soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
		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_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
		return;
	}

	soup_message_set_status (msg, SOUP_STATUS_OK);
	soup_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 <link
linkend="soup-message-headers-set-encoding"><function>soup_message_headers_set_encoding</function></link>&#160;<literal>(msg->response_headers,&#160;<link
linkend="SoupEncoding">SOUP_ENCODING_CHUNKED</link>)</literal>
to tell <application>libsoup</application> that you'll be using
chunked encoding. Then call <link
linkend="soup-message-body-append"><function>soup_message_body_append</function></link>
(or <link
linkend="soup-message-body-append-buffer"><function>soup_message_body_append_buffer</function></link>)
on <literal>msg->response_body</literal> with each chunk of the
response body as it becomes available, and call <link
linkend="soup-message-body-complete"><function>soup_message_body_complete</function></link>
when the response is complete. After each of these calls, you must
also call <link
linkend="soup-server-unpause-message"><function>soup_server_unpause_message</function></link>
to cause the chunk to be sent. (You do not normally need to call <link
linkend="soup-server-pause-message"><function>soup_server_pause_message</function></link>,
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 <link
linkend="SoupMessage-finished">finished</link> 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 <link linkend="SoupMessage-wrote-chunk">wrote_chunk</link> signal
is emitted indicating that the previous one was written successfully.)
</para>

<para>
The <emphasis role="bold"><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 <link linkend="SoupServer"><type>SoupServer</type></link>
handle HTTP authentication for you, create a <link
linkend="SoupAuthDomainBasic"><type>SoupAuthDomainBasic</type></link>
or <link
linkend="SoupAuthDomainDigest"><type>SoupAuthDomainDigest</type></link>,
and pass it to <link
linkend="soup-server-add-auth-domain"><function>soup_server_add_auth_domain</function></link>:
</para>

<informalexample><programlisting>
	SoupAuthDomain *domain;

	domain = soup_auth_domain_basic_new (
		SOUP_AUTH_DOMAIN_REALM, "My Realm",
		SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
		SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA, auth_data,
		SOUP_AUTH_DOMAIN_ADD_PATH, "/foo",
		SOUP_AUTH_DOMAIN_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, SoupMessage *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 <link
linkend="SoupAuthDomainBasicAuthCallback"><type>SoupAuthDomainBasicAuthCallback</type></link>
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 <link
linkend="SoupAuthDomainDigestAuthCallback"><type>SoupAuthDomainDigestAuthCallback</type></link>
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 <link
linkend="SoupAuthDomainDigest"><type>SoupAuthDomainDigest</type></link>
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 <link
linkend="SoupAuthDomainFilter"><type>SoupAuthDomainFilter</type></link>.
</para>

</refsect2>

</refentry>