• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<?xml version="1.0"?>
2<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
3               "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
4<refentry id="libsoup-server-howto">
5<refmeta>
6<refentrytitle>libsoup Server Basics</refentrytitle>
7<manvolnum>3</manvolnum>
8<refmiscinfo>LIBSOUP Library</refmiscinfo>
9</refmeta>
10
11<refnamediv>
12<refname>libsoup Server Basics</refname><refpurpose>Server-side tutorial</refpurpose>
13</refnamediv>
14
15<refsect2>
16<title>Creating a SoupServer</title>
17
18<para>
19As with the client API, there is a single object that will encapsulate
20most of your interactions with libsoup. In this case, <link
21linkend="SoupServer"><type>SoupServer</type></link>.
22</para>
23
24<para>
25You create the server with <link
26linkend="soup-server-new"><function>soup_server_new</function></link>,
27and as with the <type>SoupSession</type> constructor, you can specify
28a few additional options:
29</para>
30
31<variablelist>
32    <varlistentry>
33	<term><link linkend="SOUP-SERVER-TLS-CERTIFICATE:CAPS"><literal>SOUP_SERVER_TLS_CERTIFICATE</literal></link></term>
34	<listitem><para>
35	    A <link
36	    linkend="GTlsCertificate"><type>GTlsCertificate</type></link>
37	    (containing a private key) that will be used when handling
38	    HTTPS requests on the server.
39	</para></listitem>
40    </varlistentry>
41    <varlistentry>
42	<term><link linkend="SOUP-SERVER-RAW-PATHS:CAPS"><literal>SOUP_SERVER_RAW_PATHS</literal></link></term>
43	<listitem><para>
44	    Set this to <literal>TRUE</literal> if you don't want
45	    <application>libsoup</application> to decode %-encoding
46	    in the Request-URI. (Eg, because you need to treat
47	    <literal>"/foo/bar"</literal> and
48	    <literal>"/foo%2Fbar"</literal> as different paths.
49	</para></listitem>
50    </varlistentry>
51    <varlistentry>
52	<term><link linkend="SOUP-SERVER-SERVER-HEADER:CAPS"><literal>SOUP_SERVER_SERVER_HEADER</literal></link></term>
53	<listitem><para>
54	    Allows you to set a Server header string that will be sent
55	    on all responses.
56	</para></listitem>
57    </varlistentry>
58    <varlistentry>
59	<term><link linkend="SOUP-SERVER-HTTP-ALIASES:CAPS"><literal>SOUP_SERVER_HTTP_ALIASES</literal></link>
60	and <link linkend="SOUP-SERVER-HTTPS-ALIASES:CAPS"><literal>SOUP_SERVER_HTTPS_ALIASES</literal></link></term>
61	<listitem><para>
62	    Allow you to tell the server to recognize additional URI
63	    schemes as aliases for "<literal>http</literal>" or
64	    <literal>https</literal>. You can set this if you are
65	    serving URIs with schemes like "<literal>dav</literal>" or
66	    "<literal>webcal</literal>".
67	</para></listitem>
68    </varlistentry>
69</variablelist>
70
71</refsect2>
72
73<refsect2>
74<title>Adding Listening Sockets</title>
75
76<para>
77  To tell the server where to listen, call <link
78  linkend="soup-server-listen"><function>soup_server_listen</function></link>
79  (to listen on a specific <link
80  linkend="GSocketAddress"><type>GSocketAddress</type></link>), <link
81  linkend="soup-server-listen-all"><function>soup_server_listen_all</function></link>
82  (to listen on a given port on all network interfaces), or <link
83  linkend="soup-server-listen-local"><function>soup_server_listen_local</function></link>
84  (to listen to a given port on the loopback interface only). You can
85  call any of these functions multiple times, to set up multiple
86  listening sockets.
87</para>
88
89<para>
90  To set up an HTTPS server, you must first either set the <link
91  linkend="SOUP-SERVER-TLS-CERTIFICATE:CAPS"><literal>SOUP_SERVER_TLS_CERTIFICATE</literal></link>
92  property, or else call <link
93  linkend="soup-server-set-ssl-cert-file"><function>soup_server_set_ssl_cert_file</function></link>.
94  After that you can pass the <link
95  linkend="SOUP-SERVER-LISTEN-HTTPS:CAPS"><literal>SOUP_SERVER_LISTEN_HTTPS</literal></link>
96  option to <link
97  linkend="soup-server-listen"><function>soup_server_listen</function></link>,
98  etc.
99</para>
100
101<para>
102  By default, servers listen for both IPv4 and IPv6 connections; if
103  you don't want this, use the <link
104  linkend="SOUP-SERVER-LISTEN-IPV4-ONLY:CAPS"><literal>SOUP_SERVER_LISTEN_IPV4_ONLY</literal></link>
105  or <link
106  linkend="SOUP-SERVER-LISTEN-IPV6-ONLY:CAPS"><literal>SOUP_SERVER_LISTEN_IPV6_ONLY</literal></link>
107  options.
108</para>
109
110<para>
111  The server runs asynchronously, in the thread-default
112  <link linkend="GMainContext"><type>GMainContext</type></link> of the
113  thread in which the "listen" calls were made.
114</para>
115</refsect2>
116
117<refsect2 id="soup-server-old-api">
118<title>The Old SoupServer Listening API</title>
119
120<para>
121<link
122linkend="soup-server-listen"><function>soup_server_listen</function></link>,
123etc, are available only in <application>libsoup</application> 2.46 and
124later. In earlier versions, there was a simpler API, in which a server
125could only listen on a single port, determined at construct time
126either by passing the <link
127linkend="SOUP-SERVER-INTERFACE:CAPS"><literal>SOUP_SERVER_INTERFACE</literal></link>
128property (to specify a <link
129linkend="SoupAddress"><type>SoupAddress</type></link> to listen on),
130or the <link
131linkend="SOUP-SERVER-PORT:CAPS"><literal>SOUP_SERVER_PORT</literal></link>
132property (to specify a port to listen on, on all interfaces). The <link
133linkend="SOUP-SERVER-SSL-CERT-FILE:CAPS"><literal>SOUP_SERVER_SSL_CERT_FILE</literal></link>
134and <link
135linkend="SOUP-SERVER-SSL-KEY-FILE:CAPS"><literal>SOUP_SERVER_SSL_KEY_FILE</literal></link>
136properties could be used to create an HTTP server.
137</para>
138
139<para>
140When using this API, if <link
141linkend="SoupServer"><type>SoupServer</type></link> is unable to bind
142the listening socket, or unable to read the provided certificate or
143key files, then it will return <literal>NULL</literal> from its
144constructor (with no further indication of what exactly went wrong).
145</para>
146
147<para>
148Additionally, when using this API, it is necessary to call <link
149linkend="soup-server-run"><function>soup_server_run</function></link>
150or <link
151linkend="soup-server-run-async"><function>soup_server_run_async</function></link>
152to start the server after creating it.
153</para>
154
155</refsect2>
156
157<refsect2>
158<title>Adding Handlers</title>
159
160<para>
161By default, <link linkend="SoupServer"><type>SoupServer</type></link>
162returns "404 Not Found" in response to all requests (except ones that
163it can't parse, which get "400 Bad Request"). To override this
164behavior, call <link
165linkend="soup-server-add-handler"><function>soup_server_add_handler</function></link>
166to set a callback to handle certain URI paths.
167</para>
168
169<informalexample><programlisting>
170	soup_server_add_handler (server, "/foo", server_callback,
171	                         data, destroy_notify);
172</programlisting></informalexample>
173
174<para>
175The <literal>"/foo"</literal> indicates the base path for this
176handler. When a request comes in, if there is a handler registered for
177exactly the path in the request's <literal>Request-URI</literal>, then
178that handler will be called. Otherwise
179<application>libsoup</application> will strip path components one by
180one until it finds a matching handler. So for example, a request of
181the form
182"<literal>GET&#xA0;/foo/bar/baz.html?a=1&amp;b=2&#xA0;HTTP/1.1</literal>"
183would look for handlers for "<literal>/foo/bar/baz.html</literal>",
184"<literal>/foo/bar</literal>", and "<literal>/foo</literal>". If a
185handler has been registered with a <literal>NULL</literal> base path,
186then it is used as the default handler for any request that doesn't
187match any other handler.
188</para>
189
190</refsect2>
191
192<refsect2>
193<title>Responding to Requests</title>
194
195<para>
196A handler callback looks something like this:
197</para>
198
199<informalexample><programlisting>
200static void
201server_callback (SoupServer        *server,
202                 SoupMessage       *msg,
203                 const char        *path,
204                 GHashTable        *query,
205                 SoupClientContext *client,
206                 gpointer           user_data)
207{
208	...
209}
210</programlisting></informalexample>
211
212<para>
213<literal>msg</literal> is the request that has been received and
214<literal>user_data</literal> is the data that was passed to <link
215linkend="soup-server-add-handler"><function>soup_server_add_handler</function></link>.
216<literal>path</literal> is the path (from <literal>msg</literal>'s
217URI), and <literal>query</literal> contains the result of parsing the
218URI query field. (It is <literal>NULL</literal> if there was no
219query.) <literal>client</literal> is a <link
220linkend="SoupClientContext"><type>SoupClientContext</type></link>,
221which contains additional information about the client (including its
222IP address, and whether or not it used HTTP authentication).
223</para>
224
225<para>
226By default, <application>libsoup</application> assumes that you have
227completely finished processing the message when you return from the
228callback, and that it can therefore begin sending the response. If you
229are not ready to send a response immediately (eg, you have to contact
230another server, or wait for data from a database), you must call <link
231linkend="soup-server-pause-message"><function>soup_server_pause_message</function></link>
232on the message before returning from the callback. This will delay
233sending a response until you call <link
234linkend="soup-server-unpause-message"><function>soup_server_unpause_message</function></link>.
235(You must also connect to the <link
236linkend="SoupMessage-finished">finished</link> signal on the message
237in this case, so that you can break off processing if the client
238unexpectedly disconnects before you start sending the data.)
239</para>
240
241<para>
242To set the response status, call <link
243linkend="soup-message-set-status"><function>soup_message_set_status</function></link>
244or <link
245linkend="soup-message-set-status-full"><function>soup_message_set_status_full</function></link>.
246If the response requires a body, you must decide whether to use
247<literal>Content-Length</literal> encoding (the default), or
248<literal>chunked</literal> encoding.
249</para>
250
251<refsect3>
252<title>Responding with <literal>Content-Length</literal>
253Encoding</title>
254
255<para>
256This is the simpler way to set a response body, if you have all of the
257data available at once.
258</para>
259
260<informalexample><programlisting>
261static void
262server_callback (SoupServer        *server,
263                 SoupMessage       *msg,
264                 const char        *path,
265                 GHashTable        *query,
266                 SoupClientContext *client,
267                 gpointer           user_data)
268{
269	MyServerData *server_data = user_data;
270	const char *mime_type;
271	GByteArray *body;
272
273	if (msg->method != SOUP_METHOD_GET) {
274		soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
275		return;
276	}
277
278	/* This is somewhat silly. Presumably your server will do
279	 * something more interesting.
280	 */
281	body = g_hash_table_lookup (server_data->bodies, path);
282	mime_type = g_hash_table_lookup (server_data->mime_types, path);
283	if (!body || !mime_type) {
284		soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
285		return;
286	}
287
288	soup_message_set_status (msg, SOUP_STATUS_OK);
289	soup_message_set_response (msg, mime_type, SOUP_MEMORY_COPY,
290	                           body->data, body->len);
291}
292</programlisting></informalexample>
293
294</refsect3>
295
296<refsect3>
297<title>Responding with <literal>chunked</literal> Encoding</title>
298
299<para>
300If you want to supply the response body in chunks as it becomes
301available, use <literal>chunked</literal> encoding instead. In this
302case, first call <link
303linkend="soup-message-headers-set-encoding"><function>soup_message_headers_set_encoding</function></link>&#160;<literal>(msg->response_headers,&#160;<link
304linkend="SoupEncoding">SOUP_ENCODING_CHUNKED</link>)</literal>
305to tell <application>libsoup</application> that you'll be using
306chunked encoding. Then call <link
307linkend="soup-message-body-append"><function>soup_message_body_append</function></link>
308(or <link
309linkend="soup-message-body-append-buffer"><function>soup_message_body_append_buffer</function></link>)
310on <literal>msg->response_body</literal> with each chunk of the
311response body as it becomes available, and call <link
312linkend="soup-message-body-complete"><function>soup_message_body_complete</function></link>
313when the response is complete. After each of these calls, you must
314also call <link
315linkend="soup-server-unpause-message"><function>soup_server_unpause_message</function></link>
316to cause the chunk to be sent. (You do not normally need to call <link
317linkend="soup-server-pause-message"><function>soup_server_pause_message</function></link>,
318because I/O is automatically paused when doing a
319<literal>chunked</literal> transfer if no chunks are available.)
320</para>
321
322<para>
323When using chunked encoding, you must also connect to the <link
324linkend="SoupMessage-finished">finished</link> signal on the message,
325so that you will be notified if the client disconnects between two
326chunks; <type>SoupServer</type> will unref the message if that
327happens, so you must stop adding new chunks to the response at that
328point. (An alternate possibility is to write each new chunk only when
329the <link linkend="SoupMessage-wrote-chunk">wrote_chunk</link> signal
330is emitted indicating that the previous one was written successfully.)
331</para>
332
333<para>
334The <emphasis role="bold"><literal>simple-proxy</literal></emphasis>
335example in the <literal>examples/</literal> directory gives an example of
336using <literal>chunked</literal> encoding.
337</para>
338
339</refsect3>
340</refsect2>
341
342
343<refsect2>
344<title>Handling Authentication</title>
345
346<para>
347To have <link linkend="SoupServer"><type>SoupServer</type></link>
348handle HTTP authentication for you, create a <link
349linkend="SoupAuthDomainBasic"><type>SoupAuthDomainBasic</type></link>
350or <link
351linkend="SoupAuthDomainDigest"><type>SoupAuthDomainDigest</type></link>,
352and pass it to <link
353linkend="soup-server-add-auth-domain"><function>soup_server_add_auth_domain</function></link>:
354</para>
355
356<informalexample><programlisting>
357	SoupAuthDomain *domain;
358
359	domain = soup_auth_domain_basic_new (
360		SOUP_AUTH_DOMAIN_REALM, "My Realm",
361		SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
362		SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA, auth_data,
363		SOUP_AUTH_DOMAIN_ADD_PATH, "/foo",
364		SOUP_AUTH_DOMAIN_ADD_PATH, "/bar/private",
365		NULL);
366	soup_server_add_auth_domain (server, domain);
367	g_object_unref (domain);
368</programlisting></informalexample>
369
370<para>
371Then, every request under one of the auth domain's paths will be
372passed to the <literal>auth_callback</literal> first before being
373passed to the <literal>server_callback</literal>:
374</para>
375
376<informalexample><programlisting>
377static gboolean
378auth_callback (SoupAuthDomain *domain, SoupMessage *msg,
379               const char *username, const char *password,
380               gpointer user_data)
381{
382	MyServerData *server_data = user_data;
383	MyUserData *user;
384
385	user = my_server_data_lookup_user (server_data, username);
386	if (!user)
387		return FALSE;
388
389	/* FIXME: Don't do this. Keeping a cleartext password database
390	 * is bad.
391	 */
392	return strcmp (password, user->password) == 0;
393}
394</programlisting></informalexample>
395
396<para>
397The <link
398linkend="SoupAuthDomainBasicAuthCallback"><type>SoupAuthDomainBasicAuthCallback</type></link>
399is given the username and password from the
400<literal>Authorization</literal> header and must determine, in some
401server-specific manner, whether or not to accept them. (In this
402example we compare the password against a cleartext password database,
403but it would be better to store the password somehow encoded, as in
404the UNIX password database. Alternatively, you may need to delegate
405the password check to PAM or some other service.)
406</para>
407
408<para>
409If you are using Digest authentication, note that <link
410linkend="SoupAuthDomainDigestAuthCallback"><type>SoupAuthDomainDigestAuthCallback</type></link>
411works completely differently (since the server doesn't receive the
412cleartext password from the client in that case, so there's no way to
413compare it directly). See the documentation for <link
414linkend="SoupAuthDomainDigest"><type>SoupAuthDomainDigest</type></link>
415for more details.
416</para>
417
418<para>
419You can have multiple <type>SoupAuthDomain</type>s attached to a
420<literal>SoupServer</literal>, either in separate parts of the path
421hierarchy, or overlapping. (Eg, you might want to accept either Basic
422or Digest authentication for a given path.) When more than one auth
423domain covers a given path, the request will be accepted if the user
424authenticates successfully against <emphasis>any</emphasis> of the
425domains.
426</para>
427
428<para>
429If you want to require authentication for some requests under a
430certain path, but not all of them (eg, you want to authenticate
431<literal>PUT</literal> requests, but not <literal>GET</literal>
432requests), use a <link
433linkend="SoupAuthDomainFilter"><type>SoupAuthDomainFilter</type></link>.
434</para>
435
436</refsect2>
437
438</refentry>
439