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, &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, &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