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 /foo/bar/baz.html?a=1&b=2 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> <literal>(msg->response_headers, <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