Skip to content
Commits on Source (70)
......@@ -86,29 +86,30 @@ fedora-asan:
reference:
stage: docs
variables:
DESTDIR: _install
needs: []
MESON_ARGS: >-
-Ddocs=enabled
-Ddoc_tests=true
-Dvapi=disabled
script:
- meson _build --prefix=/usr -Dgtk_doc=true
- ninja -C _build libsoup-3.0-doc
- .gitlab-ci/check-docs.py
- mkdir -p _reference/libsoup-3.0
- cp -R _build/docs/reference/html/* _reference/libsoup-3.0/
- cp -R /usr/share/gtk-doc/html/{glib,gio,gobject,libsoup-2.4} _reference
- cp .gitlab-ci/index.html _reference
- gtkdoc-rebase --relative --html-dir=./_reference/ --verbose
- meson ${MESON_ARGS} _build
- meson compile -C _build libsoup-doc --verbose
- meson test -C _build docs --verbose
- mv _build/docs/reference/libsoup-3.0/* _reference/libsoup-3.0
# Add libsoup-2.4 docs.
- cp -R /usr/share/gtk-doc/html/{glib,gio,gobject,libsoup-2.4} _reference/
- cp .gitlab-ci/index.html _reference/
artifacts:
paths:
- _build/docs/reference/libsoup-3.0-*.txt
- _reference
coverage: '/^([\d]+\%) symbol docs coverage\.\s+/'
pages:
stage: deploy
needs: ['reference']
script:
- mv _reference/ public/
- mv _reference public
artifacts:
when: on_success
paths:
- public
only:
......
#!/usr/bin/env python3
import sys
with open('_build/docs/reference/libsoup-3.0-unused.txt') as f:
unused_docs = f.read()
if unused_docs:
print('There is documentation not listed in libsoup-3.0-sections.txt:')
print(unused_docs)
sys.exit(1)
with open('_build/docs/reference/libsoup-3.0-undocumented.txt') as f:
# The file starts with a summary
# undocumented_summary = ''.join(f.readline() for i in range(6)).strip()
print(f.readline()) # e.g. 95% symbol docs coverage.
for i in range(4):
f.readline()
undocumented_list = f.read().strip()
if undocumented_list:
print('There is missing documentation for these symbols:')
print(undocumented_list)
sys.exit(1)
Changes in libsoup from 3.0.7 to 3.1.1:
* Reintroduce some thread-safety to SoupSession (see https://libsoup.org/libsoup-3.0/client-thread-safety.html) [Carlos Garcia Campos]
* Add SoupServerMessage:tls-peer-certificate and SoupServerMessage:tls-peer-certificate-errors [Ignacio Casal Quinteiro]
* Port docs to gi-docgen [Maximiliano Sandoval R]
* Update documentation [Patrick Griffis]
Changes in libsoup from 3.0.6 to 3.0.7:
* Fix leak in SoupAuthNTLM [Milan Crha]
......
Title: Building with libsoup
Slug: build-howto
# Building with libsoup
## Buildsystem Integration
Like other GNOME libraries, libsoup uses
`pkg-config` to provide compiler options. The package
name is `libsoup-3.0`. For example if you use Autotools:
```
PKG_CHECK_MODULES(LIBSOUP, [libsoup-3.0])
AC_SUBST(LIBSOUP_CFLAGS)
AC_SUBST(LIBSOUP_LIBS)
```
If you use Meson:
```
libsoup_dep = dependency('libsoup-3.0')
```
## API Availability and Deprecation Warnings
If you want to restrict your program to a particular libsoup version or range of
versions, you can define [const@VERSION_MIN_REQUIRED] and/or
`SOUP_VERSION_MAX_ALLOWED`. For example with Autotools:
```
LIBSOUP_CFLAGS="$LIBSOUP_CFLAGS -DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_3_0"
LIBSOUP_CFLAGS="$LIBSOUP_CFLAGS -DSOUP_VERSION_MAX_ALLOWED=SOUP_VERSION_3_2"
```
Or with Meson:
```meson
add_project_arguments(
'-DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_2_99',
'-DSOUP_VERSION_MAX_ALLOWED=SOUP_VERSION_3_0',
language: 'c'
)
```
The [const@VERSION_MIN_REQUIRED] declaration states that the code is not
expected to compile on versions of libsoup older than the indicated version, and
so the compiler should print warnings if the code uses functions that were
deprecated as of that release.
The `SOUP_VERSION_MAX_ALLOWED` declaration states that the code *is* expected
to compile on versions of libsoup up to the indicated version, and so, when
compiling the program against a newer version than that, the compiler should
print warnings if the code uses functions that did not yet exist in the
max-allowed release.
You can use [func@CHECK_VERSION] to check the version of libsoup at compile
time, to compile different code for different libsoup versions. (If you are
setting [const@VERSION_MIN_REQUIRED] and `SOUP_VERSION_MAX_ALLOWED` to
different versions, as in the example above, then you almost certainly need to
be doing this.)
## Headers
Code using libsoup should include the header like so:
```c
#include <libsoup/soup.h>
```
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://docbook.org/xml/5.1/rng/docbook.rng" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://docbook.org/xml/5.1/sch/docbook.sch" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<sect1 xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink" version="5.1">
<title>Building with libsoup</title>
<sect3>
<title>Buildsystem Integration</title>
<para>Like other GNOME libraries, <application>libsoup</application> uses
<application>pkg-config</application> to provide compiler options. The package name is
"<literal>libsoup-3.0</literal>". </para>
<para>For example if you use Autotools:<informalexample>
<programlisting>PKG_CHECK_MODULES(LIBSOUP, [libsoup-3.0])
AC_SUBST(LIBSOUP_CFLAGS)
AC_SUBST(LIBSOUP_LIBS)</programlisting>
</informalexample></para>
<para>If you use Meson: <informalexample>
<programlisting>libsoup_dep = dependency('libsoup-3.0')</programlisting>
</informalexample></para>
</sect3>
<sect3>
<title>API Availability and Deprecation Warnings</title>
<para>
If you want to restrict your program to a particular
<application>libsoup</application> version or range of versions, you
can define <link
linkend="SOUP-VERSION-MIN-REQUIRED:CAPS"><literal>SOUP_VERSION_MIN_REQUIRED</literal></link>
and/or <link
linkend="SOUP-VERSION-MAX-ALLOWED:CAPS"><literal>SOUP_VERSION_MAX_ALLOWED</literal></link>.
For example with Autotools:
</para>
<informalexample><programlisting>LIBSOUP_CFLAGS="$LIBSOUP_CFLAGS -DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_3_0"
LIBSOUP_CFLAGS="$LIBSOUP_CFLAGS -DSOUP_VERSION_MAX_ALLOWED=SOUP_VERSION_3_2"</programlisting></informalexample>
<para>Or with Meson:</para>
<informalexample><programlisting>add_project_arguments(
'-DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_2_99',
'-DSOUP_VERSION_MAX_ALLOWED=SOUP_VERSION_3_0',
language: 'c'
)</programlisting></informalexample>
<para>The <literal>SOUP_VERSION_MIN_REQUIRED</literal> declaration states that the code is not
expected to compile on versions of <application>libsoup</application> older than the
indicated version, and so the compiler should print warnings if the code uses
functions that were deprecated as of that release.</para>
<para>The <literal>SOUP_VERSION_MAX_ALLOWED</literal> declaration states that the code
<emphasis>is</emphasis> expected to compile on versions of
<application>libsoup</application> up to the indicated version, and so, when
compiling the program against a newer version than that, the compiler should print warnings
if the code uses functions that did not yet exist in the max-allowed release.</para>
<para>You can use <link linkend="SOUP-CHECK-VERSION:CAPS"><literal>SOUP_CHECK_VERSION</literal></link> to check the version of libsoup at compile
time, to compile different code for different <application>libsoup</application> versions.
(If you are setting <literal>SOUP_VERSION_MIN_REQUIRED</literal> and
<literal>SOUP_VERSION_MAX_ALLOWED</literal> to different versions, as in the example
above, then you almost certainly need to be doing this.)</para>
</sect3>
<sect3>
<title>Headers</title>
<para>Code using <application>libsoup</application> should include the header like so:</para>
<informalexample><programlisting>
#include &lt;libsoup/soup.h&gt;
</programlisting></informalexample>
</sect3>
</sect1>
Title: Advances Usage
Slug: client-advanced
# Advanced Usage
## Customizing Session Options
When you create the session with [ctor@Session.new_with_options], you can
specify various additional options. See the [class@Session] documentation for
more details but these may be interesting: [property@Session:max-conns] and
[property@Session:max-conns-per-host], [property@Session:user-agent],
[property@Session:timeout], [property@Session:accept-language] and
[property@Session:accept-language-auto].
## Adding Session Features
Additional session functionality is provided as [iface@SessionFeature]s, which
can be added to or removed from a session.
One such feature is [class@ContentDecoder] which is added by default. This
advertises to servers that the client supports compression, and automatically
decompresses compressed responses.
Some other available features that you can add include:
<table>
<tr>
<td>[class@Logger]</td>
<td>
A debugging aid, which logs all of libsoup's HTTP traffic
to <code>stdout</code> (or another place you specify).
</td>
</tr>
<tr>
<td>
[class@CookieJar], [class@CookieJarText],
and [class@CookieJarDB].
</td>
<td>
Support for HTTP cookies. [class@CookieJar]
provides non-persistent cookie storage, while
[class@CookieJarText] uses a text file to keep
track of cookies between sessions, and
[class@CookieJarDB] uses a
<tt>SQLite</tt> database.
</td>
</tr>
<tr>
<td>[class@ContentSniffer]</td>
<td>
Uses the HTML5 sniffing rules to attempt to
determine the Content-Type of a response when the
server does not identify the Content-Type, or appears to
have provided an incorrect one.
</td>
</tr>
</table>
Use the [method@Session.add_feature_by_type] function to add features that don't
require any configuration (such as [class@ContentSniffer]), and the
[method@Session.add_feature]function to add features that must be constructed
first (such as [class@Logger]). For example, an application might do something
like the following:
```c
session = soup_session_new ();
soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
if (debug_level) {
SoupLogger *logger = soup_logger_new (debug_level);
soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
g_object_unref (logger);
}
```
You can also remove features by calling [method@Session.remove_feature] or
[method@Session.remove_feature_by_type].
## Using a proxy
By default libsoup tries to respect the default proxy (as best as
[func@Gio.ProxyResolver.get_default] knows), however you can set a custom one or
disable it outright using the [property@Session:proxy-resolver] property. For
example:
```c
{
GProxyResolver *resolver = g_simple_proxy_resolver_new ("https://my-proxy-example.org", NULL);
SoupSession *session = soup_session_new_with_options ("proxy-resolver", resolver, NULL);
g_object_unref (resolver);
}
```
## Using the SoupMessage API
The [class@Message] type contains all the state for a request and response pair
that you send and receive to a server. For many more complex tasks you will have
to create one of these and send it with the [method@Session.send] function. For
example this sends a request with the `HEAD` method:
```c
{
SoupSession *session = soup_session_new ();
SoupMessage *msg = soup_message_new (SOUP_METHOD_HEAD, "https://example.org");
// This allows you to also customize the request headers:
SoupMessageHeaders *request_headers = soup_message_get_request_headers (msg);
soup_message_headers_replace (request_headers, "Foo", "Bar");
GInputStream *in_stream = soup_session_send (session, msg, NULL, NULL);
if (in_stream) {
g_print ("Message was sent and recived a response of %u (%s)\n",
soup_message_get_status (msg), soup_message_get_reason_phrase (msg));
// You can also inspect the response headers via soup_message_get_response_headers();
g_object_unref (in_stream);
}
g_object_unref (msg);
g_object_unref (session);
}
```
## Handling authentication
```c
static gboolean
authenticate_callback (SoupMessage *msg, SoupAuth *auth, gboolean retrying, gpointer user_data)
{
if (retrying) {
// Maybe don't try again if our password failed
return FALSE;
}
soup_auth_authenticate (auth, "username", "password");
// Returning TRUE means we have or *will* handle it.
// soup_auth_authenticate() or soup_auth_cancel() can be called later
// for example after showing a prompt to the user or loading the password
// from a keyring.
return TRUE;
}
int main (int argc, char **argv)
{
SoupSession *session = soup_session_new ();
SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "https://example.org");
g_signal_connect (msg, "authenticate", G_CALLBACK (authenticate_callback), NULL);
GInputStream *in_stream = soup_session_send (session, msg, NULL, NULL);
if (in_stream) {
g_object_unref (in_stream);
}
return 0;
}
```
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://docbook.org/xml/5.1/rng/docbook.rng" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://docbook.org/xml/5.1/sch/docbook.sch" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<sect1 xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink" version="5.1">
<title>Advanced Usage</title>
<sect2>
<title>Customizing Session Options</title>
<para>When you create the session with <link linkend="soup-session-new-with-options"><function>soup_session_new_with_options()</function></link>,
you can specify various additional options. See the <link
linkend="SoupSession"><type>SoupSession</type> documentation</link> for more details but these may be interesting:
<link linkend="SoupSession:max-conns"><literal>SoupSession:max-conns</literal></link> and <link linkend="SoupSession:max-conns-per-host"><literal>SoupSession:max-conns-per-host</literal></link>,
<link linkend="SoupSession:user-agent"><literal>SoupSession:user-agent</literal></link>, <link linkend="SoupSession:timeout"><literal>SoupSession:timeout</literal></link>,
<link linkend="SoupSession:accept-language"><literal>SoupSession:accept-language</literal></link> and <link linkend="SoupSession:accept-language-auto"><literal>SoupSession:accept-language-auto</literal></link></para>
</sect2>
<sect2>
<title>Adding Session Features</title>
<para>Additional session functionality is provided as <link
linkend="SoupSessionFeature"><type>SoupSessionFeature</type></link>s,
which can be added to or removed from a session.</para>
<para>One such feature is <link linkend="SoupContentDecoder"><type>SoupContentDecoder</type></link>
which is added by default. This advertises to servers that the
client supports compression, and automatically decompresses compressed
responses.
</para>
<para>
Some other available features that you can add include:
</para>
<variablelist>
<varlistentry>
<term><link linkend="SoupLogger"><type>SoupLogger</type></link></term>
<listitem><para>
A debugging aid, which logs all of libsoup's HTTP traffic
to <literal>stdout</literal> (or another place you specify).
</para></listitem>
</varlistentry>
<varlistentry>
<term>
<link linkend="SoupCookieJar"><type>SoupCookieJar</type></link>,
<link linkend="SoupCookieJarText"><type>SoupCookieJarText</type></link>,
and <link linkend="SoupCookieJarDB"><type>SoupCookieJarDB</type></link>
</term>
<listitem><para>
Support for HTTP cookies. <type>SoupCookieJar</type>
provides non-persistent cookie storage, while
<type>SoupCookieJarText</type> uses a text file to keep
track of cookies between sessions, and
<type>SoupCookieJarDB</type> uses a
<application>SQLite</application> database.
</para></listitem>
</varlistentry>
<varlistentry>
<term><link linkend="SoupContentSniffer"><type>SoupContentSniffer</type></link></term>
<listitem><para>
Uses the HTML5 sniffing rules to attempt to
determine the Content-Type of a response when the
server does not identify the Content-Type, or appears to
have provided an incorrect one.
</para></listitem>
</varlistentry>
</variablelist>
<para>
Use the <link
linkend="soup-session-add-feature-by-type"><function>soup_session_add_feature_by_type()</function></link> function to
add features that don't require any configuration (such as <link
linkend="SoupContentSniffer"><type>SoupContentSniffer</type></link>),
and the <link
linkend="soup-session-add-feature"><function>soup_session_add_feature()</function></link>function to add features that must be
constructed first (such as <link
linkend="SoupLogger"><type>SoupLogger</type></link>). For example, an
application might do something like the following:
</para>
<informalexample><programlisting><![CDATA[session = soup_session_new ();
soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
if (debug_level) {
SoupLogger *logger = soup_logger_new (debug_level);
soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
g_object_unref (logger);
}]]></programlisting></informalexample>
<para>You can also remove features by calling <link
linkend="soup-session-remove-feature"><function>soup_session_remove_feature()</function></link> or
<link
linkend="soup-session-remove-feature-by-type"><function>soup_session_remove_feature_by_type()</function></link></para>.
<para>See the <link linkend="additional-features">Additional Features</link> section for other features.
</para>
</sect2>
<sect2>
<title>Using a proxy</title>
<para>By default libsoup tries to respect the default proxy (as best as <link
linkend="g-proxy-resolver-get-default"><function>g_proxy_resolver_get_default()</function></link> knows), however you can set
a custom one or disable it outright using the <link linkend="SoupSession:proxy-resolver"><literal>SoupSession:proxy-resolver</literal></link>
property. For example:</para>
<informalexample><programlisting><![CDATA[
{
GProxyResolver *resolver = g_simple_proxy_resolver_new ("https://my-proxy-example.org", NULL);
SoupSession *session = soup_session_new_with_options ("proxy-resolver", resolver, NULL);
g_object_unref (resolver);
}]]>
</programlisting></informalexample>
</sect2>
<sect2>
<title>Using the SoupMessage API</title>
<para>The <type>SoupMessage</type> type contains all the state for a request and response pair that you send and recieve
to a server. For many more complex tasks you will have to create one of these and send it with the <function>soup_session_send()</function>
function. For example this sends a request with the <literal>HEAD</literal> method:
</para>
<informalexample><programlisting><![CDATA[
{
SoupSession *session = soup_session_new ();
SoupMessage *msg = soup_message_new (SOUP_METHOD_HEAD, "https://example.org");
// This allows you to also customize the request headers:
SoupMessageHeaders *request_headers = soup_message_get_request_headers (msg);
soup_message_headers_replace (request_headers, "Foo", "Bar");
GInputStream *in_stream = soup_session_send (session, msg, NULL, NULL);
if (in_stream) {
g_print ("Message was sent and recived a response of %u (%s)\n",
soup_message_get_status (msg), soup_message_get_reason_phrase (msg));
// You can also inspect the response headers via soup_message_get_response_headers();
g_object_unref (in_stream);
}
g_object_unref (msg);
g_object_unref (session);
}]]>
</programlisting></informalexample>
</sect2>
<sect2>
<title>Handling authentication</title>
<informalexample><programlisting><![CDATA[
static gboolean
authenticate_callback (SoupMessage *msg, SoupAuth *auth, gboolean retrying, gpointer user_data)
{
if (retrying) {
// Maybe don't try again if our password failed
return FALSE;
}
soup_auth_authenticate (auth, "username", "password");
// Returning TRUE means we have or *will* handle it.
// soup_auth_authenticate() or soup_auth_cancel() can be called later
// for example after showing a prompt to the user or loading the password
// from a keyring.
return TRUE;
}
int main (int argc, char **argv)
{
SoupSession *session = soup_session_new ();
SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "https://example.org");
g_signal_connect (msg, "authenticate", G_CALLBACK (authenticate_callback), NULL);
GInputStream *in_stream = soup_session_send (session, msg, NULL, NULL);
if (in_stream) {
g_object_unref (in_stream);
}
return 0;
}]]>
</programlisting></informalexample>
</sect2>
</sect1>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://docbook.org/xml/5.1/rng/docbook.rng" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://docbook.org/xml/5.1/sch/docbook.sch" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<sect1 xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink" version="5.1">
<title>Creating a Basic Client</title>
<para>libsoup provides a feature rich and complete HTTP client feature-set however in this guide
we will just be touching the basics. See … for a more in-depth example.</para>
<sect2>
<title>Creating a SoupSession</title>
<para>The core of libsoup is <type>SoupSession</type>; It contains all of the state of a
client including managing connections, queuing messages, handling authentication and
redirects, and much more. For now lets assume the default set of options and
features it provides are acceptable for most usage in which case you simply need to
create one with <link linkend="soup-session-new"><function>soup_session_new()</function></link>.</para>
</sect2>
<sect2>
<title>Downloading Into Memory</title>
<para>A common use case is that you simply want to request an HTTP resource and store it
for later use. There are a few methods of doing this but libsoup provides a high
level API to accomplish this:</para>
<informalexample><programlisting><![CDATA[#include <libsoup/soup.h>
Title: Creating a Basic Client
Slug: client-basic
# Creating a Basic Client
libsoup provides a feature rich and complete HTTP client feature-set however in this guide we will just be touching the basics.
## Creating a SoupSession
The core of libsoup is [class@Session]; It contains all of the state of a client
including managing connections, queuing messages, handling authentication and
redirects, and much more. For now lets assume the default set of options and
features it provides are acceptable for most usage in which case you simply need
to create one with [ctor@Session.new].
## Downloading Into Memory
A common use case is that you simply want to request an HTTP resource and store
it for later use. There are a few methods of doing this but libsoup provides a high
level API to accomplish this:
```c
#include <libsoup/soup.h>
int main (int argc, char **argv)
{
......@@ -54,16 +54,17 @@ int main (int argc, char **argv)
g_object_unref (msg);
g_object_unref (session);
return 0;
}]]>
</programlisting></informalexample>
</sect2>
<sect2>
<title>Efficiently Streaming Data</title>
<para>While sometimes you want to store an entire download in memory it is often more
efficient to stream the data in chunks. In this example we will write the output to
a file.</para>
<para>
<informalexample><programlisting><![CDATA[#include <libsoup/soup.h>
}
```
## Efficiently Streaming Data
While sometimes you want to store an entire download in memory it is often more
efficient to stream the data in chunks. In this example we will write the output
to a file.
```c
#include <libsoup/soup.h>
int main (int argc, char **argv)
{
......@@ -128,18 +129,19 @@ int main (int argc, char **argv)
g_object_unref (msg);
g_object_unref (session);
return error ? 1 : 0;
}]]>
</programlisting></informalexample>
</para>
</sect2>
<sect2>
<title>Using Asynchronously</title>
<para>If you are using libsoup in an application with a <link linkend="GMainLoop"><type>GMainLoop</type></link> such as a GTK application
you do not want to block the mainloop by doing IO. To accomplish this libsoup provides an
asynchronous version of each of the APIs: <link linkend="soup-session-send-and-read-async"><function>soup_session_send_and_read_async()</function></link>
and <link linkend="soup-session-send-async"><function>soup_session_send_async()</function></link>. These behave the same as all async GLib
APIs, for example:</para>
<informalexample><programlisting><![CDATA[#include <libsoup/soup.h>
}
```
## Using Asynchronously
If you are using libsoup in an application with a [struct@GLib.MainLoop] such as
a GTK application you do not want to block the mainloop by doing IO. To
accomplish this libsoup provides an asynchronous version of each of the APIs:
[method@Session.send_and_read_async] and [method@Session.send_async]. These
behave the same as all async GLib APIs, for example:
```c
#include <libsoup/soup.h>
static void on_load_callback (GObject *source, GAsyncResult *result, gpointer user_data)
{
......@@ -177,7 +179,5 @@ int main (int argc, char **argv)
g_object_unref (msg);
g_object_unref (session);
return 0;
}]]>
</programlisting></informalexample>
</sect2>
</sect1>
}
```
<?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-client-howto">
<refmeta>
<refentrytitle>libsoup Client Basics</refentrytitle>
<manvolnum>3</manvolnum>
<refmiscinfo>LIBSOUP Library</refmiscinfo>
</refmeta>
<refnamediv>
<refname>libsoup Client Basics</refname><refpurpose>Client-side tutorial</refpurpose>
</refnamediv>
<refsect2>
<para>
This section explains how to use <application>libsoup</application> as
an HTTP client using several new APIs introduced in version 2.42. If
you want to be compatible with older versions of
<application>libsoup</application>, consult the documentation for that
version.
</para>
</refsect2>
<refsect2>
<title>Creating a <type>SoupSession</type></title>
<para>
The first step in using the client API is to create a <link
linkend="SoupSession"><type>SoupSession</type></link>. The session object
encapsulates all of the state that <application>libsoup</application>
is keeping on behalf of your program; cached HTTP connections,
authentication information, etc.
</para>
<para>
When you create the session with <link
linkend="soup-session-new-with-options"><function>soup_session_new_with_options</function></link>,
you can specify various additional options:
</para>
<variablelist>
<varlistentry>
<term><link linkend="SoupSession:max-conns"><literal>"max-conns"</literal></link></term>
<listitem><para>
Allows you to set the maximum total number of connections
the session will have open at one time. (Once it reaches
this limit, it will either close idle connections, or
wait for existing connections to free up before starting
new requests.) The default value is 10.
</para></listitem>
</varlistentry>
<varlistentry>
<term><link linkend="SoupSession:max-conns-per-host"><literal>"max-conns-per-host"</literal></link></term>
<listitem><para>
Allows you to set the maximum total number of connections
the session will have open <emphasis>to a single
host</emphasis> at one time. The default value is 2.
</para></listitem>
</varlistentry>
<varlistentry>
<term><link linkend="SoupSession:user-agent"><literal>"user-agent"</literal></link></term>
<listitem><para>
Allows you to set a User-Agent string that will be sent
on all outgoing requests.
</para></listitem>
</varlistentry>
<varlistentry>
<term><link linkend="SoupSession:accept-language"><literal>"accept-language"</literal></link>
and <link linkend="SoupSession:accept-language-auto"><literal>"accept-language-auto"</literal></link></term>
<listitem><para>
Allow you to set an Accept-Language header on all outgoing
requests. <literal>"accept-language"</literal>
takes a list of language tags to use, while
<literal>"accept-language-auto"</literal>
automatically generates the list from the user's locale
settings.
</para></listitem>
</varlistentry>
<varlistentry>
<term><link linkend="SoupSession:proxy-resolver"><literal>"proxy-resolver"</literal></link></term>
<listitem>
<para>
<link linkend="SoupSession:proxy-resolver"><literal>"proxy-resolver"</literal></link>
specifies a <link
linkend="GProxyResolver"><type>GProxyResolver</type></link>
to use to determine the HTTP proxies to use. By default,
this is set to the resolver returned by <link
linkend="g-proxy-resolver-get-default"><function>g_proxy_resolver_get_default</function></link>,
so you do not need to set it yourself.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><link linkend="SoupSession:add-feature"><literal>"add-feature"</literal></link> and <link linkend="SOUP-SESSION-ADD-FEATURE-BY-TYPE:CAPS"><literal>"add-feature-by-type"</literal></link></term>
<listitem><para>
These allow you to specify <link
linkend="SoupSessionFeature"><type>SoupSessionFeature</type></link>s
(discussed <link linkend="session-features">below</link>)
to add at construct-time.
</para></listitem>
</varlistentry>
</variablelist>
<para>
Other properties are also available; see the <link
linkend="SoupSession"><type>SoupSession</type></link> documentation for
more details.
</para>
<para>
If you don't need to specify any options, you can just use <link
linkend="soup-session-new"><function>soup_session_new</function></link>,
which takes no arguments.
</para>
</refsect2>
<refsect2 id="session-features">
<title>Session features</title>
<para>
Additional session functionality is provided as <link
linkend="SoupSessionFeature"><type>SoupSessionFeature</type></link>s,
which can be added to a session, via the <link
linkend="SoupSession:add-feature"><literal>"add-feature"</literal></link>
and <link
linkend="SoupSession:add-feature-by-type"><literal>"add-feature-by-type"</literal></link>
options at session-construction-time, or afterward via the <link
linkend="soup-session-add-feature"><function>soup_session_add_feature</function></link>
and <link
linkend="soup-session-add-feature-by-type"><function>soup_session_add_feature_by_type</function></link>
functions.
</para>
<para>
A <link
linkend="SoupContentDecoder"><type>SoupContentDecoder</type></link> is
added for you automatically. This advertises to servers that the
client supports compression, and automatically decompresses compressed
responses.
</para>
<para>
Some other available features that you can add include:
</para>
<variablelist>
<varlistentry>
<term><link linkend="SoupLogger"><type>SoupLogger</type></link></term>
<listitem><para>
A debugging aid, which logs all of libsoup's HTTP traffic
to <literal>stdout</literal> (or another place you specify).
</para></listitem>
</varlistentry>
<varlistentry>
<term>
<link linkend="SoupCookieJar"><type>SoupCookieJar</type></link>,
<link linkend="SoupCookieJarText"><type>SoupCookieJarText</type></link>,
and <link linkend="SoupCookieJarDB"><type>SoupCookieJarDB</type></link>
</term>
<listitem><para>
Support for HTTP cookies. <type>SoupCookieJar</type>
provides non-persistent cookie storage, while
<type>SoupCookieJarText</type> uses a text file to keep
track of cookies between sessions, and
<type>SoupCookieJarDB</type> uses a
<application>SQLite</application> database.
</para></listitem>
</varlistentry>
<varlistentry>
<term><link linkend="SoupContentSniffer"><type>SoupContentSniffer</type></link></term>
<listitem><para>
Uses the HTML5 sniffing rules to attempt to
determine the Content-Type of a response when the
server does not identify the Content-Type, or appears to
have provided an incorrect one.
</para></listitem>
</varlistentry>
</variablelist>
<para>
Use the "add_feature_by_type" property/function to add features that
don't require any configuration (such as <link
linkend="SoupContentSniffer"><type>SoupContentSniffer</type></link>),
and the "add_feature" property/function to add features that must be
constructed first (such as <link
linkend="SoupLogger"><type>SoupLogger</type></link>). For example, an
application might do something like the following:
</para>
<informalexample><programlisting>
session = soup_session_new_with_options (
"add-feature-by-type", SOUP_TYPE_CONTENT_SNIFFER,
NULL);
if (debug_level) {
SoupLogger *logger;
logger = soup_logger_new (debug_level, -1);
soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
g_object_unref (logger);
}
</programlisting></informalexample>
</refsect2>
<refsect2>
<title>Creating and Sending SoupMessages</title>
<para>
Once you have a session, you send HTTP requests using <link
linkend="SoupMessage"><type>SoupMessage</type></link>. In the simplest
case, you only need to create the message and it's ready to send:
</para>
<informalexample><programlisting>
SoupMessage *msg;
msg = soup_message_new ("GET", "http://example.com/");
</programlisting></informalexample>
<para>
In more complicated cases, you can use various <link
linkend="SoupMessage">SoupMessage</link>, <link
linkend="SoupMessageHeaders">SoupMessageHeaders</link>, and <link
linkend="SoupMessageBody">SoupMessageBody</link> methods to set the
request headers and body of the message:
</para>
<informalexample><programlisting>
SoupMessage *msg;
msg = soup_message_new ("POST", "http://example.com/form.cgi");
soup_message_set_request (msg, "application/x-www-form-urlencoded",
SOUP_MEMORY_COPY, formdata, strlen (formdata));
soup_message_headers_append (msg->request_headers, "Referer", referring_url);
</programlisting></informalexample>
<para>
(Although this is a bad example, because
<application>libsoup</application> actually has convenience methods
for dealing with <link linkend="libsoup-3.0-HTML-Form-Support">HTML
forms</link>.)
</para>
<para>
You can also use <link
linkend="soup-message-set-flags"><function>soup_message_set_flags</function></link>
to change some default behaviors. For example, by default,
<type>SoupSession</type> automatically handles responses from the
server that redirect to another URL. If you would like to handle these
yourself, you can set the <link linkend="SOUP-MESSAGE-NO-REDIRECT:CAPS"><literal>SOUP_MESSAGE_NO_REDIRECT</literal></link>
flag.
</para>
<refsect3>
<title>Sending a Message Synchronously</title>
<para>
To send a message and wait for the response, use <link
linkend="soup-session-send"><function>soup_session_send</function></link>:
</para>
<informalexample><programlisting>
GInputStream *stream;
GError *error = NULL;
stream = soup_session_send (session, msg, cancellable, &amp;error);
</programlisting></informalexample>
<para>
At the point when <function>soup_session_send</function> returns, the
request will have been sent, and the response headers read back in;
you can examine the message's <structfield>status_code</structfield>,
<structfield>reason_phrase</structfield>, and
<structfield>response_headers</structfield> fields to see the response
metadata. To get the response body, read from the returned <link
linkend="GInputStream"><type>GInputStream</type></link>, and close it
when you are done.
</para>
<para>
Note that <function>soup_session_send</function> only returns an error
if a transport-level problem occurs (eg, it could not connect to the
host, or the request was cancelled). Use the message's
<structfield>status_code</structfield> field to determine whether the
request was successful or not at the HTTP level (ie, "<literal>200
OK</literal>" vs "<literal>401 Bad Request</literal>").
</para>
<para>
If you would prefer to have <application>libsoup</application> gather
the response body for you and then return it all at once, you can use
the older
<link linkend="soup-session-send-message"><function>soup_session_send_message</function></link>
API:
</para>
<informalexample><programlisting>
guint status;
status = soup_session_send_message (session, msg);
</programlisting></informalexample>
<para>
In this case, the response body will be available in the message's
<structfield>response_body</structfield> field, and transport-level
errors will be indicated in the <structfield>status_code</structfield>
field via special pseudo-HTTP-status codes like <link
linkend="SOUP-STATUS-CANT-CONNECT:CAPS"><literal>SOUP_STATUS_CANT_CONNECT</literal></link>.
</para>
</refsect3>
<refsect3>
<title>Sending a Message Asynchronously</title>
<para>
To send a message asynchronously, use <link
linkend="soup-session-send-async"><function>soup_session_send_async</function></link>:
</para>
<informalexample><programlisting>
{
...
soup_session_send_async (session, msg, cancellable, my_callback, my_callback_data);
...
}
static void
my_callback (GObject *object, GAsyncResult *result, gpointer user_data)
{
GInputStream *stream;
GError *error = NULL;
stream = soup_session_send_finish (SOUP_SESSION (object), result, &amp;error);
...
}
</programlisting></informalexample>
<para>
The message will be added to the session's queue, and eventually (when
control is returned back to the main loop), it will be sent and the
response will be read. When the message has been sent, and its
headers received, the callback will be invoked, in the standard
<link linkend="GAsyncReadyCallback"><type>GAsyncReadyCallback</type></link>
style.
</para>
<para>
As with synchronous sending, there is also an alternate API, <link
linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>,
in which your callback is not invoked until the response has been
completely read:
</para>
<informalexample><programlisting>
{
...
soup_session_queue_message (session, msg, my_callback, my_callback_data);
...
}
static void
my_callback (SoupSession *session, SoupMessage *msg, gpointer user_data)
{
/* msg->response_body contains the response */
}
</programlisting></informalexample>
<para>
<link
linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>
is slightly unusual in that it steals a reference to the message
object, and unrefs it after the last callback is invoked on it. So
when using this API, you should not unref the message yourself.
</para>
</refsect3>
</refsect2>
<refsect2>
<title>Processing the Response</title>
<para>
Once you have received the initial response from the server,
synchronously or asynchronously, streaming or not, you can look at the
response fields in the <literal>SoupMessage</literal> to decide what
to do next. The <structfield>status_code</structfield> and
<structfield>reason_phrase</structfield> fields contain the numeric
status and textual status response from the server.
<structfield>response_headers</structfield> contains the response
headers, which you can investigate using <link
linkend="soup-message-headers-get-list"><function>soup_message_headers_get_list</function></link>
and <link
linkend="soup-message-headers-foreach"><function>soup_message_headers_foreach</function></link>.
</para>
<para>
<link
linkend="SoupMessageHeaders"><type>SoupMessageHeaders</type></link>
automatically parses several important headers in
<structfield>response_headers</structfield> for you and provides
specialized accessors for them. Eg, <link
linkend="soup-message-headers-get-content-type"><function>soup_message_headers_get_content_type</function></link>.
There are several generic methods such as <link
linkend="soup-header-parse-param-list"><function>soup_header_parse_param_list</function></link>
(for parsing an attribute-list-type header) and <link
linkend="soup-header-contains"><function>soup_header_contains</function></link>
(for quickly testing if a list-type header contains a particular
token). These handle the various syntactical oddities of parsing HTTP
headers much better than functions like
<function>g_strsplit</function> or <function>strstr</function>.
</para>
</refsect2>
<refsect2>
<title>Handling Authentication</title>
<para>
<type>SoupSession</type> handles most of the details of HTTP
authentication for you. If it receives a 401 ("Unauthorized") or 407
("Proxy Authentication Required") response, the session will emit the
<link linkend="SoupSession-authenticate">authenticate</link> signal,
providing you with a <link
linkend="SoupAuth"><type>SoupAuth</type></link> object indicating the
authentication type ("Basic", "Digest", or "NTLM") and the realm name
provided by the server. If you have a username and password available
(or can generate one), call <link
linkend="soup-auth-authenticate"><function>soup_auth_authenticate</function></link>
to give the information to libsoup. The session will automatically
requeue the message and try it again with that authentication
information. (If you don't call
<function>soup_auth_authenticate</function>, the session will just
return the message to the application with its 401 or 407 status.)
</para>
<para>
If the server doesn't accept the username and password provided, the
session will emit <link
linkend="SoupSession-authenticate">authenticate</link> again, with the
<literal>retrying</literal> parameter set to <literal>TRUE</literal>. This lets the
application know that the information it provided earlier was
incorrect, and gives it a chance to try again. If this
username/password pair also doesn't work, the session will contine to
emit <literal>authenticate</literal> again and again until the
provided username/password successfully authenticates, or until the
signal handler fails to call <link
linkend="soup-auth-authenticate"><function>soup_auth_authenticate</function></link>,
at which point <application>libsoup</application> will allow the
message to fail (with status 401 or 407).
</para>
<para>
If you need to handle authentication asynchronously (eg, to pop up a
password dialog without recursively entering the main loop), you can
do that as well. Just call <link
linkend="soup-session-pause-message"><function>soup_session_pause_message</function></link>
on the message before returning from the signal handler, and
<function>g_object_ref</function> the <type>SoupAuth</type>. Then,
later on, after calling <function>soup_auth_authenticate</function>
(or deciding not to), call <link
linkend="soup-session-unpause-message"><function>soup_session_unpause_message</function></link>
to resume the paused message.
</para>
<para>
By default, NTLM authentication is not enabled. To add NTLM support to
a session, call:
</para>
<informalexample><programlisting>
soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
</programlisting></informalexample>
<para>
(You can also disable Basic or Digest authentication by calling <link
linkend="soup-session-remove-feature-by-type"><function>soup_session_remove_feature_by_type</function></link>
on <link linkend="SOUP-TYPE-AUTH-BASIC:CAPS"><literal>SOUP_TYPE_AUTH_BASIC</literal></link>
or <link linkend="SOUP-TYPE-AUTH-DIGEST:CAPS"><literal>SOUP_TYPE_AUTH_DIGEST</literal></link>.)
</para>
</refsect2>
<refsect2>
<title>Multi-threaded usage</title>
<para>
A <link linkend="SoupSession"><type>SoupSession</type></link> can be
used from multiple threads. However, if you are using the async APIs,
then each thread you use the session from must have its own
thread-default <link linkend="GMainContext"><type>GMainContext</type></link>.
</para>
<para>
<link linkend="SoupMessage"><type>SoupMessage</type></link> is
<emphasis>not</emphasis> thread-safe, so once you send a message on
the session, you must not interact with it from any thread other than
the one where it was sent.
</para>
</refsect2>
<refsect2>
<title>Sample Programs</title>
<para>
A few sample programs are available in the
<application>libsoup</application> sources, in the
<literal>examples</literal> directory:
</para>
<itemizedlist>
<listitem><para>
<emphasis role="bold"><literal>get</literal></emphasis> is a simple command-line
HTTP GET utility using the asynchronous API.
</para></listitem>
<listitem><para>
<emphasis role="bold"><literal>simple-proxy</literal></emphasis> uses both the
client and server APIs to create a simple (and not very
RFC-compliant) proxy server.
</para></listitem>
</itemizedlist>
<para>
More complicated examples are available in GNOME git.
</para>
</refsect2>
</refentry>
Title: Client thread safety
Slug: client-thread-safety
# Client thread safety
libsoup is not fully thread safe, but since version 3.2 it's possible
to send messages from any thread. The recommended and most efficient
way to use libsoup is using only the async API from a single thread,
even when it feels natural to use the sync API from a worker
thread. While there's not much difference in HTTP/1, in the case of
HTTP/2, two messages for the same host sent from different threads
will not use the same connection, so the advantage of HTTP/2
multiplexing is lost.
There are a few important things to consider when using multiple
threads:
- Only the API to send messages can be called concurrently from
multiple threads. So, in case of using multiple threads, you must
configure the session (setting network properties, features, etc.)
from the thread it was created and before any request is made.
- All signals associated to a message
([signal@Session::request-queued],
[signal@Session::request-unqueued], and all Message signals) are
emitted from the thread that started the request, and all the IO will
happen there too.
- The session can be created in any thread, but all session APIs
except the methods to send messages must be called from the thread
where the session was created.
- To use the async API from a thread different than the one where the
session was created, the thread must have a thread default main
context where the async callbacks are dispatched.
- The sync API doesn't need any main context at all.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://docbook.org/xml/5.1/rng/docbook.rng" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://docbook.org/xml/5.1/sch/docbook.sch" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<sect1 xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink" version="5.1">
<title>Everything TLS Related</title>
<para>libsoup comes with TLS support provided by glib-networking. This has multiple backends
including gnutls (default on all platforms), SChannel on Windows, or OpenSSL.</para>
<sect2>
<title>Accepting Invalid or Pinned Certificates</title>
<para>This makes use of the <literal>SoupMessage::accept-certificate</literal> signal.</para>
<informalexample><programlisting><![CDATA[
Title: Everything TLS Related
Slug: client-tls
# Everything TLS Related
libsoup comes with TLS support provided by glib-networking. This has multiple backends
including gnutls (default on all platforms), SChannel on Windows, or OpenSSL.
## Accepting Invalid or Pinned Certificates
This makes use of the [signal@Message::accept-certificate] signal.
```c
static gboolean
accept_certificate_callback (SoupMessage *msg, GTlsCertificate *certificate,
GTlsCertificateFlags tls_errors, gpointer user_data)
......@@ -32,12 +33,12 @@ int main (int argc, char **argv)
}
return 0;
}]]>
</programlisting></informalexample>
</sect2>
<sect2>
<title>Setting a Custom CA</title>
<informalexample><programlisting><![CDATA[
}
```
## Setting a Custom CA
```c
{
GError *error = NULL;
// NOTE: This is blocking IO
......@@ -51,12 +52,12 @@ int main (int argc, char **argv)
SoupSession *session = soup_session_new_with_options ("tls-database", tls_db, NULL);
g_object_unref (tls_db);
}]]>
</programlisting></informalexample>
</sect2>
<sect2>
<title>Using Client Certificates</title>
<informalexample><programlisting><![CDATA[
}
```
## Using Client Certificates
```c
static gboolean
on_request_certificate (SoupMessage *msg, GTlsClientConnection *conn, gpointer user_data)
{
......@@ -96,7 +97,5 @@ int main (int argc, char **argv)
g_object_unref (session);
g_object_unref (client_cert);
return 0;
}]]>
</programlisting></informalexample>
</sect2>
</sect1>
\ No newline at end of file
}
```
<?xml version="1.0"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
<book xmlns="http://docbook.org/ns/docbook" id="index"
xmlns:xi="http://www.w3.org/2003/XInclude">
<bookinfo>
<title>libsoup Reference Manual</title>
<releaseinfo>
This documentation is for libsoup 3.0.
You can find older versions online at <ulink role="online-location" url="https://libsoup.org/libsoup-2.4/index.html">https://libsoup.org/libsoup-2.4/</ulink>.
</releaseinfo>
</bookinfo>
<part>
<title>Overview</title>
<chapter>
<title>Tutorial</title>
<xi:include href="build-howto.xml"/>
<xi:include href="client-basic.xml"/>
<xi:include href="client-advanced.xml"/>
<xi:include href="client-tls.xml"/>
<xi:include href="server-howto.xml"/>
</chapter>
<xi:include href="migrating-from-libsoup-2.xml"/>
</part>
<part>
<title>API Reference</title>
<chapter>
<title>Core HTTP API</title>
<xi:include href="xml/soup-cookie.xml"/>
<xi:include href="xml/soup-message.xml"/>
<xi:include href="xml/soup-message-headers.xml"/>
<xi:include href="xml/soup-headers.xml"/>
<xi:include href="xml/soup-method.xml"/>
<xi:include href="xml/soup-multipart.xml"/>
<xi:include href="xml/soup-multipart-input-stream.xml"/>
<xi:include href="xml/soup-session.xml"/>
<xi:include href="xml/soup-status.xml"/>
</chapter>
<chapter>
<title>HTTP Server</title>
<xi:include href="xml/soup-server.xml"/>
<xi:include href="xml/soup-server-message.xml"/>
<xi:include href="xml/soup-message-body.xml"/>
</chapter>
<chapter id="additional-features">
<title>Additional Features</title>
<xi:include href="xml/soup-session-feature.xml"/>
<xi:include href="xml/soup-content-decoder.xml"/>
<xi:include href="xml/soup-content-sniffer.xml"/>
<xi:include href="xml/soup-logger.xml"/>
<section>
<title>Authentication</title>
<xi:include href="xml/soup-auth-manager.xml"/>
<xi:include href="xml/soup-auth.xml"/>
<xi:include href="xml/soup-auth-domain.xml"/>
<xi:include href="xml/soup-auth-domain-basic.xml"/>
<xi:include href="xml/soup-auth-domain-digest.xml"/>
</section>
<section>
<title>Caching</title>
<xi:include href="xml/soup-cache.xml"/>
</section>
<section>
<title>Cookie Storage Support</title>
<xi:include href="xml/soup-cookie-jar.xml"/>
<xi:include href="xml/soup-cookie-jar-text.xml"/>
<xi:include href="xml/soup-cookie-jar-db.xml"/>
</section>
<section>
<title>HSTS Support</title>
<xi:include href="xml/soup-hsts-enforcer.xml"/>
<xi:include href="xml/soup-hsts-enforcer-db.xml"/>
<xi:include href="xml/soup-hsts-policy.xml"/>
</section>
<section>
<title>Metrics</title>
<xi:include href="xml/soup-message-metrics.xml"/>
</section>
</chapter>
<chapter>
<title>Web Services APIs</title>
<xi:include href="xml/soup-form.xml"/>
<section>
<title>WebSocket Support</title>
<xi:include href="xml/soup-websocket.xml"/>
<xi:include href="xml/soup-websocket-extension.xml"/>
</section>
</chapter>
<chapter>
<title>Utility API</title>
<xi:include href="xml/soup-date-utils.xml"/>
<xi:include href="xml/soup-tld.xml"/>
<xi:include href="xml/soup-uri-utils.xml"/>
<xi:include href="xml/soup-version.xml"/>
</chapter>
</part>
<index id="api-index-full">
<title>Index of all symbols</title>
<xi:include href="xml/api-index-full.xml"></xi:include>
</index>
<index id="api-index-deprecated" role="deprecated">
<title>Index of deprecated symbols</title>
<xi:include href="xml/api-index-deprecated.xml"></xi:include>
</index>
<xi:include href="xml/annotation-glossary.xml">
<xi:fallback />
</xi:include>
</book>
This diff is collapsed.
[library]
version = "@VERSION@"
browse_url = "https://gitlab.gnome.org/GNOME/libsoup"
repository_url = "https://gitlab.gnome.org/GNOME/libsoup.git"
docs_url = "https://gnome.pages.gitlab.gnome.org/libsoup/libsoup-3.0/"
website_url = "https://libsoup.org/"
authors = "Dan Winship, Claudio Saavedra, Patrick Griffis, and Carlos Garcia Campos"
license = "LGPL-2.0-or-later"
description = "HTTP client/server library for GNOME"
dependencies = [ "GObject-2.0", "GLib-1.0", "Gio-2.0" ]
devhelp = true
search_index = true
[dependencies."GObject-2.0"]
name = "GObject"
description = "The base type system library"
docs_url = "https://docs.gtk.org/gobject/"
[dependencies."GLib-2.0"]
name = "GLib"
description = "The base type system library"
docs_url = "https://docs.gtk.org/glib/"
[dependencies."Gio-2.0"]
name = "GIO"
description = "GObject Interfaces and Objects, Networking, IPC, and I/O"
docs_url = "https://docs.gtk.org/gio/"
[theme]
name = "basic"
show_index_summary = true
show_class_hierarchy = true
[source-location]
base_url = "https://gitlab.gnome.org/GNOME/libsoup/-/blob/master/"
[extra]
# The same order will be used when generating the index
content_files = [
"build-howto.md",
'client-basic.md',
'client-advanced.md',
'client-thread-safety.md',
'client-tls.md',
'server-howto.md',
"migrating-from-libsoup-2.md",
]
content_images = [
]
urlmap_file = "urlmap.js"
ignore_headers = [
'gconstructor.h',
'soup.h',
'soup-enum-types.h',
'soup-message-private.h',
'soup-session-private.h',
'soup-auth-digest-private.h',
'soup-brotli-decompressor.h',
'soup-connection.h',
'soup-connection-auth.h',
'soup-message-queue-item.h',
'soup-path-map.h',
'soup-http-input-stream.h',
'soup-converter-wrapper.h',
'soup-body-input-stream.h',
'soup-body-output-stream.h',
'soup-client-input-stream.h',
'soup-content-processor.h',
'soup-content-sniffer-stream.h',
'soup-io-stream.h',
'soup-cache-input-stream.h',
'soup-filter-input-stream.h',
'soup-cookie-jar-sqlite.h',
'soup-cache-private.h',
'soup-cache-client-input-stream.h',
'soup-logger-input-stream.h',
'soup-logger-private.h',
'soup-socket.h',
'soup-socket-properties.h',
'soup-websocket-extension-manager-private.h',
'soup-misc.h',
'soup-date-utils-private.h',
'soup-resources.h',
'soup-private-enum-types.h',
'soup-server-message-private.h',
'soup-message-io-data.h',
'soup-message-io-source.h',
'soup-uri-utils-private.h',
'soup-session-feature-private.h',
'soup-message-metrics-private.h',
'soup-client-message-io.h',
'soup-message-io-completion.h',
'soup-client-message-io-http1.h',
'soup-client-message-io-http2.h',
'soup-body-input-stream-http2.h',
'soup-tls-interaction.h',
'soup-header-names.h',
'soup-message-headers-private.h',
expand_content_md_files = [
'build-howto.md',
'client-basic.md',
'client-advanced.md',
'client-thread-safety.md',
'client-tls.md',
'server-howto.md',
'migrating-from-libsoup-2.md',
]
mkdb_args = [
'--output-format=xml'
]
toml_data = configuration_data()
toml_data.set('VERSION', meson.project_version())
scan_args = [
'--deprecated-guards=SOUP_DISABLE_DEPRECATED',
'--rebuild-types',
'--ignore-decorators="SOUP_DEPRECATED\w*\s*()|SOUP_DEPRECATED\w*|SOUP_AVAILABLE[\w_]*"'
]
gidocgen_dep = dependency('gi-docgen', version: '>= 2021.1',
fallback: ['gi-docgen', 'dummy_dep'],
native: true,
required: get_option('docs')
)
glib_prefix = glib_dep.get_pkgconfig_variable('prefix')
glib_docpath = glib_prefix / 'share' / 'gtk-doc' / 'html'
have_docs = gidocgen_dep.found()
if not enable_introspection and get_option('docs').auto()
have_docs = false
warning('Documentation will not be built as introspection was disabled')
elif not enable_introspection and get_option('docs').enabled()
error('Documentation cannot be built without introspection being enabled')
endif
gnome.gtkdoc('libsoup-3.0',
main_xml : 'libsoup-3.0-docs.xml',
src_dir : srcdir,
ignore_headers : ignore_headers,
namespace : 'soup',
mkdb_args : mkdb_args,
scan_args : scan_args,
fixxref_args : [
'--html-dir=@0@'.format(get_option('datadir') / 'gtk-doc', 'html'),
'--extra-dir=@0@'.format(glib_docpath / 'glib'),
'--extra-dir=@0@'.format(glib_docpath /'gobject'),
'--extra-dir=@0@'.format(glib_docpath /'gio'),
],
dependencies : libsoup_dep,
install : true,
content_files: [
'build-howto.xml',
'client-basic.xml',
'client-advanced.xml',
'client-tls.xml',
'server-howto.xml',
'migrating-from-libsoup-2.xml',
],
)
if have_docs
libsoup_toml = configure_file(
input: 'libsoup.toml.in',
output: 'libsoup.toml',
configuration: toml_data
)
gidocgen = find_program('gi-docgen')
docs_dir = get_option('datadir') / 'doc'
custom_target('libsoup-doc',
input: [ libsoup_toml, soup_gir_gen_sources[0] ],
output: 'libsoup-@0@'.format(apiversion),
command: [
gidocgen,
'generate',
'--quiet',
'--add-include-path=@0@'.format(meson.current_build_dir() / '../../libsoup'),
'--config=@INPUT0@',
'--output-dir=@OUTPUT@',
'--no-namespace-dir',
'--content-dir=@0@'.format(meson.current_source_dir()),
'@INPUT1@',
],
depend_files: [ expand_content_md_files ],
build_by_default: true,
install: true,
install_dir: docs_dir,
)
if get_option('doc_tests')
test('docs', gidocgen,
args: [
'check',
'--add-include-path=@0@'.format(meson.current_build_dir() / '../../libsoup'),
'--config=@0@'.format(libsoup_toml),
soup_gir_gen_sources[0],
]
)
endif
endif
\ No newline at end of file
Title: Migrating from libsoup 2
Slug: migrating-from-libsoup-2
# Migrating from libsoup 2
## Removed APIs
This is a list of APIs that have been removed:
- XML-RPC support.
- Handling of `file://` and `data://` URIs You should use [iface@Gio.File] for
the former and [func@uri_decode_data_uri] for the latter.
- Define aliases for property names You must use the string name of properties
directly which works in libsoup 2 already.
- `SoupSession:add-feature` and `SoupSession:add-feature-by-type` You must call
[method@Session.add_feature] and [method@Session.add_feature_by_type]
directly.
- `SoupRequest`: You should use [method@Session.send] or
[method@Session.send_async] methods.
- `SoupAddress` has been replaced with [class@Gio.InetAddress] and
[class@Gio.NetworkAddress].
- `SoupSocket` has been removed.
- `SoupProxyResolverDefault` is replaced by
[func@Gio.ProxyResolver.get_default].
- `SoupBuffer` has been replaced by [struct@GLib.Bytes] and
[struct@GLib.ByteArray].
- `SoupDate` has been replaced by `GDateTime`.
- `SoupSession:ssl-strict` has been removed in favor of using the
[signal@Message::accept-certificate] signal.
- `soup_session_cancel_message()` has been removed instead you
pass a [class@Gio.Cancellable] to APIs and call [method@Gio.Cancellable.cancel].
## Moved authenticate signal
The `SoupSession::authenticate` signal has been replaced by
[signal@Message::authenticate]. It now allows returning `TRUE` to signify if
you will handle authentication which allows for asynchronous handling.
## Structs are private
You can no longer directly access various structs such as [class@Message]. These are
now accessed by getters and setters. See below for direct
conversions:
<!-- TODO add links -->
<table>
<tr>
<th>Struct field</th>
<th>Getter/Setter function</th>
</tr>
<tr>
<td>SoupMessage.method</td>
<td>soup_message_get_method()</td>
</tr>
<tr>
<td>SoupMessage.status_code</td>
<td>soup_message_get_status()</td>
</tr>
<tr>
<td>SoupMessage.reason_phrase</td>
<td>soup_message_get_reason_phrase()</td>
</tr>
<tr>
<td>SoupMessage.uri</td>
<td>soup_message_get_uri(), soup_message_set_uri()</td>
</tr>
<tr>
<td>SoupMessage.request_headers</td>
<td>soup_message_get_request_headers()</td>
</tr>
<tr>
<td>SoupMessage.response_headers</td>
<td>soup_message_get_response_headers()</td>
</tr>
<tr>
<td>SoupMessage.request_body</td>
<td>soup_message_set_request_body(), soup_message_set_request_body_from_bytes()</td>
</tr>
<tr>
<td>SoupMessage.response_body</td>
<td>See section on IO</td>
</tr>
</table>
Similar struct changes exist for [struct@Cookie] but have very straightforward
replacements.
## URI type changed
The `SoupURI` type has been replaced with the [struct@GLib.Uri] type which has
some implications.
Creating a [struct@GLib.Uri] is generally as simple as `g_uri_parse (uri,
SOUP_HTTP_URI_FLAGS, NULL)`. You may want to add
`G_URI_FLAGS_PARSE_RELAXED` to accept input that used to be considered valid.
Note that unlike `SoupURI`, `GUri` is an immutable type so you cannot change the
contents of one after it has been constructed. We provide [func@uri_copy] to aid
in modifying them.
The equivalent behavior to `soup_uri_to_string (uri, FALSE)`
is `g_uri_to_string_partial (uri, G_URI_HIDE_PASSWORD)`.
Since `GUri` does not provide any function to check for equality
[func@uri_equal] still exists.
Sending a `OPTIONS` message with a path of `*` is no longer a valid URI and has
been replaced with [property@Message:is-options-ping].
## Status codes no longer used for internal errors
Previously [enum@Status] was used to hold libsoup errors
(`SOUP_STATUS_IS_TRANSPORT_ERROR()`). Now all of these errors are propagated up
through the normal [struct@GLib.Error] method on the various APIs to send
messages. Here is a mapping chart between the status codes and new errors:
<table>
<tr>
<th>Old Status Codes</th>
<th>New <code>GError</code></th>
</tr>
<tr>
<td><code>SOUP_STATUS_CANCELLED</code></td>
<td><code>G_IO_ERROR_CANCELLED</code></td>
</tr>
<tr>
<td><code>SOUP_STATUS_MALFORMED</code></td>
<td><code>SOUP_SESSION_ERROR_PARSING</code>, <code>SOUP_SESSION_ERROR_ENCODING</code></td>
</tr>
<tr>
<td><code>SOUP_STATUS_TOO_MANY_REDIRECTS</code></td>
<td><code>SOUP_SESSION_ERROR_TOO_MANY_REDIRECTS</code></td>
</tr>
</table>
## All IO is now GIOStream-based
Previously there were ways to allow libsoup to read data into buffers and for
you to read from those buffers such as `SoupMessage:response-body`
`SoupMessage:response-body-data`, and `SoupServerMessage::got-chunk`.
libsoup no longer stores a buffer of data for you to read from and instead it
returns a [class@Gio.InputStream] which you read from using normal GIO APIs.
If you want to simply request a buffer and nothing more you can use the
[method@Session.send_and_read] or [method@Session.send_and_read_async] APIs.
This also applies to writing data where you can set a [class@Gio.InputStream]
using [method@Message.set_request_body] or use the convenience API
[method@Message.set_request_body_from_bytes] to use a [struct@GLib.Bytes]
buffer.
## Clarification on thread-safety
In libsoup 2 there was an attempt at making various APIs of the library
thread-safe. However this was never well tested, maintained, or documented.
libsoup 3 was initially designed to behave in line with other GObject libraries. Once you
create a [class@Session] all usage of that session must happen on the same
thread. However, in version 3.2 thread safety support was introduced
again, with the same approach as libsoup2.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://docbook.org/xml/5.1/rng/docbook.rng" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://docbook.org/xml/5.1/sch/docbook.sch" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink" version="5.1">
<title>Migrating from libsoup 2</title>
<sect2>
<title>Removed APIs</title>
<para>This is a list of APIs that have been removed:<itemizedlist>
<listitem>
<para>XML-RPC support</para>
</listitem>
<listitem>
<para>Handling of <literal>file://</literal> and <literal>data://</literal>
URIs</para>
<para>You should use <link
linkend="GFile"><type>GFile</type></link> for the former and <link
linkend="soup-uri-decode-data-uri"><function>soup_uri_decode_data_uri()</function></link> for the
latter.</para>
</listitem>
<listitem>
<para>Define aliases for property names</para>
<para>You must use the string name of properties directly which works in libsoup
2 already.</para>
</listitem>
<listitem>
<para><literal>SoupSession:add-feature</literal> and <literal>SoupSession:add-feature-by-type</literal></para>
<para>You must call <link linkend="soup-session-add-feature"><function>soup_session_add_feature()</function></link> and
<link linkend="soup-session-add-feature-by-type"><function>soup_session_add_feature_by_type()</function></link> directly.</para>
</listitem>
<listitem>
<para><type>SoupRequest</type></para>
<para>You should use <link linkend="soup-session-send"><function>soup_session_send()</function></link> or
<link linkend="soup-session-send-async"><function>soup_session_send_async()</function></link> methods.</para>
</listitem>
<listitem>
<para><type>SoupAddress</type> has been replaced with <link linkend="GInetAddress"><type>GInetAddress</type></link>
and <link linkend="GNetworkAddress"><type>GNetworkAddress</type></link></para>
</listitem>
<listitem>
<para><type>SoupSocket</type> has been removed</para>
</listitem>
<listitem>
<para><type>SoupProxyResolverDefault</type> is replaced by
<link linkend="g-proxy-resolver-get-default"><function>g_proxy_resolver_get_default()</function></link></para>
</listitem>
<listitem>
<para><type>SoupBuffer</type> has been replaced by <link linkend="GBytes"><type>GBytes</type></link> and <link linkend="GByteArray"><type>GByteArray</type></link></para>
</listitem>
<listitem>
<para><type>SoupDate</type> has been replaced by <link linkend="GDateTime"><type>GDateTime</type></link></para>
</listitem>
<listitem>
<para><literal>SoupSession:ssl-strict</literal> has been removed in favor of using the <link linkend="SoupMessage-accept-certificate"><literal>SoupMessage:accept-certificate</literal></link> signal.</para>
</listitem>
<listitem>
<para><literal>soup_session_cancel_message()</literal> has been removed</para>
<para>Instead you pass a <link linkend="GCancellable"><type>GCancellable</type></link> to APIs and call <link linkend="g_cancellable_cancel"><function>g_cancellable_cancel()</function></link>.</para>
</listitem>
</itemizedlist></para>
</sect2>
<sect2>
<title>Moved authenticate signal</title>
<para>The <literal>SoupSession::authenticate</literal> signal has been replaced by
<link linkend="SoupMessage-authenticate"><literal>SoupMessage::authenticate</literal></link>. It now allows returning <literal>TRUE</literal> to signifiy
if you will handle authentication which allows for asynchronous handling.</para>
</sect2>
<sect2>
<title>Structs are private</title>
<para>You can no longer directly access various structs such as SoupMessage. These are now
accessed by getters and setters. See below for direct conversions:<informaltable>
<tgroup cols="2">
<colspec colname="c1" colnum="1" colwidth="1*"/>
<colspec colname="c2" colnum="2" colwidth="1*"/>
<thead>
<row>
<entry>Struct field</entry>
<entry>Getter/Setter function</entry>
</row>
</thead>
<tbody>
<row>
<entry>SoupMessage.method</entry>
<entry><link linkend="soup-message-get-method"><function>soup_message_get_method()</function></link></entry>
</row>
<row>
<entry>SoupMessage.status_code</entry>
<entry><link linkend="soup-message-get-status"><function>soup_message_get_status()</function></link></entry>
</row>
<row>
<entry>SoupMessage.reason_phrase</entry>
<entry><link linkend="soup-message-get-reason-phrase"><function>soup_message_get_reason_phrase()</function></link></entry>
</row>
<row>
<entry>SoupMessage.uri</entry>
<entry><link linkend="soup-message-get-uri"><function>soup_message_get_uri()</function></link>, <link linkend="soup-message-set-uri"><function>soup_message_set_uri()</function></link></entry>
</row>
<row>
<entry>SoupMessage.request_headers</entry>
<entry><link linkend="soup-message-get-request-headers"><function>soup_message_get_request_headers()</function></link></entry>
</row>
<row>
<entry>SoupMessage.response_headers</entry>
<entry><link linkend="soup-message-get-response-headers"><function>soup_message_get_response_headers()</function></link></entry>
</row>
<row>
<entry>SoupMessage.request_body</entry>
<entry><link linkend="soup-message-set-request-body"><function>soup_message_set_request_body()</function></link>,
<link linkend="soup-message-set-request-body-from-bytes"><function>soup_message_set_request_body_from_bytes()</function></link></entry>
</row>
<row>
<entry>SoupMessage.response_body</entry>
<entry>See <link linkend="io-stream-based">section on IO</link> </entry>
</row>
</tbody>
</tgroup>
</informaltable></para>
<para>Similar struct changes exist for <link linkend="SoupCookie"><type>SoupCookie</type></link> but have very straightforward
replacements.</para>
</sect2>
<sect2>
<title>URI type changed</title>
<para>The <type>SoupURI</type> type has been replaced with the <link linkend="GUri"><type>GUri</type></link> type which has some
implications.</para>
<para>Creating a <link linkend="GUri"><type>GUri</type></link> is generally as simple as <code>g_uri_parse (uri,
SOUP_HTTP_URI_FLAGS, NULL)</code>. You may want to add
<literal>G_URI_FLAGS_PARSE_RELAXED</literal> to accept input that used to be
considered valid.</para>
<para>Note that unlike <type>SoupURI</type> <link linkend="GUri"><type>GUri</type></link> is an immutable type so you cannot change the contents
of one after it has been constructed. We provide <link linkend="soup_copy_uri"><function>soup_copy_uri()</function></link> to aid in modifying them.</para>
<para>The equivalent behavior to <code>soup_uri_to_string (uri, FALSE)</code> is
<code>g_uri_to_string_partial (uri, G_URI_HIDE_PASSWORD)</code>.</para>
<para>Since GUri does not provide any function to check for equality
<link linkend="soup_uri_equal"><function>soup_uri_equal()</function></link> still exists.</para>
<para>Sending a <literal>OPTIONS</literal> message with a path of <literal>*</literal> is no
longer a valid URI and has been replaced with SoupMessage:options-ping.</para>
</sect2>
<sect2>
<title>Status codes no longer used for internal errors</title>
<para>Previously <link linkend="SoupStatus"><type>SoupStatus</type></link> was used to hold libsoup errors
(<code>SOUP_STATUS_IS_TRANSPORT_ERROR()</code>). Now all of these errors are
propagated up through the normal <link linkend="GError"><type>GError</type></link> method on the various APIs to send messages.
Here is a mapping chart between the status codes and new errors:<informaltable>
<tgroup cols="2">
<colspec colname="c1" colnum="1" colwidth="1*"/>
<colspec colname="newCol4" colnum="2" colwidth="1*"/>
<thead>
<row>
<entry>Old Status Codes</entry>
<entry>New GError</entry>
</row>
</thead>
<tbody>
<row>
<entry><code>SOUP_STATUS_CANCELLED</code></entry>
<entry><link linkend="G-IO-ERROR-CANCELLED:CAPS"><code>G_IO_ERROR_CANCELLED</code></link></entry>
</row>
<row>
<entry><code>SOUP_STATUS_MALFORMED</code></entry>
<entry><link linkend="SOUP-SESSION-ERROR-PARSING:CAPS"><code>SOUP_SESSION_ERROR_PARSING</code></link>, <link linkend="SOUP-SESSION-ERROR-ENCODING:CAPS"><code>SOUP_SESSION_ERROR_ENCODING</code></link></entry>
</row>
<row>
<entry><code>SOUP_STATUS_TOO_MANY_REDIRECTS</code></entry>
<entry><link linkend="SOUP-SESSION-ERROR-TOO-MANY-REDIRECTS:CAPS"><code>SOUP_SESSION_ERROR_TOO_MANY_REDIRECTS</code></link></entry>
</row>
</tbody>
</tgroup>
</informaltable></para>
</sect2>
<sect2 id="io-stream-based">
<title>All IO is now GIOStream-based</title>
<para>Previously there were ways to allow libsoup to read data into buffers and for you to
read from those buffers such as <literal>SoupMessage:response-body</literal>,
<literal>SoupMessage:response-body-data</literal>, and <literal>SoupMessage::got-chunk</literal>.</para>
<para>libsoup no longer stores a buffer of data for you to read from and instead it returns
a <link linkend="GInputStream"><type>GInputStream</type></link> which you read from using normal GIO APIs.</para>
<para>If you want to simply request a buffer and nothing more you can use the
<link linkend="soup_session_send_and_read"><function>soup_session_send_and_read()</function></link> or
<link linkend="soup_session_send_and_read_async"><function>soup_session_send_and_read_async()</function></link> APIs.</para>
<para>This also applies to writing data where you can set a <link linkend="GInputStream"><type>GInputStream</type></link> using
<link linkend="soup_message_set_request_body"><function>soup_message_set_request_body()</function></link> or use the convenience API
<link linkend="soup_message_set_request_body_from_bytes"><function>soup_message_set_request_body_from_bytes()</function></link> to use a GBytes
buffer.</para>
</sect2>
<sect2 id="threading">
<title>Clarification on thread-safety</title>
<para>In libsoup 2 there was an attempt at making various APIs of the library thread-safe. However this was never well tested, maintained, or documented.</para>
<para>In libsoup 3 it now behaves in line with other GObject libraries. Once you create a <link linkend="SoupSession"><type>SoupSession</type></link> all usage of that
session must happen on the same thread. You may create seperate sessions per thread but in most use-cases you should be using the async APIs which handle non-blocking IO for you.</para>
</sect2>
</chapter>
Title: Server Basics
Slug: server-howto
# Server Basics
## Creating a SoupServer
As with the client API, there is a single object that will encapsulate
most of your interactions with libsoup. In this case, [class@Server].
You create the server with [ctor@Server.new], and as with the [class@Session]
constructor, you can specify a few additional options:
<table>
<tr>
<td>[property@Server:tls-certificate]</td>
<td>
A [class@Gio.TlsCertificate]
(containing a private key) that will be used when handling
HTTPS requests on the server.
</td>
</tr>
<tr>
<td>[property@Server:raw-paths]</literal></td>
<td>
Set this to <tt>TRUE</tt> if you don't want
libsoup to decode %-encoding
in the Request-URI. (e.g. because you need to treat
<tt>"/foo/bar"</tt> and
<tt>"/foo%2Fbar"</tt> as different paths.
</td>
</tr>
<tr>
<td>[property@Server:server-header]</td>
<td>
Allows you to set a Server header string that will be sent
on all responses.
</td>
</tr>
</table>
## Adding Listening Sockets
To tell the server where to listen, call [method@Server.listen] (to listen on a
specific [class@Gio.SocketAddress]), [method@Server.listen_all] (to listen on a
given port on all network interfaces), or [method@Server.listen_local] (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.
To set up an HTTPS server, you must first either set the
[property@Server:tls-certificate] property, or else call
[method@Server.set_tls_certificate]. After that you can pass the
`SOUP_SERVER_LISTEN_HTTPS` option to [method@Server.listen], etc.
By default, servers listen for both IPv4 and IPv6 connections; if you don't want
this, use the `SOUP_SERVER_LISTEN_IPV4_ONLY` or `SOUP_SERVER_LISTEN_IPV6_ONLY`
options.
The server runs asynchronously, in the thread-default [struct@GLib.MainContext]
of the thread in which the `listen` calls were made.
## Adding Handlers
By default, [class@Server] 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 [class@Server.add_handler] to set a callback to handle certain
URI paths.
```c
soup_server_add_handler (server, "/foo", server_callback,
data, destroy_notify);
```
The `"/foo"` 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
`Request-URI`, then that handler will be called. Otherwise libsoup will strip
path components one by one until it finds a matching handler. So for example, a
request of the form `GET /foo/bar/baz.html?a=1&b=2 HTTP/1.1` would look for
handlers for `/foo/bar/baz.html`, `/foo/bar`, and `/foo`. If a handler has been
registered with a `NULL` base path, then it is used as the default handler for
any request that doesn't match any other handler.
## Responding to Requests
A handler callback looks something like this:
```c
static void
server_callback (SoupServer *server,
SoupServerMessage *msg,
const char *path,
GHashTable *query,
gpointer user_data)
{
// ...
}
```
`msg` is the request that has been received and `user_data` is the data that was
passed to [method@Server.add_handler]. `path` is the path (from `msg`'s URI),
and `query` contains the result of parsing the URI query field. (It is `NULL` if
there was no query.)
By default, libsoup 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 (e.g.
you have to contact another server, or wait for data from a database), you must
call [method@Server.pause_message] on the message before returning from the
callback. This will delay sending a response until you call
[method@Server.unpause_message]. (You must also connect to the
[signal@ServerMessage::finished] 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.)
To set the response status, call [method@ServerMessage.set_status]. If the
response requires a body, you must decide whether to use `Content-Length`
encoding (the default), or `chunked` encoding.
## Responding with `Content-Length` Encoding
This is the simpler way to set a response body, if you have all of the data
available at once.
```c
static void
server_callback (SoupServer *server,
SoupServerMessage *msg,
const char *path,
GHashTable *query,
gpointer user_data)
{
MyServerData *server_data = user_data;
const char *mime_type;
GByteArray *body;
if (soup_server_message_get_method (msg) != SOUP_METHOD_GET) {
soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
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_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL);
return;
}
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
soup_server_message_set_response (msg, mime_type, SOUP_MEMORY_COPY,
body->data, body->len);
}
```
# Responding with `chunked` Encoding
If you want to supply the response body in chunks as it becomes available, use
`chunked` encoding instead. In this case, first call
`soup_message_headers_set_encoding (msg->response_headers,
SOUP_ENCODING_CHUNKED)` to tell libsoup that you'll be using `chunked` encoding.
Then call [method@MessageBody.append] (or [method@MessageBody.append_bytes]) on
`msg->response_body` with each chunk of the response body as it becomes
available, and call [method@MessageBody.complete] when the response is complete.
After each of these calls, you must also call [class@Server.unpause_message] to
cause the chunk to be sent. (You do not normally need to call
[method@Server.pause_message], because I/O is automatically paused when doing a
`chunked` transfer if no chunks are available.)
When using chunked encoding, you must also connect to the
[signal@ServerMessage::finished] signal on the message, so that you will be
notified if the client disconnects between two chunks; [class@Server] 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 [signal@ServerMessage::wrote_chunk] signal is emitted indicating that the
previous one was written successfully.)
The **`simple-proxy`** example in the `examples/` directory gives an example of
using `chunked` encoding.
## Handling Authentication
To have [class@Server] handle HTTP authentication for you, create a
[class@AuthDomainBasic] or [class@AuthDomainDigest], and pass it to
[method@Server.add_auth_domain]:
```c
SoupAuthDomain *domain;
domain = soup_auth_domain_basic_new (
"realm", "My Realm",
"auth-callback", auth_callback,
"auth-data", auth_data,
"add-path", "/foo",
"add-path", "/bar/private",
NULL);
soup_server_add_auth_domain (server, domain);
g_object_unref (domain);
```
Then, every request under one of the auth domain's paths will be passed to the
`auth_callback` first before being passed to the `server_callback`:
```c
static gboolean
auth_callback (SoupAuthDomain *domain, SoupServerMessage *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;
}
```
The [callback@AuthDomainBasicAuthCallback] is given the username and password
from the `Authorization` 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.)
If you are using Digest authentication, note that
[callback@AuthDomainDigestAuthCallback] 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
[class@AuthDomainDigest] for more details.
You can have multiple [class@AuthDomain]s attached to a [class@Server], either
in separate parts of the path hierarchy, or overlapping. (e.g. 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 *any* of the domains.
If you want to require authentication for some requests under a certain path,
but not all of them (e.g. you want to authenticate `PUT` requests, but not `GET`
requests), use a [callback@AuthDomainFilter].
<?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>Server Basics</refentrytitle>
<manvolnum>3</manvolnum>
<refmiscinfo>LIBSOUP Library</refmiscinfo>
</refmeta>
<refnamediv>
<refname>libsoup Server Basics</refname>
</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="SoupServer:tls-certificate"><literal>"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="SoupServer:raw-paths"><literal>"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="SoupServer:server-header"><literal>"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>
</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="SoupServer:tls-certificate"><literal>SoupServer: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>
<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,
SoupServerMessage *msg,
const char *path,
GHashTable *query,
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.)
</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="SoupServerMessage-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-server-message-set-status"><function>soup_server_message_set_status</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,
SoupServerMessage *msg,
const char *path,
GHashTable *query,
gpointer user_data)
{
MyServerData *server_data = user_data;
const char *mime_type;
GByteArray *body;
if (soup_server_message_get_method (msg) != SOUP_METHOD_GET) {
soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
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_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL);
return;
}
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
soup_server_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-bytes"><function>soup_message_body_append_bytes</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="SoupServerMessage-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="SoupServerMessage-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 (
"realm", "My Realm",
"auth-callback", auth_callback,
"auth-data", auth_data,
"add-path", "/foo",
"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, SoupServerMessage *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>
// A map between namespaces and base URLs for their online documentation
baseURLs = [
[ 'GLib', 'https://docs.gtk.org/glib/' ],
[ 'GObject', 'https://docs.gtk.org/gobject/' ],
[ 'Gio', 'https://docs.gtk.org/gio/' ],
]