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-client-howto">
5<refmeta>
6<refentrytitle>libsoup Client Basics</refentrytitle>
7<manvolnum>3</manvolnum>
8<refmiscinfo>LIBSOUP Library</refmiscinfo>
9</refmeta>
10
11<refnamediv>
12<refname>libsoup Client Basics</refname><refpurpose>Client-side tutorial</refpurpose>
13</refnamediv>
14
15<refsect2>
16<para>
17This section explains how to use <application>libsoup</application> as
18an HTTP client using several new APIs introduced in version 2.42. If
19you want to be compatible with older versions of
20<application>libsoup</application>, consult the documentation for that
21version.
22</para>
23</refsect2>
24
25<refsect2>
26<title>Creating a <type>SoupSession</type></title>
27
28<para>
29The first step in using the client API is to create a <link
30linkend="SoupSession"><type>SoupSession</type></link>. The session object
31encapsulates all of the state that <application>libsoup</application>
32is keeping on behalf of your program; cached HTTP connections,
33authentication information, etc.
34</para>
35
36<para>
37When you create the session with <link
38linkend="soup-session-new-with-options"><function>soup_session_new_with_options</function></link>,
39you can specify various additional options:
40</para>
41
42<variablelist>
43    <varlistentry>
44	<term><link linkend="SOUP-SESSION-MAX-CONNS:CAPS"><literal>SOUP_SESSION_MAX_CONNS</literal></link></term>
45	<listitem><para>
46	    Allows you to set the maximum total number of connections
47	    the session will have open at one time. (Once it reaches
48	    this limit, it will either close idle connections, or
49	    wait for existing connections to free up before starting
50	    new requests.) The default value is 10.
51	</para></listitem>
52    </varlistentry>
53    <varlistentry>
54	<term><link linkend="SOUP-SESSION-MAX-CONNS-PER-HOST:CAPS"><literal>SOUP_SESSION_MAX_CONNS_PER_HOST</literal></link></term>
55	<listitem><para>
56	    Allows you to set the maximum total number of connections
57	    the session will have open <emphasis>to a single
58	    host</emphasis> at one time. The default value is 2.
59	</para></listitem>
60    </varlistentry>
61    <varlistentry>
62	<term><link linkend="SOUP-SESSION-USER-AGENT:CAPS"><literal>SOUP_SESSION_USER_AGENT</literal></link></term>
63	<listitem><para>
64	    Allows you to set a User-Agent string that will be sent
65	    on all outgoing requests.
66	</para></listitem>
67    </varlistentry>
68    <varlistentry>
69	<term><link linkend="SOUP-SESSION-ACCEPT-LANGUAGE:CAPS"><literal>SOUP_SESSION_ACCEPT_LANGUAGE</literal></link>
70	and <link linkend="SOUP-SESSION-ACCEPT-LANGUAGE-AUTO:CAPS"><literal>SOUP_SESSION_ACCEPT_LANGUAGE_AUTO</literal></link></term>
71	<listitem><para>
72	    Allow you to set an Accept-Language header on all outgoing
73	    requests. <literal>SOUP_SESSION_ACCEPT_LANGUAGE</literal>
74	    takes a list of language tags to use, while
75	    <literal>SOUP_SESSION_ACCEPT_LANGUAGE_AUTO</literal>
76	    automatically generates the list from the user's locale
77	    settings.
78	</para></listitem>
79    </varlistentry>
80    <varlistentry>
81	<term><link linkend="SOUP-SESSION-HTTP-ALIASES:CAPS"><literal>SOUP_SESSION_HTTP_ALIASES</literal></link>
82	and <link linkend="SOUP-SESSION-HTTPS-ALIASES:CAPS"><literal>SOUP_SESSION_HTTPS_ALIASES</literal></link></term>
83	<listitem><para>
84	    Allow you to tell the session to recognize additional URI
85	    schemes as aliases for "<literal>http</literal>" or
86	    <literal>https</literal>. You can set this if you are
87	    using URIs with schemes like "<literal>dav</literal>" or
88	    "<literal>webcal</literal>" (and in particular, you need
89	    to set this if the server you are talking to might return
90	    redirects with such a scheme).
91	</para></listitem>
92    </varlistentry>
93    <varlistentry>
94	<term><link linkend="SOUP-SESSION-PROXY-RESOLVER:CAPS"><literal>SOUP_SESSION_PROXY_RESOLVER</literal></link> and <link linkend="SOUP-SESSION-PROXY-URI:CAPS"><literal>SOUP_SESSION_PROXY_URI</literal></link></term>
95	<listitem>
96	    <para>
97		<link linkend="SOUP-SESSION-PROXY-RESOLVER:CAPS"><literal>SOUP_SESSION_PROXY_RESOLVER</literal></link>
98		specifies a <link
99		linkend="GProxyResolver"><type>GProxyResolver</type></link>
100		to use to determine the HTTP proxies to use. By default,
101		this is set to the resolver returned by <link
102		linkend="g-proxy-resolver-get-default"><function>g_proxy_resolver_get_default</function></link>,
103		so you do not need to set it yourself.
104	    </para>
105	    <para>
106		Alternatively, if you want all requests to go through a
107		single proxy, you can set <link
108		linkend="SOUP-SESSION-PROXY-URI:CAPS"><literal>SOUP_SESSION_PROXY_URI</literal></link>.
109	    </para>
110	</listitem>
111    </varlistentry>
112    <varlistentry>
113	<term><link linkend="SOUP-SESSION-ADD-FEATURE:CAPS"><literal>SOUP_SESSION_ADD_FEATURE</literal></link> and <link linkend="SOUP-SESSION-ADD-FEATURE-BY-TYPE:CAPS"><literal>SOUP_SESSION_ADD_FEATURE_BY_TYPE</literal></link></term>
114	<listitem><para>
115	    These allow you to specify <link
116	    linkend="SoupSessionFeature"><type>SoupSessionFeature</type></link>s
117	    (discussed <link linkend="session-features">below</link>)
118	    to add at construct-time.
119	</para></listitem>
120    </varlistentry>
121</variablelist>
122
123<para>
124Other properties are also available; see the <link
125linkend="SoupSession"><type>SoupSession</type></link> documentation for
126more details.
127</para>
128
129<para>
130If you don't need to specify any options, you can just use <link
131linkend="soup-session-new"><function>soup_session_new</function></link>,
132which takes no arguments.
133</para>
134
135</refsect2>
136
137<refsect2 id="session-features">
138<title>Session features</title>
139
140<para>
141Additional session functionality is provided as <link
142linkend="SoupSessionFeature"><type>SoupSessionFeature</type></link>s,
143which can be added to a session, via the <link
144linkend="SOUP-SESSION-ADD-FEATURE:CAPS"><literal>SOUP_SESSION_ADD_FEATURE</literal></link>
145and <link
146linkend="SOUP-SESSION-ADD-FEATURE-BY-TYPE:CAPS"><literal>SOUP_SESSION_ADD_FEATURE_BY_TYPE</literal></link>
147options at session-construction-time, or afterward via the <link
148linkend="soup-session-add-feature"><function>soup_session_add_feature</function></link>
149and <link
150linkend="soup-session-add-feature-by-type"><function>soup_session_add_feature_by_type</function></link>
151functions.
152</para>
153
154<para>
155A <link
156linkend="SoupContentDecoder"><type>SoupContentDecoder</type></link> is
157added for you automatically. This advertises to servers that the
158client supports compression, and automatically decompresses compressed
159responses.
160</para>
161
162<para>
163Some other available features that you can add include:
164</para>
165
166<variablelist>
167    <varlistentry>
168	<term><link linkend="SoupLogger"><type>SoupLogger</type></link></term>
169	<listitem><para>
170	    A debugging aid, which logs all of libsoup's HTTP traffic
171	    to <literal>stdout</literal> (or another place you specify).
172	</para></listitem>
173    </varlistentry>
174    <varlistentry>
175	<term>
176	    <link linkend="SoupCookieJar"><type>SoupCookieJar</type></link>,
177	    <link linkend="SoupCookieJarText"><type>SoupCookieJarText</type></link>,
178	    and <link linkend="SoupCookieJarDB"><type>SoupCookieJarDB</type></link>
179	</term>
180	<listitem><para>
181	    Support for HTTP cookies. <type>SoupCookieJar</type>
182	    provides non-persistent cookie storage, while
183	    <type>SoupCookieJarText</type> uses a text file to keep
184	    track of cookies between sessions, and
185	    <type>SoupCookieJarDB</type> uses a
186	    <application>SQLite</application> database.
187	</para></listitem>
188    </varlistentry>
189    <varlistentry>
190	<term><link linkend="SoupContentSniffer"><type>SoupContentSniffer</type></link></term>
191	<listitem><para>
192	    Uses the HTML5 sniffing rules to attempt to
193	    determine the Content-Type of a response when the
194	    server does not identify the Content-Type, or appears to
195	    have provided an incorrect one.
196	</para></listitem>
197    </varlistentry>
198</variablelist>
199
200<para>
201Use the "add_feature_by_type" property/function to add features that
202don't require any configuration (such as <link
203linkend="SoupContentSniffer"><type>SoupContentSniffer</type></link>),
204and the "add_feature" property/function to add features that must be
205constructed first (such as <link
206linkend="SoupLogger"><type>SoupLogger</type></link>). For example, an
207application might do something like the following:
208</para>
209
210<informalexample><programlisting>
211	session = soup_session_new_with_options (
212		SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_SNIFFER,
213		NULL);
214
215	if (debug_level) {
216		SoupLogger *logger;
217
218		logger = soup_logger_new (debug_level, -1);
219		soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
220		g_object_unref (logger);
221	}
222</programlisting></informalexample>
223
224</refsect2>
225
226<refsect2>
227<title>Creating and Sending SoupMessages</title>
228
229<para>
230Once you have a session, you send HTTP requests using <link
231linkend="SoupMessage"><type>SoupMessage</type></link>. In the simplest
232case, you only need to create the message and it's ready to send:
233</para>
234
235<informalexample><programlisting>
236	SoupMessage *msg;
237
238	msg = soup_message_new ("GET", "http://example.com/");
239</programlisting></informalexample>
240
241<para>
242In more complicated cases, you can use various <link
243linkend="SoupMessage">SoupMessage</link>, <link
244linkend="SoupMessageHeaders">SoupMessageHeaders</link>, and <link
245linkend="SoupMessageBody">SoupMessageBody</link> methods to set the
246request headers and body of the message:
247</para>
248
249<informalexample><programlisting>
250	SoupMessage *msg;
251
252	msg = soup_message_new ("POST", "http://example.com/form.cgi");
253	soup_message_set_request (msg, "application/x-www-form-urlencoded",
254	                          SOUP_MEMORY_COPY, formdata, strlen (formdata));
255	soup_message_headers_append (msg->request_headers, "Referer", referring_url);
256</programlisting></informalexample>
257
258<para>
259(Although this is a bad example, because
260<application>libsoup</application> actually has convenience methods
261for dealing with <link linkend="libsoup-2.4-HTML-Form-Support">HTML
262forms</link>, as well as <link
263linkend="libsoup-2.4-XMLRPC-Support">XML-RPC</link>.)
264</para>
265
266<para>
267You can also use <link
268linkend="soup-message-set-flags"><function>soup_message_set_flags</function></link>
269to change some default behaviors. For example, by default,
270<type>SoupSession</type> automatically handles responses from the
271server that redirect to another URL. If you would like to handle these
272yourself, you can set the <link linkend="SOUP-MESSAGE-NO-REDIRECT:CAPS"><literal>SOUP_MESSAGE_NO_REDIRECT</literal></link>
273flag.
274</para>
275
276<refsect3>
277<title>Sending a Message Synchronously</title>
278
279<para>
280To send a message and wait for the response, use <link
281linkend="soup-session-send"><function>soup_session_send</function></link>:
282</para>
283
284<informalexample><programlisting>
285	GInputStream *stream;
286	GError *error = NULL;
287
288	stream = soup_session_send (session, msg, cancellable, &amp;error);
289</programlisting></informalexample>
290
291<para>
292At the point when <function>soup_session_send</function> returns, the
293request will have been sent, and the response headers read back in;
294you can examine the message's <structfield>status_code</structfield>,
295<structfield>reason_phrase</structfield>, and
296<structfield>response_headers</structfield> fields to see the response
297metadata. To get the response body, read from the returned <link
298linkend="GInputStream"><type>GInputStream</type></link>, and close it
299when you are done.
300</para>
301
302<para>
303Note that <function>soup_session_send</function> only returns an error
304if a transport-level problem occurs (eg, it could not connect to the
305host, or the request was cancelled). Use the message's
306<structfield>status_code</structfield> field to determine whether the
307request was successful or not at the HTTP level (ie, "<literal>200
308OK</literal>" vs "<literal>401 Bad Request</literal>").
309</para>
310
311<para>
312If you would prefer to have <application>libsoup</application> gather
313the response body for you and then return it all at once, you can use
314the older
315<link linkend="soup-session-send-message"><function>soup_session_send_message</function></link>
316API:
317</para>
318
319<informalexample><programlisting>
320	guint status;
321
322	status = soup_session_send_message (session, msg);
323</programlisting></informalexample>
324
325<para>
326In this case, the response body will be available in the message's
327<structfield>response_body</structfield> field, and transport-level
328errors will be indicated in the <structfield>status_code</structfield>
329field via special pseudo-HTTP-status codes like <link
330linkend="SOUP-STATUS-CANT-CONNECT:CAPS"><literal>SOUP_STATUS_CANT_CONNECT</literal></link>.
331</para>
332
333</refsect3>
334
335<refsect3>
336<title>Sending a Message Asynchronously</title>
337
338<para>
339To send a message asynchronously, use <link
340linkend="soup-session-send-async"><function>soup_session_send_async</function></link>:
341</para>
342
343<informalexample><programlisting>
344{
345	...
346	soup_session_send_async (session, msg, cancellable, my_callback, my_callback_data);
347	...
348}
349
350static void
351my_callback (GObject *object, GAsyncResult *result, gpointer user_data)
352{
353	GInputStream *stream;
354	GError *error = NULL;
355
356	stream = soup_session_send_finish (SOUP_SESSION (object), result, &amp;error);
357	...
358}
359</programlisting></informalexample>
360
361<para>
362The message will be added to the session's queue, and eventually (when
363control is returned back to the main loop), it will be sent and the
364response will be read. When the message has been sent, and its
365headers received, the callback will be invoked, in the standard
366<link linkend="GAsyncReadyCallback"><type>GAsyncReadyCallback</type></link>
367style.
368</para>
369
370<para>
371As with synchronous sending, there is also an alternate API, <link
372linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>,
373in which your callback is not invoked until the response has been
374completely read:
375</para>
376
377<informalexample><programlisting>
378{
379	...
380	soup_session_queue_message (session, msg, my_callback, my_callback_data);
381	...
382}
383
384static void
385my_callback (SoupSession *session, SoupMessage *msg, gpointer user_data)
386{
387	/* msg->response_body contains the response */
388}
389</programlisting></informalexample>
390
391<para>
392<link
393linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>
394is slightly unusual in that it steals a reference to the message
395object, and unrefs it after the last callback is invoked on it. So
396when using this API, you should not unref the message yourself.
397</para>
398
399</refsect3>
400
401</refsect2>
402
403<refsect2>
404<title>Processing the Response</title>
405
406<para>
407Once you have received the initial response from the server,
408synchronously or asynchronously, streaming or not, you can look at the
409response fields in the <literal>SoupMessage</literal> to decide what
410to do next. The <structfield>status_code</structfield> and
411<structfield>reason_phrase</structfield> fields contain the numeric
412status and textual status response from the server.
413<structfield>response_headers</structfield> contains the response
414headers, which you can investigate using <link
415linkend="soup-message-headers-get"><function>soup_message_headers_get</function></link>
416and <link
417linkend="soup-message-headers-foreach"><function>soup_message_headers_foreach</function></link>.
418</para>
419
420<para>
421<link
422linkend="SoupMessageHeaders"><type>SoupMessageHeaders</type></link>
423automatically parses several important headers in
424<structfield>response_headers</structfield> for you and provides
425specialized accessors for them. Eg, <link
426linkend="soup-message-headers-get-content-type"><function>soup_message_headers_get_content_type</function></link>.
427There are several generic methods such as <link
428linkend="soup-header-parse-param-list"><function>soup_header_parse_param_list</function></link>
429(for parsing an attribute-list-type header) and <link
430linkend="soup-header-contains"><function>soup_header_contains</function></link>
431(for quickly testing if a list-type header contains a particular
432token). These handle the various syntactical oddities of parsing HTTP
433headers much better than functions like
434<function>g_strsplit</function> or <function>strstr</function>.
435</para>
436
437</refsect2>
438
439<refsect2>
440<title>Handling Authentication</title>
441
442<para>
443<type>SoupSession</type> handles most of the details of HTTP
444authentication for you. If it receives a 401 ("Unauthorized") or 407
445("Proxy Authentication Required") response, the session will emit the
446<link linkend="SoupSession-authenticate">authenticate</link> signal,
447providing you with a <link
448linkend="SoupAuth"><type>SoupAuth</type></link> object indicating the
449authentication type ("Basic", "Digest", or "NTLM") and the realm name
450provided by the server. If you have a username and password available
451(or can generate one), call <link
452linkend="soup-auth-authenticate"><function>soup_auth_authenticate</function></link>
453to give the information to libsoup. The session will automatically
454requeue the message and try it again with that authentication
455information. (If you don't call
456<function>soup_auth_authenticate</function>, the session will just
457return the message to the application with its 401 or 407 status.)
458</para>
459
460<para>
461If the server doesn't accept the username and password provided, the
462session will emit <link
463linkend="SoupSession-authenticate">authenticate</link> again, with the
464<literal>retrying</literal> parameter set to <literal>TRUE</literal>. This lets the
465application know that the information it provided earlier was
466incorrect, and gives it a chance to try again. If this
467username/password pair also doesn't work, the session will contine to
468emit <literal>authenticate</literal> again and again until the
469provided username/password successfully authenticates, or until the
470signal handler fails to call <link
471linkend="soup-auth-authenticate"><function>soup_auth_authenticate</function></link>,
472at which point <application>libsoup</application> will allow the
473message to fail (with status 401 or 407).
474</para>
475
476<para>
477If you need to handle authentication asynchronously (eg, to pop up a
478password dialog without recursively entering the main loop), you can
479do that as well. Just call <link
480linkend="soup-session-pause-message"><function>soup_session_pause_message</function></link>
481on the message before returning from the signal handler, and
482<function>g_object_ref</function> the <type>SoupAuth</type>. Then,
483later on, after calling <function>soup_auth_authenticate</function>
484(or deciding not to), call <link
485linkend="soup-session-unpause-message"><function>soup_session_unpause_message</function></link>
486to resume the paused message.
487</para>
488
489<para>
490By default, NTLM authentication is not enabled. To add NTLM support to
491a session, call:
492</para>
493
494<informalexample><programlisting>
495	soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
496</programlisting></informalexample>
497
498<para>
499(You can also disable Basic or Digest authentication by calling <link
500linkend="soup-session-remove-feature-by-type"><function>soup_session_remove_feature_by_type</function></link>
501on <link linkend="SOUP-TYPE-AUTH-BASIC:CAPS"><literal>SOUP_TYPE_AUTH_BASIC</literal></link>
502or <link linkend="SOUP-TYPE-AUTH-DIGEST:CAPS"><literal>SOUP_TYPE_AUTH_DIGEST</literal></link>.)
503</para>
504
505</refsect2>
506
507<refsect2>
508<title>Multi-threaded usage</title>
509
510<para>
511A <link linkend="SoupSession"><type>SoupSession</type></link> can be
512used from multiple threads. However, if you are using the async APIs,
513then each thread you use the session from must have its own
514thread-default <link linkend="GMainContext"><type>GMainContext</type></link>.
515</para>
516
517<para>
518<link linkend="SoupMessage"><type>SoupMessage</type></link> is
519<emphasis>not</emphasis> thread-safe, so once you send a message on
520the session, you must not interact with it from any thread other than
521the one where it was sent.
522</para>
523
524</refsect2>
525
526<refsect2>
527<title>Sample Programs</title>
528
529<para>
530A few sample programs are available in the
531<application>libsoup</application> sources, in the
532<literal>examples</literal> directory:
533</para>
534
535<itemizedlist>
536    <listitem><para>
537	<emphasis role="bold"><literal>get</literal></emphasis> is a simple command-line
538	HTTP GET utility using the asynchronous API.
539    </para></listitem>
540
541    <listitem><para>
542	<emphasis role="bold"><literal>simple-proxy</literal></emphasis> uses both the
543	client and server APIs to create a simple (and not very
544	RFC-compliant) proxy server. It shows how to use the <link
545	linkend="SoupMessageFlags"><literal>SOUP_MESSAGE_OVERWRITE_CHUNKS</literal></link>
546	flag when reading a message to save memory by processing each
547	chunk of the message as it is read, rather than accumulating
548	them all into a single buffer to process all at the end.
549    </para></listitem>
550</itemizedlist>
551
552<para>
553More complicated examples are available in GNOME git.
554</para>
555
556</refsect2>
557
558</refentry>
559