Projects
openEuler:Mainline
libsoup3
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 2
View file
_service:tar_scm:libsoup3.spec
Changed
@@ -1,15 +1,15 @@ -%global glib2_version 2.58.0 +%global glib2_version 2.69.1 Name: libsoup3 -Version: 3.0.6 +Version: 3.2.2 Release: 1 Summary: Soup, an HTTP library implementation License: LGPLv2 URL: https://wiki.gnome.org/Projects/libsoup -Source0: https://download.gnome.org/sources/libsoup/3.0/libsoup-%{version}.tar.xz -Patch0: xgettext.patch +Source0: https://download.gnome.org/sources/libsoup/3.2/libsoup-%{version}.tar.xz -BuildRequires: gcc meson gettext vala gtk-doc krb5-devel samba-winbind-clients +BuildRequires: gcc meson gettext vala krb5-devel samba-winbind-clients +BuildRequires: gi-docgen >= 2021.1 BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(gio-2.0) BuildRequires: glib-networking @@ -20,7 +20,7 @@ BuildRequires: pkgconfig(sysprof-capture-4) BuildRequires: pkgconfig(libnghttp2) -Recommends: glib-networking >= %{glib2_version} +Recommends: glib-networking >= %{glib2_version} %description Libsoup is an HTTP library implementation in C. It was originally part @@ -45,11 +45,10 @@ %package_help %prep -%autosetup -p0 -n libsoup-%{version} +%autosetup -p1 -n libsoup-%{version} %build -%global gtkdoc_flags -Dgtk_doc=true -%meson %gtkdoc_flags -Dtests=false -Dautobahn=disabled -Dhttp2_tests=disabled -Dpkcs11_tests=disabled +%meson -Ddocs=enabled -Dtests=false -Dautobahn=disabled -Dpkcs11_tests=disabled %meson_build %install @@ -72,8 +71,11 @@ %files help %doc README NEWS AUTHORS -%{_datadir}/gtk-doc/html/libsoup-3.0 +%{_datadir}/doc %changelog +* Mon Jan 2 2023 lin zhang <lin.zhang@turbolinux.com.cn> - 3.2.2-1 +- Update 3.2.2 + * Mon Apr 11 2022 lin zhang <lin.zhang@turbolinux.com.cn> - 3.0.6-1 - Initial packaging
View file
_service
Changed
@@ -2,7 +2,7 @@ <service name="tar_scm"> <param name="scm">git</param> <param name="url">git@gitee.com:src-openeuler/libsoup3.git</param> - <param name="revision">0ebfbf8ca5401f44e339b10bf310647970334d83</param> + <param name="revision">master</param> <param name="exclude">*</param> <param name="extract">*</param> </service>
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/.gitlab-ci/check-docs.py
Deleted
@@ -1,24 +0,0 @@ -#!/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)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/docs/reference/build-howto.xml
Deleted
@@ -1,70 +0,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>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 <libsoup/soup.h> -</programlisting></informalexample> - </sect3> -</sect1>
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/docs/reference/client-advanced.xml
Deleted
@@ -1,171 +0,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><!CDATAsession = 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
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/docs/reference/client-basic.xml
Deleted
@@ -1,183 +0,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>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> - -int main (int argc, char **argv) -{ - SoupSession *session = soup_session_new (); - SoupMessageHeaders *response_headers; - const char *content_type; - SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "https://upload.wikimedia.org/wikipedia/commons/5/5f/BBB-Bunny.png"); - GError *error = NULL; - GBytes *bytes = soup_session_send_and_read ( - session, - msg, - NULL, // Pass a GCancellable here if you want to cancel a download - &error); - - if (error) { - g_printerr ("Failed to download: %s\n", error->message); - g_error_free (error); - g_object_unref (msg); - g_object_unref (session); - return 1; - } - - response_headers = soup_message_get_response_headers (msg); - content_type = soup_message_headers_get_content_type (response_headers); - - // content_type = "image/png" - // bytes contains the raw data that can be used elsewhere - g_print ("Downloaded %zu bytes of type %s\n", - g_bytes_get_size (bytes), content_type); - - g_bytes_unref (bytes); - 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> - -int main (int argc, char **argv) -{ - SoupSession *session = soup_session_new (); - SoupMessageHeaders *response_headers; - const char *content_type; - goffset content_length; - SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "https://upload.wikimedia.org/wikipedia/commons/5/5f/BBB-Bunny.png"); - GError *error = NULL; - GInputStream *in_stream = soup_session_send ( - session, - msg, - NULL, - &error); - - if (error) { - g_printerr ("Failed to download: %s\n", error->message); - g_error_free (error); - g_object_unref (msg); - g_object_unref (session); - return 1; - } - - GFile *output_file = g_file_new_tmp ("BBB-Bunny-XXXXXX.png"); - GOutputStream *out_stream = g_file_create (output_file, - G_FILE_CREATE_NONE, NULL, &error); - - if (error) { - g_printerr ("Failed to create file \"%s\": %s\n", - g_file_peek_path (output_file), error->message); - g_error_free (error); - g_object_unref (output_file); - g_object_unref (in_stream); - g_object_unref (msg); - g_object_unref (session); - return 1; - } - - response_headers = soup_message_get_response_headers (msg); - content_type = soup_message_headers_get_content_type (response_headers); - content_length = soup_message_headers_get_content_length (response_headers); - - // content_type = "image/png" - g_print ("Downloading %zu bytes of type %s to %s\n", - content_length, content_type, - g_file_peek_path (output_file)); - - g_output_stream_splice (out_stream, in_stream, - G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, - NULL, &error); - - if (error) { - g_print ("Download failed: %s\n", error->message); - g_error_free (error); - } else { - g_print ("Download completed\n"); - } - - g_object_unref (output_file); - g_object_unref (in_stream); - g_object_unref (out_stream); - 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> - -static void on_load_callback (GObject *source, GAsyncResult *result, gpointer user_data) -{ - GMainLoop *loop = user_data; - GError *error = NULL; - GBytes *bytes = soup_session_send_and_read_finish (SOUP_SESSION (source), result, &error); - - // Usage here is the same as before - if (error) { - g_error_free (error); - } else { - g_bytes_unref (bytes); - } - - g_main_loop_quit (loop); -} - -int main (int argc, char **argv) -{ - SoupSession *session = soup_session_new (); - GMainLoop *loop = g_main_loop_new (NULL, FALSE); - SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "https://upload.wikimedia.org/wikipedia/commons/5/5f/BBB-Bunny.png"); - - soup_session_send_and_read_async ( - session, - msg, - G_PRIORITY_DEFAULT, - NULL, - on_load_callback, - loop); - - g_main_loop_run (loop); - - g_main_loop_unref (loop); - g_object_unref (msg); - g_object_unref (session); - return 0; -}> -</programlisting></informalexample> - </sect2> -</sect1>
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/docs/reference/client-howto.xml
Deleted
@@ -1,535 +0,0 @@ -<?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, &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, &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>
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/docs/reference/client-tls.xml
Deleted
@@ -1,102 +0,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>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 -static gboolean -accept_certificate_callback (SoupMessage *msg, GTlsCertificate *certificate, - GTlsCertificateFlags tls_errors, gpointer user_data) -{ - // Here you can inspect @certificate or compare it against a trusted one - // and you can see what is considered invalid by @tls_errors. - // Returning TRUE trusts it anyway. - 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, "accept-certificate", G_CALLBACK (accept_certificate_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> - <sect2> - <title>Setting a Custom CA</title> -<informalexample><programlisting><!CDATA -{ - GError *error = NULL; - // NOTE: This is blocking IO - GTlsDatabase *tls_db = g_tls_file_database_new ("/foo/ca.pem", &error); - - if (error) { - g_printerr ("Failed to load certificates: %s\n", error->message); - g_error_free (error); - return; - } - - 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 -static gboolean -on_request_certificate (SoupMessage *msg, GTlsClientConnection *conn, gpointer user_data) -{ - GTlsCertificate *client_cert = user_data; - - /* We immediately set this however you can set this later in an async function. */ - soup_message_set_tls_client_certificate (msg, client_cert); - - return TRUE; /* We handled the request */ -} - -int main (int argc, char **argv) -{ - GError *error = NULL; - GTlsCertificate *client_cert = g_tls_certificate_new_from_file ("/foo/cert.pem", &error); - - if (error) { - g_printerr ("Failed to load certificate: %s\n", error->message); - g_error_free (error); - return 1; - } - - SoupSession *session = soup_session_new (); - SoupMessage *msg = soup_message_new ("GET", "https://example.org"); - - /* We can set the certificate ahead of time if we already have one */ - // soup_message_set_tls_client_certificate (msg, client_cert) - - /* However we can also dynamically request one which is useful in - * applications that show a graphical prompt for example. */ - g_signal_connect (msg, "request-certificate", - G_CALLBACK (on_request_certificate), client_cert); - - // Send the message... - - g_object_unref (msg); - g_object_unref (session); - g_object_unref (client_cert); - return 0; -}> -</programlisting></informalexample> - </sect2> -</sect1> \ No newline at end of file
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/docs/reference/libsoup-3.0-docs.xml
Deleted
@@ -1,118 +0,0 @@ -<?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>
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/docs/reference/libsoup-3.0-sections.txt
Deleted
@@ -1,1033 +0,0 @@ -<INCLUDE>libsoup/soup.h</INCLUDE> -<SECTION> -<FILE>soup-message</FILE> -<TITLE>SoupMessage</TITLE> -SoupMessage -<SUBSECTION> -soup_message_new -soup_message_new_from_uri -soup_message_new_from_encoded_form -soup_message_new_from_multipart -soup_message_set_request_body -soup_message_set_request_body_from_bytes -<SUBSECTION> -soup_message_new_options_ping -soup_message_get_is_options_ping -soup_message_set_is_options_ping -<SUBSECTION> -SoupHTTPVersion -soup_message_get_http_version -soup_message_get_uri -soup_message_set_uri -soup_message_get_method -soup_message_set_method -soup_message_get_status -soup_message_get_reason_phrase -<SUBSECTION> -soup_message_get_request_headers -soup_message_get_response_headers -<SUBSECTION> -soup_message_is_keepalive -soup_message_get_connection_id -soup_message_get_remote_address -soup_message_get_tls_peer_certificate -soup_message_get_tls_peer_certificate_errors -soup_message_get_tls_protocol_version -soup_message_get_tls_ciphersuite_name -soup_message_set_tls_client_certificate -soup_message_tls_client_certificate_password_request_complete -<SUBSECTION> -soup_message_set_first_party -soup_message_get_first_party -<SUBSECTION> -soup_message_add_header_handler -soup_message_add_status_code_handler -<SUBSECTION> -SoupMessageFlags -soup_message_set_flags -soup_message_get_flags -soup_message_add_flags -soup_message_remove_flags -soup_message_query_flags -<SUBSECTION> -soup_message_disable_feature -soup_message_is_feature_disabled -<SUBSECTION> -SoupMessagePriority -soup_message_get_priority -soup_message_set_priority -<SUBSECTION> -soup_message_get_site_for_cookies -soup_message_set_site_for_cookies -soup_message_get_is_top_level_navigation -soup_message_set_is_top_level_navigation -<SUBSECTION> -soup_message_get_metrics -<SUBSECTION Standard> -SOUP_MESSAGE -SOUP_IS_MESSAGE -SOUP_TYPE_MESSAGE -soup_message_get_type -SOUP_MESSAGE_CLASS -SOUP_IS_MESSAGE_CLASS -SOUP_MESSAGE_GET_CLASS -SoupMessageClass -<SUBSECTION Private> -soup_message_wrote_headers -soup_message_wrote_body_data -soup_message_wrote_body -soup_message_got_informational -soup_message_got_headers -soup_message_content_sniffed -soup_message_got_body -soup_message_finished -soup_message_restarted -soup_message_starting -</SECTION> - -<SECTION> -<FILE>soup-method</FILE> -<TITLE>SoupMethod</TITLE> -SOUP_METHOD_OPTIONS -SOUP_METHOD_GET -SOUP_METHOD_HEAD -SOUP_METHOD_PUT -SOUP_METHOD_POST -SOUP_METHOD_DELETE -SOUP_METHOD_TRACE -SOUP_METHOD_CONNECT -<SUBSECTION> -SOUP_METHOD_PROPFIND -SOUP_METHOD_PROPPATCH -SOUP_METHOD_MKCOL -SOUP_METHOD_COPY -SOUP_METHOD_MOVE -SOUP_METHOD_LOCK -SOUP_METHOD_UNLOCK -</SECTION> - -<SECTION> -<FILE>soup-message-headers</FILE> -<TITLE>SoupMessageHeaders</TITLE> -SoupMessageHeaders -SoupMessageHeadersType -soup_message_headers_new -soup_message_headers_ref -soup_message_headers_unref -<SUBSECTION> -soup_message_headers_append -soup_message_headers_replace -soup_message_headers_remove -soup_message_headers_clear -soup_message_headers_clean_connection_headers -soup_message_headers_get_one -soup_message_headers_get_list -soup_message_headers_get_headers_type -<SUBSECTION> -soup_message_headers_header_contains -soup_message_headers_header_equals -<SUBSECTION> -SoupMessageHeadersForeachFunc -soup_message_headers_foreach -<SUBSECTION> -SoupMessageHeadersIter -soup_message_headers_iter_init -soup_message_headers_iter_next -<SUBSECTION> -SoupEncoding -soup_message_headers_get_encoding -soup_message_headers_set_encoding -soup_message_headers_get_content_length -soup_message_headers_set_content_length -<SUBSECTION> -SoupExpectation -soup_message_headers_get_expectations -soup_message_headers_set_expectations -<SUBSECTION> -soup_message_headers_get_content_type -soup_message_headers_set_content_type -<SUBSECTION> -soup_message_headers_get_content_disposition -soup_message_headers_set_content_disposition -<SUBSECTION> -SoupRange -soup_message_headers_get_ranges -soup_message_headers_set_ranges -soup_message_headers_set_range -soup_message_headers_free_ranges -soup_message_headers_get_content_range -soup_message_headers_set_content_range -<SUBSECTION Standard> -SOUP_TYPE_MESSAGE_HEADERS -soup_message_headers_get_type -</SECTION> - -<SECTION> -<FILE>soup-headers</FILE> -soup_headers_parse_request -soup_headers_parse_response -soup_headers_parse_status_line -soup_headers_parse -<SUBSECTION> -soup_header_parse_list -soup_header_parse_quality_list -soup_header_free_list -soup_header_contains -soup_header_parse_param_list -soup_header_parse_semi_param_list -soup_header_parse_param_list_strict -soup_header_parse_semi_param_list_strict -soup_header_free_param_list -soup_header_g_string_append_param -soup_header_g_string_append_param_quoted -</SECTION> - -<SECTION> -<FILE>soup-message-body</FILE> -<TITLE>SoupMessageBody</TITLE> -SoupMemoryUse -<SUBSECTION> -SoupMessageBody -soup_message_body_new -soup_message_body_ref -soup_message_body_unref -<SUBSECTION> -soup_message_body_set_accumulate -soup_message_body_get_accumulate -<SUBSECTION> -soup_message_body_append -soup_message_body_append_bytes -soup_message_body_append_take -soup_message_body_truncate -soup_message_body_complete -soup_message_body_flatten -soup_message_body_get_chunk -<SUBSECTION> -soup_message_body_got_chunk -soup_message_body_wrote_chunk -<SUBSECTION Standard> -SOUP_TYPE_MESSAGE_BODY -soup_message_body_get_type -</SECTION> - -<SECTION> -<FILE>soup-status</FILE> -<TITLE>SoupStatus</TITLE> -SOUP_STATUS_IS_INFORMATIONAL -SOUP_STATUS_IS_SUCCESSFUL -SOUP_STATUS_IS_REDIRECTION -SOUP_STATUS_IS_CLIENT_ERROR -SOUP_STATUS_IS_SERVER_ERROR -SoupStatus -soup_status_get_phrase -</SECTION> - -<SECTION> -<FILE>soup-server</FILE> -<TITLE>SoupServer</TITLE> -SoupServer -soup_server_new -soup_server_set_tls_certificate -soup_server_get_tls_certificate -soup_server_set_tls_database -soup_server_get_tls_database -soup_server_set_tls_auth_mode -soup_server_get_tls_auth_mode -<SUBSECTION> -SoupServerListenOptions -soup_server_listen -soup_server_listen_all -soup_server_listen_local -soup_server_listen_socket -soup_server_get_listeners -soup_server_get_uris -soup_server_disconnect -soup_server_is_https -soup_server_accept_iostream -<SUBSECTION> -SoupServerCallback -soup_server_add_handler -soup_server_add_early_handler -soup_server_remove_handler -<SUBSECTION> -SoupServerWebsocketCallback -soup_server_add_websocket_handler -soup_server_add_websocket_extension -soup_server_remove_websocket_extension -<SUBSECTION> -soup_server_add_auth_domain -soup_server_remove_auth_domain -soup_server_pause_message -soup_server_unpause_message -<SUBSECTION Standard> -SOUP_SERVER -SOUP_IS_SERVER -SOUP_TYPE_SERVER -soup_server_get_type -SOUP_SERVER_CLASS -SOUP_IS_SERVER_CLASS -SOUP_SERVER_GET_CLASS -SoupServerClass -SOUP_TYPE_CLIENT_CONTEXT -soup_client_context_get_type -</SECTION> - -<SECTION> -<FILE>soup-server-message</FILE> -<TITLE>SoupServerMessage</TITLE> -SoupServerMessage -soup_server_message_get_request_headers -soup_server_message_get_response_headers -soup_server_message_get_request_body -soup_server_message_get_response_body -soup_server_message_get_method -soup_server_message_get_http_version -soup_server_message_set_http_version -soup_server_message_get_status -soup_server_message_set_status -soup_server_message_get_reason_phrase -soup_server_message_get_uri -soup_server_message_set_response -soup_server_message_set_redirect -soup_server_message_get_socket -soup_server_message_get_local_address -soup_server_message_get_remote_address -soup_server_message_get_remote_host -soup_server_message_is_options_ping -soup_server_message_steal_connection -<SUBSECTION Standard> -SOUP_SERVER_MESSAGE -SOUP_IS_SERVER_MESSAGE -SOUP_TYPE_SERVER_MESSAGE -<SUBSECTION Private> -soup_server_message_get_type -</SECTION> - -<SECTION> -<FILE>soup-auth-domain</FILE> -<TITLE>SoupAuthDomain</TITLE> -SoupAuthDomain -<SUBSECTION> -SoupAuthDomainFilter -soup_auth_domain_set_filter -soup_auth_domain_get_realm -<SUBSECTION> -soup_auth_domain_add_path -soup_auth_domain_remove_path -<SUBSECTION> -SoupAuthDomainGenericAuthCallback -soup_auth_domain_set_generic_auth_callback -soup_auth_domain_check_password -<SUBSECTION> -soup_auth_domain_covers -soup_auth_domain_accepts -soup_auth_domain_challenge -<SUBSECTION Private> -soup_auth_domain_try_generic_auth_callback -<SUBSECTION Standard> -SOUP_AUTH_DOMAIN -SOUP_IS_AUTH_DOMAIN -SOUP_TYPE_AUTH_DOMAIN -soup_auth_domain_get_type -SOUP_AUTH_DOMAIN_CLASS -SOUP_IS_AUTH_DOMAIN_CLASS -SOUP_AUTH_DOMAIN_GET_CLASS -SoupAuthDomainClass -</SECTION> - -<SECTION> -<FILE>soup-auth-domain-basic</FILE> -<TITLE>SoupAuthDomainBasic</TITLE> -SoupAuthDomainBasic -soup_auth_domain_basic_new -<SUBSECTION> -SoupAuthDomainBasicAuthCallback -soup_auth_domain_basic_set_auth_callback -<SUBSECTION Standard> -SOUP_AUTH_DOMAIN_BASIC -SOUP_IS_AUTH_DOMAIN_BASIC -SOUP_TYPE_AUTH_DOMAIN_BASIC -soup_auth_domain_basic_get_type -SOUP_AUTH_DOMAIN_BASIC_CLASS -SOUP_IS_AUTH_DOMAIN_BASIC_CLASS -SOUP_AUTH_DOMAIN_BASIC_GET_CLASS -SoupAuthDomainBasicClass -</SECTION> - -<SECTION> -<FILE>soup-auth-domain-digest</FILE> -<TITLE>SoupAuthDomainDigest</TITLE> -SoupAuthDomainDigest -soup_auth_domain_digest_new -<SUBSECTION> -SoupAuthDomainDigestAuthCallback -soup_auth_domain_digest_set_auth_callback -soup_auth_domain_digest_encode_password -<SUBSECTION Standard> -SOUP_AUTH_DOMAIN_DIGEST -SOUP_IS_AUTH_DOMAIN_DIGEST -SOUP_TYPE_AUTH_DOMAIN_DIGEST -soup_auth_domain_digest_get_type -SOUP_AUTH_DOMAIN_DIGEST_CLASS -SOUP_IS_AUTH_DOMAIN_DIGEST_CLASS -SOUP_AUTH_DOMAIN_DIGEST_GET_CLASS -SoupAuthDomainDigestClass -</SECTION> - -<SECTION> -<FILE>soup-session</FILE> -<TITLE>SoupSession</TITLE> -SoupSession -SoupSessionError -<SUBSECTION> -soup_session_new -soup_session_new_with_options -<SUBSECTION> -soup_session_get_local_address -soup_session_get_max_conns -soup_session_get_max_conns_per_host -soup_session_set_proxy_resolver -soup_session_get_proxy_resolver -soup_session_set_tls_database -soup_session_get_tls_database -soup_session_set_tls_interaction -soup_session_get_tls_interaction -soup_session_set_timeout -soup_session_get_timeout -soup_session_set_idle_timeout -soup_session_get_idle_timeout -soup_session_set_user_agent -soup_session_get_user_agent -soup_session_set_accept_language -soup_session_get_accept_language -soup_session_set_accept_language_auto -soup_session_get_accept_language_auto -soup_session_get_async_result_message -soup_session_get_remote_connectable -<SUBSECTION> -soup_session_send -soup_session_send_async -soup_session_send_finish -soup_session_send_and_read -soup_session_send_and_read_async -soup_session_send_and_read_finish -<SUBSECTION> -soup_session_websocket_connect_async -soup_session_websocket_connect_finish -<SUBSECTION> -soup_session_abort -<SUBSECTION> -soup_session_add_feature -soup_session_add_feature_by_type -soup_session_remove_feature -soup_session_remove_feature_by_type -soup_session_get_feature -soup_session_get_feature_for_message -soup_session_has_feature -<SUBSECTION> -soup_session_preconnect_async -soup_session_preconnect_finish -<SUBSECTION Standard> -SOUP_IS_SESSION -SOUP_IS_SESSION_CLASS -SOUP_SESSION -SOUP_SESSION_CLASS -SOUP_SESSION_GET_CLASS -SOUP_TYPE_SESSION -SOUP_SESSION_ERROR -SoupSessionClass -soup_session_get_type -soup_session_error_quark -<SUBSECTION Private> -SoupSocket -SoupConnection -SoupConnectionState -SOUP_TYPE_CONNECTION_STATE -soup_connection_state_get_type -SoupMessageQueue -SoupMessageQueueItem -SOUP_SESSION_USE_NTLM -SoupClientMessageIO -</SECTION> - -<SECTION> -<FILE>soup-session-feature</FILE> -<TITLE>SoupSessionFeature</TITLE> -SoupSessionFeature -<SUBSECTION Standard> -soup_session_feature_get_type -SOUP_SESSION_FEATURE -SOUP_SESSION_FEATURE_CLASS -SOUP_SESSION_FEATURE_GET_CLASS -SOUP_IS_SESSION_FEATURE -SOUP_IS_SESSION_FEATURE_CLASS -SOUP_TYPE_SESSION_FEATURE -</SECTION> - -<SECTION> -<FILE>soup-auth</FILE> -<TITLE>SoupAuth</TITLE> -SoupAuth -soup_auth_new -soup_auth_update -<SUBSECTION> -SOUP_TYPE_AUTH_BASIC -SOUP_TYPE_AUTH_DIGEST -SOUP_TYPE_AUTH_NTLM -SOUP_TYPE_AUTH_NEGOTIATE -soup_auth_negotiate_supported -<SUBSECTION> -soup_auth_is_for_proxy -soup_auth_get_scheme_name -soup_auth_get_authority -soup_auth_get_realm -soup_auth_get_info -<SUBSECTION> -soup_auth_authenticate -soup_auth_can_authenticate -soup_auth_is_authenticated -soup_auth_is_ready -soup_auth_cancel -soup_auth_is_cancelled -<SUBSECTION> -soup_auth_get_authorization -soup_auth_get_protection_space -soup_auth_free_protection_space -<SUBSECTION Private> -SoupAuthBasic -SoupAuthDigest -SoupAuthNTLM -SoupAuthNegotiate -<SUBSECTION Standard> -SOUP_AUTH -SOUP_IS_AUTH -SOUP_TYPE_AUTH -soup_auth_get_type -SOUP_AUTH_CLASS -SOUP_IS_AUTH_CLASS -SOUP_AUTH_GET_CLASS -SoupAuthClass -soup_auth_basic_get_type -soup_auth_digest_get_type -soup_auth_ntlm_get_type -soup_auth_negotiate_get_type -</SECTION> - -<SECTION> -<FILE>soup-auth-manager</FILE> -<TITLE>SoupAuthManager</TITLE> -SoupAuthManager -SOUP_TYPE_AUTH_MANAGER -soup_auth_manager_use_auth -soup_auth_manager_clear_cached_credentials -<SUBSECTION Standard> -SoupAuthManagerPrivate -SoupAuthManagerClass -SOUP_AUTH_MANAGER -SOUP_IS_AUTH_MANAGER -SOUP_AUTH_MANAGER_CLASS -SOUP_IS_AUTH_MANAGER_CLASS -SOUP_AUTH_MANAGER_GET_CLASS -soup_auth_manager_get_type -</SECTION> - -<SECTION> -<FILE>soup-date-utils</FILE> -SoupDateFormat -soup_date_time_new_from_http_string -soup_date_time_to_string -<SUBSECTION Private> -soup_date_time_is_past -</SECTION> - -<SECTION> -<FILE>soup-uri-utils</FILE> -soup_uri_equal -<SUBSECTION> -SOUP_HTTP_URI_FLAGS -<SUBSECTION> -soup_uri_decode_data_uri -<SUBSECTION> -SoupURIComponent -soup_uri_copy -</SECTION> - -<SECTION> -<FILE>soup-form</FILE> -<TITLE>SoupForm</TITLE> -<SUBSECTION> -SOUP_FORM_MIME_TYPE_MULTIPART -SOUP_FORM_MIME_TYPE_URLENCODED -soup_form_decode -soup_form_decode_multipart -soup_form_encode -soup_form_encode_datalist -soup_form_encode_hash -soup_form_encode_valist -</SECTION> - -<SECTION> -<FILE>soup-logger</FILE> -<TITLE>SoupLogger</TITLE> -SoupLogger -SoupLoggerLogLevel -soup_logger_new -soup_logger_set_max_body_size -soup_logger_get_max_body_size -<SUBSECTION> -SoupLoggerFilter -soup_logger_set_request_filter -soup_logger_set_response_filter -<SUBSECTION> -SoupLoggerPrinter -soup_logger_set_printer -<SUBSECTION Standard> -SoupLoggerClass -soup_logger_get_type -SOUP_IS_LOGGER -SOUP_IS_LOGGER_CLASS -SOUP_LOGGER -SOUP_LOGGER_CLASS -SOUP_LOGGER_GET_CLASS -SOUP_TYPE_LOGGER -</SECTION> - -<SECTION> -<FILE>soup-cookie</FILE> -<TITLE>SoupCookie</TITLE> -SoupCookie -soup_cookie_new -soup_cookie_parse -soup_cookie_copy -soup_cookie_free -<SUBSECTION> -soup_cookie_set_name -soup_cookie_get_name -soup_cookie_set_value -soup_cookie_get_value -soup_cookie_set_domain -soup_cookie_get_domain -soup_cookie_set_path -soup_cookie_get_path -soup_cookie_set_max_age -SOUP_COOKIE_MAX_AGE_ONE_HOUR -SOUP_COOKIE_MAX_AGE_ONE_DAY -SOUP_COOKIE_MAX_AGE_ONE_WEEK -SOUP_COOKIE_MAX_AGE_ONE_YEAR -soup_cookie_set_expires -soup_cookie_get_expires -soup_cookie_set_secure -soup_cookie_get_secure -soup_cookie_set_http_only -soup_cookie_get_http_only -<SUBSECTION> -SoupSameSitePolicy -soup_cookie_set_same_site_policy -soup_cookie_get_same_site_policy -<SUBSECTION> -soup_cookie_applies_to_uri -soup_cookie_domain_matches -<SUBSECTION> -soup_cookie_to_cookie_header -soup_cookie_to_set_cookie_header -<SUBSECTION> -soup_cookies_from_request -soup_cookies_from_response -soup_cookies_to_request -soup_cookies_to_response -soup_cookies_to_cookie_header -soup_cookies_free -<SUBSECTION Standard> -SOUP_TYPE_COOKIE -soup_cookie_get_type -<SUBSECTION Private> -soup_cookie_equal -</SECTION> - -<SECTION> -<FILE>soup-cookie-jar</FILE> -<TITLE>SoupCookieJar</TITLE> -SoupCookieJar -soup_cookie_jar_new -soup_cookie_jar_get_cookies -soup_cookie_jar_get_cookie_list -soup_cookie_jar_get_cookie_list_with_same_site_info -soup_cookie_jar_set_cookie -soup_cookie_jar_set_cookie_with_first_party -<SUBSECTION> -soup_cookie_jar_add_cookie -soup_cookie_jar_add_cookie_with_first_party -soup_cookie_jar_add_cookie_full -soup_cookie_jar_delete_cookie -soup_cookie_jar_all_cookies -<SUBSECTION> -SoupCookieJarAcceptPolicy -soup_cookie_jar_get_accept_policy -soup_cookie_jar_set_accept_policy -<SUBSECTION> -soup_cookie_jar_is_persistent -<SUBSECTION Standard> -SoupCookieJarClass -SOUP_COOKIE_JAR -SOUP_COOKIE_JAR_CLASS -SOUP_COOKIE_JAR_GET_CLASS -SOUP_IS_COOKIE_JAR -SOUP_IS_COOKIE_JAR_CLASS -SOUP_TYPE_COOKIE_JAR -soup_cookie_jar_get_type -</SECTION> - -<SECTION> -<FILE>soup-multipart</FILE> -<TITLE>SoupMultipart</TITLE> -SoupMultipart -soup_multipart_new -soup_multipart_new_from_message -soup_multipart_free -<SUBSECTION> -soup_multipart_get_length -soup_multipart_get_part -soup_multipart_append_part -soup_multipart_append_form_string -soup_multipart_append_form_file -soup_multipart_to_message -<SUBSECTION Standard> -SOUP_TYPE_MULTIPART -soup_multipart_get_type -</SECTION> - -<SECTION> -<FILE>soup-multipart-input-stream</FILE> -<TITLE>SoupMultipartInputStream</TITLE> -SoupMultipartInputStream -soup_multipart_input_stream_new -<SUBSECTION> -soup_multipart_input_stream_get_headers -soup_multipart_input_stream_next_part -soup_multipart_input_stream_next_part_async -soup_multipart_input_stream_next_part_finish -<SUBSECTION Standard> -SOUP_IS_MULTIPART_INPUT_STREAM -SOUP_IS_MULTIPART_INPUT_STREAM_CLASS -SOUP_MULTIPART_INPUT_STREAM -SOUP_MULTIPART_INPUT_STREAM_CLASS -SOUP_MULTIPART_INPUT_STREAM_GET_CLASS -SOUP_TYPE_MULTIPART_INPUT_STREAM -soup_multipart_input_stream_get_type -SoupMultipartInputStreamPrivate -</SECTION> - -<SECTION> -<FILE>soup-cookie-jar-text</FILE> -<TITLE>SoupCookieJarText</TITLE> -SoupCookieJarText -soup_cookie_jar_text_new -<SUBSECTION Standard> -SoupCookieJarTextClass -SOUP_COOKIE_JAR_TEXT -SOUP_COOKIE_JAR_TEXT_CLASS -SOUP_COOKIE_JAR_TEXT_GET_CLASS -SOUP_TYPE_COOKIE_JAR_TEXT -SOUP_IS_COOKIE_JAR_TEXT -SOUP_IS_COOKIE_JAR_TEXT_CLASS -soup_cookie_jar_text_get_type -</SECTION> - -<SECTION> -<FILE>soup-cookie-jar-db</FILE> -<TITLE>SoupCookieJarDB</TITLE> -SoupCookieJarDB -soup_cookie_jar_db_new -<SUBSECTION Standard> -SoupCookieJarDBClass -SOUP_COOKIE_JAR_DB -SOUP_COOKIE_JAR_DB_CLASS -SOUP_COOKIE_JAR_DB_GET_CLASS -SOUP_TYPE_COOKIE_JAR_DB -SOUP_IS_COOKIE_JAR_DB -SOUP_IS_COOKIE_JAR_DB_CLASS -soup_cookie_jar_db_get_type -</SECTION> - -<SECTION> -<FILE>soup-content-sniffer</FILE> -<TITLE>SoupContentSniffer</TITLE> -SoupContentSniffer -soup_content_sniffer_new -soup_content_sniffer_sniff -<SUBSECTION Standard> -SOUP_CONTENT_SNIFFER -SOUP_CONTENT_SNIFFER_CLASS -SOUP_CONTENT_SNIFFER_GET_CLASS -SOUP_IS_CONTENT_SNIFFER -SOUP_IS_CONTENT_SNIFFER_CLASS -SOUP_TYPE_CONTENT_SNIFFER -SoupContentSnifferClass -SoupContentSnifferPrivate -soup_content_sniffer_get_type -</SECTION> - -<SECTION> -<FILE>soup-cache</FILE> -<TITLE>SoupCache</TITLE> -SoupCache -SoupCacheType -soup_cache_new -soup_cache_flush -soup_cache_clear -soup_cache_dump -soup_cache_load -soup_cache_get_max_size -soup_cache_set_max_size -<SUBSECTION Standard> -SOUP_TYPE_CACHE -SOUP_IS_CACHE -SOUP_IS_CACHE_CLASS -SOUP_CACHE -SOUP_CACHE_CLASS -SOUP_CACHE_GET_CLASS -SoupCacheClass -SoupCachePrivate -<SUBSECTION Private> -soup_cache_get_type -SoupCacheResponse -SoupCacheability -</SECTION> - -<SECTION> -<FILE>soup-content-decoder</FILE> -<TITLE>SoupContentDecoder</TITLE> -SoupContentDecoder -<SUBSECTION Standard> -SOUP_TYPE_CONTENT_DECODER -SOUP_IS_CONTENT_DECODER -SOUP_IS_CONTENT_DECODER_CLASS -SOUP_CONTENT_DECODER -SOUP_CONTENT_DECODER_CLASS -SOUP_CONTENT_DECODER_GET_CLASS -SoupContentDecoderClass -SoupContentDecoderPrivate -soup_content_decoder_get_type -</SECTION> - -<SECTION> -<FILE>soup-tld</FILE> -<TITLE>SoupTLD</TITLE> -<SUBSECTION> -soup_tld_get_base_domain -soup_tld_domain_is_public_suffix -<SUBSECTION> -SOUP_TLD_ERROR -SoupTLDError -<SUBSECTION Private> -soup_tld_error_quark -</SECTION> - -<SECTION> -<FILE>soup-version</FILE> -<TITLE>Version Information</TITLE> -soup_get_major_version -soup_get_minor_version -soup_get_micro_version -soup_check_version -<SUBSECTION> -SOUP_MAJOR_VERSION -SOUP_MINOR_VERSION -SOUP_MICRO_VERSION -SOUP_CHECK_VERSION -<SUBSECTION> -SOUP_VERSION_MIN_REQUIRED -SOUP_VERSION_MAX_ALLOWED -SOUP_VERSION_3_0 -<SUBSECTION Private> -SOUP_AVAILABLE_IN_ALL -SOUP_AVAILABLE_IN_3_0 -SOUP_DEPRECATED_IN_3_0 -SOUP_DEPRECATED_IN_3_0_FOR -SOUP_ENCODE_VERSION -SOUP_VAR -SOUP_VERSION_CUR_STABLE -SOUP_VERSION_PREV_STABLE -</SECTION> - -<SECTION> -<FILE>soup-websocket</FILE> -<TITLE>SoupWebsocket</TITLE> -soup_websocket_client_prepare_handshake -soup_websocket_client_verify_handshake -<SUBSECTION> -soup_websocket_server_check_handshake -soup_websocket_server_process_handshake -<SUBSECTION> -SoupWebsocketConnection -SoupWebsocketConnectionType -soup_websocket_connection_new -soup_websocket_connection_get_io_stream -soup_websocket_connection_get_connection_type -soup_websocket_connection_get_uri -soup_websocket_connection_get_origin -soup_websocket_connection_get_protocol -soup_websocket_connection_get_extensions -soup_websocket_connection_get_max_incoming_payload_size -soup_websocket_connection_set_max_incoming_payload_size -soup_websocket_connection_get_keepalive_interval -soup_websocket_connection_set_keepalive_interval -SoupWebsocketState -soup_websocket_connection_get_state -SoupWebsocketDataType -soup_websocket_connection_send_text -soup_websocket_connection_send_binary -soup_websocket_connection_send_message -SoupWebsocketCloseCode -soup_websocket_connection_close -soup_websocket_connection_get_close_code -soup_websocket_connection_get_close_data -<SUBSECTION> -SoupWebsocketError -SOUP_WEBSOCKET_ERROR -<SUBSECTION Standard> -SoupWebsocketConnectionClass -SoupWebsocketConnectionPrivate -SOUP_IS_WEBSOCKET_CONNECTION -SOUP_IS_WEBSOCKET_CONNECTION_CLASS -SOUP_TYPE_WEBSOCKET_CONNECTION -SOUP_WEBSOCKET_CONNECTION -SOUP_WEBSOCKET_CONNECTION_CLASS -SOUP_WEBSOCKET_CONNECTION_GET_CLASS -soup_websocket_close_code_get_type -soup_websocket_connection_get_type -soup_websocket_connection_type_get_type -soup_websocket_data_type_get_type -soup_websocket_error_quark -soup_websocket_error_get_type -soup_websocket_state_get_type -</SECTION> - -<SECTION> -<FILE>soup-websocket-extension</FILE> -<TITLE>SoupWebsocketExtension</TITLE> -SoupWebsocketExtension -soup_websocket_extension_configure -soup_websocket_extension_get_request_params -soup_websocket_extension_get_response_params -soup_websocket_extension_process_outgoing_message -soup_websocket_extension_process_incoming_message -<SUBSECTION> -SoupWebsocketExtensionClass -<SUBSECTION> -SoupWebsocketExtensionDeflate -SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE -<SUBSECTION> -SoupWebsocketExtensionManager -SOUP_TYPE_WEBSOCKET_EXTENSION_MANAGER -<SUBSECTION Standard> -soup_websocket_extension_get_type -SOUP_IS_WEBSOCKET_EXTENSION -SOUP_IS_WEBSOCKET_EXTENSION_CLASS -SOUP_TYPE_WEBSOCKET_EXTENSION -SOUP_WEBSOCKET_EXTENSION -SOUP_WEBSOCKET_EXTENSION_CLASS -SOUP_WEBSOCKET_EXTENSION_GET_CLASS -SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE -SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE_CLASS -SOUP_WEBSOCKET_EXTENSION_DEFLATE -SOUP_WEBSOCKET_EXTENSION_DEFLATE_CLASS -SOUP_WEBSOCKET_EXTENSION_DEFLATE_GET_CLASS -soup_websocket_extension_deflate_get_type -SoupWebsocketExtensionDeflateClass -SoupWebsocketExtensionManagerClass -soup_websocket_extension_manager_get_type -SOUP_IS_WEBSOCKET_EXTENSION_MANAGER -SOUP_IS_WEBSOCKET_EXTENSION_MANAGER_CLASS -SOUP_WEBSOCKET_EXTENSION_MANAGER -SOUP_WEBSOCKET_EXTENSION_MANAGER_CLASS -SOUP_WEBSOCKET_EXTENSION_MANAGER_GET_CLASS -</SECTION> - -<SECTION> -<FILE>soup-hsts-enforcer</FILE> -<TITLE>SoupHSTSEnforcer</TITLE> -SoupHSTSEnforcer -SoupHSTSEnforcerClass -soup_hsts_enforcer_new -soup_hsts_enforcer_is_persistent -soup_hsts_enforcer_has_valid_policy -soup_hsts_enforcer_set_policy -soup_hsts_enforcer_set_session_policy -soup_hsts_enforcer_get_domains -soup_hsts_enforcer_get_policies -<SUBSECTION Standard> -SOUP_HSTS_ENFORCER -SOUP_HSTS_ENFORCER_CLASS -SOUP_HSTS_ENFORCER_GET_CLASS -SOUP_TYPE_HSTS_ENFORCER -SOUP_IS_HSTS_ENFORCER -SOUP_IS_HSTS_ENFORCER_CLASS -soup_hsts_enforcer_get_type -</SECTION> - -<SECTION> -<FILE>soup-hsts-policy</FILE> -SoupHSTSPolicy -soup_hsts_policy_new -soup_hsts_policy_new_full -soup_hsts_policy_new_session_policy -soup_hsts_policy_new_from_response -soup_hsts_policy_copy -soup_hsts_policy_equal -soup_hsts_policy_free -soup_hsts_policy_get_domain -soup_hsts_policy_get_max_age -soup_hsts_policy_get_expires -soup_hsts_policy_is_expired -soup_hsts_policy_includes_subdomains -soup_hsts_policy_is_session_policy -SOUP_HSTS_POLICY_MAX_AGE_PAST -<SUBSECTION Standard> -SOUP_TYPE_HSTS_POLICY -soup_hsts_policy_get_type -</SECTION> - -<SECTION> -<FILE>soup-hsts-enforcer-db</FILE> -<TITLE>SoupHSTSEnforcerDB</TITLE> -SoupHSTSEnforcerDB -soup_hsts_enforcer_db_new -<SUBSECTION Standard> -SoupHSTSEnforcerDBClass -SOUP_HSTS_ENFORCER_DB -SOUP_HSTS_ENFORCER_DB_CLASS -SOUP_HSTS_ENFORCER_DB_GET_CLASS -SOUP_TYPE_HSTS_ENFORCER_DB -SOUP_IS_HSTS_ENFORCER_DB -SOUP_IS_HSTS_ENFORCER_DB_CLASS -soup_hsts_enforcer_db_get_type -</SECTION> - -<SECTION> -<FILE>soup-message-metrics</FILE> -<TITLE>SoupMessageMetrics</TITLE> -SoupMessageMetrics -soup_message_metrics_copy -soup_message_metrics_free -<SUBSECTION> -soup_message_metrics_get_fetch_start -soup_message_metrics_get_dns_start -soup_message_metrics_get_dns_end -soup_message_metrics_get_connect_start -soup_message_metrics_get_connect_end -soup_message_metrics_get_tls_start -soup_message_metrics_get_request_start -soup_message_metrics_get_response_start -soup_message_metrics_get_response_end -<SUBSECTION> -soup_message_metrics_get_request_header_bytes_sent -soup_message_metrics_get_request_body_size -soup_message_metrics_get_request_body_bytes_sent -soup_message_metrics_get_response_header_bytes_received -soup_message_metrics_get_response_body_size -soup_message_metrics_get_response_body_bytes_received -<SUBSECTION Standard> -SOUP_TYPE_MESSAGE_METRICS -soup_message_metrics_get_type -</SECTION>
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/docs/reference/migrating-from-libsoup-2.xml
Deleted
@@ -1,192 +0,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"?> -<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> -
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/docs/reference/server-howto.xml
Deleted
@@ -1,380 +0,0 @@ -<?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 /foo/bar/baz.html?a=1&b=2 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> <literal>(msg->response_headers, <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>
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-server-io.c
Deleted
@@ -1,964 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ -/* - * soup-server-io.c: HTTP message I/O - * - * Copyright (C) 2000-2003, Ximian, Inc. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <glib/gi18n-lib.h> - -#include "soup.h" -#include "soup-body-input-stream.h" -#include "soup-body-output-stream.h" -#include "soup-filter-input-stream.h" -#include "soup-server-message-private.h" -#include "soup-message-headers-private.h" -#include "soup-misc.h" -#include "soup-socket.h" - -struct _SoupServerMessageIOData { - SoupMessageIOData base; - - GIOStream *iostream; - GInputStream *istream; - GOutputStream *ostream; - - GBytes *write_chunk; - goffset write_body_offset; - - GSource *unpause_source; - - GMainContext *async_context; -}; - -#define RESPONSE_BLOCK_SIZE 8192 -#define HEADER_SIZE_LIMIT (64 * 1024) - -void -soup_server_message_io_data_free (SoupServerMessageIOData *io) -{ - if (!io) - return; - - g_clear_object (&io->iostream); - - soup_message_io_data_cleanup (&io->base); - - if (io->unpause_source) { - g_source_destroy (io->unpause_source); - g_source_unref (io->unpause_source); - io->unpause_source = NULL; - } - - g_clear_pointer (&io->async_context, g_main_context_unref); - g_clear_pointer (&io->write_chunk, g_bytes_unref); - - g_slice_free (SoupServerMessageIOData, io); -} - -void -soup_server_message_io_finished (SoupServerMessage *msg) -{ - SoupServerMessageIOData *io; - SoupMessageIOCompletionFn completion_cb; - gpointer completion_data; - SoupMessageIOCompletion completion; - - io = soup_server_message_get_io_data (msg); - if (!io) - return; - - completion_cb = io->base.completion_cb; - completion_data = io->base.completion_data; - - if ((io->base.read_state >= SOUP_MESSAGE_IO_STATE_FINISHING && - io->base.write_state >= SOUP_MESSAGE_IO_STATE_FINISHING)) - completion = SOUP_MESSAGE_IO_COMPLETE; - else - completion = SOUP_MESSAGE_IO_INTERRUPTED; - - g_object_ref (msg); - soup_server_message_set_io_data (msg, NULL); - if (completion_cb) - completion_cb (G_OBJECT (msg), completion, completion_data); - g_object_unref (msg); -} - -GIOStream * -soup_server_message_io_steal (SoupServerMessage *msg) -{ - SoupServerMessageIOData *io; - SoupMessageIOCompletionFn completion_cb; - gpointer completion_data; - GIOStream *iostream; - - io = soup_server_message_get_io_data (msg); - if (!io || !io->iostream) - return NULL; - - iostream = g_object_ref (io->iostream); - completion_cb = io->base.completion_cb; - completion_data = io->base.completion_data; - - g_object_ref (msg); - soup_server_message_set_io_data (msg, NULL); - if (completion_cb) - completion_cb (G_OBJECT (msg), SOUP_MESSAGE_IO_STOLEN, completion_data); - g_object_unref (msg); - - return iostream; -} - -static void -closed_async (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GOutputStream *body_ostream = G_OUTPUT_STREAM (source); - SoupServerMessage *msg = user_data; - SoupServerMessageIOData *io; - GCancellable *async_wait; - - io = soup_server_message_get_io_data (msg); - if (!io || !io->base.async_wait || io->base.body_ostream != body_ostream) { - g_object_unref (msg); - return; - } - - g_output_stream_close_finish (body_ostream, result, &io->base.async_error); - g_clear_object (&io->base.body_ostream); - - async_wait = io->base.async_wait; - io->base.async_wait = NULL; - g_cancellable_cancel (async_wait); - g_object_unref (async_wait); - - g_object_unref (msg); -} - -/* - * There are two request/response formats: the basic request/response, - * possibly with one or more unsolicited informational responses (such - * as the WebDAV "102 Processing" response): - * - * Client Server - * W:HEADERS / R:NOT_STARTED -> R:HEADERS / W:NOT_STARTED - * W:BODY / R:NOT_STARTED -> R:BODY / W:NOT_STARTED - * W:DONE / R:HEADERS (1xx) <- R:DONE / W:HEADERS (1xx) ... - * W:DONE / R:HEADERS <- R:DONE / W:HEADERS - * W:DONE / R:BODY <- R:DONE / W:BODY - * W:DONE / R:DONE R:DONE / W:DONE - * - * and the "Expect: 100-continue" request/response, with the client - * blocking halfway through its request, and then either continuing or - * aborting, depending on the server response: - * - * Client Server - * W:HEADERS / R:NOT_STARTED -> R:HEADERS / W:NOT_STARTED - * W:BLOCKING / R:HEADERS <- R:BLOCKING / W:HEADERS - * W:BODY / R:BLOCKING -> R:BODY / W:BLOCKING - * W:DONE / R:HEADERS <- R:DONE / W:HEADERS - * W:DONE / R:BODY <- R:DONE / W:BODY - * W:DONE / R:DONE R:DONE / W:DONE - */ - -static void -handle_partial_get (SoupServerMessage *msg) -{ - SoupRange *ranges; - int nranges; - GBytes *full_response; - guint status; - SoupMessageHeaders *request_headers; - SoupMessageHeaders *response_headers; - SoupMessageBody *response_body; - - request_headers = soup_server_message_get_request_headers (msg); - response_headers = soup_server_message_get_response_headers (msg); - response_body = soup_server_message_get_response_body (msg); - - /* Make sure the message is set up right for us to return a - * partial response; it has to be a GET, the status must be - * 200 OK (and in particular, NOT already 206 Partial - * Content), and the SoupServer must have already filled in - * the response body - */ - if (soup_server_message_get_method (msg) != SOUP_METHOD_GET || - soup_server_message_get_status (msg) != SOUP_STATUS_OK || - soup_message_headers_get_encoding (response_headers) != - SOUP_ENCODING_CONTENT_LENGTH || - response_body->length == 0 || - !soup_message_body_get_accumulate (response_body)) - return; - - /* Oh, and there has to have been a valid Range header on the - * request, of course. - */ - status = soup_message_headers_get_ranges_internal (request_headers, - response_body->length, - TRUE, - &ranges, &nranges); - if (status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) { - soup_server_message_set_status (msg, status, NULL); - soup_message_body_truncate (response_body); - return; - } else if (status != SOUP_STATUS_PARTIAL_CONTENT) - return; - - full_response = soup_message_body_flatten (response_body); - if (!full_response) { - soup_message_headers_free_ranges (request_headers, ranges); - return; - } - - soup_server_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT, NULL); - soup_message_body_truncate (response_body); - - if (nranges == 1) { - GBytes *range_buf; - - /* Single range, so just set Content-Range and fix the body. */ - - soup_message_headers_set_content_range (response_headers, - ranges0.start, - ranges0.end, - g_bytes_get_size (full_response)); - range_buf = g_bytes_new_from_bytes (full_response, - ranges0.start, - ranges0.end - ranges0.start + 1); - soup_message_body_append_bytes (response_body, range_buf); - g_bytes_unref (range_buf); - } else { - SoupMultipart *multipart; - SoupMessageHeaders *part_headers; - GBytes *part_body; - GBytes *body = NULL; - const char *content_type; - int i; - - /* Multiple ranges, so build a multipart/byteranges response - * to replace msg->response_body with. - */ - - multipart = soup_multipart_new ("multipart/byteranges"); - content_type = soup_message_headers_get_one_common (response_headers, SOUP_HEADER_CONTENT_TYPE); - for (i = 0; i < nranges; i++) { - part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); - if (content_type) { - soup_message_headers_append_common (part_headers, - SOUP_HEADER_CONTENT_TYPE, - content_type); - } - soup_message_headers_set_content_range (part_headers, - rangesi.start, - rangesi.end, - g_bytes_get_size (full_response)); - part_body = g_bytes_new_from_bytes (full_response, - rangesi.start, - rangesi.end - rangesi.start + 1); - soup_multipart_append_part (multipart, part_headers, - part_body); - soup_message_headers_unref (part_headers); - g_bytes_unref (part_body); - } - - soup_multipart_to_message (multipart, response_headers, &body); - soup_message_body_append_bytes (response_body, body); - g_bytes_unref (body); - soup_multipart_free (multipart); - } - - g_bytes_unref (full_response); - soup_message_headers_free_ranges (request_headers, ranges); -} - -static void -write_headers (SoupServerMessage *msg, - GString *headers, - SoupEncoding *encoding) -{ - SoupEncoding claimed_encoding; - SoupMessageHeadersIter iter; - const char *name, *value; - guint status_code; - const char *reason_phrase; - const char *method; - SoupMessageHeaders *response_headers; - - if (soup_server_message_get_status (msg) == 0) - soup_server_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL); - - handle_partial_get (msg); - - status_code = soup_server_message_get_status (msg); - reason_phrase = soup_server_message_get_reason_phrase (msg); - - g_string_append_printf (headers, "HTTP/1.%c %d %s\r\n", - soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0 ? '0' : '1', - status_code, reason_phrase); - - method = soup_server_message_get_method (msg); - response_headers = soup_server_message_get_response_headers (msg); - claimed_encoding = soup_message_headers_get_encoding (response_headers); - if ((method == SOUP_METHOD_HEAD || - status_code == SOUP_STATUS_NO_CONTENT || - status_code == SOUP_STATUS_NOT_MODIFIED || - SOUP_STATUS_IS_INFORMATIONAL (status_code)) || - (method == SOUP_METHOD_CONNECT && - SOUP_STATUS_IS_SUCCESSFUL (status_code))) - *encoding = SOUP_ENCODING_NONE; - else - *encoding = claimed_encoding; - - /* Per rfc 7230: - * A server MUST NOT send a Content-Length header field in any response - * with a status code of 1xx (Informational) or 204 (No Content). - */ - - if (status_code == SOUP_STATUS_NO_CONTENT || - SOUP_STATUS_IS_INFORMATIONAL (status_code)) { - soup_message_headers_remove (response_headers, "Content-Length"); - } else if (claimed_encoding == SOUP_ENCODING_CONTENT_LENGTH && - !soup_message_headers_get_content_length (response_headers)) { - SoupMessageBody *response_body; - - response_body = soup_server_message_get_response_body (msg); - soup_message_headers_set_content_length (response_headers, - response_body->length); - } - - soup_message_headers_iter_init (&iter, response_headers); - while (soup_message_headers_iter_next (&iter, &name, &value)) - g_string_append_printf (headers, "%s: %s\r\n", name, value); - g_string_append (headers, "\r\n"); -} - -/* Attempts to push forward the writing side of @msg's I/O. Returns - * %TRUE if it manages to make some progress, and it is likely that - * further progress can be made. Returns %FALSE if it has reached a - * stopping point of some sort (need input from the application, - * socket not writable, write is complete, etc). - */ -static gboolean -io_write (SoupServerMessage *msg, - GError **error) -{ - SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg); - SoupMessageIOData *io = &server_io->base; - GBytes *chunk; - gssize nwrote; - guint status_code; - - if (io->async_error) { - g_propagate_error (error, io->async_error); - io->async_error = NULL; - return FALSE; - } else if (io->async_wait) { - g_set_error_literal (error, G_IO_ERROR, - G_IO_ERROR_WOULD_BLOCK, - _("Operation would block")); - return FALSE; - } - - switch (io->write_state) { - case SOUP_MESSAGE_IO_STATE_HEADERS: - status_code = soup_server_message_get_status (msg); - if (io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING && status_code == 0) { - /* Client requested "Expect: 100-continue", and - * server did not set an error. - */ - soup_server_message_set_status (msg, SOUP_STATUS_CONTINUE, NULL); - } - - if (!io->write_buf->len) - write_headers (msg, io->write_buf, &io->write_encoding); - - while (io->written < io->write_buf->len) { - nwrote = g_pollable_stream_write (server_io->ostream, - io->write_buf->str + io->written, - io->write_buf->len - io->written, - FALSE, - NULL, error); - if (nwrote == -1) - return FALSE; - io->written += nwrote; - } - - io->written = 0; - g_string_truncate (io->write_buf, 0); - - status_code = soup_server_message_get_status (msg); - if (SOUP_STATUS_IS_INFORMATIONAL (status_code)) { - if (status_code == SOUP_STATUS_CONTINUE) { - /* Stop and wait for the body now */ - io->write_state = - SOUP_MESSAGE_IO_STATE_BLOCKING; - io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START; - } else { - /* We just wrote a 1xx response - * header, so stay in STATE_HEADERS. - * (The caller will pause us from the - * wrote_informational callback if he - * is not ready to send the final - * response.) - */ - } - - soup_server_message_wrote_informational (msg); - - /* If this was "101 Switching Protocols", then - * the server probably stole the connection... - */ - if (server_io != soup_server_message_get_io_data (msg)) - return FALSE; - - soup_server_message_cleanup_response (msg); - break; - } - - if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH) - io->write_length = soup_message_headers_get_content_length (soup_server_message_get_response_headers (msg)); - - io->write_state = SOUP_MESSAGE_IO_STATE_BODY_START; - /* If the client was waiting for a Continue - * but we sent something else, then they're - * now done writing. - */ - if (io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING) - io->read_state = SOUP_MESSAGE_IO_STATE_DONE; - - soup_server_message_wrote_headers (msg); - break; - - case SOUP_MESSAGE_IO_STATE_BODY_START: - io->body_ostream = soup_body_output_stream_new (server_io->ostream, - io->write_encoding, - io->write_length); - io->write_state = SOUP_MESSAGE_IO_STATE_BODY; - break; - - case SOUP_MESSAGE_IO_STATE_BODY: - if (!io->write_length && - io->write_encoding != SOUP_ENCODING_EOF && - io->write_encoding != SOUP_ENCODING_CHUNKED) { - io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; - break; - } - - if (!server_io->write_chunk) { - server_io->write_chunk = soup_message_body_get_chunk (soup_server_message_get_response_body (msg), - server_io->write_body_offset); - if (!server_io->write_chunk) { - soup_server_message_io_pause (msg); - return FALSE; - } - if (!g_bytes_get_size (server_io->write_chunk)) { - io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; - break; - } - } - - nwrote = g_pollable_stream_write (io->body_ostream, - (guchar*)g_bytes_get_data (server_io->write_chunk, NULL) + io->written, - g_bytes_get_size (server_io->write_chunk) - io->written, - FALSE, - NULL, error); - if (nwrote == -1) - return FALSE; - - chunk = g_bytes_new_from_bytes (server_io->write_chunk, io->written, nwrote); - io->written += nwrote; - if (io->write_length) - io->write_length -= nwrote; - - if (io->written == g_bytes_get_size (server_io->write_chunk)) - io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DATA; - - soup_server_message_wrote_body_data (msg, g_bytes_get_size (chunk)); - g_bytes_unref (chunk); - break; - - case SOUP_MESSAGE_IO_STATE_BODY_DATA: - io->written = 0; - if (g_bytes_get_size (server_io->write_chunk) == 0) { - io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; - break; - } - - soup_message_body_wrote_chunk (soup_server_message_get_response_body (msg), - server_io->write_chunk); - server_io->write_body_offset += g_bytes_get_size (server_io->write_chunk); - g_clear_pointer (&server_io->write_chunk, g_bytes_unref); - - io->write_state = SOUP_MESSAGE_IO_STATE_BODY; - soup_server_message_wrote_chunk (msg); - break; - - case SOUP_MESSAGE_IO_STATE_BODY_FLUSH: - if (io->body_ostream) { - if (io->write_encoding != SOUP_ENCODING_CHUNKED) { - if (!g_output_stream_close (io->body_ostream, NULL, error)) - return FALSE; - g_clear_object (&io->body_ostream); - } else { - io->async_wait = g_cancellable_new (); - g_main_context_push_thread_default (server_io->async_context); - g_output_stream_close_async (io->body_ostream, - G_PRIORITY_DEFAULT, NULL, - closed_async, g_object_ref (msg)); - g_main_context_pop_thread_default (server_io->async_context); - } - } - - io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DONE; - break; - - case SOUP_MESSAGE_IO_STATE_BODY_DONE: - io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING; - soup_server_message_wrote_body (msg); - break; - - case SOUP_MESSAGE_IO_STATE_FINISHING: - io->write_state = SOUP_MESSAGE_IO_STATE_DONE; - - break; - - default: - g_return_val_if_reached (FALSE); - } - - return TRUE; -} - -static GUri * -parse_connect_authority (const char *req_path) -{ - GUri *uri; - char *fake_uri; - - fake_uri = g_strdup_printf ("http://%s", req_path); - uri = g_uri_parse (fake_uri, SOUP_HTTP_URI_FLAGS, NULL); - g_free (fake_uri); - - if (!uri) - return NULL; - - if (g_uri_get_user (uri) || - g_uri_get_password (uri) || - g_uri_get_query (uri) || - g_uri_get_fragment (uri) || - !g_uri_get_host (uri) || - g_uri_get_port (uri) <= 0 || - strcmp (g_uri_get_path (uri), "/") != 0) { - g_uri_unref (uri); - return NULL; - } - - return uri; -} - -static guint -parse_headers (SoupServerMessage *msg, - char *headers, - guint headers_len, - SoupEncoding *encoding, - GError **error) -{ - char *req_method, *req_path, *url; - SoupHTTPVersion version; - SoupSocket *sock; - const char *req_host; - guint status; - GUri *uri; - SoupMessageHeaders *request_headers; - - request_headers = soup_server_message_get_request_headers (msg); - - status = soup_headers_parse_request (headers, headers_len, - request_headers, - &req_method, - &req_path, - &version); - if (!SOUP_STATUS_IS_SUCCESSFUL (status)) - return status; - - soup_server_message_set_method (msg, req_method); - soup_server_message_set_http_version (msg, version); - g_free (req_method); - - /* Handle request body encoding */ - *encoding = soup_message_headers_get_encoding (request_headers); - if (*encoding == SOUP_ENCODING_UNRECOGNIZED) { - if (soup_message_headers_get_list_common (request_headers, SOUP_HEADER_TRANSFER_ENCODING)) - return SOUP_STATUS_NOT_IMPLEMENTED; - else - return SOUP_STATUS_BAD_REQUEST; - } - - /* Generate correct context for request */ - req_host = soup_message_headers_get_one_common (request_headers, SOUP_HEADER_HOST); - if (req_host && strchr (req_host, '/')) { - g_free (req_path); - return SOUP_STATUS_BAD_REQUEST; - } - - sock = soup_server_message_get_soup_socket (msg); - - if (!strcmp (req_path, "*") && req_host) { - /* Eg, "OPTIONS * HTTP/1.1" */ - url = g_strdup_printf ("%s://%s/", - soup_socket_is_ssl (sock) ? "https" : "http", - req_host); - uri = g_uri_parse (url, SOUP_HTTP_URI_FLAGS, NULL); - soup_server_message_set_options_ping (msg, TRUE); - g_free (url); - } else if (soup_server_message_get_method (msg) == SOUP_METHOD_CONNECT) { - /* Authority */ - uri = parse_connect_authority (req_path); - } else if (*req_path != '/') { - /* Absolute URI */ - uri = g_uri_parse (req_path, SOUP_HTTP_URI_FLAGS, NULL); - } else if (req_host) { - url = g_strdup_printf ("%s://%s%s", - soup_socket_is_ssl (sock) ? "https" : "http", - req_host, req_path); - uri = g_uri_parse (url, SOUP_HTTP_URI_FLAGS, NULL); - g_free (url); - } else if (soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0) { - /* No Host header, no AbsoluteUri */ - GInetSocketAddress *addr = soup_socket_get_local_address (sock); - GInetAddress *inet_addr = g_inet_socket_address_get_address (addr); - char *local_ip = g_inet_address_to_string (inet_addr); - int port = g_inet_socket_address_get_port (addr); - if (port == 0) - port = -1; - - uri = g_uri_build (SOUP_HTTP_URI_FLAGS, - soup_socket_is_ssl (sock) ? "https" : "http", - NULL, local_ip, port, req_path, NULL, NULL); - g_free (local_ip); - } else - uri = NULL; - - g_free (req_path); - - if (!uri || !g_uri_get_host (uri)) { - if (uri) - g_uri_unref (uri); - return SOUP_STATUS_BAD_REQUEST; - } - - soup_server_message_set_uri (msg, uri); - g_uri_unref (uri); - - return SOUP_STATUS_OK; -} - -/* Attempts to push forward the reading side of @msg's I/O. Returns - * %TRUE if it manages to make some progress, and it is likely that - * further progress can be made. Returns %FALSE if it has reached a - * stopping point of some sort (need input from the application, - * socket not readable, read is complete, etc). - */ -static gboolean -io_read (SoupServerMessage *msg, - GError **error) -{ - SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg); - SoupMessageIOData *io = &server_io->base; - gssize nread; - guint status; - SoupMessageHeaders *request_headers; - - switch (io->read_state) { - case SOUP_MESSAGE_IO_STATE_HEADERS: - if (!soup_message_io_data_read_headers (io, SOUP_FILTER_INPUT_STREAM (server_io->istream), FALSE, NULL, NULL, error)) { - if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT)) - soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL); - return FALSE; - } - - status = parse_headers (msg, - (char *)io->read_header_buf->data, - io->read_header_buf->len, - &io->read_encoding, - error); - g_byte_array_set_size (io->read_header_buf, 0); - - request_headers = soup_server_message_get_request_headers (msg); - - if (status != SOUP_STATUS_OK) { - /* Either we couldn't parse the headers, or they - * indicated something that would mean we wouldn't - * be able to parse the body. (Eg, unknown - * Transfer-Encoding.). Skip the rest of the - * reading, and make sure the connection gets - * closed when we're done. - */ - soup_server_message_set_status (msg, status, NULL); - soup_message_headers_append_common (request_headers, SOUP_HEADER_CONNECTION, "close"); - io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; - break; - } - - if (soup_message_headers_get_expectations (request_headers) & SOUP_EXPECTATION_CONTINUE) { - /* We must return a status code and response - * headers to the client; either an error to - * be set by a got-headers handler below, or - * else %SOUP_STATUS_CONTINUE otherwise. - */ - io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; - io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING; - } else - io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START; - - if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) - io->read_length = soup_message_headers_get_content_length (request_headers); - else - io->read_length = -1; - - soup_server_message_got_headers (msg); - break; - - case SOUP_MESSAGE_IO_STATE_BODY_START: - if (!io->body_istream) { - io->body_istream = soup_body_input_stream_new (server_io->istream, - io->read_encoding, - io->read_length); - - } - - io->read_state = SOUP_MESSAGE_IO_STATE_BODY; - break; - - case SOUP_MESSAGE_IO_STATE_BODY: { - guchar bufRESPONSE_BLOCK_SIZE; - - nread = g_pollable_stream_read (io->body_istream, - buf, - RESPONSE_BLOCK_SIZE, - FALSE, - NULL, error); - if (nread > 0) { - SoupMessageBody *request_body; - - request_body = soup_server_message_get_request_body (msg); - if (request_body) { - GBytes *bytes = g_bytes_new (buf, nread); - soup_message_body_got_chunk (request_body, bytes); - soup_server_message_got_chunk (msg, bytes); - g_bytes_unref (bytes); - } - break; - } - - if (nread == -1) - return FALSE; - - /* else nread == 0 */ - io->read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE; - break; - } - - case SOUP_MESSAGE_IO_STATE_BODY_DONE: - io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; - soup_server_message_got_body (msg); - break; - - case SOUP_MESSAGE_IO_STATE_FINISHING: - io->read_state = SOUP_MESSAGE_IO_STATE_DONE; - io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; - break; - - default: - g_return_val_if_reached (FALSE); - } - - return TRUE; -} - -static gboolean -io_run_until (SoupServerMessage *msg, - SoupMessageIOState read_state, - SoupMessageIOState write_state, - GError **error) -{ - SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg); - SoupMessageIOData *io = &server_io->base; - gboolean progress = TRUE, done; - GError *my_error = NULL; - - if (!io) - return FALSE; - - g_object_ref (msg); - - while (progress && soup_server_message_get_io_data (msg) == server_io && !io->paused && !io->async_wait && - (io->read_state < read_state || io->write_state < write_state)) { - - if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->read_state)) - progress = io_read (msg, &my_error); - else if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->write_state)) - progress = io_write (msg, &my_error); - else - progress = FALSE; - } - - if (my_error) { - g_propagate_error (error, my_error); - g_object_unref (msg); - return FALSE; - } - - if (soup_server_message_get_io_data (msg) != server_io) { - g_object_unref (msg); - return FALSE; - } - - done = (io->read_state >= read_state && - io->write_state >= write_state); - - if (!done) { - g_set_error_literal (error, G_IO_ERROR, - G_IO_ERROR_WOULD_BLOCK, - _("Operation would block")); - g_object_unref (msg); - return FALSE; - } - - g_object_unref (msg); - return done; -} - -static void io_run (SoupServerMessage *msg); - -static gboolean -io_run_ready (SoupServerMessage *msg, - gpointer user_data) -{ - io_run (msg); - return FALSE; -} - -static void -io_run (SoupServerMessage *msg) -{ - SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg); - SoupMessageIOData *io = &server_io->base; - GError *error = NULL; - - if (io->io_source) { - g_source_destroy (io->io_source); - g_source_unref (io->io_source); - io->io_source = NULL; - } - - g_object_ref (msg); - if (io_run_until (msg, - SOUP_MESSAGE_IO_STATE_DONE, - SOUP_MESSAGE_IO_STATE_DONE, - &error)) { - soup_server_message_io_finished (msg); - } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { - g_clear_error (&error); - io->io_source = soup_message_io_data_get_source (io, G_OBJECT (msg), - server_io->istream, - server_io->ostream, - NULL, - (SoupMessageIOSourceFunc)io_run_ready, - NULL); - g_source_attach (io->io_source, server_io->async_context); - } else if (soup_server_message_get_io_data (msg) == server_io) { - soup_server_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, error ? error->message : NULL); - soup_server_message_io_finished (msg); - } - g_object_unref (msg); - g_clear_error (&error); -} - -void -soup_server_message_read_request (SoupServerMessage *msg, - SoupMessageIOCompletionFn completion_cb, - gpointer user_data) -{ - SoupServerMessageIOData *io; - SoupSocket *sock; - - io = g_slice_new0 (SoupServerMessageIOData); - io->base.completion_cb = completion_cb; - io->base.completion_data = user_data; - - sock = soup_server_message_get_soup_socket (msg); - io->iostream = g_object_ref (soup_socket_get_iostream (sock)); - io->istream = g_io_stream_get_input_stream (io->iostream); - io->ostream = g_io_stream_get_output_stream (io->iostream); - - io->base.read_header_buf = g_byte_array_new (); - io->base.write_buf = g_string_new (NULL); - - io->base.read_state = SOUP_MESSAGE_IO_STATE_HEADERS; - io->base.write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED; - - io->async_context = g_main_context_ref_thread_default (); - - soup_server_message_set_io_data (msg, io); - - io_run (msg); -} - -void -soup_server_message_io_pause (SoupServerMessage *msg) -{ - SoupServerMessageIOData *io = soup_server_message_get_io_data (msg); - - g_return_if_fail (io != NULL); - - if (io->unpause_source) { - g_source_destroy (io->unpause_source); - g_source_unref (io->unpause_source); - io->unpause_source = NULL; - } - - soup_message_io_data_pause (&io->base); -} - -static gboolean -io_unpause_internal (gpointer msg) -{ - SoupServerMessageIOData *io = soup_server_message_get_io_data (msg); - - g_return_val_if_fail (io != NULL, FALSE); - - g_clear_pointer (&io->unpause_source, g_source_unref); - soup_message_io_data_unpause (&io->base); - if (io->base.io_source) - return FALSE; - - io_run (msg); - return FALSE; -} - -void -soup_server_message_io_unpause (SoupServerMessage *msg) -{ - SoupServerMessageIOData *io = soup_server_message_get_io_data (msg); - - g_return_if_fail (io != NULL); - - if (!io->unpause_source) { - io->unpause_source = soup_add_completion_reffed (io->async_context, - io_unpause_internal, msg, NULL); - } -} - -gboolean -soup_server_message_is_io_paused (SoupServerMessage *msg) -{ - SoupServerMessageIOData *io = soup_server_message_get_io_data (msg); - - return io && io->base.paused; -}
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-socket.c
Deleted
@@ -1,806 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ -/* - * soup-socket.c: Socket networking code. - * - * Copyright (C) 2000-2003, Ximian, Inc. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> - -#include <glib/gi18n-lib.h> -#include <gio/gnetworking.h> - -#include "soup-socket.h" -#include "soup.h" -#include "soup-io-stream.h" - -/*<private> - * SECTION:soup-socket - * @short_description: A network socket - * - * #SoupSocket is libsoup's TCP socket type. While it is primarily - * intended for internal use, #SoupSocket<!-- -->s are exposed in the - * API in various places, and some of their methods (eg, - * soup_socket_get_remote_address()) may be useful to applications. - **/ - -enum { - DISCONNECTED, - NEW_CONNECTION, - ACCEPT_CERTIFICATE, - LAST_SIGNAL -}; - -static guint signalsLAST_SIGNAL = { 0 }; - -enum { - PROP_0, - - PROP_GSOCKET, - PROP_IOSTREAM, - PROP_LOCAL_ADDRESS, - PROP_REMOTE_ADDRESS, - PROP_REMOTE_CONNECTABLE, - PROP_IPV6_ONLY, - PROP_TLS_CERTIFICATE, - PROP_TLS_DATABASE, - PROP_TLS_AUTH_MODE, - - LAST_PROPERTY -}; - -static GParamSpec *propertiesLAST_PROPERTY = { NULL, }; - -struct _SoupSocket { - GObject parent_instance; -}; - -typedef struct { - GInetSocketAddress *local_addr, *remote_addr; - GSocketConnectable *remote_connectable; - GIOStream *conn, *iostream; - GSocket *gsock; - GInputStream *istream; - GOutputStream *ostream; - - guint ipv6_only:1; - guint ssl:1; - GTlsCertificate *tls_certificate; - GTlsDatabase *tls_database; - GTlsAuthenticationMode tls_auth_mode; - - GMainContext *async_context; - GSource *watch_src; -} SoupSocketPrivate; - -static void soup_socket_initable_interface_init (GInitableIface *initable_interface); - -G_DEFINE_FINAL_TYPE_WITH_CODE (SoupSocket, soup_socket, G_TYPE_OBJECT, - G_ADD_PRIVATE (SoupSocket) - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, - soup_socket_initable_interface_init)) - -static void finish_socket_setup (SoupSocket *sock); -static void finish_listener_setup (SoupSocket *sock); - -static void -soup_socket_init (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - priv->async_context = g_main_context_ref_thread_default (); -} - -static gboolean -soup_socket_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) -{ - SoupSocket *sock = SOUP_SOCKET (initable); - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - if (priv->conn) { - g_warn_if_fail (priv->gsock == NULL); - - finish_socket_setup (sock); - } - - if (priv->gsock != NULL) { - int listening; - - g_warn_if_fail (priv->local_addr == NULL); - g_warn_if_fail (priv->remote_addr == NULL); - - if (!g_socket_get_option (priv->gsock, - SOL_SOCKET, SO_ACCEPTCONN, - &listening, error)) { - g_prefix_error (error, _("Could not import existing socket: ")); - return FALSE; - } - - finish_socket_setup (sock); - if (listening) - finish_listener_setup (sock); - else if (!g_socket_is_connected (priv->gsock)) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - _("Can’t import unconnected socket")); - return FALSE; - } - } - - return TRUE; -} - -static void -disconnect_internal (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - g_clear_object (&priv->gsock); - if (priv->conn) { - g_io_stream_close (priv->conn, NULL, NULL); - g_signal_handlers_disconnect_by_data (priv->conn, sock); - g_clear_object (&priv->conn); - } -} - -static void -soup_socket_finalize (GObject *object) -{ - SoupSocket *sock = SOUP_SOCKET (object); - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - if (priv->conn) - disconnect_internal (SOUP_SOCKET (object)); - - g_clear_object (&priv->conn); - g_clear_object (&priv->iostream); - g_clear_object (&priv->istream); - g_clear_object (&priv->ostream); - - g_clear_object (&priv->local_addr); - g_clear_object (&priv->remote_addr); - g_clear_object (&priv->remote_connectable); - - g_clear_object (&priv->tls_certificate); - g_clear_object (&priv->tls_database); - - if (priv->watch_src) { - g_source_destroy (priv->watch_src); - g_source_unref (priv->watch_src); - } - g_clear_pointer (&priv->async_context, g_main_context_unref); - - G_OBJECT_CLASS (soup_socket_parent_class)->finalize (object); -} - -static void -finish_socket_setup (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - if (priv->gsock) { - if (!priv->conn) - priv->conn = (GIOStream *)g_socket_connection_factory_create_connection (priv->gsock); - - g_socket_set_option (priv->gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); - } - - if (!priv->conn) - return; - - if (!priv->iostream) - priv->iostream = soup_io_stream_new (priv->conn, FALSE); - if (!priv->istream) - priv->istream = g_object_ref (g_io_stream_get_input_stream (priv->iostream)); - if (!priv->ostream) - priv->ostream = g_object_ref (g_io_stream_get_output_stream (priv->iostream)); -} - -static void -soup_socket_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - SoupSocket *sock = SOUP_SOCKET (object); - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - switch (prop_id) { - case PROP_GSOCKET: - priv->gsock = g_value_dup_object (value); - break; - case PROP_IOSTREAM: - priv->conn = g_value_dup_object (value); - break; - case PROP_LOCAL_ADDRESS: - priv->local_addr = g_value_dup_object (value); - break; - case PROP_REMOTE_ADDRESS: - priv->remote_addr = g_value_dup_object (value); - break; - case PROP_REMOTE_CONNECTABLE: - priv->remote_connectable = g_value_dup_object (value); - break; - case PROP_IPV6_ONLY: - priv->ipv6_only = g_value_get_boolean (value); - break; - case PROP_TLS_CERTIFICATE: - priv->tls_certificate = g_value_dup_object (value); - break; - case PROP_TLS_DATABASE: - priv->tls_database = g_value_dup_object (value); - break; - case PROP_TLS_AUTH_MODE: - priv->tls_auth_mode = g_value_get_enum (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -soup_socket_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - SoupSocket *sock = SOUP_SOCKET (object); - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - switch (prop_id) { - case PROP_LOCAL_ADDRESS: - g_value_set_object (value, soup_socket_get_local_address (sock)); - break; - case PROP_REMOTE_ADDRESS: - g_value_set_object (value, soup_socket_get_remote_address (sock)); - break; - case PROP_REMOTE_CONNECTABLE: - g_value_set_object (value, priv->remote_connectable); - break; - case PROP_IPV6_ONLY: - g_value_set_boolean (value, priv->ipv6_only); - break; - case PROP_TLS_CERTIFICATE: - g_value_set_object (value, priv->tls_certificate); - break; - case PROP_TLS_DATABASE: - g_value_set_object (value, priv->tls_database); - break; - case PROP_TLS_AUTH_MODE: - g_value_set_enum (value, priv->tls_auth_mode); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -soup_socket_class_init (SoupSocketClass *socket_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (socket_class); - - /* virtual method override */ - object_class->finalize = soup_socket_finalize; - object_class->set_property = soup_socket_set_property; - object_class->get_property = soup_socket_get_property; - - /* signals */ - - /** - * SoupSocket::disconnected: - * @sock: the socket - * - * Emitted when the socket is disconnected, for whatever - * reason. - **/ - signalsDISCONNECTED = - g_signal_new ("disconnected", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * SoupSocket::new-connection: - * @sock: the socket - * @new: the new socket - * - * Emitted when a listening socket (set up with - * soup_socket_listen()) receives a new connection. - * - * You must ref the @new if you want to keep it; otherwise it - * will be destroyed after the signal is emitted. - **/ - signalsNEW_CONNECTION = - g_signal_new ("new_connection", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - SOUP_TYPE_SOCKET); - - signalsACCEPT_CERTIFICATE = - g_signal_new ("accept-certificate", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - g_signal_accumulator_true_handled, NULL, - NULL, - G_TYPE_BOOLEAN, 2, - G_TYPE_TLS_CERTIFICATE, - G_TYPE_TLS_CERTIFICATE_FLAGS); - - /* properties */ - propertiesPROP_GSOCKET = - g_param_spec_object ("gsocket", - "GSocket", - "The socket's underlying GSocket", - G_TYPE_SOCKET, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - propertiesPROP_IOSTREAM = - g_param_spec_object ("iostream", - "GIOStream", - "The socket's underlying GIOStream", - G_TYPE_IO_STREAM, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - propertiesPROP_LOCAL_ADDRESS = - g_param_spec_object ("local-address", - "Local address", - "Address of local end of socket", - G_TYPE_INET_SOCKET_ADDRESS, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - propertiesPROP_REMOTE_ADDRESS = - g_param_spec_object ("remote-address", - "Remote address", - "Address of remote end of socket", - G_TYPE_SOCKET_ADDRESS, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - propertiesPROP_REMOTE_CONNECTABLE = - g_param_spec_object ("remote-connectable", - "Remote address", - "Address to connect to", - G_TYPE_SOCKET_CONNECTABLE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - propertiesPROP_IPV6_ONLY = - g_param_spec_boolean ("ipv6-only", - "IPv6 only", - "IPv6 only", - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - propertiesPROP_TLS_CERTIFICATE = - g_param_spec_object ("tls-certificate", - "TLS Certificate", - "The server TLS certificate", - G_TYPE_TLS_CERTIFICATE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - propertiesPROP_TLS_DATABASE = - g_param_spec_object ("tls-database", - "TLS Database", - "The server TLS database", - G_TYPE_TLS_DATABASE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - propertiesPROP_TLS_AUTH_MODE = - g_param_spec_enum ("tls-auth-mode", - "TLS Authentication Mode", - "The server TLS authentication mode", - G_TYPE_TLS_AUTHENTICATION_MODE, - G_TLS_AUTHENTICATION_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, LAST_PROPERTY, properties); -} - -static void -soup_socket_initable_interface_init (GInitableIface *initable_interface) -{ - initable_interface->init = soup_socket_initable_init; -} - - -/** - * soup_socket_new: - * @optname1: name of first property to set (or %NULL) - * @...: value of @optname1, followed by additional property/value pairs - * - * Creates a new (disconnected) socket - * - * Returns: the new socket - **/ -SoupSocket * -soup_socket_new (const char *optname1, ...) -{ - SoupSocket *sock; - va_list ap; - - va_start (ap, optname1); - sock = (SoupSocket *)g_object_new_valist (SOUP_TYPE_SOCKET, - optname1, ap); - va_end (ap); - - return sock; -} - -GSocket * -soup_socket_get_gsocket (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - - priv = soup_socket_get_instance_private (sock); - - return priv->gsock; -} - -GSocket * -soup_socket_steal_gsocket (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - GSocket *gsock; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - priv = soup_socket_get_instance_private (sock); - - gsock = g_steal_pointer (&priv->gsock); - g_clear_object (&priv->conn); - g_clear_object (&priv->iostream); - - return gsock; -} - -GIOStream * -soup_socket_get_iostream (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - - priv = soup_socket_get_instance_private (sock); - - return priv->iostream; -} - -static gboolean -tls_connection_accept_certificate (SoupSocket *sock, - GTlsCertificate *tls_certificate, - GTlsCertificateFlags tls_errors) -{ - gboolean accept = FALSE; - - g_signal_emit (sock, signalsACCEPT_CERTIFICATE, 0, - tls_certificate, tls_errors, &accept); - return accept; -} - -static gboolean -soup_socket_setup_ssl (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - GTlsBackend *backend = g_tls_backend_get_default (); - GTlsServerConnection *conn; - - if (G_IS_TLS_CONNECTION (priv->conn)) - return TRUE; - - priv->ssl = TRUE; - - conn = g_initable_new (g_tls_backend_get_server_connection_type (backend), - NULL, NULL, - "base-io-stream", priv->conn, - "certificate", priv->tls_certificate, - "database", priv->tls_database, - "authentication-mode", priv->tls_auth_mode, - "require-close-notify", FALSE, - NULL); - if (!conn) - return FALSE; - - g_object_unref (priv->conn); - priv->conn = G_IO_STREAM (conn); - - g_signal_connect_object (priv->conn, "accept-certificate", - G_CALLBACK (tls_connection_accept_certificate), - sock, G_CONNECT_SWAPPED); - - g_clear_object (&priv->istream); - g_clear_object (&priv->ostream); - g_clear_object (&priv->iostream); - priv->iostream = soup_io_stream_new (priv->conn, FALSE); - priv->istream = g_object_ref (g_io_stream_get_input_stream (priv->iostream)); - priv->ostream = g_object_ref (g_io_stream_get_output_stream (priv->iostream)); - - return TRUE; -} - -static gboolean -listen_watch (GObject *pollable, gpointer data) -{ - SoupSocket *sock = data, *new; - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock), *new_priv; - GSocket *new_gsock; - - new_gsock = g_socket_accept (priv->gsock, NULL, NULL); - if (!new_gsock) - return FALSE; - - new = g_object_new (SOUP_TYPE_SOCKET, NULL); - new_priv = soup_socket_get_instance_private (new); - new_priv->gsock = new_gsock; - new_priv->async_context = g_main_context_ref (priv->async_context); - new_priv->ssl = priv->ssl; - if (priv->tls_certificate) - new_priv->tls_certificate = g_object_ref (priv->tls_certificate); - if (priv->tls_database) - new_priv->tls_database = g_object_ref (priv->tls_database); - new_priv->tls_auth_mode = priv->tls_auth_mode; - finish_socket_setup (new); - - if (new_priv->tls_certificate) { - if (!soup_socket_setup_ssl (new)) { - g_object_unref (new); - return TRUE; - } - } - - g_signal_emit (sock, signalsNEW_CONNECTION, 0, new); - g_object_unref (new); - - return TRUE; -} - -static void -finish_listener_setup (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - priv->watch_src = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (priv->istream), NULL); - g_source_set_callback (priv->watch_src, (GSourceFunc)listen_watch, sock, NULL); - g_source_attach (priv->watch_src, priv->async_context); -} - -/** - * soup_socket_listen: - * @sock: a server #SoupSocket (which must not already be connected or listening) - * @error: error pointer - * - * Makes @sock start listening on its local address. When connections - * come in, @sock will emit #SoupSocket::new_connection. - * - * Returns: whether or not @sock is now listening. - **/ -gboolean -soup_socket_listen (SoupSocket *sock, - GError **error) - -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE); - priv = soup_socket_get_instance_private (sock); - g_return_val_if_fail (priv->gsock == NULL, FALSE); - g_return_val_if_fail (priv->local_addr != NULL, FALSE); - - /* @local_addr may have its port set to 0. So we intentionally - * don't store it in priv->local_addr, so that if the - * caller calls soup_socket_get_local_address() later, we'll - * have to make a new addr by calling getsockname(), which - * will have the right port number. - */ - g_return_val_if_fail (priv->local_addr != NULL, FALSE); - - priv->gsock = g_socket_new (g_socket_address_get_family (G_SOCKET_ADDRESS (priv->local_addr)), - G_SOCKET_TYPE_STREAM, - G_SOCKET_PROTOCOL_DEFAULT, - error); - if (!priv->gsock) - goto cant_listen; - finish_socket_setup (sock); - - if (priv->ipv6_only) { - GError *error = NULL; - g_socket_set_option (priv->gsock, IPPROTO_IPV6, IPV6_V6ONLY, TRUE, &error); - if (error) { - g_warning ("Failed to set IPv6 only on socket: %s", error->message); - g_error_free (error); - } - } - - /* Bind */ - if (!g_socket_bind (priv->gsock, G_SOCKET_ADDRESS (priv->local_addr), TRUE, error)) - goto cant_listen; - /* Force local_addr to be re-resolved now */ - g_clear_object (&priv->local_addr); - - /* Listen */ - if (!g_socket_listen (priv->gsock, error)) - goto cant_listen; - finish_listener_setup (sock); - return TRUE; - - cant_listen: - if (priv->conn) - disconnect_internal (sock); - - return FALSE; -} - -/** - * soup_socket_is_ssl: - * @sock: a #SoupSocket - * - * Tests if @sock is doing (or has attempted to do) SSL. - * - * Returns: %TRUE if @sock has SSL credentials set - **/ -gboolean -soup_socket_is_ssl (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - return priv->ssl || priv->tls_certificate; -} - -/** - * soup_socket_disconnect: - * @sock: a #SoupSocket - * - * Disconnects @sock. Any further read or write attempts on it will - * fail. - **/ -void -soup_socket_disconnect (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_if_fail (SOUP_IS_SOCKET (sock)); - priv = soup_socket_get_instance_private (sock); - - if (!priv->conn) - return; - - disconnect_internal (sock); - - /* Keep ref around signals in case the object is unreferenced - * in a handler - */ - g_object_ref (sock); - - /* FIXME: can't disconnect until all data is read */ - - /* Then let everyone know we're disconnected */ - g_signal_emit (sock, signalsDISCONNECTED, 0); - - g_object_unref (sock); -} - -/** - * soup_socket_is_connected: - * @sock: a #SoupSocket - * - * Tests if @sock is connected to another host - * - * Returns: %TRUE or %FALSE. - **/ -gboolean -soup_socket_is_connected (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE); - priv = soup_socket_get_instance_private (sock); - - return priv->conn && !g_io_stream_is_closed (priv->conn); -} - -/** - * soup_socket_get_local_address: - * @sock: a #SoupSocket - * - * Returns the #GInetSocketAddress corresponding to the local end of @sock. - * - * Calling this method on an unconnected socket is considered to be - * an error, and produces undefined results. - * - * Returns: (transfer none): the #GInetSocketAddress - **/ -GInetSocketAddress * -soup_socket_get_local_address (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - priv = soup_socket_get_instance_private (sock); - - if (!priv->local_addr) { - GError *error = NULL; - - if (priv->gsock == NULL) { - g_warning ("%s: socket not connected", G_STRLOC); - return NULL; - } - - priv->local_addr = G_INET_SOCKET_ADDRESS (g_socket_get_local_address (priv->gsock, &error)); - if (priv->local_addr == NULL) { - g_warning ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - return NULL; - } - } - - return priv->local_addr; -} - -/** - * soup_socket_get_remote_address: - * @sock: a #SoupSocket - * - * Returns the #GInetSocketAddress corresponding to the remote end of @sock. - * - * Calling this method on an unconnected socket is considered to be - * an error, and produces undefined results. - * - * Returns: (transfer none): the #GInetSocketAddress - **/ -GInetSocketAddress * -soup_socket_get_remote_address (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - priv = soup_socket_get_instance_private (sock); - - if (!priv->remote_addr) { - GError *error = NULL; - - // We may be conencting to a socket address rather than a network address - if (G_IS_INET_SOCKET_ADDRESS (priv->remote_connectable)) { - priv->remote_addr = g_object_ref (G_INET_SOCKET_ADDRESS (priv->remote_connectable)); - return priv->remote_addr; - } - - if (priv->gsock == NULL) { - g_warning ("%s: socket not connected", G_STRLOC); - return NULL; - } - - priv->remote_addr = G_INET_SOCKET_ADDRESS (g_socket_get_remote_address (priv->gsock, &error)); - if (priv->remote_addr == NULL) { - g_warning ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - return NULL; - } - } - - return priv->remote_addr; -} - -GIOStream * -soup_socket_get_connection (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - return priv->conn; -}
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-socket.h
Deleted
@@ -1,33 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2000-2003, Ximian, Inc. - */ - -#pragma once - -#include "soup-types.h" - -G_BEGIN_DECLS - -#define SOUP_TYPE_SOCKET (soup_socket_get_type ()) -G_DECLARE_FINAL_TYPE (SoupSocket, soup_socket, SOUP, SOCKET, GObject) - -SoupSocket *soup_socket_new (const char *optname1, - ...) G_GNUC_NULL_TERMINATED; - -gboolean soup_socket_listen (SoupSocket *sock, - GError **error); - -gboolean soup_socket_is_ssl (SoupSocket *sock); - -void soup_socket_disconnect (SoupSocket *sock); -gboolean soup_socket_is_connected (SoupSocket *sock); -GIOStream *soup_socket_get_connection (SoupSocket *sock); -GSocket *soup_socket_get_gsocket (SoupSocket *sock); -GSocket *soup_socket_steal_gsocket (SoupSocket *sock); -GIOStream *soup_socket_get_iostream (SoupSocket *sock); - -GInetSocketAddress *soup_socket_get_local_address (SoupSocket *sock); -GInetSocketAddress *soup_socket_get_remote_address (SoupSocket *sock); - -G_END_DECLS
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/http2-server.py
Deleted
@@ -1,126 +0,0 @@ -#!/usr/bin/env python3 - -import asyncio -from functools import wraps -import os -from secrets import compare_digest -import sys -from urllib.parse import urlparse - -from quart import ( - request, - make_response, - Quart, -) - -app = Quart(__name__) - -timer_handle = None -loop = None - -def set_timeout(): - global timer_handle - - # ASAN is very slow, just don't have a timer. - if 'ASAN_OPTIONS' in os.environ: - return - - if timer_handle: - timer_handle.cancel() - - # This timeout just prevents a zombie process from - # running even if a test crashes. - timer_handle = loop.call_later(20, lambda: sys.exit(0)) - - -@app.route('/') -async def index(): - set_timeout() - return 'Hello world' - -@app.route('/slow') -async def slow(): - set_timeout() - await asyncio.sleep(1) - return 'Hello world' - -@app.route('/timeout') -async def timeout(): - set_timeout() - await asyncio.sleep(4) - return 'Hello world' - -@app.route('/no-content') -async def no_content(): - set_timeout() - return await make_response('', 204) - -@app.route('/large') -async def large(): - set_timeout() - - async def generate_data(): - # Send increasing letters just to aid debugging - letter = ord('A') - bytes_pending = 1024 * 24 - while bytes_pending: - await asyncio.sleep(0.1) - bytes_pending -= 1024 - string = chr(letter) * 1024 - letter += 1 - yield bytes(string, 'UTF-8') - yield b'\0' - - return generate_data() - -@app.route('/echo_query') -async def echo_query(): - set_timeout() - url = urlparse(request.url) - return url.query - -@app.route('/echo_post', methods='POST') -async def echo_post(): - set_timeout() - data = await request.get_data() - return data - -@app.route('/auth') -async def auth(): - set_timeout() - auth = request.authorization - - if ( - auth is not None and - auth.type == "basic" and - auth.username == 'username' and - compare_digest(auth.password, 'password') - ): - return 'Authenticated' - - response = await make_response('Authentication Required') - response.status_code = 401 - response.headers'WWW-Authenticate' = 'Basic' - return response - -has_been_misdirected = False - -@app.route('/misdirected_request') -async def misdirected_request(): - set_timeout() - global has_been_misdirected - - if not has_been_misdirected: - has_been_misdirected = True - response = await make_response('', 421) - return response - - return 'Success!' - -if __name__ == '__main__': - loop = asyncio.get_event_loop() - set_timeout() - - app.run(use_reloader=False, loop=loop, - certfile='test-cert.pem', - keyfile='test-key.pem')
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/socket-test.c
Deleted
@@ -1,274 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ -/* - * Copyright 2007-2012 Red Hat, Inc. - * Copyright 2012 Nokia Corporation - */ - -#include "test-utils.h" -#include "libsoup/server/soup-socket.h" - -#include <fcntl.h> -#include <gio/gnetworking.h> - -#ifdef G_OS_WIN32 -#include <io.h> -#endif - -static void -assert_host_equals (GInetSocketAddress *addr, const char *host) -{ - char *addr_host = g_inet_address_to_string (g_inet_socket_address_get_address (addr)); - g_assert_cmpstr (addr_host, ==, host); - g_free (addr_host); -} - -static void -do_unconnected_socket_test (void) -{ - GInetSocketAddress *addr; - GSocketAddress *localhost; - SoupSocket *sock; - GSocketClient *client; - GSocketConnectable *remote_connectable; - GSocketConnection *conn; - GSocket *client_socket; - guint res; - - g_test_bug ("673083"); - - localhost = g_inet_socket_address_new_from_string ("127.0.0.1", 0); - - sock = soup_socket_new ("local-address", localhost, - NULL); - g_assert_true (sock != NULL); - - addr = soup_socket_get_local_address (sock); - g_assert_true (addr != NULL); - assert_host_equals (addr, "127.0.0.1"); - g_assert_cmpuint (g_inet_socket_address_get_port (addr), ==, 0); - - /* fails with ENOTCONN */ - g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, - "*socket not connected*"); - addr = soup_socket_get_remote_address (sock); - g_test_assert_expected_messages (); - g_assert_null (addr); - - res = soup_socket_listen (sock, NULL); - g_assert_true (res); - - addr = soup_socket_get_local_address (sock); - g_assert_true (addr != NULL); - assert_host_equals (addr, "127.0.0.1"); - g_assert_cmpuint (g_inet_socket_address_get_port (addr), >, 0); - - client = g_socket_client_new (); - remote_connectable = G_SOCKET_CONNECTABLE (soup_socket_get_local_address (sock)); - conn = g_socket_client_connect (client, remote_connectable, NULL, NULL); - g_assert_true (conn != NULL); - client_socket = g_socket_connection_get_socket (conn); - g_assert_true (client_socket != NULL); - addr = G_INET_SOCKET_ADDRESS (g_socket_get_local_address (client_socket, NULL)); - g_assert_true (addr != NULL); - g_object_unref (addr); - addr = G_INET_SOCKET_ADDRESS (g_socket_get_remote_address (client_socket, NULL)); - g_assert_true (addr != NULL); - assert_host_equals (addr, "127.0.0.1"); - g_assert_cmpuint (g_inet_socket_address_get_port (addr), >, 0); - g_object_unref (addr); - g_object_unref (conn); - g_object_unref (client); - - client = g_socket_client_new (); - remote_connectable = G_SOCKET_CONNECTABLE (soup_socket_get_local_address (sock)); - /* save it for later */ - - /* listening socket fails with ENOTCONN */ - g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, - /* We can't check the error message since it comes from - * libc and is locale-dependent. - */ - "*"); - addr = soup_socket_get_remote_address (sock); - g_test_assert_expected_messages (); - g_assert_null (addr); - - soup_socket_disconnect (sock); - - g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, - /* This error message comes from soup-socket.c though */ - "*socket not connected*"); - addr = soup_socket_get_remote_address (sock); - g_test_assert_expected_messages (); - g_assert_null (addr); - - conn = g_socket_client_connect (client, remote_connectable, NULL, NULL); - g_assert_false (conn != NULL); - - g_object_unref (localhost); - g_object_unref (client); - g_object_unref (sock); -} - -static int -socket_get_fd (SoupSocket *socket) -{ - return g_socket_get_fd (soup_socket_get_gsocket (socket)); -} - -static void -do_socket_from_fd_server_test (void) -{ - GSocket *gsock; - SoupSocket *sock; - GInetSocketAddress *local; - GSocketAddress *gaddr; - GError *error = NULL; - - gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, - G_SOCKET_TYPE_STREAM, - G_SOCKET_PROTOCOL_DEFAULT, - &error); - g_assert_no_error (error); - - gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", 0); - g_socket_bind (gsock, gaddr, TRUE, &error); - g_object_unref (gaddr); - g_assert_no_error (error); - g_socket_listen (gsock, &error); - g_assert_no_error (error); - g_assert_false (g_socket_is_connected (gsock)); - - gaddr = g_socket_get_local_address (gsock, &error); - g_assert_no_error (error); - - sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error, - "gsocket", gsock, - NULL); - g_assert_no_error (error); - g_assert_nonnull (sock); - - g_object_get (G_OBJECT (sock), - "local-address", &local, - NULL); - g_assert_cmpint (socket_get_fd (sock), ==, g_socket_get_fd (gsock)); - g_assert_true (soup_socket_is_connected (sock)); - - assert_host_equals (local, "127.0.0.1"); - g_assert_cmpint (g_inet_socket_address_get_port (local), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr))); - g_object_unref (local); - g_object_unref (gaddr); - - g_object_unref (sock); - - /* Closing the SoupSocket should have closed the GSocket */ - g_assert_true (g_socket_is_closed (gsock)); - - g_object_unref (gsock); -} - -static void -do_socket_from_fd_bad_test (void) -{ - GSocket *gsock, *gsock2, *gsockcli; - SoupSocket *sock, *sock2; - GInetSocketAddress *local, *remote; - GSocketAddress *gaddr; - GError *error = NULL; - - /* Importing an unconnected socket gives an error */ - gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, - G_SOCKET_TYPE_STREAM, - G_SOCKET_PROTOCOL_DEFAULT, - &error); - g_assert_no_error (error); - g_assert_false (g_socket_is_connected (gsock)); - - sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error, - "gsocket", gsock, - NULL); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); - g_clear_error (&error); - g_assert_null (sock); - g_object_unref (gsock); - - gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, - G_SOCKET_TYPE_STREAM, - G_SOCKET_PROTOCOL_DEFAULT, - &error); - g_assert_no_error (error); - - gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", 0); - g_socket_bind (gsock, gaddr, TRUE, &error); - g_object_unref (gaddr); - g_assert_no_error (error); - g_socket_listen (gsock, &error); - g_assert_no_error (error); - g_assert_false (g_socket_is_connected (gsock)); - - gaddr = g_socket_get_local_address (gsock, &error); - g_assert_no_error (error); - - gsockcli = g_socket_new (G_SOCKET_FAMILY_IPV4, - G_SOCKET_TYPE_STREAM, - G_SOCKET_PROTOCOL_DEFAULT, - &error); - g_assert_no_error (error); - - g_socket_connect (gsockcli, gaddr, NULL, &error); - g_assert_no_error (error); - g_assert_true (g_socket_is_connected (gsockcli)); - - gsock2 = g_socket_accept (gsock, NULL, &error); - g_assert_no_error (error); - g_assert_nonnull (gsock2); - - sock2 = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error, - "gsocket", gsock2, - NULL); - g_assert_no_error (error); - g_assert_nonnull (sock2); - - g_object_get (G_OBJECT (sock2), - "local-address", &local, - "remote-address", &remote, - NULL); - g_assert_cmpint (socket_get_fd (sock2), ==, g_socket_get_fd (gsock2)); - g_assert_true (soup_socket_is_connected (sock2)); - - assert_host_equals (local, "127.0.0.1"); - g_assert_cmpint (g_inet_socket_address_get_port (local), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr))); - g_object_unref (gaddr); - - gaddr = g_socket_get_local_address (gsockcli, &error); - g_assert_no_error (error); - assert_host_equals (remote, "127.0.0.1"); - g_assert_cmpint (g_inet_socket_address_get_port (remote), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr))); - g_object_unref (gaddr); - - g_object_unref (local); - g_object_unref (remote); - - g_object_unref (sock2); - - g_object_unref (gsock); - g_object_unref (gsock2); - g_object_unref (gsockcli); -} - -int -main (int argc, char **argv) -{ - int ret; - - test_init (argc, argv, NULL); - - g_test_add_func ("/sockets/unconnected", do_unconnected_socket_test); - g_test_add_func ("/sockets/from-fd/server", do_socket_from_fd_server_test); - g_test_add_func ("/sockets/from-fd/bad", do_socket_from_fd_bad_test); - - ret = g_test_run (); - - test_cleanup (); - return ret; -}
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/.gitignore
Added
@@ -0,0 +1,3 @@ +/po/libsoup.pot +/*build/ +/html/
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/.gitlab-ci.yml -> _service:tar_scm:libsoup-3.2.2.tar.xz/.gitlab-ci.yml
Changed
@@ -1,4 +1,4 @@ -image: registry.gitlab.gnome.org/gnome/libsoup/master:v16 +image: registry.gitlab.gnome.org/gnome/libsoup/master:v17 stages: - build @@ -17,7 +17,7 @@ extends: .build script: - cp .gitlab-ci/lcovrc ~/.lcovrc - - meson _build -Db_coverage=true -Dauto_features=enabled + - meson _build -Db_coverage=true --auto-features=enabled - meson compile -C _build - meson test --no-suite autobahn-quick --no-suite autobahn -C _build --verbose - ninja -C _build coverage-html @@ -36,7 +36,7 @@ fedora-autobahn-quick: extends: .build script: - - meson _build -Dauto-features=enabled -Dautobahn=enabled + - meson _build --auto-features=enabled -Dautobahn=enabled - meson test -C _build --suite autobahn-quick --verbose artifacts: paths: @@ -46,7 +46,7 @@ fedora-scan: extends: .build script: - - meson _build -Dauto_features=enabled + - meson _build --auto-features=enabled - ninja -C _build scan-build - bash -c 'if -n "$(ls -A _build/meson-logs/scanbuild/)" ; then echo "Scan build log found, assuming defects exist"; exit 1; fi' artifacts: @@ -63,7 +63,7 @@ SOUP_TEST_NO_IPV6: 1 script: # Introspection doesn't work when linking to libasan, the NTLM tests fail most likely due to unsafe usage of setenv() - - meson _build -Dauto-features=enabled -Db_sanitize=address -Dintrospection=disabled -Dvapi=disabled -Dntlm=disabled + - meson _build --auto-features=enabled -Db_sanitize=address -Dintrospection=disabled -Dvapi=disabled -Dntlm=disabled -Ddocs=disabled - meson test --no-suite autobahn-quick --no-suite autobahn -C _build --verbose --timeout-multiplier=10 artifacts: when: on_failure @@ -76,7 +76,7 @@ # variables: # CC: clang # script: -# - meson _build -Dauto-features=enabled -Dfuzzing=enabled -Dintrospection=disabled -Dvapi=disabled +# - meson _build --auto-features=enabled -Dfuzzing=enabled -Dintrospection=disabled -Dvapi=disabled # - meson test -C _build --suite=fuzzing --timeout-multiplier=10 # artifacts: # when: on_failure @@ -86,29 +86,30 @@ 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:
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/.gitlab-ci/Dockerfile -> _service:tar_scm:libsoup-3.2.2.tar.xz/.gitlab-ci/Dockerfile
Changed
@@ -29,11 +29,11 @@ vala \ valgrind \ which \ - && dnf builddep -y glib2 vala \ + && dnf builddep -y glib2 nghttp2 vala \ && dnf clean all \ && python2.7 -m ensurepip \ && pip2.7 install virtualenv autobahntestsuite \ - && pip3 install quart gi-docgen + && pip3 install gi-docgen # We need glib 2.70 RUN git clone https://gitlab.gnome.org/GNOME/glib.git \ @@ -71,6 +71,16 @@ && popd \ && rm -rf libsoup +# Update libnghttp2 for do_invalid_header_rfc9113_received_test() +RUN git clone https://github.com/nghttp2/nghttp2.git \ + && pushd nghttp2 \ + && git checkout v1.50.0 \ + && autoreconf --install --symlink \ + && ./configure --prefix=/usr --disable-static --disable-examples \ + && make -j $(nproc) install \ + && popd \ + && rm -rf nghttp2 + ARG HOST_USER_ID=5555 ENV HOST_USER_ID ${HOST_USER_ID} RUN useradd -u $HOST_USER_ID -ms /bin/bash user
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/.gitlab-ci/run-docker.sh -> _service:tar_scm:libsoup-3.2.2.tar.xz/.gitlab-ci/run-docker.sh
Changed
@@ -2,7 +2,7 @@ set -e -TAG="registry.gitlab.gnome.org/gnome/libsoup/master:v16" +TAG="registry.gitlab.gnome.org/gnome/libsoup/master:v17" SUDO_CMD="sudo" if docker -v |& grep -q podman; then
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/NEWS -> _service:tar_scm:libsoup-3.2.2.tar.xz/NEWS
Changed
@@ -1,3 +1,53 @@ +Changes in libsoup from 3.2.1 to 3.2.2: + +* Various HTTP/2 Fixes: Carlos Garcia Campos + * Fix `content-sniffed` not being emitted for resources without content + * Fix leak of SoupServerConnection when stolen + +Changes in libsoup from 3.2.0 to 3.2.1: + +* When built against nghttp2 1.50.0+ be relaxed about header whitespace Carlos Garcia Campos +* Fix possible crash when cancelling an HTTP/2 message Carlos Garcia Campos +* Fix regresion where soup_server_message_get_socket() could return NULL Carlos Garcia Campos +* Fix minor memory leak Milan Crha + +Changes in libsoup from 3.1.4 to 3.2.0: + +Changes in libsoup from 3.1.3 to 3.1.4: + +* Numerous improvements to HTTP/2 reliablity Carlos Garcia Campos +* Fix `http` proxy authentication with default proxy resolver Carlos Garcia Campos +* Fix undefined ``ssize_t`` with MSVC Patrick Griffis + +Changes in libsoup from 3.1.2 to 3.1.3: + +* Fix compile error when `SOUP_VERSION_MAX_ALLOWED` is defined Patrick Griffis + +Changes in libsoup from 3.1.1 to 3.1.2: + +* Replace HTTP/2 tests using Quart with internal HTTP/2 server tests Carlos Garcia Campos +* Improve version macros including adding ability to define `SOUP_DISABLE_DEPRECATION_WARNINGS` Emmanuele Bassi + +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 +* Fix constructing SoupAuthNTLM objects Milan Crha +* Disable mutual negotiation in SoupAuthNegotiate Michael Catanzaro +* http2: Do not advertise the `h2` protocool for proxy connections Carlos Garcia Campos +* http2: Remove left-over headers when HTTP/1 redirects to HTTP/2 Carlos Garcia Campos +* http2: Handle HTTP_1_1_REQUIRED error Carlos Garcia Campos +* http2: Read request bodies synchronously for sync requests Carlos Garcia Campos +* http2: Properly handle server sending shut down GOAWAY Carlos Garcia Campos +* tests: Remove dependency on Apache's PHP module Carlos Garcia Campos +* tests: Depend upon Apache's http2 module Carlos Garcia Campos + Changes in libsoup from 3.0.5 to 3.0.6: * Misc HTTP/2 fixes Carlos Garcia Campos
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/docs/reference/build-howto.md
Added
@@ -0,0 +1,68 @@ +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> +```
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/docs/reference/client-advanced.md
Added
@@ -0,0 +1,156 @@ +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@SessionFeatures, 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_featurefunction 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; +} +```
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/docs/reference/client-basic.md
Added
@@ -0,0 +1,183 @@ +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) +{ + SoupSession *session = soup_session_new (); + SoupMessageHeaders *response_headers; + const char *content_type; + SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "https://upload.wikimedia.org/wikipedia/commons/5/5f/BBB-Bunny.png"); + GError *error = NULL; + GBytes *bytes = soup_session_send_and_read ( + session, + msg, + NULL, // Pass a GCancellable here if you want to cancel a download + &error); + + if (error) { + g_printerr ("Failed to download: %s\n", error->message); + g_error_free (error); + g_object_unref (msg); + g_object_unref (session); + return 1; + } + + response_headers = soup_message_get_response_headers (msg); + content_type = soup_message_headers_get_content_type (response_headers); + + // content_type = "image/png" + // bytes contains the raw data that can be used elsewhere + g_print ("Downloaded %zu bytes of type %s\n", + g_bytes_get_size (bytes), content_type); + + g_bytes_unref (bytes); + g_object_unref (msg); + g_object_unref (session); + return 0; +} +``` + +## 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) +{ + SoupSession *session = soup_session_new (); + SoupMessageHeaders *response_headers; + const char *content_type; + goffset content_length; + SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "https://upload.wikimedia.org/wikipedia/commons/5/5f/BBB-Bunny.png"); + GError *error = NULL; + GInputStream *in_stream = soup_session_send ( + session, + msg, + NULL, + &error); + + if (error) { + g_printerr ("Failed to download: %s\n", error->message); + g_error_free (error); + g_object_unref (msg); + g_object_unref (session); + return 1; + } + + GFile *output_file = g_file_new_tmp ("BBB-Bunny-XXXXXX.png"); + GOutputStream *out_stream = g_file_create (output_file, + G_FILE_CREATE_NONE, NULL, &error); + + if (error) { + g_printerr ("Failed to create file \"%s\": %s\n", + g_file_peek_path (output_file), error->message); + g_error_free (error); + g_object_unref (output_file); + g_object_unref (in_stream); + g_object_unref (msg); + g_object_unref (session); + return 1; + } + + response_headers = soup_message_get_response_headers (msg); + content_type = soup_message_headers_get_content_type (response_headers); + content_length = soup_message_headers_get_content_length (response_headers); + + // content_type = "image/png" + g_print ("Downloading %zu bytes of type %s to %s\n", + content_length, content_type, + g_file_peek_path (output_file)); + + g_output_stream_splice (out_stream, in_stream, + G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, + NULL, &error); + + if (error) { + g_print ("Download failed: %s\n", error->message); + g_error_free (error); + } else { + g_print ("Download completed\n"); + } + + g_object_unref (output_file); + g_object_unref (in_stream); + g_object_unref (out_stream); + g_object_unref (msg); + g_object_unref (session); + return error ? 1 : 0; +} +``` + +## 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) +{ + GMainLoop *loop = user_data; + GError *error = NULL; + GBytes *bytes = soup_session_send_and_read_finish (SOUP_SESSION (source), result, &error); + + // Usage here is the same as before + if (error) { + g_error_free (error); + } else { + g_bytes_unref (bytes); + } + + g_main_loop_quit (loop); +} + +int main (int argc, char **argv) +{ + SoupSession *session = soup_session_new (); + GMainLoop *loop = g_main_loop_new (NULL, FALSE); + SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "https://upload.wikimedia.org/wikipedia/commons/5/5f/BBB-Bunny.png"); + + soup_session_send_and_read_async ( + session, + msg, + G_PRIORITY_DEFAULT, + NULL, + on_load_callback, + loop); + + g_main_loop_run (loop); + + g_main_loop_unref (loop); + g_object_unref (msg); + g_object_unref (session); + return 0; +} +```
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/docs/reference/client-thread-safety.md
Added
@@ -0,0 +1,39 @@ +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. + +
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/docs/reference/client-tls.md
Added
@@ -0,0 +1,101 @@ +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) +{ + // Here you can inspect @certificate or compare it against a trusted one + // and you can see what is considered invalid by @tls_errors. + // Returning TRUE trusts it anyway. + 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, "accept-certificate", G_CALLBACK (accept_certificate_callback), NULL); + GInputStream *in_stream = soup_session_send (session, msg, NULL, NULL); + + if (in_stream) { + g_object_unref (in_stream); + } + + return 0; +} +``` + +## Setting a Custom CA + +```c +{ + GError *error = NULL; + // NOTE: This is blocking IO + GTlsDatabase *tls_db = g_tls_file_database_new ("/foo/ca.pem", &error); + + if (error) { + g_printerr ("Failed to load certificates: %s\n", error->message); + g_error_free (error); + return; + } + + SoupSession *session = soup_session_new_with_options ("tls-database", tls_db, NULL); + g_object_unref (tls_db); +} +``` + +## Using Client Certificates + +```c +static gboolean +on_request_certificate (SoupMessage *msg, GTlsClientConnection *conn, gpointer user_data) +{ + GTlsCertificate *client_cert = user_data; + + /* We immediately set this however you can set this later in an async function. */ + soup_message_set_tls_client_certificate (msg, client_cert); + + return TRUE; /* We handled the request */ +} + +int main (int argc, char **argv) +{ + GError *error = NULL; + GTlsCertificate *client_cert = g_tls_certificate_new_from_file ("/foo/cert.pem", &error); + + if (error) { + g_printerr ("Failed to load certificate: %s\n", error->message); + g_error_free (error); + return 1; + } + + SoupSession *session = soup_session_new (); + SoupMessage *msg = soup_message_new ("GET", "https://example.org"); + + /* We can set the certificate ahead of time if we already have one */ + // soup_message_set_tls_client_certificate (msg, client_cert) + + /* However we can also dynamically request one which is useful in + * applications that show a graphical prompt for example. */ + g_signal_connect (msg, "request-certificate", + G_CALLBACK (on_request_certificate), client_cert); + + // Send the message... + + g_object_unref (msg); + g_object_unref (session); + g_object_unref (client_cert); + return 0; +} +```
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/docs/reference/libsoup.toml.in
Added
@@ -0,0 +1,52 @@ +library +namespace = "Soup" +version = "@VERSION@" +browse_url = "https://gitlab.gnome.org/GNOME/libsoup" +repository_url = "https://gitlab.gnome.org/GNOME/libsoup.git" +docs_url = "https://libsoup.org/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"
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/docs/reference/meson.build -> _service:tar_scm:libsoup-3.2.2.tar.xz/docs/reference/meson.build
Changed
@@ -1,87 +1,69 @@ -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_sources0 , + 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_sources0, + + ) + endif +endif \ No newline at end of file
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/docs/reference/migrating-from-libsoup-2.md
Added
@@ -0,0 +1,163 @@ +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.
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/docs/reference/server-howto.md
Added
@@ -0,0 +1,247 @@ +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@AuthDomains 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.
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/docs/reference/urlmap.js
Added
@@ -0,0 +1,6 @@ +// 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/' , +
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/examples/simple-proxy.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/examples/simple-proxy.c
Changed
@@ -190,7 +190,7 @@ SOUP_MEMORY_COPY, error->message, strlen (error->message)); g_error_free (error); - soup_server_unpause_message (tunnel->self, tunnel->msg); + soup_server_message_unpause (tunnel->msg); tunnel_close (tunnel); return; } @@ -199,7 +199,7 @@ tunnel->server.ostream = g_io_stream_get_output_stream (tunnel->server.iostream); soup_server_message_set_status (tunnel->msg, SOUP_STATUS_OK, NULL); - soup_server_unpause_message (tunnel->self, tunnel->msg); + soup_server_message_unpause (tunnel->msg); g_signal_connect (tunnel->msg, "finished", G_CALLBACK (start_tunnel), tunnel); } @@ -211,7 +211,7 @@ GUri *dest_uri; GSocketClient *sclient; - soup_server_pause_message (server, msg); + soup_server_message_pause (msg); tunnel = g_new0 (Tunnel, 1); tunnel->self = g_object_ref (server); @@ -241,7 +241,7 @@ soup_message_headers_foreach (soup_message_get_response_headers (from), copy_header, soup_server_message_get_response_headers (to)); soup_message_headers_remove (soup_server_message_get_response_headers (to), "Content-Length"); - soup_server_unpause_message (server, to); + soup_server_message_unpause (to); } static void @@ -262,7 +262,7 @@ if (error) { g_print ("%p failed to read body: %s\n\n", server_msg, error->message); soup_server_message_set_status (server_msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL); - soup_server_unpause_message (server, server_msg); + soup_server_message_unpause (server_msg); g_error_free (error); return; } @@ -274,7 +274,7 @@ g_signal_handlers_disconnect_by_func (server_msg, client_msg_failed, client_cancellable); soup_message_body_complete (soup_server_message_get_response_body (server_msg)); - soup_server_unpause_message (server, server_msg); + soup_server_message_unpause (server_msg); g_object_unref (server_msg); return; } @@ -284,7 +284,7 @@ SoupMessageBody *body = soup_server_message_get_response_body (server_msg); soup_message_body_append_bytes (body, bytes); - soup_server_unpause_message (server, server_msg); + soup_server_message_unpause (server_msg); g_bytes_unref (bytes); @@ -303,7 +303,7 @@ if (error) { g_print ("%p failed to read body: %s\n\n", server_msg, error->message); soup_server_message_set_status (server_msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL); - soup_server_unpause_message (server, server_msg); + soup_server_message_unpause (server_msg); g_error_free (error); return; } @@ -365,7 +365,7 @@ // Keep the server message alive until the client one is finished g_object_ref (msg); - soup_server_pause_message (server, msg); + soup_server_message_pause (msg); } static gboolean
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/get_apache_modules_dirs.py -> _service:tar_scm:libsoup-3.2.2.tar.xz/get_apache_modules_dirs.py
Changed
@@ -27,13 +27,6 @@ import os import glob -def check_php_module(modules_path): - php_modules = glob.glob(os.path.join(modules_path, 'libphp7*.so')); - if len(php_modules): - # The last one in the sorted output will be the desired php module. - return sorted(php_modules)-1; - - def check_module(modules_path, module): module_path = os.path.join(modules_path, module) return os.path.isfile(module_path) @@ -52,7 +45,7 @@ 'mod_authz_user', 'mod_dir', 'mod_mime', - 'mod_mpm_prefork', + 'mod_mpm_event', 'mod_proxy', 'mod_proxy_http', 'mod_proxy_connect' @@ -105,8 +98,8 @@ apache_modules_dir = '' apache_ssl_module_dir = '' - apache_php_module_file = '' apache_mod_unixd_module_file = '' + apache_http2_module_dir = '' for lib_dir in 'lib', 'lib64': for httpd_dir in 'apache', 'apache2', 'http', 'http2', 'httpd': @@ -117,11 +110,10 @@ apache_modules_dir = modules_path if check_module(modules_path, 'mod_ssl.so'): apache_ssl_module_dir = modules_path - php_module = check_php_module(modules_path) - if (php_module): - apache_php_module_file = php_module if check_module(modules_path, 'mod_unixd.so'): apache_mod_unixd_module_file = modules_path + if check_module(modules_path, 'mod_http2.so'): + apache_http2_module_dir = modules_path # These two are mandatory for having properly configured Apache if apache_modules_dir == '' or apache_ssl_module_dir == '': @@ -129,8 +121,8 @@ print(apache_modules_dir + ":" + apache_ssl_module_dir + ":" + - apache_php_module_file + ":" + - apache_mod_unixd_module_file, end='') + apache_mod_unixd_module_file + ":" + + apache_http2_module_dir, end='') if __name__ == "__main__": main()
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup.doap -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup.doap
Changed
@@ -17,16 +17,9 @@ <maintainer> <foaf:Person> - <foaf:name>Dan Winship</foaf:name> - <foaf:mbox rdf:resource="mailto:danw@gnome.org" /> - <gnome:userid>danw</gnome:userid> - </foaf:Person> - </maintainer> - <maintainer> - <foaf:Person> - <foaf:name>Claudio Saavedra</foaf:name> - <foaf:mbox rdf:resource="mailto:csaavedra@igalia.com" /> - <gnome:userid>csaavedra</gnome:userid> + <foaf:name>Carlos Garcia Campos</foaf:name> + <foaf:mbox rdf:resource="mailto:cgarcia@igalia.com" /> + <gnome:userid>carlosgc</gnome:userid> </foaf:Person> </maintainer> <maintainer>
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/auth/soup-auth-basic.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/auth/soup-auth-basic.c
Changed
@@ -23,11 +23,12 @@ } SoupAuthBasicPrivate; /** - * SOUP_TYPE_AUTH_BASIC: + * SoupAuthBasic: * - * A #GType corresponding to HTTP "Basic" authentication. - * #SoupSessions support this by default; if you want to disable - * support for it, call soup_session_remove_feature_by_type(), + * HTTP "Basic" authentication. + * + * class@Sessions support this by default; if you want to disable + * support for it, call method@Session.remove_feature_by_type, * passing %SOUP_TYPE_AUTH_BASIC. * */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/auth/soup-auth-digest.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/auth/soup-auth-digest.c
Changed
@@ -46,11 +46,12 @@ static void recompute_hex_a1 (SoupAuthDigestPrivate *priv); /** - * SOUP_TYPE_AUTH_DIGEST: + * SoupAuthDigest: * - * A #GType corresponding to HTTP "Digest" authentication. - * #SoupSessions support this by default; if you want to disable - * support for it, call soup_session_remove_feature_by_type(), + * HTTP "Digest" authentication. + * + * class@Sessions support this by default; if you want to disable + * support for it, call method@Session.remove_feature_by_type * passing %SOUP_TYPE_AUTH_DIGEST. * */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/auth/soup-auth-manager.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/auth/soup-auth-manager.c
Changed
@@ -22,49 +22,28 @@ #include "soup-uri-utils-private.h" /** - * SECTION:soup-auth-manager - * @short_description: HTTP client-side authentication handler - * @see_also: #SoupSession, #SoupAuth + * SoupAuthManager: + * + * HTTP client-side authentication handler. * - * #SoupAuthManager is the #SoupSessionFeature that handles HTTP - * authentication for a #SoupSession. + * #SoupAuthManager is the iface@SessionFeature that handles HTTP + * authentication for a class@Session. * * A #SoupAuthManager is added to the session by default, and normally * you don't need to worry about it at all. However, if you want to * disable HTTP authentication, you can remove the feature from the - * session with soup_session_remove_feature_by_type(), or disable it on - * individual requests with soup_message_disable_feature(). - * - **/ - -/** - * SoupAuthManager: - * - * Class for managing client-side HTTP authentication. - */ - -/** - * SOUP_TYPE_AUTH_MANAGER: - * - * The #GType of #SoupAuthManager; you can use this with - * soup_session_remove_feature_by_type() or - * soup_message_disable_feature(). + * session with method@Session.remove_feature_by_type or disable it on + * individual requests with method@Message.disable_feature. * - * (Although this type has only been publicly visible since libsoup - * 2.42, it has always existed in the background, and you can use - * <literal><code>g_type_from_name ("SoupAuthManager")</code></literal> - * to get its #GType in earlier releases.) + * You can use this with method@Session.remove_feature_by_type or + * method@Message.disable_feature. * - */ + * (Although this type has only been publicly visible since libsoup 2.42, it has + * always existed in the background, and you can use `g_type_from_name + * ("SoupAuthManager")` to get its alias@GLib.Type in earlier releases.) + **/ static void soup_auth_manager_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data); -enum { - AUTHENTICATE, - LAST_SIGNAL -}; - -static guint signalsLAST_SIGNAL = { 0 }; - struct _SoupAuthManager { GObject parent_instance; }; @@ -75,6 +54,7 @@ gboolean auto_ntlm; SoupAuth *proxy_auth; + GMutex mutex; GHashTable *auth_hosts; } SoupAuthManagerPrivate; @@ -104,6 +84,7 @@ soup_uri_host_equal, NULL, (GDestroyNotify)soup_auth_host_free); + g_mutex_init (&priv->mutex); } static void @@ -117,6 +98,8 @@ g_clear_object (&priv->proxy_auth); + g_mutex_clear (&priv->mutex); + G_OBJECT_CLASS (soup_auth_manager_parent_class)->finalize (object); } @@ -126,33 +109,6 @@ GObjectClass *object_class = G_OBJECT_CLASS (auth_manager_class); object_class->finalize = soup_auth_manager_finalize; - - /** - * SoupAuthManager::authenticate: - * @manager: the #SoupAuthManager - * @msg: the #SoupMessage being sent - * @auth: the #SoupAuth to authenticate - * @retrying: %TRUE if this is the second (or later) attempt - * - * Emitted when the manager requires the application to - * provide authentication credentials. - * - * #SoupMessage connects to this signal and emits its own - * #SoupMessage::authenticate signal when it is emitted, so - * you shouldn't need to use this signal directly. - */ - signalsAUTHENTICATE = - g_signal_new ("authenticate", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 3, - SOUP_TYPE_MESSAGE, - SOUP_TYPE_AUTH, - G_TYPE_BOOLEAN); - } static int @@ -454,15 +410,18 @@ make_auto_ntlm_auth (SoupAuthManagerPrivate *priv, SoupAuthHost *host) { SoupAuth *auth; + char *authority; if (!priv->auto_ntlm) return FALSE; + authority = g_strdup_printf ("%s:%d", g_uri_get_host (host->uri), g_uri_get_port (host->uri)); auth = g_object_new (SOUP_TYPE_AUTH_NTLM, - "host", g_uri_get_host (host->uri), + "authority", authority, NULL); record_auth_for_uri (priv, host->uri, auth, FALSE); g_object_unref (auth); + g_free (authority); return TRUE; } @@ -661,6 +620,8 @@ SoupAuth *auth, *prior_auth; gboolean prior_auth_failed = FALSE; + g_mutex_lock (&priv->mutex); + /* See if we used auth last time */ prior_auth = soup_message_get_auth (msg); if (prior_auth && check_auth (msg, prior_auth)) { @@ -669,8 +630,10 @@ prior_auth_failed = TRUE; } else { auth = create_auth (priv, msg); - if (!auth) + if (!auth) { + g_mutex_unlock (&priv->mutex); return; + } } if (!soup_message_query_flags (msg, SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE)) { @@ -682,6 +645,8 @@ auth = g_object_ref (new_auth); } + g_mutex_unlock (&priv->mutex); + /* If we need to authenticate, try to do it. */ authenticate_auth (manager, auth, msg, prior_auth_failed, FALSE, TRUE); @@ -695,6 +660,8 @@ SoupAuthManagerPrivate *priv = soup_auth_manager_get_instance_private (manager); SoupAuth *auth; + g_mutex_lock (&priv->mutex); + auth = lookup_auth (priv, msg); if (auth && soup_auth_is_ready (auth, msg)) { if (SOUP_IS_CONNECTION_AUTH (auth)) @@ -708,6 +675,8 @@ soup_session_requeue_message (priv->session, msg); } + + g_mutex_unlock (&priv->mutex); } static void @@ -717,6 +686,8 @@ SoupAuth *auth = NULL, *prior_auth; gboolean prior_auth_failed = FALSE; + g_mutex_lock (&priv->mutex); + /* See if we used auth last time */ prior_auth = soup_message_get_proxy_auth (msg); if (prior_auth && check_auth (msg, prior_auth)) { @@ -729,13 +700,17 @@ if (!auth) { auth = create_auth (priv, msg); - if (!auth) + if (!auth) { + g_mutex_unlock (&priv->mutex); return; + } if (!soup_message_query_flags (msg, SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE)) priv->proxy_auth = g_object_ref (auth); } + g_mutex_unlock (&priv->mutex); + /* If we need to authenticate, try to do it. */ authenticate_auth (manager, auth, msg, prior_auth_failed, TRUE, TRUE); @@ -749,6 +724,8 @@ SoupAuthManagerPrivate *priv = soup_auth_manager_get_instance_private (manager); SoupAuth *auth; + g_mutex_lock (&priv->mutex); + auth = lookup_proxy_auth (priv, msg); if (auth && soup_auth_is_ready (auth, msg)) { /* When not using cached credentials, update the Authorization header @@ -758,6 +735,8 @@ update_authorization_header (msg, auth, TRUE); soup_session_requeue_message (priv->session, msg); } + + g_mutex_unlock (&priv->mutex); } static void @@ -769,6 +748,8 @@ if (soup_message_query_flags (msg, SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE)) return; + g_mutex_lock (&priv->mutex); + if (soup_message_get_method (msg) != SOUP_METHOD_CONNECT) { auth = lookup_auth (priv, msg); if (auth) { @@ -788,6 +769,8 @@ } soup_message_set_proxy_auth (msg, auth); update_authorization_header (msg, auth, TRUE); + + g_mutex_unlock (&priv->mutex); } static void @@ -827,10 +810,11 @@ * @auth: the #SoupAuth to use * * Records that @auth is to be used under @uri, as though a - * WWW-Authenticate header had been received at that URI. This can be - * used to "preload" @manager's auth cache, to avoid an extra HTTP - * round trip in the case where you know ahead of time that a 401 - * response will be returned. + * WWW-Authenticate header had been received at that URI. + * + * This can be used to "preload" @manager's auth cache, to avoid an extra HTTP + * round trip in the case where you know ahead of time that a 401 response will + * be returned. * * This is only useful for authentication types where the initial * Authorization header does not depend on any additional information @@ -844,14 +828,16 @@ { SoupAuthManagerPrivate *priv = soup_auth_manager_get_instance_private (manager); + g_mutex_lock (&priv->mutex); record_auth_for_uri (priv, uri, auth, FALSE); + g_mutex_unlock (&priv->mutex); } /** * soup_auth_manager_clear_cached_credentials: * @manager: a #SoupAuthManager * - * Clear all credentials cached by @manager + * Clear all credentials cached by @manager. * */ void @@ -861,7 +847,9 @@ g_return_if_fail (SOUP_IS_AUTH_MANAGER (manager)); + g_mutex_lock (&priv->mutex); g_hash_table_remove_all (priv->auth_hosts); + g_mutex_unlock (&priv->mutex); } static void
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/auth/soup-auth-negotiate.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/auth/soup-auth-negotiate.c
Changed
@@ -27,11 +27,13 @@ /** * soup_auth_negotiate_supported: * - * Indicates whether libsoup was built with GSSAPI support. If this is - * %FALSE, %SOUP_TYPE_AUTH_NEGOTIATE will still be defined and can - * still be added to a #SoupSession, but libsoup will never attempt to + * Indicates whether libsoup was built with GSSAPI support. + * + * If this is %FALSE, %SOUP_TYPE_AUTH_NEGOTIATE will still be defined and can + * still be added to a class@Session, but libsoup will never attempt to * actually use this auth type. * + * Returns: %TRUE if supported otherwise %FALSE */ gboolean soup_auth_negotiate_supported (void) @@ -76,17 +78,18 @@ } SoupAuthNegotiatePrivate; /** - * SOUP_TYPE_AUTH_NEGOTIATE: + * SoupAuthNegotiate: + * + * HTTP-based GSS-Negotiate authentication, as defined by + * RFC 4559(https://datatracker.ietf.org/doc/html/rfc4559). * - * A #GType corresponding to HTTP-based GSS-Negotiate authentication. - * #SoupSessions do not support this type by default; if you want to - * enable support for it, call soup_session_add_feature_by_type(), + * class@Sessions do not support this type by default; if you want to + * enable support for it, call method@Session.add_feature_by_type, * passing %SOUP_TYPE_AUTH_NEGOTIATE. * * This auth type will only work if libsoup was compiled with GSSAPI - * support; you can check soup_auth_negotiate_supported() to see if it + * support; you can check func@AuthNegotiate.supported to see if it * was. - * */ G_DEFINE_FINAL_TYPE_WITH_PRIVATE (SoupAuthNegotiate, soup_auth_negotiate, SOUP_TYPE_CONNECTION_AUTH) @@ -361,9 +364,20 @@ auth_headers = soup_message_headers_get_one_common (soup_message_get_response_headers (msg), SOUP_HEADER_WWW_AUTHENTICATE); if (!auth_headers || g_ascii_strncasecmp (auth_headers, "Negotiate ", 10) != 0) { - g_warning ("Failed to parse auth header"); + if (soup_message_get_status (msg) == SOUP_STATUS_OK) { + /* The server *may* supply final authentication data to + * the client, but doesn't have to. We are not + * authenticating the server, so just ignore missing + * auth data. In practice, this is required for web + * compat. + */ + priv->is_authenticated = TRUE; + return; + } + + g_warning ("Server bug: missing or invalid WWW-Authenticate header: %s", auth_headers); conn->state = SOUP_NEGOTIATE_FAILED; - goto out; + return; } ret = soup_gss_client_step (conn, auth_headers + 10, &error_message); @@ -393,7 +407,7 @@ default: conn->state = SOUP_NEGOTIATE_FAILED; } - out: + g_clear_pointer (&error_message, g_free); } @@ -584,7 +598,7 @@ &conn->context, conn->server_name, (gss_OID) &gss_mech_spnego, - GSS_C_MUTUAL_FLAG, + 0, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &in,
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/auth/soup-auth-ntlm.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/auth/soup-auth-ntlm.c
Changed
@@ -99,13 +99,13 @@ #endif /** - * SOUP_TYPE_AUTH_NTLM: + * SoupAuthNTLM: * - * A #GType corresponding to HTTP-based NTLM authentication. - * #SoupSessions do not support this type by default; if you want to - * enable support for it, call soup_session_add_feature_by_type(), - * passing %SOUP_TYPE_AUTH_NTLM. + * HTTP-based NTLM authentication. * + * class@Sessions do not support this type by default; if you want to + * enable support for it, call method@Session.add_feature_by_type, + * passing %SOUP_TYPE_AUTH_NTLM. */ G_DEFINE_FINAL_TYPE_WITH_PRIVATE (SoupAuthNTLM, soup_auth_ntlm, SOUP_TYPE_CONNECTION_AUTH) @@ -758,12 +758,14 @@ return FALSE; } + g_free (*default_domain); *default_domain = g_convert ((char *)chall + domain.offset, domain.length, "UTF-8", "UCS-2LE", NULL, NULL, NULL); } if (nonce) { + g_free (*nonce); *nonce = g_memdup2 (chall + NTLM_CHALLENGE_NONCE_OFFSET, NTLM_CHALLENGE_NONCE_LENGTH); } @@ -777,6 +779,7 @@ g_free (chall); return FALSE; } + g_free (*target_info); *target_info = g_memdup2 (chall + target.offset, target.length); *target_info_sz = target.length; }
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/auth/soup-auth.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/auth/soup-auth.c
Changed
@@ -18,22 +18,16 @@ #include "soup-uri-utils-private.h" /** - * SECTION:soup-auth - * @short_description: HTTP client-side authentication support - * @see_also: #SoupSession - * - * #SoupAuth objects store the authentication data associated with a - * given bit of web space. They are created automatically by - * #SoupSession. - **/ - -/** * SoupAuth: * - * The abstract base class for handling authentication. Specific HTTP - * Authentication mechanisms are implemented by its subclasses, but - * applications never need to be aware of the specific subclasses - * being used. + * The abstract base class for handling authentication. + * + * Specific HTTP Authentication mechanisms are implemented by its subclasses, + * but applications never need to be aware of the specific subclasses being + * used. + * + * #SoupAuth objects store the authentication data associated with a given bit + * of web space. They are created automatically by class@Session. **/ typedef struct { @@ -166,7 +160,7 @@ /* properties */ /** - * SoupAuth:scheme-name: + * SoupAuth:scheme-name: (attributes org.gtk.Property.get=soup_auth_get_scheme_name) * * The authentication scheme name. **/ @@ -178,7 +172,7 @@ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** - * SoupAuth:realm: + * SoupAuth:realm: (attributes org.gtk.Property.get=soup_auth_get_realm) * * The authentication realm. **/ @@ -190,7 +184,7 @@ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** - * SoupAuth:authority: + * SoupAuth:authority: (attributes org.gtk.Property.get=soup_auth_get_authority) * * The authority (host:port) being authenticated to. **/ @@ -202,7 +196,7 @@ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** - * SoupAuth:is-for-proxy: + * SoupAuth:is-for-proxy: (attributes org.gtk.Property.get=soup_auth_is_for_proxy) * * Whether or not the auth is for a proxy server. **/ @@ -214,7 +208,7 @@ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** - * SoupAuth:is-authenticated: + * SoupAuth:is-authenticated: (attributes org.gtk.Property.get=soup_auth_is_authenticated) * * Whether or not the auth has been authenticated. **/ @@ -226,10 +220,9 @@ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** - * SoupAuth:is-cancelled: + * SoupAuth:is-cancelled: (attributes org.gtk.Property.get=soup_auth_is_cancelled) * - * An alias for the #SoupAuth:is-cancelled property. - * (Whether or not the auth has been cancelled.) + * Whether or not the auth has been cancelled. **/ propertiesPROP_IS_CANCELLED = g_param_spec_boolean ("is-cancelled", @@ -251,11 +244,11 @@ * Creates a new #SoupAuth of type @type with the information from * @msg and @auth_header. * - * This is called by #SoupSession; you will normally not create auths + * This is called by class@Session; you will normally not create auths * yourself. * * Returns: (nullable): the new #SoupAuth, or %NULL if it could - * not be created + * not be created **/ SoupAuth * soup_auth_new (GType type, SoupMessage *msg, const char *auth_header) @@ -310,12 +303,13 @@ * @auth_header: the WWW-Authenticate/Proxy-Authenticate header * * Updates @auth with the information from @msg and @auth_header, - * possibly un-authenticating it. As with soup_auth_new(), this is - * normally only used by #SoupSession. + * possibly un-authenticating it. + * + * As with ctor@Auth.new, this is normally only used by class@Session. * * Returns: %TRUE if @auth is still a valid (but potentially - * unauthenticated) #SoupAuth. %FALSE if something about @auth_params - * could not be parsed or incorporated into @auth at all. + * unauthenticated) #SoupAuth. %FALSE if something about @auth_params + * could not be parsed or incorporated into @auth at all. **/ gboolean soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header) @@ -360,8 +354,10 @@ * @username: the username provided by the user or client * @password: the password provided by the user or client * - * Call this on an auth to authenticate it; normally this will cause - * the auth's message to be requeued with the new authentication info. + * Call this on an auth to authenticate it. + * + * Normally this will cause the auth's message to be requeued with the new + * authentication info. **/ void soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password) @@ -387,9 +383,10 @@ * soup_auth_cancel: * @auth: a #SoupAuth * - * Call this on an auth to cancel it. You need to cancel an auth to complete - * an asynchronous authenticate operation when no credentials are provided - * (soup_auth_authenticate() is not called). + * Call this on an auth to cancel it. + * + * You need to cancel an auth to complete an asynchronous authenticate operation + * when no credentials are provided (method@Auth.authenticate is not called). * The #SoupAuth will be cancelled on dispose if it hans't been authenticated. */ void @@ -408,7 +405,7 @@ } /** - * soup_auth_is_for_proxy: + * soup_auth_is_for_proxy: (attributes org.gtk.Method.get_property=is-for-proxy) * @auth: a #SoupAuth * * Tests whether or not @auth is associated with a proxy server rather @@ -428,6 +425,7 @@ /** * soup_auth_get_scheme_name: + * soup_auth_get_scheme_name: (attributes org.gtk.Method.get_property=scheme-name) * @auth: a #SoupAuth * * Returns @auth's scheme name. (Eg, "Basic", "Digest", or "NTLM") @@ -443,7 +441,7 @@ } /** - * soup_auth_get_authority: + * soup_auth_get_authority: (attributes org.gtk.Method.get_property=authority) * @auth: a #SoupAuth * * Returns the authority (host:port) that @auth is associated with. @@ -461,13 +459,14 @@ } /** - * soup_auth_get_realm: + * soup_auth_get_realm: (attributes org.gtk.Method.get_property=realm) * @auth: a #SoupAuth * - * Returns @auth's realm. This is an identifier that distinguishes - * separate authentication spaces on a given server, and may be some - * string that is meaningful to the user. (Although it is probably not - * localized.) + * Returns @auth's realm. + * + * This is an identifier that distinguishes separate authentication spaces on a + * given server, and may be some string that is meaningful to the user. + * (Although it is probably not localized.) * * Returns: the realm name **/ @@ -485,10 +484,12 @@ * soup_auth_get_info: * @auth: a #SoupAuth * - * Gets an opaque identifier for @auth, for use as a hash key or the - * like. #SoupAuth objects from the same server with the same - * identifier refer to the same authentication domain (eg, the URLs - * associated with them take the same usernames and passwords). + * Gets an opaque identifier for @auth. + * + * The identifier can be used as a hash key or the like. #SoupAuth objects from + * the same server with the same identifier refer to the same authentication + * domain (eg, the URLs associated with them take the same usernames and + * passwords). * * Returns: the identifier **/ @@ -509,10 +510,10 @@ } /** - * soup_auth_is_authenticated: + * soup_auth_is_authenticated: (attributes org.gtk.Method.get_property=is-authenticated) * @auth: a #SoupAuth * - * Tests if @auth has been given a username and password + * Tests if @auth has been given a username and password. * * Returns: %TRUE if @auth has been given a username and password **/ @@ -531,7 +532,7 @@ } /** - * soup_auth_is_cancelled: + * soup_auth_is_cancelled: (attributes org.gtk.Method.get_property=is-cancelled) * @auth: a #SoupAuth * * Tests if @auth has been cancelled @@ -554,9 +555,10 @@ * @auth: a #SoupAuth * @msg: the #SoupMessage to be authorized * - * Generates an appropriate "Authorization" header for @msg. (The - * session will only call this if soup_auth_is_authenticated() - * returned %TRUE.) + * Generates an appropriate "Authorization" header for @msg. + * + * (The session will only call this if method@Auth.is_authenticated returned + * %TRUE.) * * Returns: the "Authorization" header, which must be freed. **/ @@ -574,8 +576,9 @@ * @auth: a #SoupAuth * @msg: a #SoupMessage * - * Tests if @auth is ready to make a request for @msg with. For most - * auths, this is equivalent to soup_auth_is_authenticated(), but for + * Tests if @auth is ready to make a request for @msg with. + * + * For most auths, this is equivalent to method@Auth.is_authenticated, but for * some auth types (eg, NTLM), the auth may be sendable (eg, as an * authentication request) even before it is authenticated. * @@ -606,7 +609,7 @@ * @auth: a #SoupAuth * * Tests if @auth is able to authenticate by providing credentials to the - * soup_auth_authenticate(). + * method@Auth.authenticate. * * Returns: %TRUE if @auth is able to accept credentials. * @@ -629,15 +632,16 @@ * soup_auth_get_protection_space: * @auth: a #SoupAuth * @source_uri: the URI of the request that @auth was generated in - * response to. + * response to. * * Returns a list of paths on the server which @auth extends over. + * * (All subdirectories of these paths are also assumed to be part * of @auth's protection space, unless otherwise discovered not to * be.) * * Returns: (element-type utf8) (transfer full): the list of - * paths, which can be freed with soup_auth_free_protection_space(). + * paths, which can be freed with method@Auth.free_protection_space. **/ GSList * soup_auth_get_protection_space (SoupAuth *auth, GUri *source_uri) @@ -654,7 +658,7 @@ /** * soup_auth_free_protection_space: (skip) * @auth: a #SoupAuth - * @space: the return value from soup_auth_get_protection_space() + * @space: the return value from method@Auth.get_protection_space * * Frees @space. **/
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/auth/soup-connection-auth.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/auth/soup-connection-auth.c
Changed
@@ -100,16 +100,17 @@ conn = soup_message_get_connection (msg); state = g_hash_table_lookup (priv->conns, conn); - if (state) - return state; + if (!state) { + state = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->create_connection_state (auth); + if (conn) { + g_signal_connect (conn, "disconnected", + G_CALLBACK (connection_disconnected), auth); + } - state = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->create_connection_state (auth); - if (conn) { - g_signal_connect (conn, "disconnected", - G_CALLBACK (connection_disconnected), auth); - } + g_hash_table_insert (priv->conns, conn, state); + } + g_clear_object (&conn); - g_hash_table_insert (priv->conns, conn, state); return state; }
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/auth/soup-tls-interaction.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/auth/soup-tls-interaction.c
Changed
@@ -16,7 +16,7 @@ }; typedef struct { - SoupConnection *conn; + GWeakRef conn; } SoupTlsInteractionPrivate; G_DEFINE_FINAL_TYPE_WITH_PRIVATE (SoupTlsInteraction, soup_tls_interaction, G_TYPE_TLS_INTERACTION) @@ -31,12 +31,15 @@ { SoupTlsInteractionPrivate *priv = soup_tls_interaction_get_instance_private (SOUP_TLS_INTERACTION (tls_interaction)); GTask *task; + SoupConnection *conn = g_weak_ref_get (&priv->conn); task = g_task_new (tls_interaction, cancellable, callback, user_data); - if (priv->conn) - soup_connection_request_tls_certificate (priv->conn, connection, task); - else + if (conn) { + soup_connection_request_tls_certificate (conn, connection, task); + g_object_unref (conn); + } else { g_task_return_int (task, G_TLS_INTERACTION_FAILED); + } g_object_unref (task); } @@ -60,12 +63,15 @@ { SoupTlsInteractionPrivate *priv = soup_tls_interaction_get_instance_private (SOUP_TLS_INTERACTION (tls_interaction)); GTask *task; + SoupConnection *conn = g_weak_ref_get (&priv->conn); task = g_task_new (tls_interaction, cancellable, callback, user_data); - if (priv->conn) - soup_connection_request_tls_certificate_password (priv->conn, password, task); - else + if (conn) { + soup_connection_request_tls_certificate_password (conn, password, task); + g_object_unref (conn); + } else { g_task_return_int (task, G_TLS_INTERACTION_FAILED); + } g_object_unref (task); } @@ -85,10 +91,7 @@ { SoupTlsInteractionPrivate *priv = soup_tls_interaction_get_instance_private (SOUP_TLS_INTERACTION (object)); - if (priv->conn) { - g_object_remove_weak_pointer (G_OBJECT (priv->conn), (gpointer*)&priv->conn); - priv->conn = NULL; - } + g_weak_ref_clear (&priv->conn); G_OBJECT_CLASS (soup_tls_interaction_parent_class)->finalize (object); } @@ -96,6 +99,9 @@ static void soup_tls_interaction_init (SoupTlsInteraction *interaction) { + SoupTlsInteractionPrivate *priv = soup_tls_interaction_get_instance_private (interaction); + + g_weak_ref_init (&priv->conn, NULL); } static void @@ -120,8 +126,7 @@ interaction = g_object_new (SOUP_TYPE_TLS_INTERACTION, NULL); priv = soup_tls_interaction_get_instance_private (SOUP_TLS_INTERACTION (interaction)); - priv->conn = conn; - g_object_add_weak_pointer (G_OBJECT (priv->conn), (gpointer*)&priv->conn); + g_weak_ref_set (&priv->conn, conn); return interaction; }
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/cache/soup-cache-private.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/cache/soup-cache-private.h
Changed
@@ -27,6 +27,12 @@ G_BEGIN_DECLS +typedef enum { + SOUP_CACHE_RESPONSE_FRESH, + SOUP_CACHE_RESPONSE_NEEDS_VALIDATION, + SOUP_CACHE_RESPONSE_STALE +} SoupCacheResponse; + SoupCacheResponse soup_cache_has_response (SoupCache *cache, SoupMessage *msg); GInputStream *soup_cache_send_response (SoupCache *cache,
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/cache/soup-cache.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/cache/soup-cache.c
Changed
@@ -47,16 +47,19 @@ #include "soup-session-feature-private.h" /** - * SECTION:soup-cache - * @short_description: Caching support + * SoupCache: * - * #SoupCache implements a file-based cache for HTTP resources. + * File-based cache for HTTP resources. */ /** - * SoupCache: + * SoupCacheability: + * @SOUP_CACHE_CACHEABLE: The message should be cached + * @SOUP_CACHE_UNCACHEABLE: The message shouldn't be cached + * @SOUP_CACHE_INVALIDATES: The messages cache should be invalidated + * @SOUP_CACHE_VALIDATES: The messages cache should be updated * - * Class implementing caching for HTTP resources. + * Indicates if a message should or shouldn't be cached. */ static void soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data); @@ -120,6 +123,7 @@ typedef struct { char *cache_dir; + GMutex mutex; GHashTable *cache; guint n_pending; SoupSession *session; @@ -540,8 +544,10 @@ g_assert (!entry->dirty); g_assert (g_list_length (priv->lru_start) == g_hash_table_size (priv->cache)); - if (!g_hash_table_remove (priv->cache, GUINT_TO_POINTER (entry->key))) + if (!g_hash_table_remove (priv->cache, GUINT_TO_POINTER (entry->key))) { + g_mutex_unlock (&priv->mutex); return FALSE; + } /* Remove from LRU */ lru_item = g_list_find (priv->lru_start, entry); @@ -709,7 +715,9 @@ soup_message_set_metrics_timestamp (msg, SOUP_MESSAGE_METRICS_REQUEST_START); + g_mutex_lock (&priv->mutex); entry = soup_cache_entry_lookup (cache, msg); + g_mutex_unlock (&priv->mutex); g_return_val_if_fail (entry, NULL); file = get_file_from_entry (cache, entry); @@ -812,6 +820,8 @@ SoupCachePrivate *priv = soup_cache_get_instance_private (cache); SoupCacheEntry *entry = helper->entry; + g_mutex_lock (&priv->mutex); + --priv->n_pending; entry->dirty = FALSE; @@ -840,6 +850,7 @@ } cleanup: + g_mutex_unlock (&priv->mutex); g_object_unref (helper->cache); g_slice_free (StreamHelper, helper); } @@ -859,6 +870,8 @@ StreamHelper *helper; time_t request_time, response_time; + g_mutex_lock (&priv->mutex); + /* First of all, check if we should cache the resource. */ cacheability = soup_cache_get_cacheability (cache, msg); entry = soup_cache_entry_lookup (cache, msg); @@ -866,6 +879,7 @@ if (cacheability & SOUP_CACHE_INVALIDATES) { if (entry) soup_cache_entry_remove (cache, entry, TRUE); + g_mutex_unlock (&priv->mutex); return NULL; } @@ -877,15 +891,20 @@ */ if (entry) soup_cache_update_from_conditional_request (cache, msg); + g_mutex_unlock (&priv->mutex); return NULL; } - if (!(cacheability & SOUP_CACHE_CACHEABLE)) + if (!(cacheability & SOUP_CACHE_CACHEABLE)) { + g_mutex_unlock (&priv->mutex); return NULL; + } /* Check if we are already caching this resource */ - if (entry && (entry->dirty || entry->being_validated)) + if (entry && (entry->dirty || entry->being_validated)) { + g_mutex_unlock (&priv->mutex); return NULL; + } /* Create a new entry, deleting any old one if present */ if (entry) @@ -900,12 +919,15 @@ /* Do not continue if it can not be stored */ if (!soup_cache_entry_insert (cache, entry, TRUE)) { soup_cache_entry_free (entry); + g_mutex_unlock (&priv->mutex); return NULL; } entry->cancellable = g_cancellable_new (); ++priv->n_pending; + g_mutex_unlock (&priv->mutex); + helper = g_slice_new (StreamHelper); helper->cache = g_object_ref (cache); helper->entry = entry; @@ -946,6 +968,8 @@ priv->max_size = DEFAULT_MAX_SIZE; priv->max_entry_data_size = priv->max_size / MAX_ENTRY_DATA_PERCENTAGE; priv->size = 0; + + g_mutex_init (&priv->mutex); } static void @@ -971,6 +995,8 @@ g_list_free (priv->lru_start); + g_mutex_clear (&priv->mutex); + G_OBJECT_CLASS (soup_cache_parent_class)->finalize (object); } @@ -1036,6 +1062,10 @@ cache_class->get_cacheability = get_cacheability; + /** + * SoupCache:cache-dir: + * The directory to store the cache files. + */ propertiesPROP_CACHE_DIR = g_param_spec_string ("cache-dir", "Cache directory", @@ -1044,6 +1074,10 @@ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + /** + * SoupCache:cache-type: + * Whether the cache is private or shared. + */ propertiesPROP_CACHE_TYPE = g_param_spec_enum ("cache-type", "Cache type", @@ -1110,13 +1144,17 @@ int max_age, max_stale, min_fresh; GList *lru_item, *item; + g_mutex_lock (&priv->mutex); + entry = soup_cache_entry_lookup (cache, msg); /* 1. The presented Request-URI and that of stored response * match */ - if (!entry) + if (!entry) { + g_mutex_unlock (&priv->mutex); return SOUP_CACHE_RESPONSE_STALE; + } /* Increase hit count. Take sorting into account */ entry->hits++; @@ -1132,6 +1170,8 @@ g_list_free (lru_item); } + g_mutex_unlock (&priv->mutex); + if (entry->dirty || entry->being_validated) return SOUP_CACHE_RESPONSE_STALE; @@ -1255,8 +1295,8 @@ * * Calculates whether the @msg can be cached or not. * - * Returns: a #SoupCacheability value indicating whether the @msg can be cached or not. - * + * Returns: a #SoupCacheability value indicating whether the @msg can be cached + * or not. */ SoupCacheability soup_cache_get_cacheability (SoupCache *cache, SoupMessage *msg) @@ -1280,13 +1320,13 @@ * soup_cache_flush: * @cache: a #SoupCache * - * This function will force all pending writes in the @cache to be - * committed to disk. For doing so it will iterate the #GMainContext - * associated with @cache's session as long as needed. + * Forces all pending writes in the @cache to be + * committed to disk. * - * Contrast with soup_cache_dump(), which writes out the cache index - * file. + * For doing so it will iterate the struct@GLib.MainContext associated with + * @cache's session as long as needed. * + * Contrast with method@Cache.dump, which writes out the cache index file. */ void soup_cache_flush (SoupCache *cache) @@ -1366,6 +1406,7 @@ * * Will remove all entries in the @cache plus all the cache files. * + * This is not thread safe and must be called only from the thread that created the #SoupCache */ void soup_cache_clear (SoupCache *cache) @@ -1388,6 +1429,7 @@ SoupMessage * soup_cache_generate_conditional_request (SoupCache *cache, SoupMessage *original) { + SoupCachePrivate *priv = soup_cache_get_instance_private (cache); SoupMessage *msg; GUri *uri; SoupCacheEntry *entry; @@ -1398,7 +1440,9 @@ g_return_val_if_fail (SOUP_IS_MESSAGE (original), NULL); /* Add the validator entries in the header from the cached data */ + g_mutex_lock (&priv->mutex); entry = soup_cache_entry_lookup (cache, original); + g_mutex_unlock (&priv->mutex); g_return_val_if_fail (entry, NULL); last_modified = soup_message_headers_get_one_common (entry->headers, SOUP_HEADER_LAST_MODIFIED); @@ -1443,7 +1487,9 @@ SoupCachePrivate *priv = soup_cache_get_instance_private (cache); SoupCacheEntry *entry; + g_mutex_lock (&priv->mutex); entry = soup_cache_entry_lookup (cache, msg); + g_mutex_unlock (&priv->mutex); if (entry) entry->being_validated = FALSE; @@ -1454,7 +1500,12 @@ soup_cache_update_from_conditional_request (SoupCache *cache, SoupMessage *msg) { - SoupCacheEntry *entry = soup_cache_entry_lookup (cache, msg); + SoupCachePrivate *priv = soup_cache_get_instance_private (cache); + SoupCacheEntry *entry; + + g_mutex_lock (&priv->mutex); + entry = soup_cache_entry_lookup (cache, msg); + g_mutex_unlock (&priv->mutex); if (!entry) return; @@ -1509,13 +1560,15 @@ * soup_cache_dump: * @cache: a #SoupCache * - * Synchronously writes the cache index out to disk. Contrast with - * soup_cache_flush(), which writes pending cache - * <emphasis>entries</emphasis> to disk. + * Synchronously writes the cache index out to disk. + * + * Contrast with method@Cache.flush, which writes pending cache *entries* to + * disk. * * You must call this before exiting if you want your cache data to * persist between sessions. * + * This is not thread safe and must be called only from the thread that created the #SoupCache */ void soup_cache_dump (SoupCache *cache) @@ -1578,6 +1631,7 @@ * * Loads the contents of @cache's index into memory. * + * This is not thread safe and must be called only from the thread that created the #SoupCache */ void soup_cache_load (SoupCache *cache) @@ -1676,7 +1730,6 @@ * @max_size: the maximum size of the cache, in bytes * * Sets the maximum size of the cache. - * */ void soup_cache_set_max_size (SoupCache *cache, @@ -1694,7 +1747,6 @@ * Gets the maximum size of the cache. * * Returns: the maximum size of the cache, in bytes. - * */ guint soup_cache_get_max_size (SoupCache *cache)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/cache/soup-cache.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/cache/soup-cache.h
Changed
@@ -38,12 +38,6 @@ } SoupCacheability; typedef enum { - SOUP_CACHE_RESPONSE_FRESH, - SOUP_CACHE_RESPONSE_NEEDS_VALIDATION, - SOUP_CACHE_RESPONSE_STALE -} SoupCacheResponse; - -typedef enum { SOUP_CACHE_SINGLE_USER, SOUP_CACHE_SHARED } SoupCacheType;
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/content-decoder/soup-content-decoder.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/content-decoder/soup-content-decoder.c
Changed
@@ -21,8 +21,9 @@ #endif /** - * SECTION:soup-content-decoder - * @short_description: Content-Encoding handler + * SoupContentDecoder: + * + * Handles decoding of HTTP messages. * * #SoupContentDecoder handles adding the "Accept-Encoding" header on * outgoing messages, and processing the "Content-Encoding" header on @@ -31,7 +32,7 @@ * * A #SoupContentDecoder will automatically be * added to the session by default. (You can use - * soup_session_remove_feature_by_type() if you don't + * method@Session.remove_feature_by_type if you don't * want this.) * * If #SoupContentDecoder successfully decodes the Content-Encoding, @@ -46,15 +47,8 @@ * (Note that currently there is no way to (automatically) use * Content-Encoding when sending a request body, or to pick specific * encoding types to support.) - * **/ -/** - * SoupContentDecoder: - * - * Class handling decoding of HTTP messages. - */ - struct _SoupContentDecoder { GObject parent; };
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/content-sniffer/soup-content-sniffer.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/content-sniffer/soup-content-sniffer.c
Changed
@@ -24,24 +24,18 @@ #include "soup-session-feature-private.h" /** - * SECTION:soup-content-sniffer - * @short_description: Content sniffing for SoupSession + * SoupContentSniffer: + * + * Sniffs the mime type of messages. * * A #SoupContentSniffer tries to detect the actual content type of * the files that are being downloaded by looking at some of the data - * before the #SoupMessage emits its #SoupMessage::got-headers signal. - * #SoupContentSniffer implements #SoupSessionFeature, so you can add - * content sniffing to a session with soup_session_add_feature() or - * soup_session_add_feature_by_type(). - * + * before the class@Message emits its signal@Message::got-headers signal. + * #SoupContentSniffer implements iface@SessionFeature, so you can add + * content sniffing to a session with method@Session.add_feature or + * method@Session.add_feature_by_type. **/ -/** - * SoupContentSniffer: - * - * Class that attempts to sniff the mime type of messages. - */ - static void soup_content_sniffer_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data); static SoupContentProcessorInterface *soup_content_sniffer_default_content_processor_interface; @@ -787,13 +781,13 @@ * @params: (element-type utf8 utf8) (out) (transfer full) (nullable): return * location for Content-Type parameters (eg, "charset"), or %NULL * - * Sniffs @buffer to determine its Content-Type. The result may also - * be influenced by the Content-Type declared in @msg's response - * headers. + * Sniffs @buffer to determine its Content-Type. * - * Returns: the sniffed Content-Type of @buffer; this will never be %NULL, - * but may be "application/octet-stream". + * The result may also be influenced by the Content-Type declared in @msg's + * response headers. * + * Returns: the sniffed Content-Type of @buffer; this will never be %NULL, + * but may be `application/octet-stream`. */ char * soup_content_sniffer_sniff (SoupContentSniffer *sniffer, SoupMessage *msg, @@ -901,7 +895,6 @@ * Creates a new #SoupContentSniffer. * * Returns: a new #SoupContentSniffer - * **/ SoupContentSniffer * soup_content_sniffer_new (void)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/cookies/soup-cookie-jar-db.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/cookies/soup-cookie-jar-db.c
Changed
@@ -19,23 +19,18 @@ #include "soup.h" /** - * SECTION:soup-cookie-jar-db - * @short_description: Database-based Cookie Jar + * SoupCookieJarDB: + * + * Database-based Cookie Jar. * - * #SoupCookieJarDB is a #SoupCookieJar that reads cookies from and - * writes them to a sqlite database in the new Mozilla format. + * #SoupCookieJarDB is a class@CookieJar that reads cookies from and writes + * them to a sqlite database in the new Mozilla format. * - * (This is identical to <literal>SoupCookieJarSqlite</literal> in + * (This is identical to `SoupCookieJarSqlite` in * libsoup-gnome; it has just been moved into libsoup proper, and * renamed to avoid conflicting.) **/ -/** - * SoupCookieJarDB: - * - * Subclass of #SoupCookieJar that stores cookies in a sqlite database. - */ - enum { PROP_0, @@ -119,15 +114,13 @@ * * Creates a #SoupCookieJarDB. * - * @filename will be read in at startup to create an initial set of - * cookies. If @read_only is %FALSE, then the non-session cookies will - * be written to @filename when the 'changed' signal is emitted from - * the jar. (If @read_only is %TRUE, then the cookie jar will only be - * used for this session, and changes made to it will be lost when the - * jar is destroyed.) + * @filename will be read in at startup to create an initial set of cookies. If + * @read_only is %FALSE, then the non-session cookies will be written to + * @filename when the signal@CookieJar::changed signal is emitted from the + * jar. (If @read_only is %TRUE, then the cookie jar will only be used for this + * session, and changes made to it will be lost when the jar is destroyed.) * * Returns: the new #SoupCookieJar - * **/ SoupCookieJar * soup_cookie_jar_db_new (const char *filename, gboolean read_only) @@ -339,6 +332,11 @@ object_class->set_property = soup_cookie_jar_db_set_property; object_class->get_property = soup_cookie_jar_db_get_property; + /** + * SoupCookieJarDB:filename: + * + * Cookie-storage filename. + */ propertiesPROP_FILENAME = g_param_spec_string ("filename", "Filename",
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/cookies/soup-cookie-jar-text.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/cookies/soup-cookie-jar-text.c
Changed
@@ -17,18 +17,13 @@ #include "soup.h" /** - * SECTION:soup-cookie-jar-text - * @short_description: Text-file-based ("cookies.txt") Cookie Jar - * - * #SoupCookieJarText is a #SoupCookieJar that reads cookies from and - * writes them to a text file in format similar to Mozilla's "cookies.txt". - **/ - -/** * SoupCookieJarText: * - * Subclass of #SoupCookieJar that stores cookies in a text file. - */ + * Text-file-based ("cookies.txt") Cookie Jar + * + * #SoupCookieJarText is a class@CookieJar that reads cookies from and writes + * them to a text file in format similar to Mozilla's "cookies.txt". + **/ enum { PROP_0, @@ -113,15 +108,13 @@ * * Creates a #SoupCookieJarText. * - * @filename will be read in at startup to create an initial set of - * cookies. If @read_only is %FALSE, then the non-session cookies will - * be written to @filename when the 'changed' signal is emitted from - * the jar. (If @read_only is %TRUE, then the cookie jar will only be - * used for this session, and changes made to it will be lost when the - * jar is destroyed.) + * @filename will be read in at startup to create an initial set of cookies. If + * @read_only is %FALSE, then the non-session cookies will be written to + * @filename when the signal@CookieJar::changed signal is emitted from the + * jar. (If @read_only is %TRUE, then the cookie jar will only be used for this + * session, and changes made to it will be lost when the jar is destroyed.) * * Returns: the new #SoupCookieJar - * **/ SoupCookieJar * soup_cookie_jar_text_new (const char *filename, gboolean read_only) @@ -394,6 +387,11 @@ object_class->set_property = soup_cookie_jar_text_set_property; object_class->get_property = soup_cookie_jar_text_get_property; + /** + * SoupCookieJarText:filename: + * + * Cookie-storage filename. + */ propertiesPROP_FILENAME = g_param_spec_string ("filename", "Filename",
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/cookies/soup-cookie-jar.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/cookies/soup-cookie-jar.c
Changed
@@ -21,25 +21,19 @@ #include "soup-uri-utils-private.h" /** - * SECTION:soup-cookie-jar - * @short_description: Automatic cookie handling for SoupSession + * SoupCookieJar: + * + * Automatic cookie handling for SoupSession. * - * A #SoupCookieJar stores #SoupCookie<!-- -->s and arrange for them - * to be sent with the appropriate #SoupMessage<!-- -->s. - * #SoupCookieJar implements #SoupSessionFeature, so you can add a - * cookie jar to a session with soup_session_add_feature() or - * soup_session_add_feature_by_type(). + * A #SoupCookieJar stores struct@Cookies and arrange for them to be sent with + * the appropriate class@Messages. #SoupCookieJar implements + * iface@SessionFeature, so you can add a cookie jar to a session with + * method@Session.add_feature or method@Session.add_feature_by_type. * * Note that the base #SoupCookieJar class does not support any form * of long-term cookie persistence. **/ -/** - * SoupCookieJar: - * - * Class that stores cookies in memory. - */ - enum { CHANGED, LAST_SIGNAL @@ -59,6 +53,7 @@ static GParamSpec *propertiesLAST_PROPERTY = { NULL, }; typedef struct { + GMutex mutex; gboolean constructed, read_only; GHashTable *domains, *serials; guint serial; @@ -82,6 +77,7 @@ g_free, NULL); priv->serials = g_hash_table_new (NULL, NULL); priv->accept_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS; + g_mutex_init (&priv->mutex); } static void @@ -106,6 +102,7 @@ soup_cookies_free (value); g_hash_table_destroy (priv->domains); g_hash_table_destroy (priv->serials); + g_mutex_clear (&priv->mutex); G_OBJECT_CLASS (soup_cookie_jar_parent_class)->finalize (object); } @@ -174,7 +171,9 @@ * @old_cookie: the old #SoupCookie value * @new_cookie: the new #SoupCookie value * - * Emitted when @jar changes. If a cookie has been added, + * Emitted when @jar changes. + * + * If a cookie has been added, * @new_cookie will contain the newly-added cookie and * @old_cookie will be %NULL. If a cookie has been deleted, * @old_cookie will contain the to-be-deleted cookie and @@ -193,6 +192,11 @@ SOUP_TYPE_COOKIE | G_SIGNAL_TYPE_STATIC_SCOPE, SOUP_TYPE_COOKIE | G_SIGNAL_TYPE_STATIC_SCOPE); + /** + * SoupCookieJar:read-only: + * + * Whether or not the cookie jar is read-only. + */ propertiesPROP_READ_ONLY = g_param_spec_boolean ("read-only", "Read-only", @@ -202,10 +206,9 @@ G_PARAM_STATIC_STRINGS); /** - * SoupCookieJar:accept-policy: - * - * The policy the jar should follow to accept or reject cookies + * SoupCookieJar:accept-policy: (attributes org.gtk.Property.get=soup_cookie_jar_get_accept_policy org.gtk.Property.set=soup_cookie_jar_set_accept_policy) * + * The policy the jar should follow to accept or reject cookies. */ propertiesPROP_ACCEPT_POLICY = g_param_spec_enum ("accept-policy", @@ -222,11 +225,12 @@ /** * soup_cookie_jar_new: * - * Creates a new #SoupCookieJar. The base #SoupCookieJar class does - * not support persistent storage of cookies; use a subclass for that. + * Creates a new #SoupCookieJar. * - * Returns: a new #SoupCookieJar + * The base #SoupCookieJar class does not support persistent storage of cookies; + * use a subclass for that. * + * Returns: a new #SoupCookieJar **/ SoupCookieJar * soup_cookie_jar_new (void) @@ -341,6 +345,8 @@ next_domain = NULL; } + g_mutex_lock (&priv->mutex); + do { new_head = domain_cookies = g_hash_table_lookup (priv->domains, cur); while (domain_cookies) { @@ -377,6 +383,8 @@ } g_slist_free (cookies_to_remove); + g_mutex_unlock (&priv->mutex); + return g_slist_sort_with_data (cookies, compare_cookies, jar); } @@ -385,7 +393,7 @@ * @jar: a #SoupCookieJar * @uri: a #GUri * @for_http: whether or not the return value is being passed directly - * to an HTTP operation + * to an HTTP operation * * Retrieves (in Cookie-header form) the list of cookies that would * be sent with a request to @uri. @@ -399,8 +407,7 @@ * this. * * Returns: (nullable): the cookies, in string form, or %NULL if - * there are no cookies for @uri. - * + * there are no cookies for @uri. **/ char * soup_cookie_jar_get_cookies (SoupCookieJar *jar, GUri *uri, @@ -431,10 +438,10 @@ * @jar: a #SoupCookieJar * @uri: a #GUri * @for_http: whether or not the return value is being passed directly - * to an HTTP operation + * to an HTTP operation * * Retrieves the list of cookies that would be sent with a request to @uri - * as a #GSList of #SoupCookie objects. + * as a struct@GLib.List of #SoupCookie objects. * * If @for_http is %TRUE, the return value will include cookies marked * "HttpOnly" (that is, cookies that the server wishes to keep hidden @@ -445,8 +452,7 @@ * this. * * Returns: (transfer full) (element-type Soup.Cookie): a #GSList - * with the cookies in the @jar that would be sent with a request to @uri. - * + * with the cookies in the @jar that would be sent with a request to @uri. **/ GSList * soup_cookie_jar_get_cookie_list (SoupCookieJar *jar, GUri *uri, gboolean for_http) @@ -464,19 +470,20 @@ * @top_level: (nullable): a #GUri for the top level document * @site_for_cookies: (nullable): a #GUri indicating the origin to get cookies for * @for_http: whether or not the return value is being passed directly - * to an HTTP operation + * to an HTTP operation * @is_safe_method: if the HTTP method is safe, as defined by RFC 7231, ignored when @for_http is %FALSE * @is_top_level_navigation: whether or not the HTTP request is part of - * top level navigation + * top level navigation * - * This is an extended version of soup_cookie_jar_get_cookie_list() that - * provides more information required to use SameSite cookies. See the - * SameSite cookies spec(https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00) - * for more detailed information. + * This is an extended version of method@CookieJar.get_cookie_list that + * provides more information required to use SameSite cookies. * - * Returns: (transfer full) (element-type Soup.Cookie): a #GSList - * with the cookies in the @jar that would be sent with a request to @uri. + * See the SameSite cookies + * spec(https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00) for + * more detailed information. * + * Returns: (transfer full) (element-type Soup.Cookie): a #GSList + * with the cookies in the @jar that would be sent with a request to @uri. */ GSList * soup_cookie_jar_get_cookie_list_with_same_site_info (SoupCookieJar *jar, @@ -515,6 +522,7 @@ const char *cookie_base_domain; const char *first_party_base_domain; const char *first_party_host; + gboolean retval; if (policy != SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY && policy != SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY) @@ -549,7 +557,11 @@ * previously visited directly. */ priv = soup_cookie_jar_get_instance_private (jar); - return !g_hash_table_lookup (priv->domains, soup_cookie_get_domain (cookie)); + g_mutex_lock (&priv->mutex); + retval = !g_hash_table_lookup (priv->domains, soup_cookie_get_domain (cookie)); + g_mutex_unlock (&priv->mutex); + + return retval; } /** @@ -559,9 +571,11 @@ * @uri: (nullable): the URI setting the cookie * @first_party: (nullable): the URI for the main document * - * Adds @cookie to @jar, emitting the 'changed' signal if we are modifying - * an existing cookie or adding a valid new cookie ('valid' means - * that the cookie's expire date is not in the past). + * Adds @cookie to @jar. + * + * Emits the signal@CookieJar::changed signal if we are modifying an existing + * cookie or adding a valid new cookie ('valid' means that the cookie's expire + * date is not in the past). * * @first_party will be used to reject cookies coming from third party * resources in case such a security policy is set in the @jar. @@ -570,7 +584,6 @@ * from insecure origins. %NULL is treated as secure. * * @cookie will be 'stolen' by the jar, so don't free it afterwards. - * **/ void soup_cookie_jar_add_cookie_full (SoupCookieJar *jar, SoupCookie *cookie, GUri *uri, GUri *first_party) @@ -605,6 +618,8 @@ return; } + g_mutex_lock (&priv->mutex); + old_cookies = g_hash_table_lookup (priv->domains, soup_cookie_get_domain (cookie)); for (oc = old_cookies; oc; oc = oc->next) { old_cookie = oc->data; @@ -634,6 +649,8 @@ soup_cookie_free (old_cookie); } + g_mutex_unlock (&priv->mutex); + return; } last = oc; @@ -642,6 +659,7 @@ /* The new cookie is... a new cookie */ if (soup_cookie_get_expires (cookie) && soup_date_time_is_past (soup_cookie_get_expires (cookie))) { soup_cookie_free (cookie); + g_mutex_unlock (&priv->mutex); return; } @@ -654,6 +672,8 @@ } soup_cookie_jar_changed (jar, NULL, cookie); + + g_mutex_unlock (&priv->mutex); } /** @@ -661,12 +681,13 @@ * @jar: a #SoupCookieJar * @cookie: (transfer full): a #SoupCookie * - * Adds @cookie to @jar, emitting the 'changed' signal if we are modifying + * Adds @cookie to @jar. + * + * Emits the signal@CookieJar::changed signal if we are modifying * an existing cookie or adding a valid new cookie ('valid' means * that the cookie's expire date is not in the past). * * @cookie will be 'stolen' by the jar, so don't free it afterwards. - * **/ void soup_cookie_jar_add_cookie (SoupCookieJar *jar, SoupCookie *cookie) @@ -680,7 +701,9 @@ * @first_party: the URI for the main document * @cookie: (transfer full): a #SoupCookie * - * Adds @cookie to @jar, emitting the 'changed' signal if we are modifying + * Adds @cookie to @jar. + * + * Emits the signal@CookieJar::changed signal if we are modifying * an existing cookie or adding a valid new cookie ('valid' means * that the cookie's expire date is not in the past). * @@ -690,8 +713,7 @@ * @cookie will be 'stolen' by the jar, so don't free it afterwards. * * For secure cookies to work properly you may want to use - * soup_cookie_jar_add_cookie_full(). - * + * method@CookieJar.add_cookie_full. **/ void soup_cookie_jar_add_cookie_with_first_party (SoupCookieJar *jar, GUri *first_party, SoupCookie *cookie) @@ -710,13 +732,12 @@ * Adds @cookie to @jar, exactly as though it had appeared in a * Set-Cookie header returned from a request to @uri. * - * Keep in mind that if the #SoupCookieJarAcceptPolicy set is either + * Keep in mind that if the enum@CookieJarAcceptPolicy set is either * %SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY or * %SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY you'll need to use - * soup_cookie_jar_set_cookie_with_first_party(), otherwise the jar + * method@CookieJar.set_cookie_with_first_party, otherwise the jar * will have no way of knowing if the cookie is being set by a third * party or not. - * **/ void soup_cookie_jar_set_cookie (SoupCookieJar *jar, GUri *uri, @@ -754,10 +775,10 @@ * @cookie: the stringified cookie to set * * Adds @cookie to @jar, exactly as though it had appeared in a - * Set-Cookie header returned from a request to @uri. @first_party - * will be used to reject cookies coming from third party resources in - * case such a security policy is set in the @jar. + * Set-Cookie header returned from a request to @uri. * + * @first_party will be used to reject cookies coming from third party resources + * in case such a security policy is set in the @jar. **/ void soup_cookie_jar_set_cookie_with_first_party (SoupCookieJar *jar, @@ -860,13 +881,13 @@ * soup_cookie_jar_all_cookies: * @jar: a #SoupCookieJar * - * Constructs a #GSList with every cookie inside the @jar. + * Constructs a struct@GLib.List with every cookie inside the @jar. + * * The cookies in the list are a copy of the original, so * you have to free them when you are done with them. * * Returns: (transfer full) (element-type Soup.Cookie): a #GSList - * with all the cookies in the @jar. - * + * with all the cookies in the @jar. **/ GSList * soup_cookie_jar_all_cookies (SoupCookieJar *jar) @@ -880,6 +901,8 @@ priv = soup_cookie_jar_get_instance_private (jar); + g_mutex_lock (&priv->mutex); + g_hash_table_iter_init (&iter, priv->domains); while (g_hash_table_iter_next (&iter, &key, &value)) { @@ -888,6 +911,8 @@ l = g_slist_prepend (l, soup_cookie_copy (p->data)); } + g_mutex_unlock (&priv->mutex); + return l; } @@ -896,8 +921,9 @@ * @jar: a #SoupCookieJar * @cookie: a #SoupCookie * - * Deletes @cookie from @jar, emitting the 'changed' signal. + * Deletes @cookie from @jar. * + * Emits the signal@CookieJar::changed signal. **/ void soup_cookie_jar_delete_cookie (SoupCookieJar *jar, @@ -911,9 +937,13 @@ priv = soup_cookie_jar_get_instance_private (jar); + g_mutex_lock (&priv->mutex); + cookies = g_hash_table_lookup (priv->domains, soup_cookie_get_domain (cookie)); - if (cookies == NULL) + if (cookies == NULL) { + g_mutex_unlock (&priv->mutex); return; + } for (p = cookies; p; p = p->next ) { SoupCookie *c = (SoupCookie*)p->data; @@ -924,53 +954,54 @@ cookies); soup_cookie_jar_changed (jar, c, NULL); soup_cookie_free (c); + g_mutex_unlock (&priv->mutex); return; } } + + g_mutex_unlock (&priv->mutex); } /** * SoupCookieJarAcceptPolicy: * @SOUP_COOKIE_JAR_ACCEPT_ALWAYS: accept all cookies unconditionally. * @SOUP_COOKIE_JAR_ACCEPT_NEVER: reject all cookies unconditionally. - * @SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY: accept all cookies set by - * the main document loaded in the application using libsoup. An - * example of the most common case, web browsers, would be: If - * http://www.example.com is the page loaded, accept all cookies set - * by example.com, but if a resource from http://www.third-party.com - * is loaded from that page reject any cookie that it could try to - * set. For libsoup to be able to tell apart first party cookies from - * the rest, the application must call soup_message_set_first_party() - * on each outgoing #SoupMessage, setting the #GUri of the main - * document. If no first party is set in a message when this policy is - * in effect, cookies will be assumed to be third party by default. - * @SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY: accept all cookies - * set by the main document loaded in the application using libsoup, and - * from domains that have previously set at least one cookie when loaded - * as the main document. An example of the most common case, web browsers, - * would be: if http://www.example.com is the page loaded, accept all - * cookies set by example.com, but if a resource from http://www.third-party.com - * is loaded from that page, reject any cookie that it could try to - * set unless it already has a cookie in the cookie jar. For libsoup to - * be able to tell apart first party cookies from the rest, the - * application must call soup_message_set_first_party() on each outgoing - * #SoupMessage, setting the #GUri of the main document. If no first - * party is set in a message when this policy is in effect, cookies will - * be assumed to be third party by default. + * @SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY: accept all cookies set by the main + * document loaded in the application using libsoup. An example of the most + * common case, web browsers, would be: If http://www.example.com is the page + * loaded, accept all cookies set by example.com, but if a resource from + * http://www.third-party.com is loaded from that page reject any cookie that + * it could try to set. For libsoup to be able to tell apart first party + * cookies from the rest, the application must call + * method@Message.set_first_party on each outgoing class@Message, setting + * the struct@GLib.Uri of the main document. If no first party is set in a + * message when this policy is in effect, cookies will be assumed to be third + * party by default. + * @SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY: accept all cookies set by + * the main document loaded in the application using libsoup, and from domains + * that have previously set at least one cookie when loaded as the main + * document. An example of the most common case, web browsers, would be: if + * http://www.example.com is the page loaded, accept all cookies set by + * example.com, but if a resource from http://www.third-party.com is loaded + * from that page, reject any cookie that it could try to set unless it + * already has a cookie in the cookie jar. For libsoup to be able to tell + * apart first party cookies from the rest, the application must call + * method@Message.set_first_party on each outgoing #SoupMessage, setting the + * struct@GLib.Uri of the main document. If no first party is set in a + * message when this policy is in effect, cookies will be assumed to be third + * party by default. * * The policy for accepting or rejecting cookies returned in * responses. - * */ /** - * soup_cookie_jar_get_accept_policy: + * soup_cookie_jar_get_accept_policy: (attributes org.gtk.Method.get_property=accept-policy) * @jar: a #SoupCookieJar * - * Gets @jar's #SoupCookieJarAcceptPolicy + * Gets @jar's enum@CookieJarAcceptPolicy. * * Returns: the #SoupCookieJarAcceptPolicy set in the @jar - * **/ SoupCookieJarAcceptPolicy soup_cookie_jar_get_accept_policy (SoupCookieJar *jar) @@ -984,12 +1015,11 @@ } /** - * soup_cookie_jar_set_accept_policy: + * soup_cookie_jar_set_accept_policy: (attributes org.gtk.Method.set_property=accept-policy) * @jar: a #SoupCookieJar * @policy: a #SoupCookieJarAcceptPolicy * * Sets @policy as the cookie acceptance policy for @jar. - * **/ void soup_cookie_jar_set_accept_policy (SoupCookieJar *jar, @@ -1014,7 +1044,6 @@ * Gets whether @jar stores cookies persistenly. * * Returns: %TRUE if @jar storage is persistent or %FALSE otherwise. - * **/ gboolean soup_cookie_jar_is_persistent (SoupCookieJar *jar)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/cookies/soup-cookie.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/cookies/soup-cookie.c
Changed
@@ -20,21 +20,13 @@ #include "soup.h" /** - * SECTION:soup-cookie - * @short_description: HTTP Cookies - * @see_also: #SoupMessage, #SoupCookieJar - * - * #SoupCookie implements HTTP cookies, as described by <ulink - * url="http://tools.ietf.org/html/rfc6265.txt">RFC 6265</ulink>. - * - * To have a #SoupSession handle cookies for your appliction - * automatically, use a #SoupCookieJar. - **/ - -/** * SoupCookie: * - * An HTTP cookie. + * Implements HTTP cookies, as described by + * RFC 6265(http://tools.ietf.org/html/rfc6265.txt). + * + * To have a class@Session handle cookies for your appliction + * automatically, use a class@CookieJar. * * @name and @value will be set for all cookies. If the cookie is * generated from a string that appears to have no name, then @name @@ -54,7 +46,6 @@ * If @http_only is set, the cookie should not be exposed to untrusted * code (eg, javascript), so as to minimize the danger posed by * cross-site scripting attacks. - * **/ struct _SoupCookie { @@ -76,7 +67,6 @@ * Copies @cookie. * * Returns: a copy of @cookie - * **/ SoupCookie * soup_cookie_copy (SoupCookie *cookie) @@ -101,12 +91,12 @@ * @cookie: a #SoupCookie * @host: a URI * - * Checks if the @cookie's domain and @host match in the sense that - * @cookie should be sent when making a request to @host, or that - * @cookie should be accepted when receiving a response from @host. + * Checks if the @cookie's domain and @host match. + * + * The domains match if @cookie should be sent when making a request to @host, + * or that @cookie should be accepted when receiving a response from @host. * * Returns: %TRUE if the domains match, %FALSE otherwise - * **/ gboolean soup_cookie_domain_matches (SoupCookie *cookie, const char *host) @@ -349,9 +339,10 @@ * @path: cookie path, or %NULL * @max_age: max age of the cookie, or -1 for a session cookie * - * Creates a new #SoupCookie with the given attributes. (Use - * soup_cookie_set_secure() and soup_cookie_set_http_only() if you - * need to set those attributes on the returned cookie.) + * Creates a new #SoupCookie with the given attributes. + * + * Use method@Cookie.set_secure and method@Cookie.set_http_only if you + * need to set those attributes on the returned cookie. * * If @domain starts with ".", that indicates a domain (which matches * the string after the ".", or any hostname that has @domain as a @@ -365,10 +356,9 @@ * %SOUP_COOKIE_MAX_AGE_ONE_WEEK and %SOUP_COOKIE_MAX_AGE_ONE_YEAR (or * multiples thereof) to calculate this value. (If you really care * about setting the exact time that the cookie will expire, use - * soup_cookie_set_expires().) + * method@Cookie.set_expires.) * * Returns: a new #SoupCookie. - * **/ SoupCookie * soup_cookie_new (const char *name, const char *value, @@ -393,10 +383,11 @@ /** * soup_cookie_parse: * @header: a cookie string (eg, the value of a Set-Cookie header) - * @origin: (nullable): origin of the cookie, or %NULL + * @origin: (nullable): origin of the cookie + * + * Parses @header and returns a #SoupCookie. * - * Parses @header and returns a #SoupCookie. (If @header contains - * multiple cookies, only the first one will be parsed.) + * If @header contains multiple cookies, only the first one will be parsed. * * If @header does not have "path" or "domain" attributes, they will * be defaulted from @origin. If @origin is %NULL, path will default @@ -406,9 +397,8 @@ * of the cookie. * * Returns: (nullable): a new #SoupCookie, or %NULL if it could - * not be parsed, or contained an illegal "domain" attribute for a - * cookie originating from @origin. - * + * not be parsed, or contained an illegal "domain" attribute for a + * cookie originating from @origin. **/ SoupCookie * soup_cookie_parse (const char *cookie, GUri *origin) @@ -423,10 +413,9 @@ * soup_cookie_get_name: * @cookie: a #SoupCookie * - * Gets @cookie's name + * Gets @cookie's name. * * Returns: @cookie's name - * **/ const char * soup_cookie_get_name (SoupCookie *cookie) @@ -439,8 +428,7 @@ * @cookie: a #SoupCookie * @name: the new name * - * Sets @cookie's name to @name - * + * Sets @cookie's name to @name. **/ void soup_cookie_set_name (SoupCookie *cookie, const char *name) @@ -453,10 +441,9 @@ * soup_cookie_get_value: * @cookie: a #SoupCookie * - * Gets @cookie's value + * Gets @cookie's value. * * Returns: @cookie's value - * **/ const char * soup_cookie_get_value (SoupCookie *cookie) @@ -469,8 +456,7 @@ * @cookie: a #SoupCookie * @value: the new value * - * Sets @cookie's value to @value - * + * Sets @cookie's value to @value. **/ void soup_cookie_set_value (SoupCookie *cookie, const char *value) @@ -483,10 +469,9 @@ * soup_cookie_get_domain: * @cookie: a #SoupCookie * - * Gets @cookie's domain + * Gets @cookie's domain. * * Returns: @cookie's domain - * **/ const char * soup_cookie_get_domain (SoupCookie *cookie) @@ -499,8 +484,7 @@ * @cookie: a #SoupCookie * @domain: the new domain * - * Sets @cookie's domain to @domain - * + * Sets @cookie's domain to @domain. **/ void soup_cookie_set_domain (SoupCookie *cookie, const char *domain) @@ -513,10 +497,9 @@ * soup_cookie_get_path: * @cookie: a #SoupCookie * - * Gets @cookie's path + * Gets @cookie's path. * * Returns: @cookie's path - * **/ const char * soup_cookie_get_path (SoupCookie *cookie) @@ -529,8 +512,7 @@ * @cookie: a #SoupCookie * @path: the new path * - * Sets @cookie's path to @path - * + * Sets @cookie's path to @path. **/ void soup_cookie_set_path (SoupCookie *cookie, const char *path) @@ -544,17 +526,17 @@ * @cookie: a #SoupCookie * @max_age: the new max age * - * Sets @cookie's max age to @max_age. If @max_age is -1, the cookie - * is a session cookie, and will expire at the end of the client's - * session. Otherwise, it is the number of seconds until the cookie - * expires. You can use the constants %SOUP_COOKIE_MAX_AGE_ONE_HOUR, - * %SOUP_COOKIE_MAX_AGE_ONE_DAY, %SOUP_COOKIE_MAX_AGE_ONE_WEEK and - * %SOUP_COOKIE_MAX_AGE_ONE_YEAR (or multiples thereof) to calculate - * this value. (A value of 0 indicates that the cookie should be - * considered already-expired.) + * Sets @cookie's max age to @max_age. * - * (This sets the same property as soup_cookie_set_expires().) + * If @max_age is -1, the cookie is a session cookie, and will expire at the end + * of the client's session. Otherwise, it is the number of seconds until the + * cookie expires. You can use the constants %SOUP_COOKIE_MAX_AGE_ONE_HOUR, + * %SOUP_COOKIE_MAX_AGE_ONE_DAY, %SOUP_COOKIE_MAX_AGE_ONE_WEEK and + * %SOUP_COOKIE_MAX_AGE_ONE_YEAR (or multiples thereof) to calculate this value. + * (A value of 0 indicates that the cookie should be considered + * already-expired.) * + * This sets the same property as method@Cookie.set_expires. **/ void soup_cookie_set_max_age (SoupCookie *cookie, int max_age) @@ -579,30 +561,30 @@ /** * SOUP_COOKIE_MAX_AGE_ONE_HOUR: * - * A constant corresponding to 1 hour, for use with soup_cookie_new() - * and soup_cookie_set_max_age(). + * A constant corresponding to 1 hour. * + * For use with ctor@Cookie.new and method@Cookie.set_max_age. **/ /** * SOUP_COOKIE_MAX_AGE_ONE_DAY: * - * A constant corresponding to 1 day, for use with soup_cookie_new() - * and soup_cookie_set_max_age(). + * A constant corresponding to 1 day. * + * For use with ctor@Cookie.new and method@Cookie.set_max_age. **/ /** * SOUP_COOKIE_MAX_AGE_ONE_WEEK: * - * A constant corresponding to 1 week, for use with soup_cookie_new() - * and soup_cookie_set_max_age(). + * A constant corresponding to 1 week. * + * For use with ctor@Cookie.new and method@Cookie.set_max_age. **/ /** * SOUP_COOKIE_MAX_AGE_ONE_YEAR: * - * A constant corresponding to 1 year, for use with soup_cookie_new() - * and soup_cookie_set_max_age(). + * A constant corresponding to 1 year. * + * For use with ctor@Cookie.new and method@Cookie.set_max_age. **/ /** @@ -611,10 +593,8 @@ * * Gets @cookie's expiration time. * - * Returns: (nullable) (transfer none): @cookie's expiration - * time, which is owned by @cookie and should not be modified or - * freed. - * + * Returns: (nullable) (transfer none): @cookie's expiration time, which is + * owned by @cookie and should not be modified or freed. **/ GDateTime * soup_cookie_get_expires (SoupCookie *cookie) @@ -627,12 +607,12 @@ * @cookie: a #SoupCookie * @expires: the new expiration time, or %NULL * - * Sets @cookie's expiration time to @expires. If @expires is %NULL, - * @cookie will be a session cookie and will expire at the end of the - * client's session. + * Sets @cookie's expiration time to @expires. * - * (This sets the same property as soup_cookie_set_max_age().) + * If @expires is %NULL, @cookie will be a session cookie and will expire at the + * end of the client's session. * + * (This sets the same property as method@Cookie.set_max_age.) **/ void soup_cookie_set_expires (SoupCookie *cookie, GDateTime *expires) @@ -650,10 +630,9 @@ * soup_cookie_get_secure: * @cookie: a #SoupCookie * - * Gets @cookie's secure attribute + * Gets @cookie's secure attribute. * * Returns: @cookie's secure attribute - * **/ gboolean soup_cookie_get_secure (SoupCookie *cookie) @@ -666,10 +645,10 @@ * @cookie: a #SoupCookie * @secure: the new value for the secure attribute * - * Sets @cookie's secure attribute to @secure. If %TRUE, @cookie will - * only be transmitted from the client to the server over secure - * (https) connections. + * Sets @cookie's secure attribute to @secure. * + * If %TRUE, @cookie will only be transmitted from the client to the server over + * secure (https) connections. **/ void soup_cookie_set_secure (SoupCookie *cookie, gboolean secure) @@ -681,10 +660,9 @@ * soup_cookie_get_http_only: * @cookie: a #SoupCookie * - * Gets @cookie's HttpOnly attribute + * Gets @cookie's HttpOnly attribute. * * Returns: @cookie's HttpOnly attribute - * **/ gboolean soup_cookie_get_http_only (SoupCookie *cookie) @@ -697,10 +675,10 @@ * @cookie: a #SoupCookie * @http_only: the new value for the HttpOnly attribute * - * Sets @cookie's HttpOnly attribute to @http_only. If %TRUE, @cookie - * will be marked as "http only", meaning it should not be exposed to - * web page scripts or other untrusted code. + * Sets @cookie's HttpOnly attribute to @http_only. * + * If %TRUE, @cookie will be marked as "http only", meaning it should not be + * exposed to web page scripts or other untrusted code. **/ void soup_cookie_set_http_only (SoupCookie *cookie, gboolean http_only) @@ -772,9 +750,9 @@ * @cookie: a #SoupCookie * @policy: a #SoupSameSitePolicy * - * When used in conjunction with soup_cookie_jar_get_cookie_list_with_same_site_info() this - * sets the policy of when this cookie should be exposed. - * + * When used in conjunction with + * method@CookieJar.get_cookie_list_with_same_site_info this sets the policy + * of when this cookie should be exposed. **/ void soup_cookie_set_same_site_policy (SoupCookie *cookie, @@ -798,7 +776,6 @@ * Returns the same-site policy for this cookie. * * Returns: a #SoupSameSitePolicy - * **/ SoupSameSitePolicy soup_cookie_get_same_site_policy (SoupCookie *cookie) @@ -810,11 +787,11 @@ * soup_cookie_to_set_cookie_header: * @cookie: a #SoupCookie * - * Serializes @cookie in the format used by the Set-Cookie header - * (ie, for sending a cookie from a #SoupServer to a client). + * Serializes @cookie in the format used by the Set-Cookie header. * - * Returns: the header + * i.e. for sending a cookie from a class@Server to a client. * + * Returns: the header **/ char * soup_cookie_to_set_cookie_header (SoupCookie *cookie) @@ -830,10 +807,9 @@ * @cookie: a #SoupCookie * * Serializes @cookie in the format used by the Cookie header (ie, for - * returning a cookie from a #SoupSession to a server). + * returning a cookie from a class@Session to a server). * * Returns: the header - * **/ char * soup_cookie_to_cookie_header (SoupCookie *cookie) @@ -848,8 +824,7 @@ * soup_cookie_free: * @cookie: a #SoupCookie * - * Frees @cookie - * + * Frees @cookie. **/ void soup_cookie_free (SoupCookie *cookie) @@ -870,14 +845,14 @@ * soup_cookies_from_response: * @msg: a #SoupMessage containing a "Set-Cookie" response header * - * Parses @msg's Set-Cookie response headers and returns a #GSList of - * #SoupCookie<!-- -->s. Cookies that do not specify "path" or - * "domain" attributes will have their values defaulted from @msg. + * Parses @msg's Set-Cookie response headers and returns a struct@GLib.SList + * of `SoupCookie`s. * - * Returns: (element-type SoupCookie) (transfer full): a #GSList - * of #SoupCookie<!-- -->s, which can be freed with - * soup_cookies_free(). + * Cookies that do not specify "path" or "domain" attributes will have their + * values defaulted from @msg. * + * Returns: (element-type SoupCookie) (transfer full): a #GSList of + * `SoupCookie`s, which can be freed with method@Cookie.free. **/ GSList * soup_cookies_from_response (SoupMessage *msg) @@ -910,17 +885,16 @@ * soup_cookies_from_request: * @msg: a #SoupMessage containing a "Cookie" request header * - * Parses @msg's Cookie request header and returns a #GSList of - * #SoupCookie<!-- -->s. As the "Cookie" header, unlike "Set-Cookie", - * only contains cookie names and values, none of the other - * #SoupCookie fields will be filled in. (Thus, you can't generally - * pass a cookie returned from this method directly to - * soup_cookies_to_response().) + * Parses @msg's Cookie request header and returns a struct@GLib.SList of + * `SoupCookie`s. * - * Returns: (element-type SoupCookie) (transfer full): a #GSList - * of #SoupCookie<!-- -->s, which can be freed with - * soup_cookies_free(). + * As the "Cookie" header, unlike "Set-Cookie", only contains cookie names and + * values, none of the other #SoupCookie fields will be filled in. (Thus, you + * can't generally pass a cookie returned from this method directly to + * func@cookies_to_response.) * + * Returns: (element-type SoupCookie) (transfer full): a #GSList of + * `SoupCookie`s, which can be freed with method@Cookie.free. **/ GSList * soup_cookies_from_request (SoupMessage *msg) @@ -956,9 +930,10 @@ * @msg: a #SoupMessage * * Appends a "Set-Cookie" response header to @msg for each cookie in - * @cookies. (This is in addition to any other "Set-Cookie" headers - * @msg may already have.) + * @cookies. * + * This is in addition to any other "Set-Cookie" headers + * @msg may already have. **/ void soup_cookies_to_response (GSList *cookies, SoupMessage *msg) @@ -982,11 +957,11 @@ * @msg: a #SoupMessage * * Adds the name and value of each cookie in @cookies to @msg's - * "Cookie" request. (If @msg already has a "Cookie" request header, - * these cookies will be appended to the cookies already present. Be - * careful that you do not append the same cookies twice, eg, when - * requeuing a message.) + * "Cookie" request. * + * If @msg already has a "Cookie" request header, these cookies will be appended + * to the cookies already present. Be careful that you do not append the same + * cookies twice, eg, when requeuing a message. **/ void soup_cookies_to_request (GSList *cookies, SoupMessage *msg) @@ -1009,7 +984,6 @@ * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie * * Frees @cookies. - * **/ void soup_cookies_free (GSList *cookies) @@ -1021,11 +995,10 @@ * soup_cookies_to_cookie_header: * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie * - * Serializes a #GSList of #SoupCookie into a string suitable for + * Serializes a struct@GLib.SList of #SoupCookie into a string suitable for * setting as the value of the "Cookie" header. * * Returns: the serialization of @cookies - * **/ char * soup_cookies_to_cookie_header (GSList *cookies) @@ -1054,9 +1027,7 @@ * @uri, because it assumes that the caller has already done that. * But don't rely on that; it may change in the future.) * - * Returns: %TRUE if @cookie should be sent to @uri, %FALSE if - * not - * + * Returns: %TRUE if @cookie should be sent to @uri, %FALSE if not **/ gboolean soup_cookie_applies_to_uri (SoupCookie *cookie, GUri *uri) @@ -1100,7 +1071,6 @@ * match. This may change in the future. * * Returns: whether the cookies are equal. - * */ gboolean soup_cookie_equal (SoupCookie *cookie1, SoupCookie *cookie2)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/generate-version-header.py -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/generate-version-header.py
Changed
@@ -19,8 +19,8 @@ # Specific to 3.x dev releases versions.append((3, 0)) else: - minor_max = minor if minor != 0 and is_stable(minor) else minor + 1 - for i in range(0, minor_max): + minor_max = minor if is_stable(minor) else minor + 1 + for i in range(0, minor_max + 1): if is_stable(i): versions.append((3, i)) @@ -37,19 +37,32 @@ * Since: {major_version}.{minor_version} */ #define SOUP_VERSION_{major_version}_{minor_version} (G_ENCODE_VERSION ({major_version}, {minor_version})) - '''.format(major_version=version0, minor_version=version1) - version_attributes += '''#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_{major_version}_{minor_version} -# define SOUP_DEPRECATED_IN_{major_version}_{minor_version} G_DEPRECATED -# define SOUP_DEPRECATED_IN_{major_version}_{minor_version}_FOR(f) G_DEPRECATED_FOR(f) + version_attributes += '''/** + * SOUP_DEPRECATED_IN_{major_version}_{minor_version}: + * A macro used to indicate a symbol was deprecated in this version. + */ +/** + * SOUP_DEPRECATED_IN_{major_version}_{minor_version}_FOR: + * @f: The recommended replacement function. + * + * A macro used to indicate a symbol was deprecated in this version with a replacement. + */ +#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_{major_version}_{minor_version} +# define SOUP_DEPRECATED_IN_{major_version}_{minor_version} SOUP_DEPRECATED +# define SOUP_DEPRECATED_IN_{major_version}_{minor_version}_FOR(f) SOUP_DEPRECATED_FOR(f) #else -# define SOUP_DEPRECATED_IN_{major_version}_{minor_version} -# define SOUP_DEPRECATED_IN_{major_version}_{minor_version}_FOR(f) +# define SOUP_DEPRECATED_IN_{major_version}_{minor_version} _SOUP_EXTERN +# define SOUP_DEPRECATED_IN_{major_version}_{minor_version}_FOR(f) _SOUP_EXTERN #endif +/** + * SOUP_AVAILABLE_IN_{major_version}_{minor_version}: + * A macro used to indicate a symbol is available in this version or later. + */ #if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_{major_version}_{minor_version} -# define SOUP_AVAILABLE_IN_{major_version}_{minor_version} G_UNAVAILABLE({major_version}, {minor_version}) _SOUP_EXTERN +# define SOUP_AVAILABLE_IN_{major_version}_{minor_version} SOUP_UNAVAILABLE({major_version}, {minor_version}) #else # define SOUP_AVAILABLE_IN_{major_version}_{minor_version} _SOUP_EXTERN #endif
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/hsts/soup-hsts-enforcer-db.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/hsts/soup-hsts-enforcer-db.c
Changed
@@ -19,19 +19,14 @@ #include "soup.h" /** - * SECTION:soup-hsts-enforcer-db - * @short_description: Persistent HTTP Strict Transport Security enforcer + * SoupHSTSEnforcerDB: + * + * Persistent HTTP Strict Transport Security enforcer. * - * #SoupHSTSEnforcerDB is a #SoupHSTSEnforcer that uses a SQLite + * #SoupHSTSEnforcerDB is a class@HSTSEnforcer that uses a SQLite * database as a backend for persistency. **/ -/** - * SoupHSTSEnforcerDB: - * - * Subclass of #SoupHSTSEnforcer using an sqlite database. - */ - enum { PROP_0, @@ -116,10 +111,9 @@ * policies. If the file doesn't exist, a new database will be created * and initialized. Changes to the policies during the lifetime of a * #SoupHSTSEnforcerDB will be written to @filename when - * #SoupHSTSEnforcer::changed is emitted. + * signal@HSTSEnforcer::changed is emitted. * * Returns: the new #SoupHSTSEnforcer - * **/ SoupHSTSEnforcer * soup_hsts_enforcer_db_new (const char *filename)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/hsts/soup-hsts-enforcer.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/hsts/soup-hsts-enforcer.c
Changed
@@ -19,39 +19,32 @@ #include "soup-uri-utils-private.h" /** - * SECTION:soup-hsts-enforcer - * @short_description: Automatic HTTP Strict Transport Security enforcing - * for #SoupSession + * SoupHSTSEnforcer: + * + * Automatic HTTP Strict Transport Security enforcing for class@Session. * * A #SoupHSTSEnforcer stores HSTS policies and enforces them when - * required. #SoupHSTSEnforcer implements #SoupSessionFeature, so you + * required. #SoupHSTSEnforcer implements iface@SessionFeature, so you * can add an HSTS enforcer to a session with - * soup_session_add_feature() or soup_session_add_feature_by_type(). + * method@Session.add_feature or method@Session.add_feature_by_type. * * #SoupHSTSEnforcer keeps track of all the HTTPS destinations that, * when connected to, return the Strict-Transport-Security header with * valid values. #SoupHSTSEnforcer will forget those destinations * upon expiry or when the server requests it. * - * When the #SoupSession the #SoupHSTSEnforcer is attached to queues - * or restarts a message, the #SoupHSTSEnforcer will rewrite the URI - * to HTTPS if the destination is a known HSTS host and is contacted - * over an insecure transport protocol (HTTP). Users of - * #SoupHSTSEnforcer are advised to listen to changes in - * SoupMessage:uri in order to be aware of changes in the message URI. + * When the class@Session the #SoupHSTSEnforcer is attached to queues or + * restarts a message, the #SoupHSTSEnforcer will rewrite the URI to HTTPS if + * the destination is a known HSTS host and is contacted over an insecure + * transport protocol (HTTP). Users of #SoupHSTSEnforcer are advised to listen + * to changes in the property@Message:uri property in order to be aware of + * changes in the message URI. * * Note that #SoupHSTSEnforcer does not support any form of long-term - * HSTS policy persistence. See #SoupHSTSEnforcerDB for a persistent + * HSTS policy persistence. See class@HSTSEnforcerDB for a persistent * enforcer. - * **/ -/** - * SoupHSTSEnforcer: - * - * Class for storing and enforcing a #SoupHSTSPolicy. - */ - static void soup_hsts_enforcer_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data); enum { @@ -63,6 +56,7 @@ typedef struct { SoupSession *session; + GMutex mutex; GHashTable *host_policies; GHashTable *session_policies; } SoupHSTSEnforcerPrivate; @@ -84,6 +78,7 @@ priv->session_policies = g_hash_table_new_full (soup_str_case_hash, soup_str_case_equal, g_free, NULL); + g_mutex_init (&priv->mutex); } static void @@ -103,6 +98,8 @@ soup_hsts_policy_free (value); g_hash_table_destroy (priv->session_policies); + g_mutex_clear (&priv->mutex); + G_OBJECT_CLASS (soup_hsts_enforcer_parent_class)->finalize (object); } @@ -160,7 +157,9 @@ * @old_policy: the old #SoupHSTSPolicy value * @new_policy: the new #SoupHSTSPolicy value * - * Emitted when @hsts_enforcer changes. If a policy has been added, + * Emitted when @hsts_enforcer changes. + * + * If a policy has been added, * @new_policy will contain the newly-added policy and * @old_policy will be %NULL. If a policy has been deleted, * @old_policy will contain the to-be-deleted policy and @@ -186,12 +185,12 @@ /** * soup_hsts_enforcer_new: * - * Creates a new #SoupHSTSEnforcer. The base #SoupHSTSEnforcer class - * does not support persistent storage of HSTS policies, see - * #SoupHSTSEnforcerDB for that. + * Creates a new #SoupHSTSEnforcer. * - * Returns: a new #SoupHSTSEnforcer + * The base #SoupHSTSEnforcer class does not support persistent storage of HSTS + * policies, see class@HSTSEnforcerDB for that. * + * Returns: a new #SoupHSTSEnforcer **/ SoupHSTSEnforcer * soup_hsts_enforcer_new (void) @@ -317,14 +316,14 @@ * @hsts_enforcer: a #SoupHSTSEnforcer * @policy: (transfer none): the policy of the HSTS host * - * Sets @policy to @hsts_enforcer. If @policy is expired, any - * existing HSTS policy for its host will be removed instead. If a - * policy existed for this host, it will be replaced. Otherwise, the - * new policy will be inserted. If the policy is a session policy, that - * is, one created with soup_hsts_policy_new_session_policy(), the policy - * will not expire and will be enforced during the lifetime of - * @hsts_enforcer's #SoupSession. + * Sets @policy to @hsts_enforcer. * + * If @policy is expired, any existing HSTS policy for its host will be removed + * instead. If a policy existed for this host, it will be replaced. Otherwise, + * the new policy will be inserted. If the policy is a session policy, that is, + * one created with ctor@HSTSPolicy.new_session_policy, the policy will not + * expire and will be enforced during the lifetime of @hsts_enforcer's + * class@Session. **/ void soup_hsts_enforcer_set_policy (SoupHSTSEnforcer *hsts_enforcer, @@ -342,21 +341,24 @@ domain = soup_hsts_policy_get_domain (policy); g_return_if_fail (domain != NULL); - is_session_policy = soup_hsts_policy_is_session_policy (policy); - policies = is_session_policy ? priv->session_policies : - priv->host_policies; + g_mutex_lock (&priv->mutex); + is_session_policy = soup_hsts_policy_is_session_policy (policy); if (!is_session_policy && soup_hsts_policy_is_expired (policy)) { soup_hsts_enforcer_remove_host_policy (hsts_enforcer, domain); + g_mutex_unlock (&priv->mutex); return; } + policies = is_session_policy ? priv->session_policies : priv->host_policies; current_policy = g_hash_table_lookup (policies, domain); if (current_policy) soup_hsts_enforcer_replace_policy (hsts_enforcer, policy); else soup_hsts_enforcer_insert_policy (hsts_enforcer, policy); + + g_mutex_unlock (&priv->mutex); } /** @@ -365,10 +367,10 @@ * @domain: policy domain or hostname * @include_subdomains: %TRUE if the policy applies on sub domains * - * Sets a session policy for @domain. A session policy is a policy - * that is permanent to the lifetime of @hsts_enforcer's #SoupSession - * and doesn't expire. + * Sets a session policy for @domain. * + * A session policy is a policy that is permanent to the lifetime of + * @hsts_enforcer's class@Session and doesn't expire. **/ void soup_hsts_enforcer_set_session_policy (SoupHSTSEnforcer *hsts_enforcer, @@ -426,19 +428,28 @@ soup_hsts_enforcer_must_enforce_secure_transport (SoupHSTSEnforcer *hsts_enforcer, const char *domain) { + SoupHSTSEnforcerPrivate *priv = soup_hsts_enforcer_get_instance_private (hsts_enforcer); const char *super_domain = domain; g_return_val_if_fail (domain != NULL, FALSE); - if (soup_hsts_enforcer_has_valid_policy (hsts_enforcer, domain)) + g_mutex_lock (&priv->mutex); + + if (soup_hsts_enforcer_has_valid_policy (hsts_enforcer, domain)) { + g_mutex_unlock (&priv->mutex); return TRUE; + } while ((super_domain = super_domain_of (super_domain)) != NULL) { if (soup_hsts_enforcer_host_includes_subdomains (hsts_enforcer, super_domain) && - soup_hsts_enforcer_has_valid_policy (hsts_enforcer, super_domain)) + soup_hsts_enforcer_has_valid_policy (hsts_enforcer, super_domain)) { + g_mutex_unlock (&priv->mutex); return TRUE; + } } + g_mutex_unlock (&priv->mutex); + return FALSE; } @@ -581,7 +592,6 @@ * Gets whether @hsts_enforcer stores policies persistenly. * * Returns: %TRUE if @hsts_enforcer storage is persistent or %FALSE otherwise. - * **/ gboolean soup_hsts_enforcer_is_persistent (SoupHSTSEnforcer *hsts_enforcer) @@ -599,8 +609,7 @@ * Gets whether @hsts_enforcer has a currently valid policy for @domain. * * Returns: %TRUE if access to @domain should happen over HTTPS, false - * otherwise. - * + * otherwise. **/ gboolean soup_hsts_enforcer_has_valid_policy (SoupHSTSEnforcer *hsts_enforcer, @@ -641,10 +650,9 @@ * * Gets a list of domains for which there are policies in @enforcer. * - * * Returns: (element-type utf8) (transfer full): a newly allocated - * list of domains. Use g_list_free_full() and g_free() to free the - * list. + * list of domains. Use func@GLib.List.free_full and func@GLib.free to free the + * list. **/ GList* soup_hsts_enforcer_get_domains (SoupHSTSEnforcer *hsts_enforcer, @@ -679,10 +687,8 @@ * Gets a list with the policies in @enforcer. * * Returns: (element-type SoupHSTSPolicy) (transfer full): a newly - * allocated list of policies. Use g_list_free_full() and - * soup_hsts_policy_free() to free the list. - * - * + * allocated list of policies. Use func@GLib.List.free_full and + * method@HSTSPolicy.free to free the list. **/ GList* soup_hsts_enforcer_get_policies (SoupHSTSEnforcer *hsts_enforcer,
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/hsts/soup-hsts-enforcer.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/hsts/soup-hsts-enforcer.h
Changed
@@ -18,7 +18,7 @@ * SoupHSTSEnforcerClass: * @parent_class: The parent class. * @is_persistent: The @is_persistent function advertises whether the enforcer is persistent or - * whether changes made to it will be lost when the underlying #SoupSession is finished. + * whether changes made to it will be lost when the underlying class@Session is finished. * @has_valid_policy: The @has_valid_policy function is called to check whether there is a valid * policy for the given domain. This method should return %TRUE for #SoupHSTSEnforcer to * change the scheme of the #GUri in the #SoupMessage to HTTPS. Implementations might want to
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/hsts/soup-hsts-policy.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/hsts/soup-hsts-policy.c
Changed
@@ -19,22 +19,13 @@ #include "soup.h" /** - * SECTION:soup-hsts-policy - * @title: SoupHSTSPolicy - * @short_description: HSTS policies - * - * Policies to be used with #SoupHSTSEnforcer. - * - */ - -/** * SoupHSTSPolicy: * * #SoupHSTSPolicy implements HTTP policies, as described by * RFC 6797(http://tools.ietf.org/html/rfc6797). * * @domain represents the host that this policy applies to. The domain - * must be IDNA-canonicalized. soup_hsts_policy_new() and related methods + * must be IDNA-canonicalized. ctor@HSTSPolicy.new and related methods * will do this for you. * * @max_age contains the 'max-age' value from the Strict Transport @@ -47,7 +38,6 @@ * * If @include_subdomains is %TRUE, the Strict Transport Security policy * must also be enforced on subdomains of @domain. - * **/ struct _SoupHSTSPolicy { @@ -66,7 +56,6 @@ * Copies @policy. * * Returns: (transfer full): a copy of @policy - * **/ SoupHSTSPolicy * soup_hsts_policy_copy (SoupHSTSPolicy *policy) @@ -90,7 +79,6 @@ * Tests if @policy1 and @policy2 are equal. * * Returns: whether the policies are equal. - * */ gboolean soup_hsts_policy_equal (SoupHSTSPolicy *policy1, SoupHSTSPolicy *policy2) @@ -141,14 +129,13 @@ * represented by this object must be enforced. * * @max_age is used to set the "expires" attribute on the policy; pass - * SOUP_HSTS_POLICY_MAX_AGE_PAST for an already-expired policy, or a + * %SOUP_HSTS_POLICY_MAX_AGE_PAST for an already-expired policy, or a * lifetime in seconds. * * If @include_subdomains is %TRUE, the strict transport security policy * must also be enforced on all subdomains of @domain. * * Returns: a new #SoupHSTSPolicy. - * **/ SoupHSTSPolicy * soup_hsts_policy_new (const char *domain, @@ -178,11 +165,12 @@ * @expires: the date of expiration of the policy or %NULL for a permanent policy * @include_subdomains: %TRUE if the policy applies on subdomains * - * Full version of #soup_hsts_policy_new(), to use with an existing - * expiration date. See #soup_hsts_policy_new() for details. + * Full version of ctor@HSTSPolicy.new, to use with an existing + * expiration date. * - * Returns: a new #SoupHSTSPolicy. + * See ctor@HSTSPolicy.new for details. * + * Returns: a new #SoupHSTSPolicy. **/ SoupHSTSPolicy * soup_hsts_policy_new_full (const char *domain, @@ -219,8 +207,9 @@ * @include_subdomains: %TRUE if the policy applies on sub domains * * Creates a new session #SoupHSTSPolicy with the given attributes. + * * A session policy is a policy that is valid during the lifetime of - * the #SoupHSTSEnforcer it is added to. Contrary to regular policies, + * the class@HSTSEnforcer it is added to. Contrary to regular policies, * it has no expiration date and is not stored in persistent * enforcers. These policies are useful for user-agent to load their * own or user-defined rules. @@ -232,7 +221,6 @@ * must also be enforced on all subdomains of @domain. * * Returns: a new #SoupHSTSPolicy. - * **/ SoupHSTSPolicy * soup_hsts_policy_new_session_policy (const char *domain, @@ -253,8 +241,7 @@ * returns a #SoupHSTSPolicy. * * Returns: (nullable): a new #SoupHSTSPolicy, or %NULL if no valid - * "Strict-Transport-Security" response header was found. - * + * "Strict-Transport-Security" response header was found. **/ SoupHSTSPolicy * soup_hsts_policy_new_from_response (SoupMessage *msg) @@ -315,7 +302,6 @@ * Gets @policy's domain. * * Returns: (transfer none): @policy's domain. - * **/ const char * soup_hsts_policy_get_domain (SoupHSTSPolicy *policy) @@ -329,11 +315,11 @@ * soup_hsts_policy_is_expired: * @policy: a #SoupHSTSPolicy * - * Gets whether @policy is expired. Permanent policies never - * expire. + * Gets whether @policy is expired. * - * Returns: %TRUE if @policy is expired, %FALSE otherwise. + * Permanent policies never expire. * + * Returns: %TRUE if @policy is expired, %FALSE otherwise. **/ gboolean soup_hsts_policy_is_expired (SoupHSTSPolicy *policy) @@ -350,7 +336,6 @@ * Gets whether @policy include its subdomains. * * Returns: %TRUE if @policy includes subdomains, %FALSE otherwise. - * **/ gboolean soup_hsts_policy_includes_subdomains (SoupHSTSPolicy *policy) @@ -365,10 +350,10 @@ * @policy: a #SoupHSTSPolicy * * Gets whether @policy is a non-permanent, non-expirable session policy. - * see soup_hsts_policy_new_session_policy() for details. * - * Returns: %TRUE if @policy is permanent, %FALSE otherwise + * See ctor@HSTSPolicy.new_session_policy for details. * + * Returns: %TRUE if @policy is permanent, %FALSE otherwise **/ gboolean soup_hsts_policy_is_session_policy (SoupHSTSPolicy *policy) @@ -415,7 +400,6 @@ * @policy: (transfer full): a #SoupHSTSPolicy * * Frees @policy. - * **/ void soup_hsts_policy_free (SoupHSTSPolicy *policy)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/http1/soup-client-message-io-http1.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/http1/soup-client-message-io-http1.c
Changed
@@ -38,6 +38,9 @@ SoupMessageMetrics *metrics; + /* Request body logger */ + SoupLogger *logger; + #ifdef HAVE_SYSPROF gint64 begin_time_nsec; #endif @@ -146,8 +149,11 @@ client_io->msg_io->metrics->request_body_size += count; } - if (!is_metadata) + if (!is_metadata) { + if (client_io->msg_io->logger) + soup_logger_log_request_data (client_io->msg_io->logger, msg, (const char *)buffer, count); soup_message_wrote_body_data (msg, count); + } } static void @@ -252,7 +258,10 @@ uri_string = g_strdup_printf ("%s:%d", uri_host, g_uri_get_port (uri)); g_free (uri_host); } else { - gboolean proxy = soup_connection_is_via_proxy (soup_message_get_connection (msg)); + SoupConnection *conn = soup_message_get_connection (msg); + gboolean proxy = soup_connection_is_via_proxy (conn); + + g_object_unref (conn); /* Proxy expects full URI to destination. Otherwise * just the path. @@ -354,10 +363,7 @@ io->write_state = SOUP_MESSAGE_IO_STATE_BODY; logger = soup_session_get_feature_for_message (client_io->msg_io->item->session, SOUP_TYPE_LOGGER, msg); - if (logger) { - soup_logger_request_body_setup (SOUP_LOGGER (logger), msg, - SOUP_BODY_OUTPUT_STREAM (io->body_ostream)); - } + client_io->msg_io->logger = logger ? SOUP_LOGGER (logger) : NULL; break; case SOUP_MESSAGE_IO_STATE_BODY: @@ -854,9 +860,12 @@ soup_client_message_io_http1_get_priority (client_io)); g_source_attach (io->io_source, g_main_context_get_thread_default ()); } else { - if ((SoupClientMessageIOHTTP1 *)soup_message_get_io_data (msg) == client_io) - soup_message_io_finish (msg, error); - g_error_free (error); + if ((SoupClientMessageIOHTTP1 *)soup_message_get_io_data (msg) == client_io) { + g_assert (!client_io->msg_io->item->error); + client_io->msg_io->item->error = g_steal_pointer (&error); + soup_message_io_finish (msg, client_io->msg_io->item->error); + } + g_clear_error (&error); }
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/http2/soup-body-input-stream-http2.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/http2/soup-body-input-stream-http2.c
Changed
@@ -180,7 +180,7 @@ if (count == 0 && blocking && !priv->completed) { GError *read_error = NULL; g_signal_emit (memory_stream, signalsNEED_MORE_DATA, 0, - cancellable, &read_error); + blocking, cancellable, &read_error); if (read_error) { g_propagate_error (error, read_error); @@ -429,5 +429,6 @@ NULL, NULL, NULL, G_TYPE_ERROR, - 1, G_TYPE_CANCELLABLE); + 2, G_TYPE_BOOLEAN, + G_TYPE_CANCELLABLE); }
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/http2/soup-client-message-io-http2.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/http2/soup-client-message-io-http2.c
Changed
@@ -40,29 +40,19 @@ #include "soup-client-input-stream.h" #include "soup-logger-private.h" #include "soup-uri-utils-private.h" +#include "soup-http2-utils.h" #include "content-decoder/soup-content-decoder.h" #include "soup-body-input-stream-http2.h" -#include <nghttp2/nghttp2.h> - #define FRAME_HEADER_SIZE 9 -typedef enum { - STATE_NONE, - STATE_WRITE_HEADERS, - STATE_WRITE_DATA, - STATE_WRITE_DONE, - STATE_READ_HEADERS, - STATE_READ_DATA_START, - STATE_READ_DATA, - STATE_READ_DONE, -} SoupHTTP2IOState; - typedef struct { SoupClientMessageIO iface; - SoupConnection *conn; + GThread *owner; + gboolean async; + GWeakRef conn; GIOStream *stream; GInputStream *istream; GOutputStream *ostream; @@ -87,6 +77,7 @@ GTask *close_task; gboolean session_terminated; gboolean goaway_sent; + gboolean ever_used; guint in_callback; } SoupClientMessageIOHTTP2; @@ -116,6 +107,7 @@ gpointer completion_data; SoupHTTP2IOState state; GError *error; + uint32_t http2_error; gboolean paused; guint32 stream_id; gboolean can_be_restarted; @@ -125,93 +117,6 @@ static void soup_client_message_io_http2_finished (SoupClientMessageIO *iface, SoupMessage *msg); static ssize_t on_data_source_read_callback (nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data); -static void -NGCHECK (int return_code) -{ - if (return_code == NGHTTP2_ERR_NOMEM) - g_abort (); - else if (return_code < 0) - g_debug ("Unhandled NGHTTP2 Error: %s", nghttp2_strerror (return_code)); -} - -static const char * -frame_type_to_string (nghttp2_frame_type type) -{ - switch (type) { - case NGHTTP2_DATA: - return "DATA"; - case NGHTTP2_HEADERS: - return "HEADERS"; - case NGHTTP2_PRIORITY: - return "PRIORITY"; - case NGHTTP2_RST_STREAM: - return "RST_STREAM"; - case NGHTTP2_SETTINGS: - return "SETTINGS"; - case NGHTTP2_PING: - return "PING"; - case NGHTTP2_GOAWAY: - return "GOAWAY"; - case NGHTTP2_WINDOW_UPDATE: - return "WINDOW_UPDATE"; - /* LCOV_EXCL_START */ - case NGHTTP2_PUSH_PROMISE: - return "PUSH_PROMISE"; - case NGHTTP2_CONTINUATION: - return "CONTINUATION"; - case NGHTTP2_ALTSVC: - return "ALTSVC"; - case NGHTTP2_ORIGIN: - return "ORIGIN"; - default: - g_warn_if_reached (); - return "UNKNOWN"; - /* LCOV_EXCL_STOP */ - } -} - -static const char * -headers_category_to_string (nghttp2_headers_category catergory) -{ - switch (catergory) { - case NGHTTP2_HCAT_REQUEST: - return "REQUEST"; - case NGHTTP2_HCAT_RESPONSE: - return "RESPONSE"; - case NGHTTP2_HCAT_PUSH_RESPONSE: - return "PUSH_RESPONSE"; - case NGHTTP2_HCAT_HEADERS: - return "HEADERS"; - } - g_assert_not_reached (); -} - -static const char * -state_to_string (SoupHTTP2IOState state) -{ - switch (state) { - case STATE_NONE: - return "NONE"; - case STATE_WRITE_HEADERS: - return "WRITE_HEADERS"; - case STATE_WRITE_DATA: - return "WRITE_DATA"; - case STATE_WRITE_DONE: - return "WRITE_DONE"; - case STATE_READ_HEADERS: - return "READ_HEADERS"; - case STATE_READ_DATA_START: - return "READ_DATA_START"; - case STATE_READ_DATA: - return "READ_DATA"; - case STATE_READ_DONE: - return "READ_DONE"; - default: - g_assert_not_reached (); - return ""; - } -} - G_GNUC_PRINTF(3, 0) static void h2_debug (SoupClientMessageIOHTTP2 *io, @@ -234,7 +139,7 @@ stream_id = data->stream_id; g_assert (io); - g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "C%" G_GUINT64_FORMAT "-S%u %s %s", io->connection_id, stream_id, data ? state_to_string (data->state) : "-", message); + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "CLIENT C%" G_GUINT64_FORMAT "-S%u %s %s", io->connection_id, stream_id, data ? soup_http2_io_state_to_string (data->state) : "-", message); g_free (message); } @@ -268,6 +173,20 @@ } static void +set_http2_error_for_data (SoupHTTP2MessageData *data, + uint32_t error_code) +{ + h2_debug (data->io, data, "SESSION Error: %s", nghttp2_http2_strerror (error_code)); + + if (data->error) + return; + + data->http2_error = error_code; + data->error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, + "HTTP/2 Error: %s", nghttp2_http2_strerror (error_code)); +} + +static void set_io_error (SoupClientMessageIOHTTP2 *io, GError *error) { @@ -277,6 +196,11 @@ io->error = error; else g_error_free (error); + + if (io->close_task && !io->goaway_sent) { + g_task_return_boolean (io->close_task, TRUE); + g_clear_object (&io->close_task); + } } static void @@ -286,23 +210,40 @@ { if (data->state != from) { g_warning ("Unexpected state changed %s -> %s, expected to be from %s", - state_to_string (data->state), state_to_string (to), - state_to_string (from)); + soup_http2_io_state_to_string (data->state), soup_http2_io_state_to_string (to), + soup_http2_io_state_to_string (from)); } /* State never goes backwards */ if (to < data->state) { g_warning ("Unexpected state changed %s -> %s, expected %s -> %s\n", - state_to_string (data->state), state_to_string (to), - state_to_string (from), state_to_string (to)); + soup_http2_io_state_to_string (data->state), soup_http2_io_state_to_string (to), + soup_http2_io_state_to_string (from), soup_http2_io_state_to_string (to)); return; } h2_debug (data->io, data, "SESSION State %s -> %s", - state_to_string (data->state), state_to_string (to)); + soup_http2_io_state_to_string (data->state), soup_http2_io_state_to_string (to)); data->state = to; } +static gboolean +soup_http2_message_data_can_be_restarted (SoupHTTP2MessageData *data, + GError *error) +{ + if (data->can_be_restarted) + return TRUE; + + return data->state < STATE_READ_DATA_START && + data->io->ever_used && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT) && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + error->domain != G_TLS_ERROR && + data->http2_error == NGHTTP2_NO_ERROR && + SOUP_METHOD_IS_IDEMPOTENT (soup_message_get_method (data->msg)); +} + static void soup_http2_message_data_check_status (SoupHTTP2MessageData *data) { @@ -314,6 +255,7 @@ if (g_cancellable_set_error_if_cancelled (g_task_get_cancellable (task), &error)) { io->pending_io_messages = g_list_remove (io->pending_io_messages, data); data->task = NULL; + soup_client_message_io_http2_finished ((SoupClientMessageIO *)io, msg); g_task_return_error (task, error); g_object_unref (task); return; @@ -328,7 +270,7 @@ if (data->error) { GError *error = g_steal_pointer (&data->error); - if (data->can_be_restarted) + if (soup_http2_message_data_can_be_restarted (data, error)) data->item->state = SOUP_MESSAGE_RESTARTING; else soup_message_set_metrics_timestamp (data->msg, SOUP_MESSAGE_METRICS_RESPONSE_END); @@ -397,7 +339,7 @@ return G_SOURCE_REMOVE; } - while (nghttp2_session_want_write (io->session) && !error) + while (!error && nghttp2_session_want_write (io->session)) io_write (io, FALSE, NULL, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { @@ -425,14 +367,16 @@ if (blocking || !nghttp2_session_want_write (io->session)) return; } else { - while (nghttp2_session_want_write (io->session) && !error) + while (!error && nghttp2_session_want_write (io->session)) io_write (io, blocking, NULL, &error); } - if (io->in_callback || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + if (!blocking && (io->in_callback || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))) { g_clear_error (&error); io->write_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (io->ostream), NULL); g_source_set_name (io->write_source, "Soup HTTP/2 write source"); + /* Give write more priority than read */ + g_source_set_priority (io->write_source, G_PRIORITY_DEFAULT - 1); g_source_set_callback (io->write_source, (GSourceFunc)io_write_ready, io, NULL); g_source_attach (io->write_source, g_main_context_get_thread_default ()); } @@ -451,14 +395,24 @@ gssize read; int ret; + /* Always try to write before read, in case there's a pending reset stream after an error. */ + io_try_write (io, blocking); + if ((read = g_pollable_stream_read (io->istream, buffer, sizeof (buffer), blocking, cancellable, error)) < 0) return FALSE; + if (read == 0) { + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + _("Connection terminated unexpectedly")); + return FALSE; + } + g_warn_if_fail (io->in_callback == 0); ret = nghttp2_session_mem_recv (io->session, buffer, read); NGCHECK (ret); - return ret != 0; + return ret > 0; } static gboolean @@ -467,6 +421,7 @@ { GError *error = NULL; gboolean progress = TRUE; + SoupConnection *conn; if (io->error) { g_clear_pointer (&io->read_source, g_source_unref); @@ -476,10 +431,11 @@ /* Mark the connection as in use to make sure it's not disconnected while * processing pending messages, for example if a goaway is received. */ - if (io->conn) - soup_connection_set_in_use (io->conn, TRUE); + conn = g_weak_ref_get (&io->conn); + if (conn) + soup_connection_set_in_use (conn, TRUE); - while (nghttp2_session_want_read (io->session) && progress) { + while (progress && nghttp2_session_want_read (io->session)) { progress = io_read (io, FALSE, NULL, &error); if (progress) { g_list_foreach (io->pending_io_messages, @@ -490,11 +446,15 @@ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_error_free (error); - if (io->conn) - soup_connection_set_in_use (io->conn, FALSE); + if (conn) { + soup_connection_set_in_use (conn, FALSE); + g_object_unref (conn); + } return G_SOURCE_CONTINUE; } + io->is_shutdown = TRUE; + if (error) { set_io_error (io, error); g_list_foreach (io->pending_io_messages, @@ -502,11 +462,11 @@ NULL); } - io->is_shutdown = TRUE; - g_clear_pointer (&io->read_source, g_source_unref); - if (io->conn) - soup_connection_set_in_use (io->conn, FALSE); + if (conn) { + soup_connection_set_in_use (conn, FALSE); + g_object_unref (conn); + } return G_SOURCE_REMOVE; } @@ -546,7 +506,7 @@ io->session_terminated = TRUE; NGCHECK (nghttp2_session_terminate_session (io->session, NGHTTP2_NO_ERROR)); - io_try_write (io, FALSE); + io_try_write (io, !io->async); } /* HTTP2 read callbacks */ @@ -587,8 +547,25 @@ return 0; } +static int +on_invalid_header_callback (nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, + size_t namelen, + const uint8_t *value, + size_t valuelen, + uint8_t flags, + void *user_data) +{ + SoupHTTP2MessageData *data = nghttp2_session_get_stream_user_data (session, frame->hd.stream_id); + + h2_debug (user_data, data, "HEADERS Invalid header received: name=%.*s value=%.*s", namelen, name, valuelen, value); + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; +} + static GError * memory_stream_need_more_data_callback (SoupBodyInputStreamHttp2 *stream, + gboolean blocking, GCancellable *cancellable, gpointer user_data) { @@ -596,7 +573,7 @@ GError *error = NULL; if (nghttp2_session_want_read (data->io->session)) - io_read (data->io, TRUE, cancellable, &error); + io_read (data->io, blocking, cancellable, &error); return error; } @@ -608,7 +585,7 @@ { SoupHTTP2MessageData *data = nghttp2_session_get_stream_user_data (session, hd->stream_id); - h2_debug (user_data, data, "RECV %s Beginning", frame_type_to_string (hd->type)); + h2_debug (user_data, data, "RECV %s Beginning: stream_id=%u", soup_http2_frame_type_to_string (hd->type), hd->stream_id); if (!data) return 0; @@ -652,6 +629,9 @@ GHashTableIter iter; SoupHTTP2MessageData *data; + if (last_stream_id == G_MAXINT32) + return; + g_hash_table_iter_init (&iter, io->messages); while (g_hash_table_iter_next (&iter, NULL, (gpointer*)&data)) { /* If there is no error it is a graceful shutdown and @@ -659,8 +639,7 @@ if ((error_code == 0 && (int32_t)data->stream_id > last_stream_id) || data->state < STATE_READ_DONE) { /* TODO: We can restart unfinished messages */ - set_error_for_data (data, g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, - "HTTP/2 Error: %s", nghttp2_http2_strerror (error_code))); + set_http2_error_for_data (data, error_code); } } } @@ -676,7 +655,7 @@ io->in_callback++; if (frame->hd.stream_id == 0) { - h2_debug (io, NULL, "RECV %s Recieved (%u)", frame_type_to_string (frame->hd.type), frame->hd.flags); + h2_debug (io, NULL, "RECV %s Received: stream_id=%u, flags=%u", soup_http2_frame_type_to_string (frame->hd.type), frame->hd.stream_id, frame->hd.flags); switch (frame->hd.type) { case NGHTTP2_GOAWAY: @@ -699,11 +678,10 @@ } data = nghttp2_session_get_stream_user_data (session, frame->hd.stream_id); - h2_debug (io, data, "RECV %s Recieved (%u)", frame_type_to_string (frame->hd.type), frame->hd.flags); + h2_debug (io, data, "RECV %s Received: stream_id=%u, flags=%u", soup_http2_frame_type_to_string (frame->hd.type), frame->hd.stream_id, frame->hd.flags); if (!data) { - if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) && frame->hd.type != NGHTTP2_RST_STREAM) - g_warn_if_reached (); + /* This can happen in case of cancellation */ io->in_callback--; return 0; } @@ -716,7 +694,7 @@ data->metrics->response_header_bytes_received += frame->hd.length + FRAME_HEADER_SIZE; h2_debug (io, data, "HEADERS category=%s status=%u", - headers_category_to_string (frame->headers.cat), status); + soup_http2_headers_category_to_string (frame->headers.cat), status); switch (frame->headers.cat) { case NGHTTP2_HCAT_HEADERS: if (!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)) { @@ -748,12 +726,15 @@ g_assert_not_reached (); } + soup_message_got_headers (data->msg); + if (soup_message_get_status (data->msg) == SOUP_STATUS_NO_CONTENT || frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { h2_debug (io, data, "Stream done"); - advance_state_from (data, STATE_READ_HEADERS, STATE_READ_DATA); + advance_state_from (data, STATE_READ_HEADERS, STATE_READ_DATA_START); + if (soup_message_has_content_sniffer (data->msg)) + soup_message_content_sniffed (data->msg, "text/plain", NULL); + advance_state_from (data, STATE_READ_DATA_START, STATE_READ_DATA); } - soup_message_got_headers (data->msg); - break; } case NGHTTP2_DATA: @@ -774,10 +755,8 @@ } break; case NGHTTP2_RST_STREAM: - if (frame->rst_stream.error_code != NGHTTP2_NO_ERROR) { - set_error_for_data (data, g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, - nghttp2_http2_strerror (frame->rst_stream.error_code))); - } + if (frame->rst_stream.error_code != NGHTTP2_NO_ERROR) + set_http2_error_for_data (data, frame->rst_stream.error_code); break; case NGHTTP2_WINDOW_UPDATE: h2_debug (io, data, "RECV WINDOW_UPDATE: increment=%d, total=%d", frame->window_update.window_size_increment, @@ -802,12 +781,14 @@ SoupClientMessageIOHTTP2 *io = user_data; SoupHTTP2MessageData *msgdata = nghttp2_session_get_stream_user_data (session, stream_id); - if (!msgdata) - return NGHTTP2_ERR_CALLBACK_FAILURE; + h2_debug (io, msgdata, "DATA Received chunk, stream_id=%u len=%zu, flags=%u, paused=%d", stream_id, len, flags, msgdata ? msgdata->paused : 0); - io->in_callback++; + if (!msgdata) { + /* This can happen in case of cancellation */ + return 0; + } - h2_debug (io, msgdata, "DATA Recieved chunk, len=%zu, flags=%u, paused=%d", len, flags, msgdata->paused); + io->in_callback++; g_assert (msgdata->body_istream != NULL); soup_body_input_stream_http2_add_data (SOUP_BODY_INPUT_STREAM_HTTP2 (msgdata->body_istream), data, len); @@ -871,11 +852,16 @@ switch (frame->hd.type) { case NGHTTP2_HEADERS: - g_assert (data); - h2_debug (io, data, "SEND HEADERS category=%s finished=%d", - headers_category_to_string (frame->headers.cat), + h2_debug (io, data, "SEND HEADERS stream_id=%u, category=%s finished=%d", + frame->hd.stream_id, soup_http2_headers_category_to_string (frame->headers.cat), (frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) ? 1 : 0); + if (!data) { + /* This can happen in case of cancellation */ + io->in_callback--; + return 0; + } + if (data->metrics) data->metrics->request_header_bytes_sent += frame->hd.length + FRAME_HEADER_SIZE; @@ -888,12 +874,17 @@ } break; case NGHTTP2_DATA: - g_assert (data); + if (!data) { + /* This can happen in case of cancellation */ + io->in_callback--; + return 0; + } + if (data->state < STATE_WRITE_DATA) advance_state_from (data, STATE_WRITE_HEADERS, STATE_WRITE_DATA); - h2_debug (io, data, "SEND DATA bytes=%zu, finished=%d", - frame->data.hd.length, frame->hd.flags & NGHTTP2_FLAG_END_STREAM); + h2_debug (io, data, "SEND DATA stream_id=%u, bytes=%zu, finished=%d", + frame->hd.stream_id, frame->data.hd.length, frame->hd.flags & NGHTTP2_FLAG_END_STREAM); if (data->metrics) { data->metrics->request_body_bytes_sent += frame->hd.length + FRAME_HEADER_SIZE; data->metrics->request_body_size += frame->data.hd.length; @@ -907,10 +898,18 @@ break; case NGHTTP2_RST_STREAM: h2_debug (io, data, "SEND RST_STREAM stream_id=%u", frame->hd.stream_id); - g_hash_table_foreach_remove (io->closed_messages, (GHRFunc)remove_closed_stream, (gpointer)frame); + if (g_hash_table_foreach_remove (io->closed_messages, (GHRFunc)remove_closed_stream, (gpointer)frame)) { + SoupConnection *conn = g_weak_ref_get (&io->conn); + + if (conn) { + soup_connection_set_in_use (conn, FALSE); + g_object_unref (conn); + } + } + break; case NGHTTP2_GOAWAY: - h2_debug (io, data, "SEND %s", frame_type_to_string (frame->hd.type)); + h2_debug (io, data, "SEND %s", soup_http2_frame_type_to_string (frame->hd.type)); io->goaway_sent = TRUE; if (io->close_task) { GSource *source; @@ -919,12 +918,12 @@ source = g_idle_source_new (); g_source_set_name (source, "Soup HTTP/2 close source"); g_source_set_callback (source, (GSourceFunc)close_in_idle_cb, io, NULL); - g_source_attach (source, g_main_context_get_thread_default ()); + g_source_attach (source, g_task_get_context (io->close_task)); g_source_unref (source); } break; default: - h2_debug (io, data, "SEND %s", frame_type_to_string (frame->hd.type)); + h2_debug (io, data, "SEND %s stream_id=%u", soup_http2_frame_type_to_string (frame->hd.type), frame->hd.stream_id); break; } @@ -932,16 +931,44 @@ return 0; } +static gboolean +update_connection_in_use (gpointer key, + gpointer value, + SoupConnection *conn) +{ + soup_connection_set_in_use (conn, FALSE); + + return TRUE; +} + +static void +process_pending_closed_messages (SoupClientMessageIOHTTP2 *io) +{ + SoupConnection *conn = g_weak_ref_get (&io->conn); + + if (!conn) { + g_hash_table_remove_all (io->closed_messages); + return; + } + + g_hash_table_foreach_remove (io->closed_messages, (GHRFunc)update_connection_in_use, conn); + g_object_unref (conn); +} + static int on_frame_not_send_callback (nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) { + SoupClientMessageIOHTTP2 *io = user_data; SoupHTTP2MessageData *data = nghttp2_session_get_stream_user_data (session, frame->hd.stream_id); - h2_debug (user_data, data, "SEND %s Failed: %s", frame_type_to_string (frame->hd.type), - nghttp2_strerror (lib_error_code)); + h2_debug (io, data, "SEND %s Failed stream %u: %s", soup_http2_frame_type_to_string (frame->hd.type), + frame->hd.stream_id, nghttp2_strerror (lib_error_code)); + + if (lib_error_code == NGHTTP2_ERR_SESSION_CLOSING) + process_pending_closed_messages (io); return 0; } @@ -954,14 +981,27 @@ { SoupHTTP2MessageData *data = nghttp2_session_get_stream_user_data (session, stream_id); - h2_debug (user_data, data, "SESSION Closed: %s", nghttp2_http2_strerror (error_code)); + h2_debug (user_data, data, "SESSION Closed stream %u: %s", stream_id, nghttp2_http2_strerror (error_code)); if (!data) return 0; data->io->in_callback++; - if (error_code == NGHTTP2_REFUSED_STREAM && data->state < STATE_READ_DATA) + switch (error_code) { + case NGHTTP2_NO_ERROR: + break; + case NGHTTP2_REFUSED_STREAM: + if (data->state < STATE_READ_DATA_START) + data->can_be_restarted = TRUE; + break; + case NGHTTP2_HTTP_1_1_REQUIRED: + soup_message_set_force_http_version (data->item->msg, SOUP_HTTP_1_1); data->can_be_restarted = TRUE; + break; + default: + set_http2_error_for_data (data, error_code); + break; + } data->io->in_callback--; return 0; @@ -991,8 +1031,6 @@ GError *error = NULL; gssize read = g_input_stream_read_finish (source, res, &error); - h2_debug (data->io, data, "SEND_BODY Read %zd", read); - /* This operation may have outlived the message data in which case this will have been cancelled. */ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { @@ -1000,6 +1038,8 @@ return; } + h2_debug (data->io, data, "SEND_BODY Read %zd", read); + if (read < 0) { g_byte_array_set_size (data->data_source_buffer, 0); data->data_source_error = g_steal_pointer (&error); @@ -1037,9 +1077,43 @@ nghttp2_data_source *source, void *user_data) { + SoupClientMessageIOHTTP2 *io = user_data; SoupHTTP2MessageData *data = nghttp2_session_get_stream_user_data (session, stream_id); + + h2_debug (io, data, "SEND_BODY stream_id=%u, paused=%d", stream_id, data ? data->paused : 0); + + if (!data) { + /* This can happen in case of cancellation */ + return 0; + } + data->io->in_callback++; + if (!data->item->async) { + gssize read; + GError *error = NULL; + + read = g_input_stream_read (source->ptr, buf, length, data->item->cancellable, &error); + if (read) { + h2_debug (data->io, data, "SEND_BODY Read %zd", read); + log_request_data (data, buf, read); + } + + if (read < 0) { + set_error_for_data (data, g_steal_pointer (&error)); + data->io->in_callback--; + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (read == 0) { + h2_debug (data->io, data, "SEND_BODY EOF"); + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + + data->io->in_callback--; + return read; + } + /* We support pollable streams in the best case because they * should perform better with one fewer copy of each buffer and no threading. */ if (G_IS_POLLABLE_INPUT_STREAM (source->ptr) && g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (source->ptr))) { @@ -1246,24 +1320,6 @@ return !g_hash_table_contains (invalid_request_headers, name); } -#define MAKE_NV(NAME, VALUE, VALUELEN) \ - { \ - (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), VALUELEN, \ - NGHTTP2_NV_FLAG_NONE \ - } - -#define MAKE_NV2(NAME, VALUE) \ - { \ - (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), strlen (VALUE), \ - NGHTTP2_NV_FLAG_NONE \ - } - -#define MAKE_NV3(NAME, VALUE, FLAGS) \ - { \ - (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), strlen (VALUE), \ - FLAGS \ - } - static void send_message_request (SoupMessage *msg, SoupClientMessageIOHTTP2 *io, @@ -1371,21 +1427,26 @@ SoupMessageIOCompletionFn completion_cb; gpointer completion_data; SoupMessageIOCompletion completion; + gboolean is_closed; + SoupConnection *conn; data = get_data_for_message (io, msg); completion = data->state < STATE_READ_DONE ? SOUP_MESSAGE_IO_INTERRUPTED : SOUP_MESSAGE_IO_COMPLETE; - h2_debug (io, data, "Finished: %s", completion == SOUP_MESSAGE_IO_COMPLETE ? "completed" : "interrupted"); + h2_debug (io, data, "Finished stream %u: %s", data->stream_id, completion == SOUP_MESSAGE_IO_COMPLETE ? "completed" : "interrupted"); completion_cb = data->completion_cb; completion_data = data->completion_data; g_object_ref (msg); + is_closed = nghttp2_session_get_stream_user_data (io->session, data->stream_id) == NULL; nghttp2_session_set_stream_user_data (io->session, data->stream_id, NULL); - if (!io->is_shutdown) { + conn = g_weak_ref_get (&io->conn); + + if (!io->is_shutdown && !is_closed) { NGCHECK (nghttp2_submit_rst_stream (io->session, NGHTTP2_FLAG_NONE, data->stream_id, completion == SOUP_MESSAGE_IO_COMPLETE ? NGHTTP2_NO_ERROR : NGHTTP2_CANCEL)); soup_http2_message_data_close (data); @@ -1394,6 +1455,11 @@ g_warn_if_reached (); if (!g_hash_table_add (io->closed_messages, data)) g_warn_if_reached (); + + if (conn) + soup_connection_set_in_use (conn, TRUE); + + io_try_write (io, !io->async); } else { if (!g_hash_table_remove (io->messages, msg)) g_warn_if_reached (); @@ -1404,12 +1470,10 @@ g_object_unref (msg); - if (io->is_shutdown) { + if (io->is_shutdown) soup_client_message_io_http2_terminate_session (io); - return; - } - io_try_write (io, FALSE); + g_clear_object (&conn); } static void @@ -1513,6 +1577,7 @@ h2_debug (io, data, "Client stream EOF"); soup_message_set_metrics_timestamp (msg, SOUP_MESSAGE_METRICS_RESPONSE_END); advance_state_from (data, STATE_READ_DATA, STATE_READ_DONE); + io->ever_used = TRUE; g_signal_handlers_disconnect_by_func (stream, client_stream_eof, msg); soup_message_got_body (data->msg); } @@ -1544,12 +1609,13 @@ GCancellable *cancellable, GError **error) { + SoupClientMessageIOHTTP2 *io = data->io; gboolean progress = FALSE; - if (data->state < STATE_WRITE_DONE && nghttp2_session_want_write (data->io->session)) - progress = io_write (data->io, TRUE, cancellable, error); - else if (data->state < STATE_READ_DONE && nghttp2_session_want_read (data->io->session)) - progress = io_read (data->io, TRUE, cancellable, error); + if (data->state < STATE_WRITE_DONE && !io->in_callback && nghttp2_session_want_write (io->session)) + progress = io_write (io, TRUE, cancellable, error); + else if (data->state < STATE_READ_DONE && !io->in_callback && nghttp2_session_want_read (io->session)) + progress = io_read (io, TRUE, cancellable, error); return progress; } @@ -1580,11 +1646,13 @@ progress = io_run (data, cancellable, &my_error); if (my_error) { - g_propagate_error (error, my_error); - g_object_unref (msg); - return FALSE; + io->is_shutdown = TRUE; + set_io_error (io, my_error); } + if (io->error && !data->error) + data->error = g_error_copy (io->error); + if (data->error) { g_propagate_error (error, g_steal_pointer (&data->error)); g_object_unref (msg); @@ -1613,12 +1681,13 @@ { SoupClientMessageIOHTTP2 *io = (SoupClientMessageIOHTTP2 *)iface; SoupHTTP2MessageData *data = get_data_for_message (io, msg); + GError *my_error = NULL; - if (io_run_until (io, msg, STATE_READ_DATA, cancellable, error)) + if (io_run_until (io, msg, STATE_READ_DATA, cancellable, &my_error)) return TRUE; if (get_io_data (msg) == io) { - if (data->can_be_restarted) + if (soup_http2_message_data_can_be_restarted (data, my_error)) data->item->state = SOUP_MESSAGE_RESTARTING; else soup_message_set_metrics_timestamp (msg, SOUP_MESSAGE_METRICS_RESPONSE_END); @@ -1626,6 +1695,8 @@ soup_client_message_io_http2_finished (iface, msg); } + g_propagate_error (error, my_error); + return FALSE; } @@ -1678,6 +1749,32 @@ soup_http2_message_data_check_status (data); } +static void +soup_client_message_io_http2_set_owner (SoupClientMessageIOHTTP2 *io, + GThread *owner) +{ + if (owner == io->owner) + return; + + io->owner = owner; + g_assert (!io->write_source); + if (io->read_source) { + g_source_destroy (io->read_source); + g_source_unref (io->read_source); + io->read_source = NULL; + } + + io->async = g_main_context_is_owner (g_main_context_get_thread_default ()); + if (!io->async) + return; + + io->read_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (io->istream), NULL); + g_source_set_name (io->read_source, "Soup HTTP/2 read source"); + g_source_set_priority (io->read_source, G_PRIORITY_DEFAULT); + g_source_set_callback (io->read_source, (GSourceFunc)io_read_ready, io, NULL); + g_source_attach (io->read_source, g_main_context_get_thread_default ()); +} + static gboolean soup_client_message_io_http2_close_async (SoupClientMessageIO *iface, SoupConnection *conn, @@ -1688,9 +1785,18 @@ if (io->goaway_sent) return FALSE; - g_assert (!io->close_task); - io->close_task = g_task_new (conn, NULL, callback, NULL); + soup_client_message_io_http2_set_owner (io, g_thread_self ()); + if (io->async) { + g_assert (!io->close_task); + io->close_task = g_task_new (conn, NULL, callback, NULL); + } + soup_client_message_io_http2_terminate_session (io); + if (!io->async) { + g_assert (io->goaway_sent || io->error); + return FALSE; + } + return TRUE; } @@ -1708,8 +1814,7 @@ g_source_unref (io->write_source); } - if (io->conn) - g_object_remove_weak_pointer (G_OBJECT (io->conn), (gpointer*)&io->conn); + g_weak_ref_clear (&io->conn); g_clear_object (&io->stream); g_clear_object (&io->close_task); g_clear_pointer (&io->session, nghttp2_session_del); @@ -1721,6 +1826,14 @@ g_free (io); } +static void +soup_client_message_io_http2_owner_changed (SoupClientMessageIO *iface) +{ + SoupClientMessageIOHTTP2 *io = (SoupClientMessageIOHTTP2 *)iface; + + soup_client_message_io_http2_set_owner (io, g_thread_self ()); +} + static const SoupClientMessageIOFuncs io_funcs = { soup_client_message_io_http2_destroy, soup_client_message_io_http2_finished, @@ -1738,40 +1851,19 @@ soup_client_message_io_http2_is_open, soup_client_message_io_http2_in_progress, soup_client_message_io_http2_is_reusable, - soup_client_message_io_http2_get_cancellable + soup_client_message_io_http2_get_cancellable, + soup_client_message_io_http2_owner_changed }; -G_GNUC_PRINTF(1, 0) -static void -debug_nghttp2 (const char *format, - va_list args) -{ - char *message; - gsize len; - - if (g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, "nghttp2")) - return; - - message = g_strdup_vprintf (format, args); - len = strlen (message); - if (len >= 1 && messagelen - 1 == '\n') - messagelen - 1 = '\0'; - g_log ("nghttp2", G_LOG_LEVEL_DEBUG, "NGHTTP2 %s", message); - g_free (message); -} - static void soup_client_message_io_http2_init (SoupClientMessageIOHTTP2 *io) { - static gsize nghttp2_debug_init = 0; - if (g_once_init_enter (&nghttp2_debug_init)) { - nghttp2_set_debug_vprintf_callback(debug_nghttp2); - g_once_init_leave (&nghttp2_debug_init, 1); - } + soup_http2_debug_init (); nghttp2_session_callbacks *callbacks; NGCHECK (nghttp2_session_callbacks_new (&callbacks)); nghttp2_session_callbacks_set_on_header_callback (callbacks, on_header_callback); + nghttp2_session_callbacks_set_on_invalid_header_callback (callbacks, on_invalid_header_callback); nghttp2_session_callbacks_set_on_frame_recv_callback (callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_on_data_chunk_recv_callback (callbacks, on_data_chunk_recv_callback); nghttp2_session_callbacks_set_on_begin_frame_callback (callbacks, on_begin_frame_callback); @@ -1780,7 +1872,17 @@ nghttp2_session_callbacks_set_on_frame_send_callback (callbacks, on_frame_send_callback); nghttp2_session_callbacks_set_on_stream_close_callback (callbacks, on_stream_close_callback); +#ifdef HAVE_NGHTTP2_OPTION_SET_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION + nghttp2_option *option; + + nghttp2_option_new (&option); + nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation (option, 1); + NGCHECK (nghttp2_session_client_new2 (&io->session, callbacks, io, option)); + nghttp2_option_del (option); +#else NGCHECK (nghttp2_session_client_new (&io->session, callbacks, io)); +#endif + nghttp2_session_callbacks_del (callbacks); io->messages = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)soup_http2_message_data_free); @@ -1798,18 +1900,14 @@ SoupClientMessageIOHTTP2 *io = g_new0 (SoupClientMessageIOHTTP2, 1); soup_client_message_io_http2_init (io); - io->conn = conn; - g_object_add_weak_pointer (G_OBJECT (io->conn), (gpointer*)&io->conn); + g_weak_ref_init (&io->conn, conn); io->stream = g_object_ref (soup_connection_get_iostream (conn)); io->istream = g_io_stream_get_input_stream (io->stream); io->ostream = g_io_stream_get_output_stream (io->stream); io->connection_id = soup_connection_get_id (conn); - io->read_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (io->istream), NULL); - g_source_set_name (io->read_source, "Soup HTTP/2 read source"); - g_source_set_callback (io->read_source, (GSourceFunc)io_read_ready, io, NULL); - g_source_attach (io->read_source, g_main_context_get_thread_default ()); + soup_client_message_io_http2_set_owner (io, soup_connection_get_owner (conn)); NGCHECK (nghttp2_session_set_local_window_size (io->session, NGHTTP2_FLAG_NONE, 0, INITIAL_WINDOW_SIZE)); @@ -1819,7 +1917,7 @@ { NGHTTP2_SETTINGS_ENABLE_PUSH, 0 }, }; NGCHECK (nghttp2_submit_settings (io->session, NGHTTP2_FLAG_NONE, settings, G_N_ELEMENTS (settings))); - io_try_write (io, FALSE); + io_try_write (io, !io->async); return (SoupClientMessageIO *)io; }
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/meson.build -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/meson.build
Changed
@@ -42,15 +42,18 @@ 'http2/soup-client-message-io-http2.c', 'http2/soup-body-input-stream-http2.c', + 'server/http1/soup-server-message-io-http1.c', + 'server/http2/soup-server-message-io-http2.c', 'server/soup-auth-domain.c', 'server/soup-auth-domain-basic.c', 'server/soup-auth-domain-digest.c', + 'server/soup-listener.c', 'server/soup-message-body.c', 'server/soup-path-map.c', 'server/soup-server.c', - 'server/soup-server-io.c', + 'server/soup-server-connection.c', 'server/soup-server-message.c', - 'server/soup-socket.c', + 'server/soup-server-message-io.c', 'websocket/soup-websocket.c', 'websocket/soup-websocket-connection.c', @@ -61,11 +64,13 @@ 'soup-client-input-stream.c', 'soup-client-message-io.c', 'soup-connection.c', + 'soup-connection-manager.c', 'soup-date-utils.c', 'soup-filter-input-stream.c', 'soup-form.c', 'soup-headers.c', 'soup-header-names.c', + 'soup-http2-utils.c', 'soup-init.c', 'soup-io-stream.c', 'soup-logger.c', @@ -199,6 +204,8 @@ 'http1', 'http2', 'server', + 'server/http1', + 'server/http2', 'websocket', '.' ), @@ -211,7 +218,6 @@ soup_enums, , version : libversion, - soversion : soversion, darwin_versions: darwin_versions, c_args : libsoup_c_args, include_directories : libsoup_includes,
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/http1
Added
+(directory)
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/http1/soup-server-message-io-http1.c
Added
@@ -0,0 +1,1062 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * soup-server-message-io-http1.c: HTTP message I/O + * + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <glib/gi18n-lib.h> + +#include "soup-server-message-io-http1.h" +#include "soup.h" +#include "soup-body-input-stream.h" +#include "soup-body-output-stream.h" +#include "soup-filter-input-stream.h" +#include "soup-message-io-data.h" +#include "soup-message-headers-private.h" +#include "soup-server-message-private.h" +#include "soup-misc.h" + +typedef struct { + SoupMessageIOData base; + + SoupServerMessage *msg; + + GBytes *write_chunk; + goffset write_body_offset; + + GSource *unpause_source; + + GMainContext *async_context; +} SoupMessageIOHTTP1; + +typedef struct { + SoupServerMessageIO iface; + + GIOStream *iostream; + GInputStream *istream; + GOutputStream *ostream; + + SoupMessageIOStartedFn started_cb; + gpointer started_user_data; + + gboolean in_io_run; + + SoupMessageIOHTTP1 *msg_io; +} SoupServerMessageIOHTTP1; + +#define RESPONSE_BLOCK_SIZE 8192 +#define HEADER_SIZE_LIMIT (64 * 1024) + +static gboolean io_run_ready (SoupServerMessage *msg, + gpointer user_data); +static void io_run (SoupServerMessageIOHTTP1 *server_io); + +static SoupMessageIOHTTP1 * +soup_message_io_http1_new (SoupServerMessage *msg) +{ + SoupMessageIOHTTP1 *msg_io; + + msg_io = g_new0 (SoupMessageIOHTTP1, 1); + msg_io->msg = msg; + msg_io->base.read_header_buf = g_byte_array_new (); + msg_io->base.write_buf = g_string_new (NULL); + msg_io->base.read_state = SOUP_MESSAGE_IO_STATE_HEADERS; + msg_io->base.write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED; + msg_io->async_context = g_main_context_ref_thread_default (); + + return msg_io; +} + +static void +soup_message_io_http1_free (SoupMessageIOHTTP1 *msg_io) +{ + soup_message_io_data_cleanup (&msg_io->base); + + if (msg_io->unpause_source) { + g_source_destroy (msg_io->unpause_source); + g_source_unref (msg_io->unpause_source); + msg_io->unpause_source = NULL; + } + + g_clear_object (&msg_io->msg); + g_clear_pointer (&msg_io->async_context, g_main_context_unref); + g_clear_pointer (&msg_io->write_chunk, g_bytes_unref); + + g_free (msg_io); +} + +static void +soup_server_message_io_http1_destroy (SoupServerMessageIO *iface) +{ + SoupServerMessageIOHTTP1 *io = (SoupServerMessageIOHTTP1 *)iface; + + g_clear_object (&io->iostream); + g_clear_pointer (&io->msg_io, soup_message_io_http1_free); + + g_slice_free (SoupServerMessageIOHTTP1, io); +} + +static void +soup_server_message_io_http1_finished (SoupServerMessageIO *iface, + SoupServerMessage *msg) +{ + SoupServerMessageIOHTTP1 *io = (SoupServerMessageIOHTTP1 *)iface; + SoupMessageIOCompletionFn completion_cb; + gpointer completion_data; + SoupMessageIOCompletion completion; + SoupServerConnection *conn; + + completion_cb = io->msg_io->base.completion_cb; + completion_data = io->msg_io->base.completion_data; + + if ((io->msg_io->base.read_state >= SOUP_MESSAGE_IO_STATE_FINISHING && + io->msg_io->base.write_state >= SOUP_MESSAGE_IO_STATE_FINISHING)) + completion = SOUP_MESSAGE_IO_COMPLETE; + else + completion = SOUP_MESSAGE_IO_INTERRUPTED; + + g_object_ref (msg); + g_clear_pointer (&io->msg_io, soup_message_io_http1_free); + if (completion_cb) + completion_cb (G_OBJECT (msg), completion, completion_data); + conn = soup_server_message_get_connection (msg); + if (completion == SOUP_MESSAGE_IO_COMPLETE && + soup_server_connection_is_connected (conn) && + soup_server_message_is_keepalive (msg)) { + io->msg_io = soup_message_io_http1_new (soup_server_message_new (conn)); + io->msg_io->base.io_source = soup_message_io_data_get_source (&io->msg_io->base, + G_OBJECT (io->msg_io->msg), + io->istream, + io->ostream, + NULL, + (SoupMessageIOSourceFunc)io_run_ready, + NULL); + g_source_attach (io->msg_io->base.io_source, io->msg_io->async_context); + } + g_object_unref (msg); +} + +static GIOStream * +soup_server_message_io_http1_steal (SoupServerMessageIO *iface) +{ + SoupServerMessageIOHTTP1 *io = (SoupServerMessageIOHTTP1 *)iface; + SoupServerMessage *msg; + SoupMessageIOCompletionFn completion_cb; + gpointer completion_data; + GIOStream *iostream; + + if (!io->iostream) + return NULL; + + iostream = g_object_ref (io->iostream); + completion_cb = io->msg_io->base.completion_cb; + completion_data = io->msg_io->base.completion_data; + + msg = io->msg_io->msg; + g_object_ref (msg); + g_clear_pointer (&io->msg_io, soup_message_io_http1_free); + if (completion_cb) + completion_cb (G_OBJECT (msg), SOUP_MESSAGE_IO_STOLEN, completion_data); + g_object_unref (msg); + + return iostream; +} + +static void +closed_async (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GOutputStream *body_ostream = G_OUTPUT_STREAM (source); + SoupServerMessage *msg = user_data; + SoupServerMessageIOHTTP1 *io; + GCancellable *async_wait; + + io = (SoupServerMessageIOHTTP1 *)soup_server_message_get_io_data (msg); + if (!io || !io->msg_io || !io->msg_io->base.async_wait || io->msg_io->base.body_ostream != body_ostream) { + g_object_unref (msg); + return; + } + + g_output_stream_close_finish (body_ostream, result, &io->msg_io->base.async_error); + g_clear_object (&io->msg_io->base.body_ostream); + + async_wait = g_steal_pointer (&io->msg_io->base.async_wait); + g_cancellable_cancel (async_wait); + g_object_unref (async_wait); + + g_object_unref (msg); +} + +/* + * There are two request/response formats: the basic request/response, + * possibly with one or more unsolicited informational responses (such + * as the WebDAV "102 Processing" response): + * + * Client Server + * W:HEADERS / R:NOT_STARTED -> R:HEADERS / W:NOT_STARTED + * W:BODY / R:NOT_STARTED -> R:BODY / W:NOT_STARTED + * W:DONE / R:HEADERS (1xx) <- R:DONE / W:HEADERS (1xx) ... + * W:DONE / R:HEADERS <- R:DONE / W:HEADERS + * W:DONE / R:BODY <- R:DONE / W:BODY + * W:DONE / R:DONE R:DONE / W:DONE + * + * and the "Expect: 100-continue" request/response, with the client + * blocking halfway through its request, and then either continuing or + * aborting, depending on the server response: + * + * Client Server + * W:HEADERS / R:NOT_STARTED -> R:HEADERS / W:NOT_STARTED + * W:BLOCKING / R:HEADERS <- R:BLOCKING / W:HEADERS + * W:BODY / R:BLOCKING -> R:BODY / W:BLOCKING + * W:DONE / R:HEADERS <- R:DONE / W:HEADERS + * W:DONE / R:BODY <- R:DONE / W:BODY + * W:DONE / R:DONE R:DONE / W:DONE + */ + +static void +handle_partial_get (SoupServerMessage *msg) +{ + SoupRange *ranges; + int nranges; + GBytes *full_response; + guint status; + SoupMessageHeaders *request_headers; + SoupMessageHeaders *response_headers; + SoupMessageBody *response_body; + + request_headers = soup_server_message_get_request_headers (msg); + response_headers = soup_server_message_get_response_headers (msg); + response_body = soup_server_message_get_response_body (msg); + + /* Make sure the message is set up right for us to return a + * partial response; it has to be a GET, the status must be + * 200 OK (and in particular, NOT already 206 Partial + * Content), and the SoupServer must have already filled in + * the response body + */ + if (soup_server_message_get_method (msg) != SOUP_METHOD_GET || + soup_server_message_get_status (msg) != SOUP_STATUS_OK || + soup_message_headers_get_encoding (response_headers) != + SOUP_ENCODING_CONTENT_LENGTH || + response_body->length == 0 || + !soup_message_body_get_accumulate (response_body)) + return; + + /* Oh, and there has to have been a valid Range header on the + * request, of course. + */ + status = soup_message_headers_get_ranges_internal (request_headers, + response_body->length, + TRUE, + &ranges, &nranges); + if (status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) { + soup_server_message_set_status (msg, status, NULL); + soup_message_body_truncate (response_body); + return; + } else if (status != SOUP_STATUS_PARTIAL_CONTENT) + return; + + full_response = soup_message_body_flatten (response_body); + if (!full_response) { + soup_message_headers_free_ranges (request_headers, ranges); + return; + } + + soup_server_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT, NULL); + soup_message_body_truncate (response_body); + + if (nranges == 1) { + GBytes *range_buf; + + /* Single range, so just set Content-Range and fix the body. */ + + soup_message_headers_set_content_range (response_headers, + ranges0.start, + ranges0.end, + g_bytes_get_size (full_response)); + range_buf = g_bytes_new_from_bytes (full_response, + ranges0.start, + ranges0.end - ranges0.start + 1); + soup_message_body_append_bytes (response_body, range_buf); + g_bytes_unref (range_buf); + } else { + SoupMultipart *multipart; + SoupMessageHeaders *part_headers; + GBytes *part_body; + GBytes *body = NULL; + const char *content_type; + int i; + + /* Multiple ranges, so build a multipart/byteranges response + * to replace msg->response_body with. + */ + + multipart = soup_multipart_new ("multipart/byteranges"); + content_type = soup_message_headers_get_one_common (response_headers, SOUP_HEADER_CONTENT_TYPE); + for (i = 0; i < nranges; i++) { + part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); + if (content_type) { + soup_message_headers_append_common (part_headers, + SOUP_HEADER_CONTENT_TYPE, + content_type); + } + soup_message_headers_set_content_range (part_headers, + rangesi.start, + rangesi.end, + g_bytes_get_size (full_response)); + part_body = g_bytes_new_from_bytes (full_response, + rangesi.start, + rangesi.end - rangesi.start + 1); + soup_multipart_append_part (multipart, part_headers, + part_body); + soup_message_headers_unref (part_headers); + g_bytes_unref (part_body); + } + + soup_multipart_to_message (multipart, response_headers, &body); + soup_message_body_append_bytes (response_body, body); + g_bytes_unref (body); + soup_multipart_free (multipart); + } + + g_bytes_unref (full_response); + soup_message_headers_free_ranges (request_headers, ranges); +} + +static void +write_headers (SoupServerMessage *msg, + GString *headers, + SoupEncoding *encoding) +{ + SoupEncoding claimed_encoding; + SoupMessageHeadersIter iter; + const char *name, *value; + guint status_code; + const char *reason_phrase; + const char *method; + SoupMessageHeaders *response_headers; + + if (soup_server_message_get_status (msg) == 0) + soup_server_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL); + + handle_partial_get (msg); + + status_code = soup_server_message_get_status (msg); + reason_phrase = soup_server_message_get_reason_phrase (msg); + + g_string_append_printf (headers, "HTTP/1.%c %d %s\r\n", + soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0 ? '0' : '1', + status_code, reason_phrase); + + method = soup_server_message_get_method (msg); + response_headers = soup_server_message_get_response_headers (msg); + claimed_encoding = soup_message_headers_get_encoding (response_headers); + if ((method == SOUP_METHOD_HEAD || + status_code == SOUP_STATUS_NO_CONTENT || + status_code == SOUP_STATUS_NOT_MODIFIED || + SOUP_STATUS_IS_INFORMATIONAL (status_code)) || + (method == SOUP_METHOD_CONNECT && + SOUP_STATUS_IS_SUCCESSFUL (status_code))) + *encoding = SOUP_ENCODING_NONE; + else + *encoding = claimed_encoding; + + /* Per rfc 7230: + * A server MUST NOT send a Content-Length header field in any response + * with a status code of 1xx (Informational) or 204 (No Content). + */ + + if (status_code == SOUP_STATUS_NO_CONTENT || + SOUP_STATUS_IS_INFORMATIONAL (status_code)) { + soup_message_headers_remove (response_headers, "Content-Length"); + } else if (claimed_encoding == SOUP_ENCODING_CONTENT_LENGTH && + !soup_message_headers_get_content_length (response_headers)) { + SoupMessageBody *response_body; + + response_body = soup_server_message_get_response_body (msg); + soup_message_headers_set_content_length (response_headers, + response_body->length); + } + + soup_message_headers_iter_init (&iter, response_headers); + while (soup_message_headers_iter_next (&iter, &name, &value)) + g_string_append_printf (headers, "%s: %s\r\n", name, value); + g_string_append (headers, "\r\n"); +} + +/* Attempts to push forward the writing side of @msg's I/O. Returns + * %TRUE if it manages to make some progress, and it is likely that + * further progress can be made. Returns %FALSE if it has reached a + * stopping point of some sort (need input from the application, + * socket not writable, write is complete, etc). + */ +static gboolean +io_write (SoupServerMessageIOHTTP1 *server_io, + GError **error) +{ + SoupServerMessage *msg = server_io->msg_io->msg; + SoupMessageIOData *io = &server_io->msg_io->base; + GBytes *chunk; + gssize nwrote; + guint status_code; + + if (io->async_error) { + g_propagate_error (error, io->async_error); + io->async_error = NULL; + return FALSE; + } else if (io->async_wait) { + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_WOULD_BLOCK, + _("Operation would block")); + return FALSE; + } + + switch (io->write_state) { + case SOUP_MESSAGE_IO_STATE_HEADERS: + status_code = soup_server_message_get_status (msg); + if (io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING && status_code == 0) { + /* Client requested "Expect: 100-continue", and + * server did not set an error. + */ + soup_server_message_set_status (msg, SOUP_STATUS_CONTINUE, NULL); + } + + if (!io->write_buf->len) + write_headers (msg, io->write_buf, &io->write_encoding); + + while (io->written < io->write_buf->len) { + nwrote = g_pollable_stream_write (server_io->ostream, + io->write_buf->str + io->written, + io->write_buf->len - io->written, + FALSE, + NULL, error); + if (nwrote == -1) + return FALSE; + io->written += nwrote; + } + + io->written = 0; + g_string_truncate (io->write_buf, 0); + + status_code = soup_server_message_get_status (msg); + if (SOUP_STATUS_IS_INFORMATIONAL (status_code)) { + if (status_code == SOUP_STATUS_CONTINUE) { + /* Stop and wait for the body now */ + io->write_state = + SOUP_MESSAGE_IO_STATE_BLOCKING; + io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START; + } else { + /* We just wrote a 1xx response + * header, so stay in STATE_HEADERS. + * (The caller will pause us from the + * wrote_informational callback if he + * is not ready to send the final + * response.) + */ + } + + soup_server_message_wrote_informational (msg); + + /* If this was "101 Switching Protocols", then + * the server probably stole the connection... + */ + if ((SoupServerMessageIO *)server_io != soup_server_message_get_io_data (msg)) + return FALSE; + + soup_server_message_cleanup_response (msg); + break; + } + + if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH) + io->write_length = soup_message_headers_get_content_length (soup_server_message_get_response_headers (msg)); + + io->write_state = SOUP_MESSAGE_IO_STATE_BODY_START; + /* If the client was waiting for a Continue + * but we sent something else, then they're + * now done writing. + */ + if (io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING) + io->read_state = SOUP_MESSAGE_IO_STATE_DONE; + + soup_server_message_wrote_headers (msg); + break; + + case SOUP_MESSAGE_IO_STATE_BODY_START: + io->body_ostream = soup_body_output_stream_new (server_io->ostream, + io->write_encoding, + io->write_length); + io->write_state = SOUP_MESSAGE_IO_STATE_BODY; + break; + + case SOUP_MESSAGE_IO_STATE_BODY: + if (!io->write_length && + io->write_encoding != SOUP_ENCODING_EOF && + io->write_encoding != SOUP_ENCODING_CHUNKED) { + io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; + break; + } + + if (!server_io->msg_io->write_chunk) { + server_io->msg_io->write_chunk = soup_message_body_get_chunk (soup_server_message_get_response_body (msg), + server_io->msg_io->write_body_offset); + if (!server_io->msg_io->write_chunk) { + soup_server_message_pause (msg); + return FALSE; + } + if (!g_bytes_get_size (server_io->msg_io->write_chunk)) { + io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; + break; + } + } + + nwrote = g_pollable_stream_write (io->body_ostream, + (guchar*)g_bytes_get_data (server_io->msg_io->write_chunk, NULL) + io->written, + g_bytes_get_size (server_io->msg_io->write_chunk) - io->written, + FALSE, + NULL, error); + if (nwrote == -1) + return FALSE; + + chunk = g_bytes_new_from_bytes (server_io->msg_io->write_chunk, io->written, nwrote); + io->written += nwrote; + if (io->write_length) + io->write_length -= nwrote; + + if (io->written == g_bytes_get_size (server_io->msg_io->write_chunk)) + io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DATA; + + soup_server_message_wrote_body_data (msg, g_bytes_get_size (chunk)); + g_bytes_unref (chunk); + break; + + case SOUP_MESSAGE_IO_STATE_BODY_DATA: + io->written = 0; + if (g_bytes_get_size (server_io->msg_io->write_chunk) == 0) { + io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; + break; + } + + soup_message_body_wrote_chunk (soup_server_message_get_response_body (msg), + server_io->msg_io->write_chunk); + server_io->msg_io->write_body_offset += g_bytes_get_size (server_io->msg_io->write_chunk); + g_clear_pointer (&server_io->msg_io->write_chunk, g_bytes_unref); + + io->write_state = SOUP_MESSAGE_IO_STATE_BODY; + soup_server_message_wrote_chunk (msg); + break; + + case SOUP_MESSAGE_IO_STATE_BODY_FLUSH: + if (io->body_ostream) { + if (io->write_encoding != SOUP_ENCODING_CHUNKED) { + if (!g_output_stream_close (io->body_ostream, NULL, error)) + return FALSE; + g_clear_object (&io->body_ostream); + } else { + io->async_wait = g_cancellable_new (); + g_main_context_push_thread_default (server_io->msg_io->async_context); + g_output_stream_close_async (io->body_ostream, + G_PRIORITY_DEFAULT, NULL, + closed_async, g_object_ref (msg)); + g_main_context_pop_thread_default (server_io->msg_io->async_context); + } + } + + io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DONE; + break; + + case SOUP_MESSAGE_IO_STATE_BODY_DONE: + io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING; + soup_server_message_wrote_body (msg); + break; + + case SOUP_MESSAGE_IO_STATE_FINISHING: + io->write_state = SOUP_MESSAGE_IO_STATE_DONE; + + break; + + default: + g_return_val_if_reached (FALSE); + } + + return TRUE; +} + +static GUri * +parse_connect_authority (const char *req_path) +{ + GUri *uri; + char *fake_uri; + + fake_uri = g_strdup_printf ("http://%s", req_path); + uri = g_uri_parse (fake_uri, SOUP_HTTP_URI_FLAGS, NULL); + g_free (fake_uri); + + if (!uri) + return NULL; + + if (g_uri_get_user (uri) || + g_uri_get_password (uri) || + g_uri_get_query (uri) || + g_uri_get_fragment (uri) || + !g_uri_get_host (uri) || + g_uri_get_port (uri) <= 0 || + strcmp (g_uri_get_path (uri), "/") != 0) { + g_uri_unref (uri); + return NULL; + } + + return uri; +} + +static guint +parse_headers (SoupServerMessage *msg, + char *headers, + guint headers_len, + SoupEncoding *encoding, + GError **error) +{ + char *req_method, *req_path, *url; + SoupHTTPVersion version; + SoupServerConnection *conn; + const char *req_host; + guint status; + GUri *uri; + SoupMessageHeaders *request_headers; + + request_headers = soup_server_message_get_request_headers (msg); + + status = soup_headers_parse_request (headers, headers_len, + request_headers, + &req_method, + &req_path, + &version); + if (!SOUP_STATUS_IS_SUCCESSFUL (status)) + return status; + + soup_server_message_set_method (msg, req_method); + soup_server_message_set_http_version (msg, version); + g_free (req_method); + + /* Handle request body encoding */ + *encoding = soup_message_headers_get_encoding (request_headers); + if (*encoding == SOUP_ENCODING_UNRECOGNIZED) { + if (soup_message_headers_get_list_common (request_headers, SOUP_HEADER_TRANSFER_ENCODING)) + return SOUP_STATUS_NOT_IMPLEMENTED; + else + return SOUP_STATUS_BAD_REQUEST; + } + + /* Generate correct context for request */ + req_host = soup_message_headers_get_one_common (request_headers, SOUP_HEADER_HOST); + if (req_host && strchr (req_host, '/')) { + g_free (req_path); + return SOUP_STATUS_BAD_REQUEST; + } + + conn = soup_server_message_get_connection (msg); + + if (!strcmp (req_path, "*") && req_host) { + /* Eg, "OPTIONS * HTTP/1.1" */ + url = g_strdup_printf ("%s://%s/", + soup_server_connection_is_ssl (conn) ? "https" : "http", + req_host); + uri = g_uri_parse (url, SOUP_HTTP_URI_FLAGS, NULL); + soup_server_message_set_options_ping (msg, TRUE); + g_free (url); + } else if (soup_server_message_get_method (msg) == SOUP_METHOD_CONNECT) { + /* Authority */ + uri = parse_connect_authority (req_path); + } else if (*req_path != '/') { + /* Absolute URI */ + uri = g_uri_parse (req_path, SOUP_HTTP_URI_FLAGS, NULL); + } else if (req_host) { + url = g_strdup_printf ("%s://%s%s", + soup_server_connection_is_ssl (conn) ? "https" : "http", + req_host, req_path); + uri = g_uri_parse (url, SOUP_HTTP_URI_FLAGS, NULL); + g_free (url); + } else if (soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0) { + /* No Host header, no AbsoluteUri */ + GInetSocketAddress *addr = G_INET_SOCKET_ADDRESS (soup_server_connection_get_local_address (conn)); + GInetAddress *inet_addr = g_inet_socket_address_get_address (addr); + char *local_ip = g_inet_address_to_string (inet_addr); + int port = g_inet_socket_address_get_port (addr); + if (port == 0) + port = -1; + + uri = g_uri_build (SOUP_HTTP_URI_FLAGS, + soup_server_connection_is_ssl (conn) ? "https" : "http", + NULL, local_ip, port, req_path, NULL, NULL); + g_free (local_ip); + } else + uri = NULL; + + g_free (req_path); + + if (!uri || !g_uri_get_host (uri)) { + if (uri) + g_uri_unref (uri); + return SOUP_STATUS_BAD_REQUEST; + } + + soup_server_message_set_uri (msg, uri); + g_uri_unref (uri); + + return SOUP_STATUS_OK; +} + +/* Attempts to push forward the reading side of @msg's I/O. Returns + * %TRUE if it manages to make some progress, and it is likely that + * further progress can be made. Returns %FALSE if it has reached a + * stopping point of some sort (need input from the application, + * socket not readable, read is complete, etc). + */ +static gboolean +io_read (SoupServerMessageIOHTTP1 *server_io, + GError **error) +{ + SoupServerMessage *msg = server_io->msg_io->msg; + SoupMessageIOData *io = &server_io->msg_io->base; + gssize nread; + guint status; + SoupMessageHeaders *request_headers; + gboolean succeeded; + gboolean is_first_read; + + switch (io->read_state) { + case SOUP_MESSAGE_IO_STATE_HEADERS: + is_first_read = io->read_header_buf->len == 0 && !soup_server_message_get_method (msg); + + succeeded = soup_message_io_data_read_headers (io, SOUP_FILTER_INPUT_STREAM (server_io->istream), FALSE, NULL, NULL, error); + if (is_first_read && io->read_header_buf->len > 0 && !io->completion_cb) + server_io->started_cb (msg, server_io->started_user_data); + + if (!succeeded) { + if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT)) + soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL); + return FALSE; + } + + status = parse_headers (msg, + (char *)io->read_header_buf->data, + io->read_header_buf->len, + &io->read_encoding, + error); + g_byte_array_set_size (io->read_header_buf, 0); + + request_headers = soup_server_message_get_request_headers (msg); + + if (status != SOUP_STATUS_OK) { + /* Either we couldn't parse the headers, or they + * indicated something that would mean we wouldn't + * be able to parse the body. (Eg, unknown + * Transfer-Encoding.). Skip the rest of the + * reading, and make sure the connection gets + * closed when we're done. + */ + soup_server_message_set_status (msg, status, NULL); + soup_message_headers_append_common (request_headers, SOUP_HEADER_CONNECTION, "close"); + io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; + break; + } + + if (soup_message_headers_get_expectations (request_headers) & SOUP_EXPECTATION_CONTINUE) { + /* We must return a status code and response + * headers to the client; either an error to + * be set by a got-headers handler below, or + * else %SOUP_STATUS_CONTINUE otherwise. + */ + io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; + io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING; + } else + io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START; + + if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) + io->read_length = soup_message_headers_get_content_length (request_headers); + else + io->read_length = -1; + + soup_server_message_got_headers (msg); + break; + + case SOUP_MESSAGE_IO_STATE_BODY_START: + if (!io->body_istream) { + io->body_istream = soup_body_input_stream_new (server_io->istream, + io->read_encoding, + io->read_length); + + } + + io->read_state = SOUP_MESSAGE_IO_STATE_BODY; + break; + + case SOUP_MESSAGE_IO_STATE_BODY: { + guchar bufRESPONSE_BLOCK_SIZE; + + nread = g_pollable_stream_read (io->body_istream, + buf, + RESPONSE_BLOCK_SIZE, + FALSE, + NULL, error); + if (nread > 0) { + SoupMessageBody *request_body; + + request_body = soup_server_message_get_request_body (msg); + if (request_body) { + GBytes *bytes = g_bytes_new (buf, nread); + soup_message_body_got_chunk (request_body, bytes); + soup_server_message_got_chunk (msg, bytes); + g_bytes_unref (bytes); + } + break; + } + + if (nread == -1) + return FALSE; + + /* else nread == 0 */ + io->read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE; + break; + } + + case SOUP_MESSAGE_IO_STATE_BODY_DONE: + io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; + soup_server_message_got_body (msg); + break; + + case SOUP_MESSAGE_IO_STATE_FINISHING: + io->read_state = SOUP_MESSAGE_IO_STATE_DONE; + io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; + break; + + default: + g_return_val_if_reached (FALSE); + } + + return TRUE; +} + +static gboolean +io_run_until (SoupServerMessageIOHTTP1 *server_io, + SoupMessageIOState read_state, + SoupMessageIOState write_state, + GError **error) +{ + SoupServerMessage *msg = server_io->msg_io->msg; + SoupMessageIOData *io = &server_io->msg_io->base; + gboolean progress = TRUE, done; + GError *my_error = NULL; + + if (!io) + return FALSE; + + g_object_ref (msg); + + while (progress && soup_server_message_get_io_data (msg) == (SoupServerMessageIO *)server_io && !io->paused && !io->async_wait && + (io->read_state < read_state || io->write_state < write_state)) { + + if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->read_state)) + progress = io_read (server_io, &my_error); + else if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->write_state)) + progress = io_write (server_io, &my_error); + else + progress = FALSE; + } + + if (my_error) { + g_propagate_error (error, my_error); + g_object_unref (msg); + return FALSE; + } + + if (soup_server_message_get_io_data (msg) != (SoupServerMessageIO *)server_io) { + g_object_unref (msg); + return FALSE; + } + + done = (io->read_state >= read_state && + io->write_state >= write_state); + + if (!done) { + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_WOULD_BLOCK, + _("Operation would block")); + g_object_unref (msg); + return FALSE; + } + + g_object_unref (msg); + return done; +} + +static gboolean +io_run_ready (SoupServerMessage *msg, + gpointer user_data) +{ + io_run ((SoupServerMessageIOHTTP1 *)soup_server_message_get_io_data (msg)); + return FALSE; +} + +static void +io_run (SoupServerMessageIOHTTP1 *server_io) +{ + SoupServerMessage *msg = server_io->msg_io->msg; + SoupMessageIOData *io = &server_io->msg_io->base; + gboolean success; + GError *error = NULL; + + g_assert (!server_io->in_io_run); + server_io->in_io_run = TRUE; + + if (io->io_source) { + g_source_destroy (io->io_source); + g_source_unref (io->io_source); + io->io_source = NULL; + } + + g_object_ref (msg); + success = io_run_until (server_io, + SOUP_MESSAGE_IO_STATE_DONE, + SOUP_MESSAGE_IO_STATE_DONE, + &error); + + if (soup_server_message_get_io_data (msg) != (SoupServerMessageIO *)server_io) { + g_object_unref (msg); + g_clear_error (&error); + + return; + } + + server_io->in_io_run = FALSE; + + if (success) { + soup_server_message_finish (msg); + } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_clear_error (&error); + io->io_source = soup_message_io_data_get_source (io, G_OBJECT (msg), + server_io->istream, + server_io->ostream, + NULL, + (SoupMessageIOSourceFunc)io_run_ready, + NULL); + g_source_attach (io->io_source, server_io->msg_io->async_context); + } else if (soup_server_message_get_io_data (msg) == (SoupServerMessageIO *)server_io) { + soup_server_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, error ? error->message : NULL); + soup_server_message_finish (msg); + } + g_object_unref (msg); + g_clear_error (&error); +} + +static void +soup_server_message_io_http1_read_request (SoupServerMessageIO *iface, + SoupServerMessage *msg, + SoupMessageIOCompletionFn completion_cb, + gpointer user_data) +{ + SoupServerMessageIOHTTP1 *io = (SoupServerMessageIOHTTP1 *)iface; + SoupMessageIOHTTP1 *msg_io = io->msg_io; + + g_assert (msg_io->msg == msg); + + msg_io->base.completion_cb = completion_cb; + msg_io->base.completion_data = user_data; + + if (!io->in_io_run) + io_run (io); +} + +static void +soup_server_message_io_http1_pause (SoupServerMessageIO *iface, + SoupServerMessage *msg) +{ + SoupServerMessageIOHTTP1 *io = (SoupServerMessageIOHTTP1 *)iface; + + g_assert (io->msg_io && io->msg_io->msg == msg); + + if (io->msg_io->unpause_source) { + g_source_destroy (io->msg_io->unpause_source); + g_clear_pointer (&io->msg_io->unpause_source, g_source_unref); + } + + soup_message_io_data_pause (&io->msg_io->base); +} + +static gboolean +io_unpause_internal (SoupServerMessageIOHTTP1 *io) +{ + g_assert (io != NULL && io->msg_io != NULL); + + g_clear_pointer (&io->msg_io->unpause_source, g_source_unref); + soup_message_io_data_unpause (&io->msg_io->base); + if (io->msg_io->base.io_source) + return FALSE; + + io_run (io); + return FALSE; +} + +static void +soup_server_message_io_http1_unpause (SoupServerMessageIO *iface, + SoupServerMessage *msg) +{ + SoupServerMessageIOHTTP1 *io = (SoupServerMessageIOHTTP1 *)iface; + + g_assert (io->msg_io && io->msg_io->msg == msg); + + if (!io->msg_io->unpause_source) { + io->msg_io->unpause_source = soup_add_completion_reffed (io->msg_io->async_context, + (GSourceFunc)io_unpause_internal, + io, NULL); + } +} + +static gboolean +soup_server_message_io_http1_is_paused (SoupServerMessageIO *iface, + SoupServerMessage *msg) +{ + SoupServerMessageIOHTTP1 *io = (SoupServerMessageIOHTTP1 *)iface; + + g_assert (io->msg_io && io->msg_io->msg == msg); + + return io->msg_io->base.paused; +} + +static const SoupServerMessageIOFuncs io_funcs = { + soup_server_message_io_http1_destroy, + soup_server_message_io_http1_finished, + soup_server_message_io_http1_steal, + soup_server_message_io_http1_read_request, + soup_server_message_io_http1_pause, + soup_server_message_io_http1_unpause, + soup_server_message_io_http1_is_paused +}; + +SoupServerMessageIO * +soup_server_message_io_http1_new (SoupServerConnection *conn, + SoupServerMessage *msg, + SoupMessageIOStartedFn started_cb, + gpointer user_data) +{ + SoupServerMessageIOHTTP1 *io; + + io = g_slice_new0 (SoupServerMessageIOHTTP1); + io->iostream = g_object_ref (soup_server_connection_get_iostream (conn)); + io->istream = g_io_stream_get_input_stream (io->iostream); + io->ostream = g_io_stream_get_output_stream (io->iostream); + + io->started_cb = started_cb; + io->started_user_data = user_data; + + io->iface.funcs = &io_funcs; + + io->msg_io = soup_message_io_http1_new (msg); + + return (SoupServerMessageIO *)io; +}
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/http1/soup-server-message-io-http1.h
Added
@@ -0,0 +1,14 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2022 Igalia S.L. + */ + +#pragma once + +#include "soup-server-connection.h" +#include "soup-server-message-io.h" + +SoupServerMessageIO *soup_server_message_io_http1_new (SoupServerConnection *conn, + SoupServerMessage *msg, + SoupMessageIOStartedFn started_cb, + gpointer user_data);
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/http2
Added
+(directory)
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/http2/soup-server-message-io-http2.c
Added
@@ -0,0 +1,865 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * soup-server-message-io-http1.c: HTTP message I/O + * + * Copyright (C) 2022, Igalia S.L. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "libsoup-server-http2" + +#include <glib/gi18n-lib.h> + +#include "soup-server-message-io-http2.h" +#include "soup.h" +#include "soup-body-input-stream.h" +#include "soup-body-output-stream.h" +#include "soup-filter-input-stream.h" +#include "soup-message-io-data.h" +#include "soup-message-headers-private.h" +#include "soup-server-message-private.h" +#include "soup-misc.h" +#include "soup-http2-utils.h" + +typedef struct { + SoupServerMessage *msg; + guint32 stream_id; + SoupHTTP2IOState state; + GSource *unpause_source; + gboolean paused; + + SoupMessageIOCompletionFn completion_cb; + gpointer completion_data; + + char *scheme; + char *authority; + char *path; + + GBytes *write_chunk; + goffset write_offset; + goffset chunk_written; +} SoupMessageIOHTTP2; + +typedef struct { + SoupServerMessageIO iface; + + SoupServerConnection *conn; + GIOStream *iostream; + GInputStream *istream; + GOutputStream *ostream; + + GSource *read_source; + GSource *write_source; + + nghttp2_session *session; + + /* Owned by nghttp2 */ + guint8 *write_buffer; + gssize write_buffer_size; + gssize written_bytes; + + SoupMessageIOStartedFn started_cb; + gpointer started_user_data; + + GHashTable *messages; + + guint in_callback; +} SoupServerMessageIOHTTP2; + +static void soup_server_message_io_http2_send_response (SoupServerMessageIOHTTP2 *io, + SoupMessageIOHTTP2 *msg_io); + +G_GNUC_PRINTF(3, 0) +static void +h2_debug (SoupServerMessageIOHTTP2 *io, + SoupMessageIOHTTP2 *msg_io, + const char *format, + ...) +{ + va_list args; + char *message; + SoupServerConnection *conn; + + if (g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN)) + return; + + va_start (args, format); + message = g_strdup_vprintf (format, args); + va_end (args); + + conn = io ? io->conn : msg_io ? soup_server_message_get_connection (msg_io->msg) : NULL; + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "SERVER C%p-S%u %s %s", conn, msg_io ? msg_io->stream_id : 0, msg_io ? soup_http2_io_state_to_string (msg_io->state) : "-", message); + + g_free (message); +} + + +static void +advance_state_from (SoupMessageIOHTTP2 *msg_io, + SoupHTTP2IOState from, + SoupHTTP2IOState to) +{ + if (msg_io->state != from) { + g_warning ("Unexpected state changed %s -> %s, expected to be from %s", + soup_http2_io_state_to_string (msg_io->state), soup_http2_io_state_to_string (to), + soup_http2_io_state_to_string (from)); + } + + h2_debug (NULL, msg_io, "SESSION State %s -> %s", soup_http2_io_state_to_string (msg_io->state), soup_http2_io_state_to_string (to)); + + msg_io->state = to; +} + +static SoupMessageIOHTTP2 * +soup_message_io_http2_new (SoupServerMessage *msg) +{ + SoupMessageIOHTTP2 *msg_io; + + msg_io = g_new0 (SoupMessageIOHTTP2, 1); + msg_io->msg = msg; + + return msg_io; +} + +static void +soup_message_io_http2_free (SoupMessageIOHTTP2 *msg_io) +{ + if (msg_io->unpause_source) { + g_source_destroy (msg_io->unpause_source); + g_source_unref (msg_io->unpause_source); + } + g_clear_object (&msg_io->msg); + g_free (msg_io->scheme); + g_free (msg_io->authority); + g_free (msg_io->path); + g_clear_pointer (&msg_io->write_chunk, g_bytes_unref); + g_free (msg_io); +} + +static void +soup_server_message_io_http2_destroy (SoupServerMessageIO *iface) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)iface; + + if (io->read_source) { + g_source_destroy (io->read_source); + g_source_unref (io->read_source); + } + if (io->write_source) { + g_source_destroy (io->write_source); + g_source_unref (io->write_source); + } + + g_clear_object (&io->iostream); + g_clear_pointer (&io->session, nghttp2_session_del); + g_clear_pointer (&io->messages, g_hash_table_unref); + + g_free (io); +} + +static void +soup_server_message_io_http2_finished (SoupServerMessageIO *iface, + SoupServerMessage *msg) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)iface; + SoupMessageIOHTTP2 *msg_io = NULL; + SoupMessageIOCompletionFn completion_cb; + gpointer completion_data; + SoupMessageIOCompletion completion; + + g_hash_table_steal_extended (io->messages, msg, NULL, (gpointer *)&msg_io); + completion = msg_io->state != STATE_WRITE_DONE ? SOUP_MESSAGE_IO_INTERRUPTED : SOUP_MESSAGE_IO_COMPLETE; + + h2_debug (io, msg_io, "Finished: %s", completion == SOUP_MESSAGE_IO_COMPLETE ? "completed" : "interrupted"); + + completion_cb = msg_io->completion_cb; + completion_data = msg_io->completion_data; + + g_object_ref (msg); + soup_message_io_http2_free (msg_io); + + if (completion_cb) + completion_cb (G_OBJECT (msg), completion, completion_data); + + g_object_unref (msg); +} + +static GIOStream * +soup_server_message_io_http2_steal (SoupServerMessageIO *iface) +{ + g_assert_not_reached (); + return NULL; +} + +static void +soup_server_message_io_http2_read_request (SoupServerMessageIO *iface, + SoupServerMessage *msg, + SoupMessageIOCompletionFn completion_cb, + gpointer user_data) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)iface; + SoupMessageIOHTTP2 *msg_io; + + msg_io = g_hash_table_lookup (io->messages, msg); + g_assert (msg_io); + + h2_debug (io, msg_io, "SESSION Read request"); + + msg_io->completion_cb = completion_cb; + msg_io->completion_data = user_data; +} + +static void +soup_server_message_io_http2_pause (SoupServerMessageIO *iface, + SoupServerMessage *msg) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)iface; + SoupMessageIOHTTP2 *msg_io; + + msg_io = g_hash_table_lookup (io->messages, msg); + g_assert (msg_io); + + h2_debug (io, msg_io, "SESSION Paused"); + + if (msg_io->paused) + g_warn_if_reached (); + + if (msg_io->unpause_source) { + g_source_destroy (msg_io->unpause_source); + g_clear_pointer (&msg_io->unpause_source, g_source_unref); + } + + msg_io->paused = TRUE; +} + +typedef struct { + SoupServerMessageIOHTTP2 *io; + SoupMessageIOHTTP2 *msg_io; +} UnpauseSourceData; + +static gboolean +io_unpause_internal (UnpauseSourceData *data) +{ + SoupMessageIOHTTP2 *msg_io = data->msg_io; + + g_clear_pointer (&msg_io->unpause_source, g_source_unref); + if (msg_io->paused) + return FALSE; + + if (!nghttp2_session_get_stream_user_data (data->io->session, msg_io->stream_id)) { + soup_server_message_finish (msg_io->msg); + return FALSE; + } + + switch (msg_io->state) { + case STATE_READ_DONE: + soup_server_message_io_http2_send_response (data->io, msg_io); + break; + default: + g_warn_if_reached (); + } + return FALSE; +} + +static void +soup_server_message_io_http2_unpause (SoupServerMessageIO *iface, + SoupServerMessage *msg) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)iface; + SoupMessageIOHTTP2 *msg_io; + + msg_io = g_hash_table_lookup (io->messages, msg); + g_assert (msg_io); + + h2_debug (io, msg_io, "SESSION Unpaused"); + + if (!msg_io->paused) + g_warn_if_reached (); + + msg_io->paused = FALSE; + + if (!msg_io->unpause_source) { + UnpauseSourceData *data = g_new (UnpauseSourceData, 1); + + data->io = io; + data->msg_io = msg_io; + msg_io->unpause_source = soup_add_completion_reffed (g_main_context_get_thread_default (), + (GSourceFunc)io_unpause_internal, + data, g_free); + } +} + +static gboolean +soup_server_message_io_http2_is_paused (SoupServerMessageIO *iface, + SoupServerMessage *msg) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)iface; + SoupMessageIOHTTP2 *msg_io; + + msg_io = g_hash_table_lookup (io->messages, msg); + g_assert (msg_io); + + return msg_io->paused; +} + +static const SoupServerMessageIOFuncs io_funcs = { + soup_server_message_io_http2_destroy, + soup_server_message_io_http2_finished, + soup_server_message_io_http2_steal, + soup_server_message_io_http2_read_request, + soup_server_message_io_http2_pause, + soup_server_message_io_http2_unpause, + soup_server_message_io_http2_is_paused +}; + +static gboolean +io_write (SoupServerMessageIOHTTP2 *io, + GError **error) +{ + /* We must write all of nghttp2's buffer before we ask for more */ + if (io->written_bytes == io->write_buffer_size) + io->write_buffer = NULL; + + if (io->write_buffer == NULL) { + io->written_bytes = 0; + g_assert (io->in_callback == 0); + io->write_buffer_size = nghttp2_session_mem_send (io->session, (const guint8**)&io->write_buffer); + if (io->write_buffer_size == 0) { + /* Done */ + io->write_buffer = NULL; + return TRUE; + } + } + + gssize ret = g_pollable_stream_write (io->ostream, + io->write_buffer + io->written_bytes, + io->write_buffer_size - io->written_bytes, + FALSE, NULL, error); + if (ret < 0) + return FALSE; + + io->written_bytes += ret; + return TRUE; +} + +static gboolean +io_write_ready (GObject *stream, + SoupServerMessageIOHTTP2 *io) +{ + SoupServerConnection *conn = io->conn; + GError *error = NULL; + + g_object_ref (conn); + + while (!error && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io && nghttp2_session_want_write (io->session)) + io_write (io, &error); + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_error_free (error); + g_object_unref (conn); + return G_SOURCE_CONTINUE; + } + + if (error && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) + h2_debug (io, NULL, "SESSION IO error: %s", error->message); + + g_clear_error (&error); + g_object_unref (conn); + g_clear_pointer (&io->write_source, g_source_unref); + + return G_SOURCE_REMOVE; +} + +static void +io_try_write (SoupServerMessageIOHTTP2 *io) +{ + SoupServerConnection *conn = io->conn; + GError *error = NULL; + + if (io->write_source) + return; + + g_object_ref (conn); + + while (!error && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io && !io->in_callback && nghttp2_session_want_write (io->session)) + io_write (io, &error); + + if (io->in_callback || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_clear_error (&error); + io->write_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (io->ostream), NULL); + g_source_set_name (io->write_source, "Soup server HTTP/2 write source"); + g_source_set_callback (io->write_source, (GSourceFunc)io_write_ready, io, NULL); + g_source_attach (io->write_source, g_main_context_get_thread_default ()); + } + + if (error && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) + h2_debug (io, NULL, "SESSION IO error: %s", error->message); + + g_clear_error (&error); + g_object_unref (conn); +} + +static gboolean +io_read (SoupServerMessageIOHTTP2 *io, + GError **error) +{ + guint8 buffer8192; + gssize read; + int ret; + + if ((read = g_pollable_stream_read (io->istream, buffer, sizeof (buffer), FALSE, NULL, error)) < 0) + return FALSE; + + if (read == 0) { + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + _("Connection terminated unexpectedly")); + return FALSE; + } + + g_assert (io->in_callback == 0); + ret = nghttp2_session_mem_recv (io->session, buffer, read); + if (ret < 0) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "HTTP/2 IO error: %s", nghttp2_strerror (ret)); + return FALSE; + } + + return TRUE; +} + +static gboolean +io_read_ready (GObject *stream, + SoupServerMessageIOHTTP2 *io) +{ + SoupServerConnection *conn = io->conn; + gboolean progress = TRUE; + GError *error = NULL; + + g_object_ref (conn); + + while (progress && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io && nghttp2_session_want_read (io->session)) + progress = io_read (io, &error); + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_error_free (error); + g_object_unref (conn); + return G_SOURCE_CONTINUE; + } + + if (error && soup_server_connection_get_io_data (conn) == (SoupServerMessageIO *)io) + h2_debug (io, NULL, "SESSION IO error: %s", error->message); + + g_clear_error (&error); + g_object_unref (conn); + + return G_SOURCE_REMOVE; +} + +static SoupMessageIOHTTP2 * +soup_server_message_io_http2_get_or_create_msg_io (SoupServerMessageIOHTTP2 *io, + int32_t stream_id) +{ + SoupMessageIOHTTP2 *msg_io; + + /* The initial message is created earlier to handle the TLS certificate. + * If there's only one message without a stream id, that means it's the + * initial message and should be used now. + */ + if (g_hash_table_size (io->messages) == 1) { + GList *values = g_hash_table_get_values (io->messages); + + msg_io = (SoupMessageIOHTTP2 *)values->data; + g_list_free (values); + + if (msg_io->stream_id == 0) { + msg_io->stream_id = stream_id; + return msg_io; + } + } + + msg_io = soup_message_io_http2_new (soup_server_message_new (io->conn)); + msg_io->stream_id = stream_id; + soup_server_message_set_http_version (msg_io->msg, SOUP_HTTP_2_0); + g_hash_table_insert (io->messages, msg_io->msg, msg_io); + + return msg_io; +} + +static int +on_begin_headers_callback (nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)user_data; + SoupMessageIOHTTP2 *msg_io; + + if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) + return 0; + + io->in_callback++; + + msg_io = soup_server_message_io_http2_get_or_create_msg_io (io, frame->hd.stream_id); + h2_debug (io, msg_io, "SESSION Message IO created"); + + nghttp2_session_set_stream_user_data (session, frame->hd.stream_id, msg_io); + + if (!msg_io->completion_cb) + io->started_cb (msg_io->msg, io->started_user_data); + + advance_state_from (msg_io, STATE_NONE, STATE_READ_HEADERS); + + io->in_callback--; + + return 0; +} + +static int +on_header_callback (nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, + size_t namelen, + const uint8_t *value, + size_t valuelen, + uint8_t flags, + void *user_data) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)user_data; + SoupMessageIOHTTP2 *msg_io; + SoupServerMessage *msg; + + if (frame->hd.type != NGHTTP2_HEADERS) + return 0; + + if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) + return 0; + + msg_io = nghttp2_session_get_stream_user_data (session, frame->hd.stream_id); + if (!msg_io) + return 0; + + io->in_callback++; + + msg = msg_io->msg; + if (name0 == ':') { + if (strcmp ((char *)name, ":method") == 0) + soup_server_message_set_method (msg, (char *)value); + else if (strcmp ((char *)name, ":scheme") == 0) + msg_io->scheme = g_strndup ((char *)value, valuelen); + else if (strcmp ((char *)name, ":authority") == 0) + msg_io->authority = g_strndup ((char *)value, valuelen); + else if (strcmp ((char *)name, ":path") == 0) + msg_io->path = g_strndup ((char *)value, valuelen); + else + g_debug ("Unknown header: %s = %s", name, value); + io->in_callback--; + return 0; + } + + soup_message_headers_append_untrusted_data (soup_server_message_get_request_headers (msg), + (const char*)name, (const char*)value); + + io->in_callback--; + return 0; +} + +static int +on_data_chunk_recv_callback (nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + const uint8_t *data, + size_t len, + void *user_data) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)user_data; + SoupMessageIOHTTP2 *msg_io; + GBytes *bytes; + + msg_io = nghttp2_session_get_stream_user_data (session, stream_id); + if (!msg_io) + return NGHTTP2_ERR_CALLBACK_FAILURE; + + h2_debug (user_data, msg_io, "DATA Received chunk, len=%zu, flags=%u, paused=%d", len, flags, msg_io->paused); + + io->in_callback++; + + bytes = g_bytes_new (data, len); + soup_message_body_got_chunk (soup_server_message_get_request_body (msg_io->msg), bytes); + soup_server_message_got_chunk (msg_io->msg, bytes); + g_bytes_unref (bytes); + + io->in_callback--; + + return 0; +} + +static ssize_t +on_data_source_read_callback (nghttp2_session *session, + int32_t stream_id, + uint8_t *buf, + size_t length, + uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)user_data; + SoupMessageIOHTTP2 *msg_io; + gsize bytes_written = 0; + SoupMessageBody *response_body = (SoupMessageBody *)source->ptr; + + io->in_callback++; + + msg_io = nghttp2_session_get_stream_user_data (session, stream_id); + + h2_debug (user_data, msg_io, "SEND_BODY paused=%d", msg_io->paused); + + while (bytes_written < length && msg_io->write_offset < response_body->length) { + gconstpointer data; + gsize data_length; + gsize bytes_to_write; + + if (!msg_io->write_chunk) + msg_io->write_chunk = soup_message_body_get_chunk (response_body, msg_io->write_offset); + + data = g_bytes_get_data (msg_io->write_chunk, &data_length); + bytes_to_write = MIN (length - bytes_written, data_length - msg_io->chunk_written); + memcpy (buf + bytes_written, (uint8_t *)data + msg_io->chunk_written, bytes_to_write); + bytes_written += bytes_to_write; + msg_io->chunk_written += bytes_to_write; + msg_io->write_offset += bytes_to_write; + h2_debug (user_data, msg_io, "SEND_BODY wrote %zd %u/%u", bytes_to_write, msg_io->write_offset, response_body->length); + soup_server_message_wrote_body_data (msg_io->msg, bytes_to_write); + + if (msg_io->chunk_written == data_length) { + soup_message_body_wrote_chunk (response_body, msg_io->write_chunk); + g_clear_pointer (&msg_io->write_chunk, g_bytes_unref); + soup_server_message_wrote_chunk (msg_io->msg); + msg_io->chunk_written = 0; + } + } + + if (msg_io->write_offset == response_body->length) { + soup_server_message_wrote_body (msg_io->msg); + h2_debug (user_data, msg_io, "SEND_BODY EOF"); + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + + io->in_callback--; + + return bytes_written; +} + +static void +soup_server_message_io_http2_send_response (SoupServerMessageIOHTTP2 *io, + SoupMessageIOHTTP2 *msg_io) +{ + h2_debug (io, msg_io, "SESSION Send response, paused=%d", msg_io->paused); + if (msg_io->paused) + return; + + SoupServerMessage *msg = msg_io->msg; + GArray *headers = g_array_new (FALSE, FALSE, sizeof (nghttp2_nv)); + guint status_code = soup_server_message_get_status (msg); + if (status_code == 0) { + status_code = SOUP_STATUS_INTERNAL_SERVER_ERROR; + soup_server_message_set_status (msg, status_code, NULL); + } + char *status = g_strdup_printf ("%u", status_code); + const nghttp2_nv status_nv = MAKE_NV2 (":status", status); + g_array_append_val (headers, status_nv); + + SoupMessageHeaders *response_headers = soup_server_message_get_response_headers (msg); + if (status_code == SOUP_STATUS_NO_CONTENT || SOUP_STATUS_IS_INFORMATIONAL (status_code)) { + soup_message_headers_remove (response_headers, "Content-Length"); + } else if (!soup_message_headers_get_content_length (response_headers)) { + SoupMessageBody *response_body; + + response_body = soup_server_message_get_response_body (msg); + soup_message_headers_set_content_length (response_headers, response_body->length); + } + + SoupMessageHeadersIter iter; + const char *name, *value; + soup_message_headers_iter_init (&iter, response_headers); + while (soup_message_headers_iter_next (&iter, &name, &value)) { + const nghttp2_nv nv = MAKE_NV2 (name, value); + g_array_append_val (headers, nv); + } + + advance_state_from (msg_io, STATE_READ_DONE, STATE_WRITE_HEADERS); + + nghttp2_data_provider data_provider; + data_provider.source.ptr = soup_server_message_get_response_body (msg); + data_provider.read_callback = on_data_source_read_callback; + nghttp2_submit_response (io->session, msg_io->stream_id, (const nghttp2_nv *)headers->data, headers->len, &data_provider); + io_try_write (io); + g_array_free (headers, TRUE); + g_free (status); +} + +static int +on_frame_recv_callback (nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)user_data; + SoupMessageIOHTTP2 *msg_io; + + msg_io = nghttp2_session_get_stream_user_data (session, frame->hd.stream_id); + h2_debug (io, msg_io, "RECV %s Received (%u)", soup_http2_frame_type_to_string (frame->hd.type), frame->hd.flags); + if (!msg_io) + return 0; + + io->in_callback++; + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + char *uri_string; + GUri *uri; + + uri_string = g_strdup_printf ("%s://%s%s", msg_io->scheme, msg_io->authority, msg_io->path); + uri = g_uri_parse (uri_string, SOUP_HTTP_URI_FLAGS, NULL); + g_free (uri_string); + soup_server_message_set_uri (msg_io->msg, uri); + g_uri_unref (uri); + + advance_state_from (msg_io, STATE_READ_HEADERS, STATE_READ_DATA); + soup_server_message_got_headers (msg_io->msg); + break; + } + case NGHTTP2_DATA: + break; + default: + io->in_callback--; + return 0; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + advance_state_from (msg_io, STATE_READ_DATA, STATE_READ_DONE); + soup_server_message_got_body (msg_io->msg); + soup_server_message_io_http2_send_response (io, msg_io); + } + + io->in_callback--; + return 0; +} + +static int +on_frame_send_callback (nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)user_data; + SoupMessageIOHTTP2 *msg_io; + + io->in_callback++; + + msg_io = nghttp2_session_get_stream_user_data (session, frame->hd.stream_id); + + h2_debug (user_data, msg_io, "SEND %s", soup_http2_frame_type_to_string (frame->hd.type)); + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) { + advance_state_from (msg_io, STATE_WRITE_HEADERS, STATE_WRITE_DATA); + soup_server_message_wrote_headers (msg_io->msg); + } + break; + case NGHTTP2_DATA: + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + advance_state_from (msg_io, STATE_WRITE_DATA, STATE_WRITE_DONE); + soup_server_message_wrote_body (msg_io->msg); + } + break; + default: + break; + } + + io->in_callback--; + return 0; +} + +static int +on_stream_close_callback (nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, + void *user_data) +{ + SoupServerMessageIOHTTP2 *io = (SoupServerMessageIOHTTP2 *)user_data; + SoupMessageIOHTTP2 *msg_io; + + msg_io = nghttp2_session_get_stream_user_data (session, stream_id); + h2_debug (user_data, msg_io, "SESSION Closed %u, error: %s", stream_id, nghttp2_http2_strerror (error_code)); + if (!msg_io) + return 0; + + io->in_callback++; + + if (!msg_io->paused) + soup_server_message_finish (msg_io->msg); + + io->in_callback--; + return 0; +} + +static void +soup_server_message_io_http2_init (SoupServerMessageIOHTTP2 *io) +{ + nghttp2_session_callbacks *callbacks; + + soup_http2_debug_init (); + + nghttp2_session_callbacks_new (&callbacks); + nghttp2_session_callbacks_set_on_begin_headers_callback (callbacks, on_begin_headers_callback); + nghttp2_session_callbacks_set_on_header_callback (callbacks, on_header_callback); + nghttp2_session_callbacks_set_on_data_chunk_recv_callback (callbacks, on_data_chunk_recv_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback (callbacks, on_frame_recv_callback); + nghttp2_session_callbacks_set_on_frame_send_callback (callbacks, on_frame_send_callback); + nghttp2_session_callbacks_set_on_stream_close_callback (callbacks, on_stream_close_callback); + + nghttp2_session_server_new (&io->session, callbacks, io); + nghttp2_session_callbacks_del (callbacks); +} + +SoupServerMessageIO * +soup_server_message_io_http2_new (SoupServerConnection *conn, + SoupServerMessage *msg, + SoupMessageIOStartedFn started_cb, + gpointer user_data) +{ + SoupServerMessageIOHTTP2 *io; + + io = g_new0 (SoupServerMessageIOHTTP2, 1); + io->conn = conn; + io->iostream = g_object_ref (soup_server_connection_get_iostream (io->conn)); + io->istream = g_io_stream_get_input_stream (io->iostream); + io->ostream = g_io_stream_get_output_stream (io->iostream); + + io->started_cb = started_cb; + io->started_user_data = user_data; + + soup_server_message_io_http2_init (io); + + io->read_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (io->istream), NULL); + g_source_set_name (io->read_source, "Soup server HTTP/2 read source"); + g_source_set_callback (io->read_source, (GSourceFunc)io_read_ready, io, NULL); + g_source_attach (io->read_source, g_main_context_get_thread_default ()); + + io->iface.funcs = &io_funcs; + + io->messages = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)soup_message_io_http2_free); + g_hash_table_insert (io->messages, msg, soup_message_io_http2_new (msg)); + soup_server_message_set_http_version (msg, SOUP_HTTP_2_0); + + const nghttp2_settings_entry settings = { + { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }, + { NGHTTP2_SETTINGS_ENABLE_PUSH, 0 } + }; + nghttp2_submit_settings (io->session, NGHTTP2_FLAG_NONE, settings, G_N_ELEMENTS (settings)); + io_try_write (io); + + return (SoupServerMessageIO *)io; +}
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/http2/soup-server-message-io-http2.h
Added
@@ -0,0 +1,14 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2022 Igalia S.L. + */ + +#pragma once + +#include "soup-server-connection.h" +#include "soup-server-message-io.h" + +SoupServerMessageIO *soup_server_message_io_http2_new (SoupServerConnection *conn, + SoupServerMessage *msg, + SoupMessageIOStartedFn started_cb, + gpointer user_data);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-auth-domain-basic.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-auth-domain-basic.c
Changed
@@ -12,21 +12,17 @@ #include <string.h> #include "soup-auth-domain-basic.h" +#include "soup-auth-domain-private.h" #include "soup-message-headers-private.h" #include "soup.h" /** - * SECTION:soup-auth-domain-basic - * @short_description: Server-side "Basic" authentication + * SoupAuthDomainBasic: + * + * Server-side "Basic" authentication. * * #SoupAuthDomainBasic handles the server side of HTTP "Basic" (ie, * cleartext password) authentication. - **/ - -/** - * SoupAuthDomainBasic: - * - * Subclass of #SoupAuthDomain for Basic authentication. */ enum { @@ -118,9 +114,10 @@ * @optname1: name of first option, or %NULL * @...: option name/value pairs * - * Creates a #SoupAuthDomainBasic. You must set the - * SoupAuthDomain:realm property, to indicate the realm name to be - * returned with the authentication challenge to the client. Other + * Creates a #SoupAuthDomainBasic. + * + * You must set the property@AuthDomain:realm property, to indicate the realm + * name to be returned with the authentication challenge to the client. Other * parameters are optional. * * Returns: the new #SoupAuthDomain @@ -147,9 +144,10 @@ * @msg: the message being authenticated * @username: the username provided by the client * @password: the password provided by the client - * @user_data: the data passed to soup_auth_domain_basic_set_auth_callback() + * @user_data: the data passed to method@AuthDomainBasic.set_auth_callback * * Callback used by #SoupAuthDomainBasic for authentication purposes. + * * The application should verify that @username and @password and valid * and return %TRUE or %FALSE. * @@ -168,22 +166,24 @@ **/ /** - * soup_auth_domain_basic_set_auth_callback: + * soup_auth_domain_basic_set_auth_callback: (attributes org.gtk.Method.set_property=auth-callback) * @domain: (type SoupAuthDomainBasic): the domain * @callback: the callback * @user_data: data to pass to @auth_callback * @dnotify: destroy notifier to free @user_data when @domain - * is destroyed + * is destroyed * * Sets the callback that @domain will use to authenticate incoming - * requests. For each request containing authorization, @domain will - * invoke the callback, and then either accept or reject the request - * based on @callback's return value. + * requests. + * + * For each request containing authorization, @domain will invoke the callback, + * and then either accept or reject the request based on @callback's return + * value. * * You can also set the auth callback by setting the - * SoupAuthDomainBasic:auth-callback and - * SoupAuthDomainBasic:auth-data properties, which can also be - * used to set the callback at construct time. + * property@AuthDomainBasic:auth-callback and + * property@AuthDomainBasic:auth-data properties, which can also be used to + * set the callback at construct time. **/ void soup_auth_domain_basic_set_auth_callback (SoupAuthDomain *domain, @@ -322,9 +322,9 @@ object_class->get_property = soup_auth_domain_basic_get_property; /** - * SoupAuthDomainBasic:auth-callback: (type SoupAuthDomainBasicAuthCallback) + * SoupAuthDomainBasic:auth-callback: (type SoupAuthDomainBasicAuthCallback) (attributes org.gtk.Property.set=soup_auth_domain_basic_set_auth_callback) * - * The #SoupAuthDomainBasicAuthCallback + * The callback@AuthDomainBasicAuthCallback. */ propertiesPROP_AUTH_CALLBACK = g_param_spec_pointer ("auth-callback", @@ -335,7 +335,7 @@ /** * SoupAuthDomainBasic:auth-data: * - * The data to pass to the #SoupAuthDomainBasicAuthCallback + * The data to pass to the callback@AuthDomainBasicAuthCallback. */ propertiesPROP_AUTH_DATA = g_param_spec_pointer ("auth-data",
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-auth-domain-digest.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-auth-domain-digest.c
Changed
@@ -13,23 +13,19 @@ #include <stdlib.h> #include "soup-auth-domain-digest.h" +#include "soup-auth-domain-private.h" #include "soup-uri-utils-private.h" #include "soup-message-headers-private.h" #include "soup.h" #include "auth/soup-auth-digest-private.h" /** - * SECTION:soup-auth-domain-digest - * @short_description: Server-side "Digest" authentication + * SoupAuthDomainDigest: + * + * Server-side "Digest" authentication. * * #SoupAuthDomainDigest handles the server side of HTTP "Digest" * authentication. - **/ - -/** - * SoupAuthDomainDigest: - * - * Subclass of #SoupAuthDomain for Digest authentication. */ enum { @@ -122,10 +118,11 @@ * @optname1: name of first option, or %NULL * @...: option name/value pairs * - * Creates a #SoupAuthDomainDigest. You must set the - * SoupAuthDomain:realm property, to indicate the realm name to be - * returned with the authentication challenge to the client. Other - * parameters are optional. + * Creates a #SoupAuthDomainDigest. + * + * You must set the property@AuthDomain:realm property, to indicate the realm name to + * be returned with the authentication challenge to the client. Other parameters + * are optional. * * Returns: the new #SoupAuthDomain **/ @@ -150,35 +147,38 @@ * @domain: (type SoupAuthDomainDigest): the domain * @msg: the message being authenticated * @username: the username provided by the client - * @user_data: the data passed to soup_auth_domain_digest_set_auth_callback() + * @user_data: the data passed to method@AuthDomainDigest.set_auth_callback * * Callback used by #SoupAuthDomainDigest for authentication purposes. + * * The application should look up @username in its password database, * and return the corresponding encoded password (see - * soup_auth_domain_digest_encode_password()). + * func@AuthDomainDigest.encode_password. * * Returns: (nullable): the encoded password, or %NULL if - * @username is not a valid user. @domain will free the password when - * it is done with it. + * @username is not a valid user. @domain will free the password when + * it is done with it. **/ /** - * soup_auth_domain_digest_set_auth_callback: + * soup_auth_domain_digest_set_auth_callback: (attributes org.gtk.Method.set_property=auth-callback) * @domain: (type SoupAuthDomainDigest): the domain * @callback: the callback * @user_data: data to pass to @auth_callback * @dnotify: destroy notifier to free @user_data when @domain - * is destroyed + * is destroyed * * Sets the callback that @domain will use to authenticate incoming - * requests. For each request containing authorization, @domain will + * requests. + * + * For each request containing authorization, @domain will * invoke the callback, and then either accept or reject the request * based on @callback's return value. * * You can also set the auth callback by setting the - * SoupAuthDomainDigest:auth-callback and - * SoupAuthDomainDigest:auth-data properties, which can also be - * used to set the callback at construct time. + * property@AuthDomainDigest:auth-callback and + * property@AuthDomainDigest:auth-data properties, which can also be used to + * set the callback at construct time. **/ void soup_auth_domain_digest_set_auth_callback (SoupAuthDomain *domain, @@ -354,8 +354,10 @@ * @password: the password for @username in @realm * * Encodes the username/realm/password triplet for Digest - * authentication. (That is, it returns a stringified MD5 hash of - * @username, @realm, and @password concatenated together). This is + * authentication. + * + * That is, it returns a stringified MD5 hash of + * @username, @realm, and @password concatenated together. This is * the form that is needed as the return value of * #SoupAuthDomainDigest's auth handler. * @@ -431,9 +433,9 @@ object_class->get_property = soup_auth_domain_digest_get_property; /** - * SoupAuthDomainDigest:auth-callback: (type SoupAuthDomainDigestAuthCallback) + * SoupAuthDomainDigest:auth-callback: (type SoupAuthDomainDigestAuthCallback) (attributes org.gtk.Property.set=soup_auth_domain_digest_set_auth_callback) * - * The #SoupAuthDomainDigestAuthCallback + * The callback@AuthDomainDigestAuthCallback. */ propertiesPROP_AUTH_CALLBACK = g_param_spec_pointer ("auth-callback", @@ -444,7 +446,7 @@ /** * SoupAuthDomainDigest:auth-data: * - * The data to pass to the #SoupAuthDomainDigestAuthCallback + * The data to pass to the callback@AuthDomainDigestAuthCallback. */ propertiesPROP_AUTH_DATA = g_param_spec_pointer ("auth-data",
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-auth-domain-private.h
Added
@@ -0,0 +1,12 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2022 Igalia S.L. + */ + +#pragma once + +#include "soup-auth-domain.h" + +gboolean soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain, + SoupServerMessage *msg, + const char *username);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-auth-domain.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-auth-domain.c
Changed
@@ -11,41 +11,33 @@ #include <string.h> -#include "soup-auth-domain.h" +#include "soup-auth-domain-private.h" #include "soup-message-headers-private.h" #include "soup.h" #include "soup-path-map.h" /** - * SECTION:soup-auth-domain - * @short_description: Server-side authentication - * @see_also: #SoupServer + * SoupAuthDomain: + * + * Server-side authentication. * * A #SoupAuthDomain manages authentication for all or part of a - * #SoupServer. To make a server require authentication, first create + * class@Server. To make a server require authentication, first create * an appropriate subclass of #SoupAuthDomain, and then add it to the - * server with soup_server_add_auth_domain(). - * - * In order for an auth domain to have any effect, you must add one or - * more paths to it (via soup_auth_domain_add_path() or the - * SoupAuthDomain:add-path property). To require authentication for - * all ordinary requests, add the path "/". (Note that this does not - * include the special "*" URI (eg, "OPTIONS *"), which must be added - * as a separate path if you want to cover it.) - * - * If you need greater control over which requests should and - * shouldn't be authenticated, add paths covering everything you - * <emphasis>might</emphasis> want authenticated, and then use a - * filter (soup_auth_domain_set_filter()) to bypass authentication for - * those requests that don't need it. + * server with method@Server.add_auth_domain. + * + * In order for an auth domain to have any effect, you must add one or more + * paths to it (via method@AuthDomain.add_path). To require authentication for + * all ordinary requests, add the path `"/"`. (Note that this does not include + * the special `"*"` URI (eg, "OPTIONS *"), which must be added as a separate + * path if you want to cover it.) + * + * If you need greater control over which requests should and shouldn't be + * authenticated, add paths covering everything you *might* want authenticated, + * and then use a filter (method@AuthDomain.set_filter to bypass + * authentication for those requests that don't need it. **/ -/** - * SoupAuthDomain: - * - * Class managing authentication for #SoupServer. - */ - enum { PROP_0, @@ -183,6 +175,11 @@ object_class->set_property = soup_auth_domain_set_property; object_class->get_property = soup_auth_domain_get_property; + /** + * SoupAuthDomain:realm: (attributes org.gtk.Property.get=soup_auth_domain_get_realm) + * + * The realm of this auth domain. + */ propertiesPROP_REALM = g_param_spec_string ("realm", "Realm", @@ -191,6 +188,11 @@ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + /** + * SoupAuthDomain:proxy: + * + * Whether or not this is a proxy auth domain. + */ propertiesPROP_PROXY = g_param_spec_boolean ("proxy", "Proxy", @@ -200,9 +202,9 @@ G_PARAM_STATIC_STRINGS); /** - * SoupAuthDomain:filter: (type SoupAuthDomainFilter) + * SoupAuthDomain:filter: (type SoupAuthDomainFilter) (attributes org.gtk.Property.set=soup_auth_domain_set_filter) * - * The #SoupAuthDomainFilter for the domain. + * The callback@AuthDomainFilter for the domain. */ propertiesPROP_FILTER = g_param_spec_pointer ("filter", @@ -213,7 +215,7 @@ /** * SoupAuthDomain:filter-data: * - * Data to pass to the #SoupAuthDomainFilter. + * Data to pass to the callback@AuthDomainFilter. **/ propertiesPROP_FILTER_DATA = g_param_spec_pointer ("filter-data", @@ -222,9 +224,9 @@ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** - * SoupAuthDomain:generic-auth-callback: (type SoupAuthDomainGenericAuthCallback) + * SoupAuthDomain:generic-auth-callback: (type SoupAuthDomainGenericAuthCallback) (attributes org.gtk.Property.set=soup_auth_domain_set_generic_auth_callback) * - * The #SoupAuthDomainGenericAuthCallback. + * The callback@AuthDomainGenericAuthCallback. **/ propertiesPROP_GENERIC_AUTH_CALLBACK = g_param_spec_pointer ("generic-auth-callback", @@ -235,7 +237,7 @@ /** * SoupAuthDomain:generic-auth-data: * - * The data to pass to the #SoupAuthDomainGenericAuthCallback. + * The data to pass to the callback@AuthDomainGenericAuthCallback. **/ propertiesPROP_GENERIC_AUTH_DATA = g_param_spec_pointer ("generic-auth-data", @@ -252,13 +254,11 @@ * @domain: a #SoupAuthDomain * @path: the path to add to @domain * - * Adds @path to @domain, such that requests under @path on @domain's - * server will require authentication (unless overridden by - * soup_auth_domain_remove_path() or soup_auth_domain_set_filter()). + * Adds @path to @domain. * - * You can also add paths by setting the SoupAuthDomain:add-path - * property, which can also be used to add one or more paths at - * construct time. + * Requests under @path on @domain's server will require authentication (unless + * overridden by method@AuthDomain.remove_path or + * method@AuthDomain.set_filter). **/ void soup_auth_domain_add_path (SoupAuthDomain *domain, const char *path) @@ -277,20 +277,18 @@ * @domain: a #SoupAuthDomain * @path: the path to remove from @domain * - * Removes @path from @domain, such that requests under @path on - * @domain's server will NOT require authentication. + * Removes @path from @domain. + * + * Requests under @path on @domain's server will NOT require + * authentication. * - * This is not simply an undo-er for soup_auth_domain_add_path(); it + * This is not simply an undo-er for method@AuthDomain.add_path; it * can be used to "carve out" a subtree that does not require * authentication inside a hierarchy that does. Note also that unlike - * with soup_auth_domain_add_path(), this cannot be overridden by + * with method@AuthDomain.add_path, this cannot be overridden by * adding a filter, as filters can only bypass authentication that * would otherwise be required, not require it where it would * otherwise be unnecessary. - * - * You can also remove paths by setting the - * SoupAuthDomain:remove-path property, which can also be used to - * remove one or more paths at construct time. **/ void soup_auth_domain_remove_path (SoupAuthDomain *domain, const char *path) @@ -308,27 +306,29 @@ * SoupAuthDomainFilter: * @domain: a #SoupAuthDomain * @msg: a #SoupServerMessage - * @user_data: the data passed to soup_auth_domain_set_filter() + * @user_data: the data passed to method@AuthDomain.set_filter * - * The prototype for a #SoupAuthDomain filter; see - * soup_auth_domain_set_filter() for details. + * The prototype for a #SoupAuthDomain filter. + * + * See method@AuthDomain.set_filter for details. * * Returns: %TRUE if @msg requires authentication, %FALSE if not. **/ /** - * soup_auth_domain_set_filter: + * soup_auth_domain_set_filter: (attributes org.gtk.Method.set_property=filter) * @domain: a #SoupAuthDomain * @filter: the auth filter for @domain * @filter_data: data to pass to @filter * @dnotify: destroy notifier to free @filter_data when @domain - * is destroyed + * is destroyed + * + * Adds @filter as an authentication filter to @domain. * - * Adds @filter as an authentication filter to @domain. The filter - * gets a chance to bypass authentication for certain requests that - * would otherwise require it. Eg, it might check the message's path - * in some way that is too complicated to do via the other methods, or - * it might check the message's method, and allow GETs but not PUTs. + * The filter gets a chance to bypass authentication for certain requests that + * would otherwise require it. Eg, it might check the message's path in some way + * that is too complicated to do via the other methods, or it might check the + * message's method, and allow GETs but not PUTs. * * The filter function returns %TRUE if the request should still * require authentication, or %FALSE if authentication is unnecessary @@ -346,7 +346,7 @@ * unauthenticated users. * * You can also set the filter by setting the SoupAuthDomain:filter - * and SoupAuthDomain:filter-data properties, which can also be + * and property@AuthDomain:filter-data properties, which can also be * used to set the filter at construct time. **/ void @@ -369,10 +369,10 @@ } /** - * soup_auth_domain_get_realm: + * soup_auth_domain_get_realm: (attributes org.gtk.Method.get_property=realm) * @domain: a #SoupAuthDomain * - * Gets the realm name associated with @domain + * Gets the realm name associated with @domain. * * Returns: @domain's realm **/ @@ -389,43 +389,43 @@ * @domain: a #SoupAuthDomain * @msg: the #SoupServerMessage being authenticated * @username: the username from @msg - * @user_data: the data passed to - * soup_auth_domain_set_generic_auth_callback() + * @user_data: the data passed to method@AuthDomain.set_generic_auth_callback * * The prototype for a #SoupAuthDomain generic authentication callback. * * The callback should look up the user's password, call - * soup_auth_domain_check_password(), and use the return value from - * that method as its own return value. + * method@AuthDomain.check_password, and use the return value from that method + * as its own return value. * * In general, for security reasons, it is preferable to use the * auth-domain-specific auth callbacks (eg, - * #SoupAuthDomainBasicAuthCallback and - * #SoupAuthDomainDigestAuthCallback), because they don't require + * callback@AuthDomainBasicAuthCallback and + * callback@AuthDomainDigestAuthCallback), because they don't require * keeping a cleartext password database. Most users will use the same * password for many different sites, meaning if any site with a * cleartext password database is compromised, accounts on other * servers might be compromised as well. For many of the cases where - * #SoupServer is used, this is not really relevant, but it may still + * class@Server is used, this is not really relevant, but it may still * be worth considering. * * Returns: %TRUE if @msg is authenticated, %FALSE if not. **/ /** - * soup_auth_domain_set_generic_auth_callback: + * soup_auth_domain_set_generic_auth_callback: (attributes org.gtk.Method.get_property=generic-auth-callback) * @domain: a #SoupAuthDomain * @auth_callback: the auth callback * @auth_data: data to pass to @auth_callback * @dnotify: destroy notifier to free @auth_data when @domain - * is destroyed - * - * Sets @auth_callback as an authentication-handling callback for - * @domain. Whenever a request comes in to @domain which cannot be - * authenticated via a domain-specific auth callback (eg, - * #SoupAuthDomainDigestAuthCallback), the generic auth callback - * will be invoked. See #SoupAuthDomainGenericAuthCallback for information - * on what the callback should do. + * is destroyed + * + * Sets @auth_callback as an authentication-handling callback for @domain. + * + * Whenever a request comes in to @domain which cannot be authenticated via a + * domain-specific auth callback (eg, callback@AuthDomainDigestAuthCallback), + * the generic auth callback will be invoked. See + * callback@AuthDomainGenericAuthCallback for information on what the callback + * should do. **/ void soup_auth_domain_set_generic_auth_callback (SoupAuthDomain *domain, @@ -467,8 +467,10 @@ * @password: a password * * Checks if @msg authenticates to @domain via @username and - * @password. This would normally be called from a - * #SoupAuthDomainGenericAuthCallback. + * @password. + * + * This would normally be called from a + * callback@AuthDomainGenericAuthCallback. * * Returns: whether or not the message is authenticated **/ @@ -489,11 +491,12 @@ * @msg: a #SoupServerMessage * * Checks if @domain requires @msg to be authenticated (according to - * its paths and filter function). This does not actually look at - * whether @msg <emphasis>is</emphasis> authenticated, merely whether - * or not it needs to be. + * its paths and filter function). + * + * This does not actually look at whether @msg *is* authenticated, merely + * whether or not it needs to be. * - * This is used by #SoupServer internally and is probably of no use to + * This is used by class@Server internally and is probably of no use to * anyone else. * * Returns: %TRUE if @domain requires @msg to be authenticated @@ -523,15 +526,16 @@ * @msg: a #SoupServerMessage * * Checks if @msg contains appropriate authorization for @domain to - * accept it. Mirroring soup_auth_domain_covers(), this does not check - * whether or not @domain <emphasis>cares</emphasis> if @msg is - * authorized. + * accept it. * - * This is used by #SoupServer internally and is probably of no use to + * Mirroring method@AuthDomain.covers, this does not check whether or not + * @domain *cares* if @msg is authorized. + * + * This is used by class@Server internally and is probably of no use to * anyone else. * * Returns: (nullable): the username that @msg has authenticated - * as, if in fact it has authenticated. %NULL otherwise. + * as, if in fact it has authenticated. %NULL otherwise. **/ char * soup_auth_domain_accepts (SoupAuthDomain *domain, @@ -554,11 +558,11 @@ * @domain: a #SoupAuthDomain * @msg: a #SoupServerMessage * - * Adds a "WWW-Authenticate" or "Proxy-Authenticate" header to @msg, - * requesting that the client authenticate, and sets @msg's status - * accordingly. + * Adds a "WWW-Authenticate" or "Proxy-Authenticate" header to @msg. + * + * It requests that the client authenticate, and sets @msg's status accordingly. * - * This is used by #SoupServer internally and is probably of no use to + * This is used by class@Server internally and is probably of no use to * anyone else. **/ void
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-auth-domain.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-auth-domain.h
Changed
@@ -74,10 +74,4 @@ void soup_auth_domain_challenge (SoupAuthDomain *domain, SoupServerMessage *msg); -/* protected */ -SOUP_AVAILABLE_IN_ALL -gboolean soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain, - SoupServerMessage *msg, - const char *username); - G_END_DECLS
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-listener.c
Added
@@ -0,0 +1,378 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * soup-listener.c: Socket listening networking code. + * + * Copyright (C) 2022 Igalia S.L. + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n-lib.h> +#include <gio/gnetworking.h> + +#include "soup-listener.h" +#include "soup.h" +#include "soup-io-stream.h" +#include "soup-server-connection.h" + +enum { + NEW_CONNECTION, + LAST_SIGNAL +}; + +static guint signalsLAST_SIGNAL = { 0 }; + +enum { + PROP_0, + + PROP_SOCKET, + PROP_TLS_CERTIFICATE, + PROP_TLS_DATABASE, + PROP_TLS_AUTH_MODE, + + LAST_PROPERTY +}; + +static GParamSpec *propertiesLAST_PROPERTY = { NULL, }; + +struct _SoupListener { + GObject parent_instance; +}; + +typedef struct { + GSocket *socket; + GIOStream *conn; + GIOStream *iostream; + GInetSocketAddress *local_addr; + + GTlsCertificate *tls_certificate; + GTlsDatabase *tls_database; + GTlsAuthenticationMode tls_auth_mode; + + GSource *source; +} SoupListenerPrivate; + +G_DEFINE_FINAL_TYPE_WITH_PRIVATE (SoupListener, soup_listener, G_TYPE_OBJECT) + +static void +soup_listener_init (SoupListener *listener) +{ +} + +static gboolean +listen_watch (GObject *pollable, + SoupListener *listener) +{ + SoupListenerPrivate *priv = soup_listener_get_instance_private (listener); + GSocket *socket; + SoupServerConnection *conn; + + socket = g_socket_accept (priv->socket, NULL, NULL); + if (!socket) + return G_SOURCE_REMOVE; + + conn = soup_server_connection_new (socket, priv->tls_certificate, priv->tls_database, priv->tls_auth_mode); + g_signal_emit (listener, signalsNEW_CONNECTION, 0, conn); + g_object_unref (conn); + + return G_SOURCE_CONTINUE; +} + +static void +soup_listener_constructed (GObject *object) +{ + SoupListener *listener = SOUP_LISTENER (object); + SoupListenerPrivate *priv = soup_listener_get_instance_private (listener); + + g_socket_set_option (priv->socket, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); + + priv->conn = (GIOStream *)g_socket_connection_factory_create_connection (priv->socket); + priv->iostream = soup_io_stream_new (priv->conn, FALSE); + priv->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (priv->iostream)), NULL); + g_source_set_callback (priv->source, (GSourceFunc)listen_watch, listener, NULL); + g_source_attach (priv->source, g_main_context_get_thread_default ()); + + G_OBJECT_CLASS (soup_listener_parent_class)->constructed (object); +} + +static void +soup_listener_finalize (GObject *object) +{ + SoupListener *listener = SOUP_LISTENER (object); + SoupListenerPrivate *priv = soup_listener_get_instance_private (listener); + + if (priv->conn) { + g_io_stream_close (priv->conn, NULL, NULL); + g_clear_object (&priv->conn); + } + + g_clear_object (&priv->socket); + g_clear_object (&priv->iostream); + + g_clear_object (&priv->tls_certificate); + g_clear_object (&priv->tls_database); + + if (priv->source) { + g_source_destroy (priv->source); + g_source_unref (priv->source); + } + + G_OBJECT_CLASS (soup_listener_parent_class)->finalize (object); +} + +static void +soup_listener_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SoupListener *listener = SOUP_LISTENER (object); + SoupListenerPrivate *priv = soup_listener_get_instance_private (listener); + + switch (prop_id) { + case PROP_SOCKET: + priv->socket = g_value_dup_object (value); + break; + case PROP_TLS_CERTIFICATE: + priv->tls_certificate = g_value_dup_object (value); + break; + case PROP_TLS_DATABASE: + priv->tls_database = g_value_dup_object (value); + break; + case PROP_TLS_AUTH_MODE: + priv->tls_auth_mode = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +soup_listener_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SoupListener *listener = SOUP_LISTENER (object); + SoupListenerPrivate *priv = soup_listener_get_instance_private (listener); + + switch (prop_id) { + case PROP_SOCKET: + g_value_set_object (value, priv->socket); + break; + case PROP_TLS_CERTIFICATE: + g_value_set_object (value, priv->tls_certificate); + break; + case PROP_TLS_DATABASE: + g_value_set_object (value, priv->tls_database); + break; + case PROP_TLS_AUTH_MODE: + g_value_set_enum (value, priv->tls_auth_mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +soup_listener_class_init (SoupListenerClass *listener_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (listener_class); + + object_class->constructed = soup_listener_constructed; + object_class->finalize = soup_listener_finalize; + object_class->set_property = soup_listener_set_property; + object_class->get_property = soup_listener_get_property; + + /** + * SoupListener::new-connection: + * @listener: the listener + * @conn: the new connection + * + * Emitted when a listening socket receives a new connection. + * + * You must ref the @new if you want to keep it; otherwise it + * will be destroyed after the signal is emitted. + **/ + signalsNEW_CONNECTION = + g_signal_new ("new-connection", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + SOUP_TYPE_SERVER_CONNECTION); + + /* properties */ + propertiesPROP_SOCKET = + g_param_spec_object ("socket", + "Socket", + "The underlying GSocket", + G_TYPE_SOCKET, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + propertiesPROP_TLS_CERTIFICATE = + g_param_spec_object ("tls-certificate", + "TLS Certificate", + "The server TLS certificate", + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + propertiesPROP_TLS_DATABASE = + g_param_spec_object ("tls-database", + "TLS Database", + "The server TLS database", + G_TYPE_TLS_DATABASE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + propertiesPROP_TLS_AUTH_MODE = + g_param_spec_enum ("tls-auth-mode", + "TLS Authentication Mode", + "The server TLS authentication mode", + G_TYPE_TLS_AUTHENTICATION_MODE, + G_TLS_AUTHENTICATION_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, LAST_PROPERTY, properties); +} + +SoupListener * +soup_listener_new (GSocket *socket, + GError **error) +{ + int listening; + + g_return_val_if_fail (G_IS_SOCKET (socket), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (!g_socket_get_option (socket, SOL_SOCKET, SO_ACCEPTCONN, &listening, error)) { + g_prefix_error (error, _("Could not import existing socket: ")); + return NULL; + } + + if (!listening && !g_socket_is_connected (socket)) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Can’t import unconnected socket")); + return NULL; + } + + return g_object_new (SOUP_TYPE_LISTENER, "socket", socket, NULL); +} + +SoupListener * +soup_listener_new_for_address (GSocketAddress *address, + GError **error) +{ + GSocket *socket; + GSocketFamily family; + SoupListener *listener; + + g_return_val_if_fail (G_IS_SOCKET_ADDRESS (address), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + family = g_socket_address_get_family (address); + socket = g_socket_new (family, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, error); + if (!socket) + return NULL; + + if (family == G_SOCKET_FAMILY_IPV6) { + GError *option_error = NULL; + + g_socket_set_option (socket, IPPROTO_IPV6, IPV6_V6ONLY, TRUE, &option_error); + if (option_error) { + g_warning ("Failed to set IPv6 only on socket: %s", option_error->message); + g_error_free (option_error); + } + } + + if (!g_socket_bind (socket, address, TRUE, error)) { + g_object_unref (socket); + + return NULL; + } + + if (!g_socket_listen (socket, error)) { + g_object_unref (socket); + + return NULL; + } + + listener = g_object_new (SOUP_TYPE_LISTENER, "socket", socket, NULL); + g_object_unref (socket); + + return listener; +} + +GSocket * +soup_listener_get_socket (SoupListener *listener) +{ + SoupListenerPrivate *priv; + + g_return_val_if_fail (SOUP_IS_LISTENER (listener), NULL); + + priv = soup_listener_get_instance_private (listener); + + return priv->socket; +} + +void +soup_listener_disconnect (SoupListener *listener) +{ + SoupListenerPrivate *priv; + + g_return_if_fail (SOUP_IS_LISTENER (listener)); + + priv = soup_listener_get_instance_private (listener); + g_clear_object (&priv->socket); + if (priv->conn) { + g_io_stream_close (priv->conn, NULL, NULL); + g_clear_object (&priv->conn); + } +} + +gboolean +soup_listener_is_ssl (SoupListener *listener) +{ + SoupListenerPrivate *priv; + + g_return_val_if_fail (SOUP_IS_LISTENER (listener), FALSE); + + priv = soup_listener_get_instance_private (listener); + + return priv->tls_certificate != NULL; +} + +GInetSocketAddress * +soup_listener_get_address (SoupListener *listener) +{ + SoupListenerPrivate *priv; + + g_return_val_if_fail (SOUP_IS_LISTENER (listener), NULL); + + priv = soup_listener_get_instance_private (listener); + + if (!priv->local_addr) { + GError *error = NULL; + + priv->local_addr = G_INET_SOCKET_ADDRESS (g_socket_get_local_address (priv->socket, &error)); + if (priv->local_addr == NULL) { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + return NULL; + } + } + + return priv->local_addr; +}
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-listener.h
Added
@@ -0,0 +1,26 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2022 Igalia S.L. + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#pragma once + +#include "soup-types.h" + +G_BEGIN_DECLS + +#define SOUP_TYPE_LISTENER (soup_listener_get_type ()) +G_DECLARE_FINAL_TYPE (SoupListener, soup_listener, SOUP, LISTENER, GObject) + +SoupListener *soup_listener_new (GSocket *socket, + GError **error); +SoupListener *soup_listener_new_for_address (GSocketAddress *address, + GError **error); + +void soup_listener_disconnect (SoupListener *listener); +gboolean soup_listener_is_ssl (SoupListener *listener); +GSocket *soup_listener_get_socket (SoupListener *listener); +GInetSocketAddress *soup_listener_get_address (SoupListener *listener); + +G_END_DECLS
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-message-body.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-message-body.c
Changed
@@ -15,24 +15,15 @@ #include "soup.h" /** - * SECTION:soup-message-body - * @short_description: HTTP message body - * @see_also: #SoupMessage - * - * #SoupMessageBody represents the request or response body of a - * #SoupMessage. - **/ - -/** * SoupMemoryUse: * @SOUP_MEMORY_STATIC: The memory is statically allocated and - * constant; libsoup can use the passed-in buffer directly and not - * need to worry about it being modified or freed. + * constant; libsoup can use the passed-in buffer directly and not + * need to worry about it being modified or freed. * @SOUP_MEMORY_TAKE: The caller has allocated the memory and libsoup - * will assume ownership of it and free it with g_free(). + * will assume ownership of it and free it with func@GLib.free. * @SOUP_MEMORY_COPY: The passed-in data belongs to the caller and - * libsoup will copy it into new memory leaving the caller free - * to reuse the original memory. + * libsoup will copy it into new memory leaving the caller free + * to reuse the original memory. * * The lifetime of the memory being passed. **/ @@ -42,18 +33,19 @@ * @data: (array length=length) (element-type guint8): the data * @length: length of @data * - * A #SoupMessage request or response body. + * #SoupMessageBody represents the request or response body of a + * class@Message. * * Note that while @length always reflects the full length of the * message body, @data is normally %NULL, and will only be filled in - * after soup_message_body_flatten() is called. For client-side + * after method@MessageBody.flatten is called. For client-side * messages, this automatically happens for the response body after it * has been fully read. Likewise, for server-side * messages, the request body is automatically filled in after being * read. * * As an added bonus, when @data is filled in, it is always terminated - * with a '\0' byte (which is not reflected in @length). + * with a `\0` byte (which is not reflected in @length). **/ typedef struct { @@ -67,7 +59,9 @@ /** * soup_message_body_new: * - * Creates a new #SoupMessageBody. #SoupMessage uses this internally; you + * Creates a new #SoupMessageBody. + * + * class@Message uses this internally; you * will not normally need to call it yourself. * * Returns: a new #SoupMessageBody. @@ -88,19 +82,19 @@ * @body: a #SoupMessageBody * @accumulate: whether or not to accumulate body chunks in @body * - * Sets or clears the accumulate flag on @body. (The default value is - * %TRUE.) If set to %FALSE, @body's data field will not be filled in - * after the body is fully sent/received, and the chunks that make up - * @body may be discarded when they are no longer needed. + * Sets or clears the accumulate flag on @body. + * + * (The default value is %TRUE.) If set to %FALSE, @body's data field will not + * be filled in after the body is fully sent/received, and the chunks that make + * up @body may be discarded when they are no longer needed. * - * If you set the flag to %FALSE on the #SoupMessage request_body of a + * If you set the flag to %FALSE on the class@Message request_body of a * client-side message, it will block the accumulation of chunks into * @body's data field, but it will not normally cause the chunks to * be discarded after being written like in the server-side - * #SoupMessage response_body case, because the request body needs to + * class@Message response_body case, because the request body needs to * be kept around in case the request needs to be sent a second time * due to redirection or authentication. - * **/ void soup_message_body_set_accumulate (SoupMessageBody *body, @@ -115,11 +109,11 @@ * soup_message_body_get_accumulate: * @body: a #SoupMessageBody * - * Gets the accumulate flag on @body; see - * soup_message_body_set_accumulate() for details. + * Gets the accumulate flag on @body. * - * Returns: the accumulate flag for @body. + * See method@MessageBody.set_accumulate. for details. * + * Returns: the accumulate flag for @body. **/ gboolean soup_message_body_get_accumulate (SoupMessageBody *body) @@ -180,10 +174,9 @@ * * Appends @length bytes from @data to @body. * - * This function is exactly equivalent to soup_message_body_append() + * This function is exactly equivalent to method@MessageBody.append * with %SOUP_MEMORY_TAKE as second argument; it exists mainly for * convenience and simplifying language bindings. - * **/ void soup_message_body_append_take (SoupMessageBody *body, @@ -229,8 +222,9 @@ * soup_message_body_complete: * @body: a #SoupMessageBody * - * Tags @body as being complete; Call this when using chunked encoding - * after you have appended the last chunk. + * Tags @body as being complete. + * + * Call this when using chunked encoding after you have appended the last chunk. **/ void soup_message_body_complete (SoupMessageBody *body) @@ -243,11 +237,13 @@ * @body: a #SoupMessageBody * * Fills in @body's data field with a buffer containing all of the - * data in @body (plus an additional '\0' byte not counted by @body's - * length field). + * data in @body. * - * Return: (transfer full): a #GBytes containing the same data as @body. - * (You must g_bytes_unref() this if you do not want it.) + * Adds an additional `\0` byte not counted by @body's + * length field. + * + * Returns: (transfer full): a #GBytes containing the same data as @body. + * (You must method@GLib.Bytes.unref this if you do not want it.) **/ GBytes * soup_message_body_flatten (SoupMessageBody *body) @@ -284,23 +280,24 @@ * @body: a #SoupMessageBody * @offset: an offset * - * Gets a #GBytes containing data from @body starting at @offset. + * Gets a struct@GLib.Bytes containing data from @body starting at @offset. + * * The size of the returned chunk is unspecified. You can iterate * through the entire body by first calling - * soup_message_body_get_chunk() with an offset of 0, and then on each + * method@MessageBody.get_chunk with an offset of 0, and then on each * successive call, increment the offset by the length of the * previously-returned chunk. * * If @offset is greater than or equal to the total length of @body, * then the return value depends on whether or not - * soup_message_body_complete() has been called or not; if it has, - * then soup_message_body_get_chunk() will return a 0-length chunk + * method@MessageBody.complete has been called or not; if it has, + * then method@MessageBody.get_chunk will return a 0-length chunk * (indicating the end of @body). If it has not, then - * soup_message_body_get_chunk() will return %NULL (indicating that + * method@MessageBody.get_chunk will return %NULL (indicating that * @body may still potentially have more data, but that data is not * currently available). * - * Returns: (nullable): a #GBytes, or %NULL. + * Returns: (nullable): a #GBytes **/ GBytes * soup_message_body_get_chunk (SoupMessageBody *body, goffset offset) @@ -332,13 +329,14 @@ * @chunk: a #GBytes received from the network * * Handles the #SoupMessageBody part of receiving a chunk of data from - * the network. Normally this means appending @chunk to @body, exactly - * as with soup_message_body_append_bytes(), but if you have set - * @body's accumulate flag to %FALSE, then that will not happen. + * the network. + * + * Normally this means appending @chunk to @body, exactly as with + * method@MessageBody.append_bytes, but if you have set @body's accumulate + * flag to %FALSE, then that will not happen. * * This is a low-level method which you should not normally need to * use. - * **/ void soup_message_body_got_chunk (SoupMessageBody *body, GBytes *chunk) @@ -354,17 +352,17 @@ /** * soup_message_body_wrote_chunk: * @body: a #SoupMessageBody - * @chunk: a #GBytes returned from soup_message_body_get_chunk() + * @chunk: a #GBytes returned from method@MessageBody.get_chunk * * Handles the #SoupMessageBody part of writing a chunk of data to the - * network. Normally this is a no-op, but if you have set @body's - * accumulate flag to %FALSE, then this will cause @chunk to be - * discarded to free up memory. + * network. + * + * Normally this is a no-op, but if you have set @body's accumulate flag to + * %FALSE, then this will cause @chunk to be discarded to free up memory. * * This is a low-level method which you should not need to use, and * there are further restrictions on its proper use which are not * documented here. - * **/ void soup_message_body_wrote_chunk (SoupMessageBody *body, GBytes *chunk) @@ -408,6 +406,7 @@ * @body: a #SoupMessageBody * * Atomically decrements the reference count of @body by one. + * * When the reference count reaches zero, the resources allocated by * @body are freed */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-path-map.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-path-map.c
Changed
@@ -34,7 +34,7 @@ /** * soup_path_map_new: * @data_free_func: function to use to free data added with - * soup_path_map_add(). + * soup_path_map_add(). * * Creates a new %SoupPathMap. * @@ -176,7 +176,7 @@ * closest parent directory of @path that has data associated with it. * * Returns: (nullable): the data set with soup_path_map_add(), or - * %NULL if no data could be found for @path or any of its ancestors. + * %NULL if no data could be found for @path or any of its ancestors. **/ gpointer soup_path_map_lookup (SoupPathMap *map, const char *path)
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-server-connection.c
Added
@@ -0,0 +1,753 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * soup-server-connection.c: Connection networking code. + * + * Copyright (C) 2022 Igalia S.L. + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n-lib.h> +#include <gio/gnetworking.h> + +#include "soup-server-connection.h" +#include "soup.h" +#include "soup-io-stream.h" +#include "soup-server-message-private.h" +#include "soup-server-message-io-http1.h" +#include "soup-server-message-io-http2.h" + +enum { + CONNECTED, + DISCONNECTED, + ACCEPT_CERTIFICATE, + REQUEST_STARTED, + LAST_SIGNAL +}; + +static guint signalsLAST_SIGNAL = { 0 }; + +enum { + PROP_0, + + PROP_SOCKET, + PROP_CONNECTION, + PROP_LOCAL_ADDRESS, + PROP_REMOTE_ADDRESS, + PROP_TLS_CERTIFICATE, + PROP_TLS_DATABASE, + PROP_TLS_AUTH_MODE, + PROP_TLS_PEER_CERTIFICATE, + PROP_TLS_PEER_CERTIFICATE_ERRORS, + + LAST_PROPERTY +}; + +static GParamSpec *propertiesLAST_PROPERTY = { NULL, }; + +struct _SoupServerConnection { + GObject parent_instance; +}; + +typedef struct { + GSocket *socket; + GIOStream *conn; + GIOStream *iostream; + SoupServerMessage *initial_msg; + gboolean advertise_http2; + SoupHTTPVersion http_version; + SoupServerMessageIO *io_data; + + GSocketAddress *local_addr; + GSocketAddress *remote_addr; + + GTlsCertificate *tls_certificate; + GTlsDatabase *tls_database; + GTlsAuthenticationMode tls_auth_mode; +} SoupServerConnectionPrivate; + +G_DEFINE_FINAL_TYPE_WITH_PRIVATE (SoupServerConnection, soup_server_connection, G_TYPE_OBJECT) + +static void +request_started_cb (SoupServerMessage *msg, + SoupServerConnection *conn) +{ + g_signal_emit (conn, signalsREQUEST_STARTED, 0, msg); +} + +static void +soup_server_connection_init (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + priv->http_version = SOUP_HTTP_1_1; +} + +static void +disconnect_internal (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + g_clear_object (&priv->socket); + + g_io_stream_close (priv->conn, NULL, NULL); + g_signal_handlers_disconnect_by_data (priv->conn, conn); + g_clear_object (&priv->conn); + + g_clear_pointer (&priv->io_data, soup_server_message_io_destroy); +} + +static void +soup_server_connection_finalize (GObject *object) +{ + SoupServerConnection *conn = SOUP_SERVER_CONNECTION (object); + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + if (priv->conn) { + disconnect_internal (conn); + } else { + g_clear_object (&priv->socket); + g_clear_pointer (&priv->io_data, soup_server_message_io_destroy); + } + + g_clear_object (&priv->iostream); + + g_clear_object (&priv->local_addr); + g_clear_object (&priv->remote_addr); + + g_clear_object (&priv->tls_certificate); + g_clear_object (&priv->tls_database); + + G_OBJECT_CLASS (soup_server_connection_parent_class)->finalize (object); +} + +static void +soup_server_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SoupServerConnection *conn = SOUP_SERVER_CONNECTION (object); + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + switch (prop_id) { + case PROP_SOCKET: + priv->socket = g_value_dup_object (value); + break; + case PROP_CONNECTION: + priv->conn = g_value_dup_object (value); + if (priv->conn) + priv->iostream = soup_io_stream_new (priv->conn, FALSE); + break; + case PROP_LOCAL_ADDRESS: + priv->local_addr = g_value_dup_object (value); + break; + case PROP_REMOTE_ADDRESS: + priv->remote_addr = g_value_dup_object (value); + break; + case PROP_TLS_CERTIFICATE: + priv->tls_certificate = g_value_dup_object (value); + break; + case PROP_TLS_DATABASE: + priv->tls_database = g_value_dup_object (value); + break; + case PROP_TLS_AUTH_MODE: + priv->tls_auth_mode = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +soup_server_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SoupServerConnection *conn = SOUP_SERVER_CONNECTION (object); + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + switch (prop_id) { + case PROP_SOCKET: + g_value_set_object (value, priv->socket); + break; + case PROP_CONNECTION: + g_value_set_object (value, priv->conn); + break; + case PROP_LOCAL_ADDRESS: + g_value_set_object (value, soup_server_connection_get_local_address (conn)); + break; + case PROP_REMOTE_ADDRESS: + g_value_set_object (value, soup_server_connection_get_remote_address (conn)); + break; + case PROP_TLS_CERTIFICATE: + g_value_set_object (value, priv->tls_certificate); + break; + case PROP_TLS_DATABASE: + g_value_set_object (value, priv->tls_database); + break; + case PROP_TLS_AUTH_MODE: + g_value_set_enum (value, priv->tls_auth_mode); + break; + case PROP_TLS_PEER_CERTIFICATE: + g_value_set_object (value, soup_server_connection_get_tls_peer_certificate (conn)); + break; + case PROP_TLS_PEER_CERTIFICATE_ERRORS: + g_value_set_flags (value, soup_server_connection_get_tls_peer_certificate_errors (conn)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +soup_server_connection_class_init (SoupServerConnectionClass *conn_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (conn_class); + + object_class->finalize = soup_server_connection_finalize; + object_class->set_property = soup_server_connection_set_property; + object_class->get_property = soup_server_connection_get_property; + + signalsCONNECTED = + g_signal_new ("connected", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * SoupServerConnection::disconnected: + * @conn: the connection + * + * Emitted when the connection is disconnected, for whatever reason. + **/ + signalsDISCONNECTED = + g_signal_new ("disconnected", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + signalsACCEPT_CERTIFICATE = + g_signal_new ("accept-certificate", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + g_signal_accumulator_true_handled, NULL, + NULL, + G_TYPE_BOOLEAN, 2, + G_TYPE_TLS_CERTIFICATE, + G_TYPE_TLS_CERTIFICATE_FLAGS); + signalsREQUEST_STARTED = + g_signal_new ("request-started", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + SOUP_TYPE_SERVER_MESSAGE); + + /* properties */ + propertiesPROP_SOCKET = + g_param_spec_object ("socket", + "Socket", + "The connection underlying GSocket", + G_TYPE_SOCKET, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + propertiesPROP_CONNECTION = + g_param_spec_object ("connection", + "GIOStream", + "The socket's underlying GIOStream", + G_TYPE_IO_STREAM, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + propertiesPROP_LOCAL_ADDRESS = + g_param_spec_object ("local-address", + "Local address", + "Address of local end of socket", + G_TYPE_SOCKET_ADDRESS, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + propertiesPROP_REMOTE_ADDRESS = + g_param_spec_object ("remote-address", + "Remote address", + "Address of remote end of socket", + G_TYPE_SOCKET_ADDRESS, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + propertiesPROP_TLS_CERTIFICATE = + g_param_spec_object ("tls-certificate", + "TLS Certificate", + "The server TLS certificate", + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + propertiesPROP_TLS_DATABASE = + g_param_spec_object ("tls-database", + "TLS Database", + "The server TLS database", + G_TYPE_TLS_DATABASE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + propertiesPROP_TLS_AUTH_MODE = + g_param_spec_enum ("tls-auth-mode", + "TLS Authentication Mode", + "The server TLS authentication mode", + G_TYPE_TLS_AUTHENTICATION_MODE, + G_TLS_AUTHENTICATION_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + propertiesPROP_TLS_PEER_CERTIFICATE = + g_param_spec_object ("tls-peer-certificate", + "TLS Peer Certificate", + "The TLS peer certificate associated with the message", + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + propertiesPROP_TLS_PEER_CERTIFICATE_ERRORS = + g_param_spec_flags ("tls-peer-certificate-errors", + "TLS Peer Certificate Errors", + "The verification errors on the message's TLS peer certificate", + G_TYPE_TLS_CERTIFICATE_FLAGS, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, LAST_PROPERTY, properties); +} + +SoupServerConnection * +soup_server_connection_new (GSocket *socket, + GTlsCertificate *tls_certificate, + GTlsDatabase *tls_database, + GTlsAuthenticationMode tls_auth_mode) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), NULL); + g_return_val_if_fail (!tls_certificate || G_IS_TLS_CERTIFICATE (tls_certificate), NULL); + g_return_val_if_fail (!tls_database || G_IS_TLS_DATABASE (tls_database), NULL); + + return g_object_new (SOUP_TYPE_SERVER_CONNECTION, + "socket", socket, + "tls-certificate", tls_certificate, + "tls-database", tls_database, + "tls-auth-mode", tls_auth_mode, + NULL); +} + +SoupServerConnection * +soup_server_connection_new_for_connection (GIOStream *connection, + GSocketAddress *local_addr, + GSocketAddress *remote_addr) +{ + g_return_val_if_fail (G_IS_IO_STREAM (connection), NULL); + g_return_val_if_fail (G_IS_SOCKET_ADDRESS (local_addr), NULL); + g_return_val_if_fail (G_IS_SOCKET_ADDRESS (remote_addr), NULL); + + return g_object_new (SOUP_TYPE_SERVER_CONNECTION, + "connection", connection, + "local-address", local_addr, + "remote-address", remote_addr, + NULL); +} + +SoupServerMessageIO * +soup_server_connection_get_io_data (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + return priv->io_data; +} + +static void +soup_server_connection_connected (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + g_assert (!priv->io_data); + switch (priv->http_version) { + case SOUP_HTTP_1_0: + case SOUP_HTTP_1_1: + priv->io_data = soup_server_message_io_http1_new (conn, + g_steal_pointer (&priv->initial_msg), + (SoupMessageIOStartedFn)request_started_cb, + conn); + break; + case SOUP_HTTP_2_0: + priv->io_data = soup_server_message_io_http2_new (conn, + g_steal_pointer (&priv->initial_msg), + (SoupMessageIOStartedFn)request_started_cb, + conn); + break; + } + g_signal_emit (conn, signalsCONNECTED, 0); +} + +static gboolean +tls_connection_accept_certificate (SoupServerConnection *conn, + GTlsCertificate *tls_certificate, + GTlsCertificateFlags tls_errors) +{ + gboolean accept = FALSE; + + g_signal_emit (conn, signalsACCEPT_CERTIFICATE, 0, + tls_certificate, tls_errors, &accept); + return accept; +} + +static void +tls_connection_peer_certificate_changed (SoupServerConnection *conn) +{ + g_object_notify_by_pspec (G_OBJECT (conn), propertiesPROP_TLS_CERTIFICATE); +} + +static void +tls_connection_handshake_ready_cb (GTlsConnection *tls_conn, + GAsyncResult *result, + SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + if (g_tls_connection_handshake_finish (tls_conn, result, NULL)) { + const char *protocol = g_tls_connection_get_negotiated_protocol (tls_conn); + + if (g_strcmp0 (protocol, "h2") == 0) + priv->http_version = SOUP_HTTP_2_0; + else if (g_strcmp0 (protocol, "http/1.0") == 0) + priv->http_version = SOUP_HTTP_1_0; + else if (g_strcmp0 (protocol, "http/1.1") == 0) + priv->http_version = SOUP_HTTP_1_1; + + soup_server_connection_connected (conn); + } else { + soup_server_connection_disconnect (conn); + } +} + +void +soup_server_connection_set_advertise_http2 (SoupServerConnection *conn, + gboolean advertise_http2) +{ + SoupServerConnectionPrivate *priv; + + g_return_if_fail (SOUP_IS_SERVER_CONNECTION (conn)); + + priv = soup_server_connection_get_instance_private (conn); + priv->advertise_http2 = advertise_http2; +} + +void +soup_server_connection_accepted (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + GIOStream *connection; + + g_return_if_fail (SOUP_IS_SERVER_CONNECTION (conn)); + + priv = soup_server_connection_get_instance_private (conn); + + /* We need to create the first message earlier here because SoupServerMessage is used + * to accept the TLS certificate. + */ + g_assert (!priv->initial_msg); + priv->initial_msg = soup_server_message_new (conn); + g_signal_emit (conn, signalsREQUEST_STARTED, 0, priv->initial_msg); + + if (priv->conn || !priv->socket) { + soup_server_connection_connected (conn); + return; + } + + connection = (GIOStream *)g_socket_connection_factory_create_connection (priv->socket); + g_socket_set_option (priv->socket, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); + + if (priv->tls_certificate) { + GPtrArray *advertised_protocols; + + advertised_protocols = g_ptr_array_sized_new (4); + if (priv->advertise_http2 && priv->tls_auth_mode == G_TLS_AUTHENTICATION_NONE) + g_ptr_array_add (advertised_protocols, "h2"); + g_ptr_array_add (advertised_protocols, "http/1.1"); + g_ptr_array_add (advertised_protocols, "http/1.0"); + g_ptr_array_add (advertised_protocols, NULL); + + priv->conn = g_initable_new (g_tls_backend_get_server_connection_type (g_tls_backend_get_default ()), + NULL, NULL, + "base-io-stream", connection, + "certificate", priv->tls_certificate, + "database", priv->tls_database, + "authentication-mode", priv->tls_auth_mode, + "require-close-notify", FALSE, + "advertised-protocols", advertised_protocols->pdata, + NULL); + g_ptr_array_unref (advertised_protocols); + g_object_unref (connection); + if (!priv->conn) { + soup_server_connection_disconnect (conn); + return; + } + + priv->iostream = soup_io_stream_new (priv->conn, FALSE); + + g_signal_connect_object (priv->conn, "accept-certificate", + G_CALLBACK (tls_connection_accept_certificate), + conn, G_CONNECT_SWAPPED); + g_signal_connect_object (priv->conn, "notify::peer-certificate", + G_CALLBACK (tls_connection_peer_certificate_changed), + conn, G_CONNECT_SWAPPED); + + g_tls_connection_handshake_async (G_TLS_CONNECTION (priv->conn), + G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)tls_connection_handshake_ready_cb, + conn); + return; + } + + priv->conn = connection; + priv->iostream = soup_io_stream_new (priv->conn, FALSE); + soup_server_connection_connected (conn); +} + +GSocket * +soup_server_connection_get_socket (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + + return priv->socket; +} + +GIOStream * +soup_server_connection_steal (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + GIOStream *stream; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + + stream = priv->io_data ? soup_server_message_io_steal (priv->io_data) : NULL; + if (stream && priv->socket) { + g_object_set_data_full (G_OBJECT (stream), "GSocket", + g_object_ref (priv->socket), + g_object_unref); + } + + /* Cache local and remote address */ + soup_server_connection_get_local_address (conn); + soup_server_connection_get_remote_address (conn); + + g_clear_pointer (&priv->io_data, soup_server_message_io_destroy); + g_clear_object (&priv->conn); + g_clear_object (&priv->iostream); + + g_signal_emit (conn, signalsDISCONNECTED, 0); + + return stream; +} + +GIOStream * +soup_server_connection_get_iostream (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + + return priv->iostream; +} + +/** + * soup_server_connection_is_ssl: + * @conn: a #SoupServerConnection + * + * Tests if @sock is doing (or has attempted to do) SSL. + * + * Returns: %TRUE if @conn has SSL credentials set + **/ +gboolean +soup_server_connection_is_ssl (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), FALSE); + + priv = soup_server_connection_get_instance_private (conn); + + return G_IS_TLS_CONNECTION (priv->conn) || priv->tls_certificate; +} + +/** + * soup_server_connection_disconnect: + * @sock: a #SoupServerConnection + * + * Disconnects @conn. Any further read or write attempts on it will fail. + **/ +void +soup_server_connection_disconnect (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_if_fail (SOUP_IS_SERVER_CONNECTION (conn)); + + priv = soup_server_connection_get_instance_private (conn); + + if (!priv->conn) + return; + + disconnect_internal (conn); + + /* Keep ref around signals in case the object is unreferenced + * in a handler + */ + g_object_ref (conn); + + /* FIXME: can't disconnect until all data is read */ + + /* Then let everyone know we're disconnected */ + g_signal_emit (conn, signalsDISCONNECTED, 0); + + g_object_unref (conn); +} + +/** + * soup_server_connection_is_connected: + * @conn: a #SoupServerConnection + * + * Tests if @conn is connected to another host + * + * Returns: %TRUE or %FALSE. + **/ +gboolean +soup_server_connection_is_connected (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), FALSE); + + priv = soup_server_connection_get_instance_private (conn); + + return priv->conn && !g_io_stream_is_closed (priv->conn); +} + +/** + * soup_server_connection_get_local_address: + * @conn: a #SoupServerConnection + * + * Returns the #GInetSocketAddress corresponding to the local end of @conn. + * + * Calling this method on an unconnected socket is considered to be + * an error, and produces undefined results. + * + * Returns: (transfer none): the #GSocketAddress + **/ +GSocketAddress * +soup_server_connection_get_local_address (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + if (!priv->local_addr) { + GError *error = NULL; + + priv->local_addr = g_socket_get_local_address (priv->socket, &error); + if (priv->local_addr == NULL) { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + return NULL; + } + } + + return priv->local_addr; +} + +/** + * soup_server_connection_get_remote_address: + * @conn: a #SoupServerConnection + * + * Returns the #GInetSocketAddress corresponding to the remote end of @conn. + * + * Calling this method on an unconnected socket is considered to be + * an error, and produces undefined results. + * + * Returns: (transfer none): the #GSocketAddress + **/ +GSocketAddress * +soup_server_connection_get_remote_address (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + + if (!priv->remote_addr) { + GError *error = NULL; + + priv->remote_addr = g_socket_get_remote_address (priv->socket, &error); + if (priv->remote_addr == NULL) { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + return NULL; + } + } + + return priv->remote_addr; +} + +GTlsCertificate * +soup_server_connection_get_tls_peer_certificate (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + + if (!G_IS_TLS_CONNECTION (priv->conn)) + return NULL; + + return g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (priv->conn)); +} + +GTlsCertificateFlags +soup_server_connection_get_tls_peer_certificate_errors (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), 0); + + priv = soup_server_connection_get_instance_private (conn); + + if (!G_IS_TLS_CONNECTION (priv->conn)) + return 0; + + return g_tls_connection_get_peer_certificate_errors (G_TLS_CONNECTION (priv->conn)); +}
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-server-connection.h
Added
@@ -0,0 +1,40 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2022 Igalia S.L. + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#pragma once + +#include "soup-types.h" +#include "soup-server-message-io.h" +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define SOUP_TYPE_SERVER_CONNECTION (soup_server_connection_get_type ()) +G_DECLARE_FINAL_TYPE (SoupServerConnection, soup_server_connection, SOUP, SERVER_CONNECTION, GObject) + +SoupServerConnection *soup_server_connection_new (GSocket *socket, + GTlsCertificate *tls_certificate, + GTlsDatabase *tls_database, + GTlsAuthenticationMode tls_auth_mode); +SoupServerConnection *soup_server_connection_new_for_connection (GIOStream *connection, + GSocketAddress *local_addr, + GSocketAddress *remote_addr); +void soup_server_connection_set_advertise_http2 (SoupServerConnection *conn, + gboolean advertise_http2); +void soup_server_connection_accepted (SoupServerConnection *conn); +SoupServerMessageIO *soup_server_connection_get_io_data (SoupServerConnection *conn); +gboolean soup_server_connection_is_ssl (SoupServerConnection *conn); +void soup_server_connection_disconnect (SoupServerConnection *conn); +gboolean soup_server_connection_is_connected (SoupServerConnection *conn); +GSocket *soup_server_connection_get_socket (SoupServerConnection *conn); +GIOStream *soup_server_connection_steal (SoupServerConnection *conn); +GIOStream *soup_server_connection_get_iostream (SoupServerConnection *conn); +GSocketAddress *soup_server_connection_get_local_address (SoupServerConnection *conn); +GSocketAddress *soup_server_connection_get_remote_address (SoupServerConnection *conn); +GTlsCertificate *soup_server_connection_get_tls_peer_certificate (SoupServerConnection *conn); +GTlsCertificateFlags soup_server_connection_get_tls_peer_certificate_errors (SoupServerConnection *conn); + +G_END_DECLS
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-server-message-io.c
Added
@@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2022 Igalia S.L. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "soup-server-message-io.h" + +void +soup_server_message_io_destroy (SoupServerMessageIO *io) +{ + if (!io) + return; + + io->funcs->destroy (io); +} + +void +soup_server_message_io_finished (SoupServerMessageIO *io, + SoupServerMessage *msg) +{ + io->funcs->finished (io, msg); +} + +GIOStream * +soup_server_message_io_steal (SoupServerMessageIO *io) +{ + return io->funcs->steal (io); +} + +void +soup_server_message_io_read_request (SoupServerMessageIO *io, + SoupServerMessage *msg, + SoupMessageIOCompletionFn completion_cb, + gpointer user_data) +{ + io->funcs->read_request (io, msg, completion_cb, user_data); +} + +void +soup_server_message_io_pause (SoupServerMessageIO *io, + SoupServerMessage *msg) +{ + io->funcs->pause (io, msg); +} + +void +soup_server_message_io_unpause (SoupServerMessageIO *io, + SoupServerMessage *msg) +{ + io->funcs->unpause (io, msg); +} + +gboolean +soup_server_message_io_is_paused (SoupServerMessageIO *io, + SoupServerMessage *msg) +{ + return io->funcs->is_paused (io, msg); +}
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-server-message-io.h
Added
@@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2022 Igalia S.L. + */ + +#pragma once + +#include "soup-server-message.h" +#include "soup-message-io-completion.h" + +typedef struct _SoupServerMessageIO SoupServerMessageIO; + +typedef struct { + void (*destroy) (SoupServerMessageIO *io); + void (*finished) (SoupServerMessageIO *io, + SoupServerMessage *msg); + GIOStream *(*steal) (SoupServerMessageIO *io); + void (*read_request) (SoupServerMessageIO *io, + SoupServerMessage *msg, + SoupMessageIOCompletionFn completion_cb, + gpointer user_data); + void (*pause) (SoupServerMessageIO *io, + SoupServerMessage *msg); + void (*unpause) (SoupServerMessageIO *io, + SoupServerMessage *msg); + gboolean (*is_paused) (SoupServerMessageIO *io, + SoupServerMessage *msg); +} SoupServerMessageIOFuncs; + +struct _SoupServerMessageIO { + const SoupServerMessageIOFuncs *funcs; +}; + +typedef void (* SoupMessageIOStartedFn) (SoupServerMessage *msg, + gpointer user_data); + +void soup_server_message_io_destroy (SoupServerMessageIO *io); +void soup_server_message_io_finished (SoupServerMessageIO *io, + SoupServerMessage *msg); +GIOStream *soup_server_message_io_steal (SoupServerMessageIO *io); +void soup_server_message_io_read_request (SoupServerMessageIO *io, + SoupServerMessage *msg, + SoupMessageIOCompletionFn completion_cb, + gpointer user_data); +void soup_server_message_io_pause (SoupServerMessageIO *io, + SoupServerMessage *msg); +void soup_server_message_io_unpause (SoupServerMessageIO *io, + SoupServerMessage *msg); +gboolean soup_server_message_io_is_paused (SoupServerMessageIO *io, + SoupServerMessage *msg);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-server-message-private.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-server-message-private.h
Changed
@@ -8,23 +8,20 @@ #include "soup-server-message.h" #include "soup-auth-domain.h" #include "soup-message-io-data.h" -#include "soup-socket.h" +#include "soup-server-connection.h" -SoupServerMessage *soup_server_message_new (SoupSocket *sock); +SoupServerMessage *soup_server_message_new (SoupServerConnection *conn); void soup_server_message_set_uri (SoupServerMessage *msg, GUri *uri); void soup_server_message_set_method (SoupServerMessage *msg, const char *method); -SoupSocket *soup_server_message_get_soup_socket (SoupServerMessage *msg); +SoupServerConnection *soup_server_message_get_connection (SoupServerMessage *msg); void soup_server_message_set_auth (SoupServerMessage *msg, SoupAuthDomain *domain, char *user); gboolean soup_server_message_is_keepalive (SoupServerMessage *msg); -GIOStream *soup_server_message_io_steal (SoupServerMessage *msg); -void soup_server_message_io_pause (SoupServerMessage *msg); -void soup_server_message_io_unpause (SoupServerMessage *msg); gboolean soup_server_message_is_io_paused (SoupServerMessage *msg); -void soup_server_message_io_finished (SoupServerMessage *msg); +void soup_server_message_finish (SoupServerMessage *msg); void soup_server_message_cleanup_response (SoupServerMessage *msg); void soup_server_message_wrote_informational (SoupServerMessage *msg); void soup_server_message_wrote_headers (SoupServerMessage *msg); @@ -40,13 +37,11 @@ void soup_server_message_read_request (SoupServerMessage *msg, SoupMessageIOCompletionFn completion_cb, gpointer user_data); + void soup_server_message_set_options_ping (SoupServerMessage *msg, gboolean is_options_ping); -typedef struct _SoupServerMessageIOData SoupServerMessageIOData; -void soup_server_message_io_data_free (SoupServerMessageIOData *io); -void soup_server_message_set_io_data (SoupServerMessage *msg, - SoupServerMessageIOData *io); -SoupServerMessageIOData *soup_server_message_get_io_data (SoupServerMessage *msg); +SoupServerMessageIO *soup_server_message_get_io_data (SoupServerMessage *msg); + #endif /* __SOUP_SERVER_MESSAGE_PRIVATE_H__ */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-server-message.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-server-message.c
Changed
@@ -15,44 +15,33 @@ #include "soup-connection.h" #include "soup-server-message-private.h" #include "soup-message-headers-private.h" -#include "soup-socket.h" #include "soup-uri-utils-private.h" /** - * SECTION:soup-server-message - * @short_description: An HTTP server request and response. - * @see_also: #SoupMessageHeaders, #SoupMessageBody + * SoupServerMessage: + * + * An HTTP server request and response pair. * * A SoupServerMessage represents an HTTP message that is being sent or - * received on a #SoupServer + * received on a class@Server. * - * #SoupServer will create #SoupServerMessage<!-- -->s automatically for + * class@Server will create `SoupServerMessage`s automatically for * incoming requests, which your application will receive via handlers. * * Note that libsoup's terminology here does not quite match the HTTP - * specification: in RFC 2616, an "HTTP-message" is - * <emphasis>either</emphasis> a Request, <emphasis>or</emphasis> a - * Response. In libsoup, a #SoupServerMessage combines both the request and - * the response. + * specification: in RFC 2616, an "HTTP-message" is *either* a Request, *or* a + * Response. In libsoup, a #SoupServerMessage combines both the request and the + * response. **/ -/** - * SoupServerMessage: - * - * Class represnting an HTTP request and response pair for a server. - */ - struct _SoupServerMessage { GObject parent; - SoupSocket *sock; - GSocket *gsock; + SoupServerConnection *conn; SoupAuthDomain *auth_domain; char *auth_user; - GSocketAddress *remote_addr; char *remote_ip; - GSocketAddress *local_addr; const char *method; SoupHTTPVersion http_version; @@ -69,9 +58,12 @@ SoupMessageBody *response_body; SoupMessageHeaders *response_headers; - SoupServerMessageIOData *io_data; + SoupServerMessageIO *io_data; gboolean options_ping; + + GTlsCertificate *tls_peer_certificate; + GTlsCertificateFlags tls_peer_certificate_errors; }; struct _SoupServerMessageClass { @@ -91,6 +83,7 @@ GOT_CHUNK, GOT_BODY, + CONNECTED, DISCONNECTED, FINISHED, @@ -101,6 +94,17 @@ static guint signalsLAST_SIGNAL = { 0 }; +enum { + PROP_0, + + PROP_TLS_PEER_CERTIFICATE, + PROP_TLS_PEER_CERTIFICATE_ERRORS, + + LAST_PROPERTY +}; + +static GParamSpec *propertiesLAST_PROPERTY = { NULL, }; + static void soup_server_message_init (SoupServerMessage *msg) { @@ -116,18 +120,13 @@ { SoupServerMessage *msg = SOUP_SERVER_MESSAGE (object); - soup_server_message_io_data_free (msg->io_data); - g_clear_object (&msg->auth_domain); g_clear_pointer (&msg->auth_user, g_free); - g_clear_object (&msg->remote_addr); - g_clear_object (&msg->local_addr); - if (msg->sock) { - g_signal_handlers_disconnect_by_data (msg->sock, msg); - g_object_unref (msg->sock); + if (msg->conn) { + g_signal_handlers_disconnect_by_data (msg->conn, msg); + g_object_unref (msg->conn); } - g_clear_object (&msg->gsock); g_clear_pointer (&msg->remote_ip, g_free); g_clear_pointer (&msg->uri, g_uri_unref); @@ -142,11 +141,31 @@ } static void +soup_server_message_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + SoupServerMessage *msg = SOUP_SERVER_MESSAGE (object); + + switch (prop_id) { + case PROP_TLS_PEER_CERTIFICATE: + g_value_set_object (value, msg->tls_peer_certificate); + break; + case PROP_TLS_PEER_CERTIFICATE_ERRORS: + g_value_set_flags (value, msg->tls_peer_certificate_errors); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void soup_server_message_class_init (SoupServerMessageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = soup_server_message_finalize; + object_class->get_property = soup_server_message_get_property; /** * SoupServerMessage::wrote-informational: @@ -186,11 +205,11 @@ * Emitted immediately after writing a body chunk for a message. * * Note that this signal is not parallel to - * #SoupServerMessage::got-chunk; it is emitted only when a complete - * chunk (added with soup_message_body_append() or - * soup_message_body_append_bytes()) has been written. To get + * signal@ServerMessage::got-chunk; it is emitted only when a complete + * chunk (added with method@MessageBody.append or + * method@MessageBody.append_bytes has been written. To get * more useful continuous progress information, use - * #SoupServerMessage::wrote-body-data. + * signal@ServerMessage::wrote-body-data. */ signalsWROTE_CHUNK = g_signal_new ("wrote-chunk", @@ -255,10 +274,10 @@ * @msg: the message * @chunk: the just-read chunk * - * Emitted after receiving a chunk of a message body. Note - * that "chunk" in this context means any subpiece of the - * body, not necessarily the specific HTTP 1.1 chunks sent by - * the other side. + * Emitted after receiving a chunk of a message body. + * + * Note that "chunk" in this context means any subpiece of the body, not + * necessarily the specific HTTP 1.1 chunks sent by the other side. */ signalsGOT_CHUNK = g_signal_new ("got-chunk", @@ -290,7 +309,7 @@ * @msg: the message * * Emitted when all HTTP processing is finished for a message. - * (After #SoupServerMessage::wrote-body). + * (After signal@ServerMessage::wrote-body). */ signalsFINISHED = g_signal_new ("finished", @@ -300,6 +319,20 @@ NULL, NULL, NULL, G_TYPE_NONE, 0); + /** + * SoupServerMessage::connected: + * @msg: the message + * + * Emitted when the @msg's socket is connected and the TLS handshake completed. + */ + signalsCONNECTED = + g_signal_new ("connected", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); /** * SoupServerMessage::disconnected: @@ -328,8 +361,8 @@ * @tls_errors. * * Returns: %TRUE to accept the TLS certificate and stop other - * handlers from being invoked, or %FALSE to propagate the - * event further. + * handlers from being invoked, or %FALSE to propagate the + * event further. */ signalsACCEPT_CERTIFICATE = g_signal_new ("accept-certificate", @@ -341,18 +374,59 @@ G_TYPE_BOOLEAN, 2, G_TYPE_TLS_CERTIFICATE, G_TYPE_TLS_CERTIFICATE_FLAGS); + + /** + * SoupServerMessage:tls-peer-certificate: + * + * The peer's #GTlsCertificate associated with the message + * + * Since: 3.2 + */ + propertiesPROP_TLS_PEER_CERTIFICATE = + g_param_spec_object ("tls-peer-certificate", + "TLS Peer Certificate", + "The TLS peer certificate associated with the message", + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + /** + * SoupServerMessage:tls-peer-certificate-errors: + * + * The verification errors on #SoupServerMessage:tls-peer-certificate + * + * Since: 3.2 + */ + propertiesPROP_TLS_PEER_CERTIFICATE_ERRORS = + g_param_spec_flags ("tls-peer-certificate-errors", + "TLS Peer Certificate Errors", + "The verification errors on the message's TLS peer certificate", + G_TYPE_TLS_CERTIFICATE_FLAGS, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, LAST_PROPERTY, properties); } static void -socket_disconnected (SoupServerMessage *msg) +connection_connected (SoupServerMessage *msg) { + g_assert (!msg->io_data); + msg->io_data = soup_server_connection_get_io_data (msg->conn); + g_signal_emit (msg, signalsCONNECTED, 0); +} + +static void +connection_disconnected (SoupServerMessage *msg) +{ + msg->io_data = NULL; g_signal_emit (msg, signalsDISCONNECTED, 0); } static gboolean -socket_accept_certificate (SoupServerMessage *msg, - GTlsCertificate *tls_certificate, - GTlsCertificateFlags *tls_errors) +connection_accept_certificate (SoupServerMessage *msg, + GTlsCertificate *tls_certificate, + GTlsCertificateFlags *tls_errors) { gboolean accept = FALSE; @@ -361,22 +435,51 @@ return accept; } +static void +soup_server_message_set_tls_peer_certificate (SoupServerMessage *msg, + GTlsCertificate *tls_certificate, + GTlsCertificateFlags tls_errors) +{ + if (msg->tls_peer_certificate == tls_certificate && msg->tls_peer_certificate_errors == tls_errors) + return; + + g_clear_object (&msg->tls_peer_certificate); + msg->tls_peer_certificate = tls_certificate ? g_object_ref (tls_certificate) : NULL; + msg->tls_peer_certificate_errors = tls_errors; + g_object_notify_by_pspec (G_OBJECT (msg), propertiesPROP_TLS_PEER_CERTIFICATE); + g_object_notify_by_pspec (G_OBJECT (msg), propertiesPROP_TLS_PEER_CERTIFICATE_ERRORS); +} + +static void +re_emit_tls_certificate_changed (SoupServerMessage *msg, + GParamSpec *pspec, + SoupServerConnection *conn) +{ + soup_server_message_set_tls_peer_certificate (msg, + soup_server_connection_get_tls_peer_certificate (conn), + soup_server_connection_get_tls_peer_certificate_errors (conn)); +} + SoupServerMessage * -soup_server_message_new (SoupSocket *sock) +soup_server_message_new (SoupServerConnection *conn) { SoupServerMessage *msg; msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL); - msg->sock = g_object_ref (sock); - msg->gsock = soup_socket_get_gsocket (sock); - if (msg->gsock) - g_object_ref (msg->gsock); + msg->conn = g_object_ref (conn); + msg->io_data = soup_server_connection_get_io_data (msg->conn); - g_signal_connect_object (sock, "disconnected", - G_CALLBACK (socket_disconnected), + g_signal_connect_object (conn, "connected", + G_CALLBACK (connection_connected), msg, G_CONNECT_SWAPPED); - g_signal_connect_object (sock, "accept-certificate", - G_CALLBACK (socket_accept_certificate), + g_signal_connect_object (conn, "disconnected", + G_CALLBACK (connection_disconnected), + msg, G_CONNECT_SWAPPED); + g_signal_connect_object (conn, "accept-certificate", + G_CALLBACK (connection_accept_certificate), + msg, G_CONNECT_SWAPPED); + g_signal_connect_object (conn, "notify::tls-certificate", + G_CALLBACK (re_emit_tls_certificate_changed), msg, G_CONNECT_SWAPPED); return msg; @@ -391,10 +494,10 @@ msg->uri = soup_uri_copy_with_normalized_flags (uri); } -SoupSocket * -soup_server_message_get_soup_socket (SoupServerMessage *msg) +SoupServerConnection * +soup_server_message_get_connection (SoupServerMessage *msg) { - return msg->sock; + return msg->conn; } void @@ -414,6 +517,9 @@ gboolean soup_server_message_is_keepalive (SoupServerMessage *msg) { + if (msg->http_version == SOUP_HTTP_2_0) + return TRUE; + if (msg->status_code == SOUP_STATUS_OK && msg->method == SOUP_METHOD_CONNECT) return TRUE; @@ -449,19 +555,76 @@ } void -soup_server_message_set_io_data (SoupServerMessage *msg, - SoupServerMessageIOData *io) +soup_server_message_read_request (SoupServerMessage *msg, + SoupMessageIOCompletionFn completion_cb, + gpointer user_data) { - soup_server_message_io_data_free (msg->io_data); - msg->io_data = io; + soup_server_message_io_read_request (msg->io_data, msg, completion_cb, user_data); } -SoupServerMessageIOData * +SoupServerMessageIO * soup_server_message_get_io_data (SoupServerMessage *msg) { return msg->io_data; } +/** + * soup_server_message_pause: + * @msg: a SoupServerMessage + * + * Pauses I/O on @msg. + * + * This can be used when you need to return from the server handler without + * having the full response ready yet. Use method@ServerMessage.unpause to + * resume I/O. + * + * Since: 3.2 + */ +void +soup_server_message_pause (SoupServerMessage *msg) +{ + g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg)); + g_return_if_fail (msg->io_data != NULL); + + soup_server_message_io_pause (msg->io_data, msg); +} + +/** + * soup_server_message_unpause: + * @msg: a SoupServerMessage + * + * Resumes I/O on @msg. + * + * Use this to resume after calling method@ServerMessage.pause, or after + * adding a new chunk to a chunked response. I/O won't actually resume until you + * return to the main loop. + * + * Since: 3.2 + */ +void +soup_server_message_unpause (SoupServerMessage *msg) +{ + g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg)); + g_return_if_fail (msg->io_data != NULL); + + soup_server_message_io_unpause (msg->io_data, msg); +} + +gboolean +soup_server_message_is_io_paused (SoupServerMessage *msg) +{ + return msg->io_data && soup_server_message_io_is_paused (msg->io_data, msg); +} + +void +soup_server_message_finish (SoupServerMessage *msg) +{ + if (!msg->io_data) + return; + + soup_server_message_io_finished (g_steal_pointer (&msg->io_data), msg); +} + void soup_server_message_cleanup_response (SoupServerMessage *msg) { @@ -680,9 +843,9 @@ * soup_server_message_get_reason_phrase: * @msg: a #SoupServerMessage: * - * Get the HTTP reason phrase of @msg or %NULL. + * Get the HTTP reason phrase of @msg. * - * Returns: the reason phrase. + * Returns: (nullable): the reason phrase. */ const char * soup_server_message_get_reason_phrase (SoupServerMessage *msg) @@ -714,9 +877,10 @@ * @status_code: an HTTP status code * @reason_phrase: (nullable): a reason phrase * - * Sets @msg's status code to @status_code. If @status_code is a - * known value and @reason_phrase is %NULL, the reason_phrase will - * be set automatically. + * Sets @msg's status code to @status_code. + * + * If @status_code is a known value and @reason_phrase is %NULL, the + * reason_phrase will be set automatically. **/ void soup_server_message_set_status (SoupServerMessage *msg, @@ -792,13 +956,13 @@ * @redirect_uri: the URI to redirect @msg to * * Sets @msg's status_code to @status_code and adds a Location header - * pointing to @redirect_uri. Use this from a #SoupServer when you + * pointing to @redirect_uri. Use this from a class@Server when you * want to redirect the client to another URI. * * @redirect_uri can be a relative URI, in which case it is * interpreted relative to @msg's current URI. In particular, if * @redirect_uri is just a path, it will replace the path - * <emphasis>and query</emphasis> of @msg's URI. + * *and query* of @msg's URI. */ void soup_server_message_set_redirect (SoupServerMessage *msg, @@ -825,7 +989,7 @@ * soup_server_message_get_socket: * @msg: a #SoupServerMessage * - * Retrieves the #GSocket that @msg is associated with. + * Retrieves the class@Gio.Socket that @msg is associated with. * * If you are using this method to observe when multiple requests are * made on the same persistent HTTP connection (eg, as the ntlm-test @@ -835,66 +999,52 @@ * previously-destroyed socket to represent a new socket. * * Returns: (nullable) (transfer none): the #GSocket that @msg is - * associated with, %NULL if you used soup_server_accept_iostream(). + * associated with, %NULL if you used method@Server.accept_iostream. */ GSocket * soup_server_message_get_socket (SoupServerMessage *msg) { g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); - return msg->gsock; + return soup_server_connection_get_socket (msg->conn); } /** * soup_server_message_get_remote_address: * @msg: a #SoupServerMessage * - * Retrieves the #GSocketAddress associated with the remote end + * Retrieves the class@Gio.SocketAddress associated with the remote end * of a connection. * * Returns: (nullable) (transfer none): the #GSocketAddress - * associated with the remote end of a connection, it may be - * %NULL if you used soup_server_accept_iostream(). + * associated with the remote end of a connection, it may be + * %NULL if you used class@Server.accept_iostream. */ GSocketAddress * soup_server_message_get_remote_address (SoupServerMessage *msg) { g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); - if (msg->remote_addr) - return msg->remote_addr; - - msg->remote_addr = msg->gsock ? - g_socket_get_remote_address (msg->gsock, NULL) : - G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_remote_address (msg->sock))); - - return msg->remote_addr; + return soup_server_connection_get_remote_address (msg->conn); } /** * soup_server_message_get_local_address: * @msg: a #SoupServerMessage * - * Retrieves the #GSocketAddress associated with the local end + * Retrieves the class@Gio.SocketAddress associated with the local end * of a connection. * * Returns: (nullable) (transfer none): the #GSocketAddress - * associated with the local end of a connection, it may be - * %NULL if you used soup_server_accept_iostream(). + * associated with the local end of a connection, it may be + * %NULL if you used method@Server.accept_iostream. */ GSocketAddress * soup_server_message_get_local_address (SoupServerMessage *msg) { g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); - if (msg->local_addr) - return msg->local_addr; - - msg->local_addr = msg->gsock ? - g_socket_get_local_address (msg->gsock, NULL) : - G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_local_address (msg->sock))); - - return msg->local_addr; + return soup_server_connection_get_local_address (msg->conn); } /** @@ -905,29 +1055,23 @@ * connection. * * Returns: (nullable): the IP address associated with the remote - * end of a connection, it may be %NULL if you used - * soup_server_accept_iostream(). + * end of a connection, it may be %NULL if you used + * method@Server.accept_iostream. */ const char * soup_server_message_get_remote_host (SoupServerMessage *msg) { g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); - if (msg->remote_ip) - return msg->remote_ip; - - if (msg->gsock) { - GSocketAddress *addr = soup_server_message_get_remote_address (msg); + if (!msg->remote_ip) { + GSocketAddress *addr = soup_server_connection_get_remote_address (msg->conn); GInetAddress *iaddr; if (!addr || !G_IS_INET_SOCKET_ADDRESS (addr)) return NULL; + iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)); msg->remote_ip = g_inet_address_to_string (iaddr); - } else { - GInetSocketAddress *addr = G_INET_SOCKET_ADDRESS (soup_socket_get_remote_address (msg->sock)); - GInetAddress *inet_addr = g_inet_socket_address_get_address (addr); - msg->remote_ip = g_inet_address_to_string (inet_addr); } return msg->remote_ip; @@ -937,12 +1081,13 @@ * soup_server_message_steal_connection: * @msg: a #SoupServerMessage * - * "Steals" the HTTP connection associated with @msg from its - * #SoupServer. This happens immediately, regardless of the current - * state of the connection; if the response to @msg has not yet finished - * being sent, then it will be discarded; you can steal the connection from a - * #SoupServerMessage::wrote-informational or #SoupServerMessage::wrote-body signal - * handler if you need to wait for part or all of the response to be sent. + * "Steals" the HTTP connection associated with @msg from its #SoupServer. This + * happens immediately, regardless of the current state of the connection; if + * the response to @msg has not yet finished being sent, then it will be + * discarded; you can steal the connection from a + * signal@ServerMessage::wrote-informational or + * signal@ServerMessage::wrote-body signal handler if you need to wait for + * part or all of the response to be sent. * * Note that when calling this function from C, @msg will most * likely be freed as a side effect. @@ -958,17 +1103,50 @@ GIOStream *stream; g_object_ref (msg); - stream = soup_server_message_io_steal (msg); - if (stream) { - g_object_set_data_full (G_OBJECT (stream), "GSocket", - soup_socket_steal_gsocket (msg->sock), - g_object_unref); - } - - g_signal_handlers_disconnect_by_data (msg, msg->sock); - - socket_disconnected (msg); + stream = soup_server_connection_steal (msg->conn); + g_signal_handlers_disconnect_by_data (msg, msg->conn); g_object_unref (msg); return stream; } + +/** + * soup_server_message_get_tls_peer_certificate: + * @msg: a #SoupMessage + * + * Gets the peer's #GTlsCertificate associated with @msg's connection. + * Note that this is not set yet during the emission of + * SoupServerMessage::accept-certificate signal. + * + * Returns: (transfer none) (nullable): @msg's TLS peer certificate, + * or %NULL if @msg's connection is not SSL. + * + * Since: 3.2 + */ +GTlsCertificate * +soup_server_message_get_tls_peer_certificate (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL); + + return msg->tls_peer_certificate; +} + +/** + * soup_server_message_get_tls_peer_certificate_errors: + * @msg: a #SoupMessage + * + * Gets the errors associated with validating @msg's TLS peer certificate. + * Note that this is not set yet during the emission of + * SoupServerMessage::accept-certificate signal. + * + * Returns: a #GTlsCertificateFlags with @msg's TLS peer certificate errors. + * + * Since: 3.2 + */ +GTlsCertificateFlags +soup_server_message_get_tls_peer_certificate_errors (SoupServerMessage *msg) +{ + g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), 0); + + return msg->tls_peer_certificate_errors; +}
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-server-message.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-server-message.h
Changed
@@ -80,6 +80,18 @@ SOUP_AVAILABLE_IN_ALL gboolean soup_server_message_is_options_ping (SoupServerMessage *msg); +SOUP_AVAILABLE_IN_3_2 +void soup_server_message_pause (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_3_2 +void soup_server_message_unpause (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_3_2 +GTlsCertificate *soup_server_message_get_tls_peer_certificate (SoupServerMessage *msg); + +SOUP_AVAILABLE_IN_3_2 +GTlsCertificateFlags soup_server_message_get_tls_peer_certificate_errors (SoupServerMessage *msg); + G_END_DECLS #endif /* __SOUP_SERVER_MESSAGE_H__ */
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-server-private.h
Added
@@ -0,0 +1,14 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2022, Igalia S.L. + */ + +#ifndef __SOUP_SERVER_PRIVATE_H__ +#define __SOUP_SERVER_PRIVATE_H__ 1 + +#include "soup-server.h" + +void soup_server_set_http2_enabled (SoupServer *server, + gboolean enabled); + +#endif /* __SOUP_SERVER_PRIVATE_H__ */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-server.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-server.c
Changed
@@ -13,63 +13,57 @@ #include <glib/gi18n-lib.h> -#include "soup-server.h" +#include "soup-server-private.h" #include "soup-server-message-private.h" #include "soup-message-headers-private.h" #include "soup.h" #include "soup-misc.h" #include "soup-path-map.h" -#include "soup-socket.h" +#include "soup-listener.h" #include "soup-uri-utils-private.h" #include "websocket/soup-websocket.h" #include "websocket/soup-websocket-connection.h" #include "websocket/soup-websocket-extension-deflate.h" /** - * SECTION:soup-server - * @short_description: HTTP server - * @see_also: #SoupAuthDomain + * SoupServer: + * + * A HTTP server. * * #SoupServer implements a simple HTTP server. * - * (The following documentation describes the current #SoupServer API, - * available in <application>libsoup</application> 2.48 and later. See - * the section "<link linkend="soup-server-old-api">The Old SoupServer - * Listening API</link>" in the server how-to documentation for - * details on the older #SoupServer API.) - * - * To begin, create a server using soup_server_new(). Add at least one - * handler by calling soup_server_add_handler() or - * soup_server_add_early_handler(); the handler will be called to + * To begin, create a server using ctor@Server.new. Add at least one + * handler by calling method@Server.add_handler or + * method@Server.add_early_handler; the handler will be called to * process any requests underneath the path you pass. (If you want all * requests to go to the same handler, just pass "/" (or %NULL) for * the path.) * * When a new connection is accepted (or a new request is started on * an existing persistent connection), the #SoupServer will emit - * #SoupServer::request-started and then begin processing the request + * signal@Server::request-started and then begin processing the request * as described below, but note that once the message is assigned a * status-code, then callbacks after that point will be * skipped. Note also that it is not defined when the callbacks happen - * relative to various #SoupServerMessage signals. + * relative to various class@ServerMessage signals. * * Once the headers have been read, #SoupServer will check if there is - * a #SoupAuthDomain (qv) covering the Request-URI; if so, and if the + * a class@AuthDomain `(qv)` covering the Request-URI; if so, and if the * message does not contain suitable authorization, then the - * #SoupAuthDomain will set a status of %SOUP_STATUS_UNAUTHORIZED on + * class@AuthDomain will set a status of %SOUP_STATUS_UNAUTHORIZED on * the message. * * After checking for authorization, #SoupServer will look for "early" - * handlers (added with soup_server_add_early_handler()) matching the + * handlers (added with method@Server.add_early_handler) matching the * Request-URI. If one is found, it will be run; in particular, this * can be used to connect to signals to do a streaming read of the * request body. * - * (At this point, if the request headers contain "<literal>Expect: - * 100-continue</literal>", and a status code has been set, then + * (At this point, if the request headers contain `Expect: + * 100-continue`, and a status code has been set, then * #SoupServer will skip the remaining steps and return the response. - * If the request headers contain "<literal>Expect: - * 100-continue</literal>" and no status code has been set, + * If the request headers contain `Expect: + * 100-continue` and no status code has been set, * #SoupServer will return a %SOUP_STATUS_CONTINUE status before * continuing.) * @@ -80,7 +74,7 @@ * * Otherwise (assuming no previous step assigned a status to the * message) any "normal" handlers (added with - * soup_server_add_handler()) for the message's Request-URI will be + * method@Server.add_handler) for the message's Request-URI will be * run. * * Then, if the path has a WebSocket handler registered (and has @@ -90,12 +84,12 @@ * %SOUP_STATUS_BAD_REQUEST accordingly. * * If the message still has no status code at this point (and has not - * been paused with soup_server_pause_message()), then it will be + * been paused with method@ServerMessage.pause), then it will be * given a status of %SOUP_STATUS_INTERNAL_SERVER_ERROR (because at * least one handler ran, but returned without assigning a status). * - * Finally, the server will emit #SoupServer::request-finished (or - * #SoupServer::request-aborted if an I/O error occurred before + * Finally, the server will emit signal@Server::request-finished (or + * signal@Server::request-aborted if an I/O error occurred before * handling was completed). * * If you want to handle the special "*" URI (eg, "OPTIONS *"), you @@ -103,27 +97,21 @@ * will not be used for that case. * * If you want to process https connections in addition to (or instead - * of) http connections, you can set the #SoupServer:tls-certificate + * of) http connections, you can set the property@Server:tls-certificate * property. * * Once the server is set up, make one or more calls to - * soup_server_listen(), soup_server_listen_local(), or - * soup_server_listen_all() to tell it where to listen for + * method@Server.listen, method@Server.listen_local, or + * method@Server.listen_all to tell it where to listen for * connections. (All ports on a #SoupServer use the same handlers; if * you need to handle some ports differently, such as returning * different data for http and https, you'll need to create multiple - * #SoupServers, or else check the passed-in URI in the handler + * `SoupServer`s, or else check the passed-in URI in the handler * function.). * * #SoupServer will begin processing connections as soon as you return * to (or start) the main loop for the current thread-default - * #GMainContext. - */ - -/** - * SoupServer: - * - * Class implementing an HTTP server. + * struct@GLib.MainContext. */ enum { @@ -176,6 +164,7 @@ GPtrArray *websocket_extension_types; gboolean disposed; + gboolean http2_enabled; } SoupServerPrivate; @@ -197,8 +186,10 @@ G_DEFINE_TYPE_WITH_PRIVATE (SoupServer, soup_server, G_TYPE_OBJECT) -static void start_request (SoupServer *server, - SoupServerMessage *msg); +static void request_finished (SoupServerMessage *msg, + SoupMessageIOCompletion completion, + SoupServer *server); + static void free_handler (SoupServerHandler *handler) { @@ -220,6 +211,7 @@ { SoupServerPrivate *priv = soup_server_get_instance_private (server); + priv->http2_enabled = !!g_getenv ("SOUP_SERVER_HTTP2"); priv->handlers = soup_path_map_new ((GDestroyNotify)free_handler); priv->websocket_extension_types = g_ptr_array_new_with_free_func ((GDestroyNotify)g_type_class_unref); @@ -352,16 +344,17 @@ * @message: the new message * * Emitted when the server has started reading a new request. + * * @message will be completely blank; not even the * Request-Line will have been read yet. About the only thing * you can usefully do with it is connect to its signals. * * If the request is read successfully, this will eventually - * be followed by a #SoupServer::request_read signal. If a + * be followed by a signal@Server::request_read signal. If a * response is then sent, the request processing will end with - * a #SoupServer::request_finished signal. If a network error + * a signal@Server::request-finished signal. If a network error * occurs, the processing will instead end with - * #SoupServer::request_aborted. + * signal@Server::request-aborted. **/ signalsREQUEST_STARTED = g_signal_new ("request-started", @@ -379,6 +372,7 @@ * @message: the message * * Emitted when the server has successfully read a request. + * * @message will have all of its request-side information * filled in, and if the message was authenticated, @client * will have information about that. This signal is emitted @@ -419,17 +413,17 @@ * @server: the server * @message: the message * - * Emitted when processing has failed for a message; this - * could mean either that it could not be read (if - * #SoupServer::request_read has not been emitted for it yet), - * or that the response could not be written back (if - * #SoupServer::request_read has been emitted but - * #SoupServer::request_finished has not been). + * Emitted when processing has failed for a message. + * + * This could mean either that it could not be read (if + * signal@Server::request-read has not been emitted for it yet), or that + * the response could not be written back (if signal@Server::request-read + * has been emitted but signal@Server::request-finished has not been). * * @message is in an undefined state when this signal is * emitted; the signal exists primarily to allow the server to * free any state that it may have allocated in - * #SoupServer::request_started. + * signal@Server::request-started. **/ signalsREQUEST_ABORTED = g_signal_new ("request-aborted", @@ -443,10 +437,12 @@ /* properties */ /** - * SoupServer:tls-certificate: + * SoupServer:tls-certificate: (attributes org.gtk.Property.get=soup_server_get_tls_certificate org.gtk.Property.set=soup_server_set_tls_certificate) + * + * A class@Gio.TlsCertificate that has a + * property@Gio.TlsCertificate:private-key set. * - * A #GTlsCertificate that has a #GTlsCertificate:private-key - * set. If this is set, then the server will be able to speak + * If this is set, then the server will be able to speak * https in addition to (or instead of) plain http. */ propertiesPROP_TLS_CERTIFICATE = @@ -459,9 +455,10 @@ G_PARAM_STATIC_STRINGS); /** - * SoupServer:tls-database: + * SoupServer:tls-database: (attributes org.gtk.Property.get=soup_server_get_tls_database org.gtk.Property.set=soup_server_set_tls_database) * - * A #GTlsDatabase to use for validating SSL/TLS client certificates. + * A class@Gio.TlsDatabase to use for validating SSL/TLS client + * certificates. */ propertiesPROP_TLS_DATABASE = g_param_spec_object ("tls-database", @@ -473,9 +470,9 @@ G_PARAM_STATIC_STRINGS); /** - * SoupServer:tls-auth-mode: + * SoupServer:tls-auth-mode: (attributes org.gtk.Property.get=soup_server_get_tls_auth_mode org.gtk.Property.set=soup_server_set_tls_auth_mode) * - * A #GTlsAuthenticationMode for SSL/TLS client authentication + * A enum@Gio.TlsAuthenticationMode for SSL/TLS client authentication. */ propertiesPROP_TLS_AUTH_MODE = g_param_spec_enum ("tls-auth-mode", @@ -487,6 +484,12 @@ G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + /** + * SoupServer:raw-paths: + * + * If %TRUE, percent-encoding in the Request-URI path will not be + * automatically decoded. + */ propertiesPROP_RAW_PATHS = g_param_spec_boolean ("raw-paths", "Raw paths", @@ -499,8 +502,10 @@ /** * SoupServer:server-header: * + * Server header. + * * If non-%NULL, the value to use for the "Server" header on - * #SoupServerMessage<!-- -->s processed by this server. + * class@ServerMessages processed by this server. * * The Server header is the server equivalent of the * User-Agent header, and provides information about the @@ -518,11 +523,10 @@ * end up advertising their vulnerability to specific security * holes. * - * As with #SoupSession:user_agent, if you set a - * #SoupServer:server_header property that has trailing whitespace, - * #SoupServer will append its own product token (eg, - * "<literal>libsoup/2.3.2</literal>") to the end of the - * header for you. + * As with property@Session:user_agent, if you set a + * property@Server:server-header property that has trailing + * whitespace, #SoupServer will append its own product token (eg, + * `libsoup/2.3.2`) to the end of the header for you. **/ propertiesPROP_SERVER_HEADER = g_param_spec_string ("server-header", @@ -541,12 +545,14 @@ * @optname1: name of first property to set * @...: value of @optname1, followed by additional property/value pairs * - * Creates a new #SoupServer. This is exactly equivalent to calling - * g_object_new() and specifying %SOUP_TYPE_SERVER as the type. + * Creates a new #SoupServer. + * + * This is exactly equivalent to calling ctor@GObject.Object.new and + * specifying %SOUP_TYPE_SERVER as the type. * * Returns: (nullable): a new #SoupServer. If you are using - * certain legacy properties, this may also return %NULL if an error - * occurs. + * certain legacy properties, this may also return %NULL if an error + * occurs. **/ SoupServer * soup_server_new (const char *optname1, ...) @@ -563,7 +569,7 @@ } /** - * soup_server_set_tls_certificate: + * soup_server_set_tls_certificate: (attributes org.gtk.Method.set_property=tls-certificate) * @server: a #SoupServer * @certificate: a #GTlsCertificate * @@ -587,10 +593,10 @@ } /** - * soup_server_get_tls_certificate: + * soup_server_get_tls_certificate: (attributes org.gtk.Method.get_property=tls-certificate) * @server: a #SoupServer * - * Gets the @server SSL/TLS certificate + * Gets the @server SSL/TLS certificate. * * Returns: (transfer none) (nullable): a #GTlsCertificate or %NULL */ @@ -606,11 +612,11 @@ } /** - * soup_server_set_tls_database: + * soup_server_set_tls_database: (attributes org.gtk.Method.set_property=tls-database) * @server: a #SoupServer * @tls_database: a #GTlsDatabase * - * Sets @server's #GTlsDatabase to use for validating SSL/TLS client certificates + * Sets @server's #GTlsDatabase to use for validating SSL/TLS client certificates. */ void soup_server_set_tls_database (SoupServer *server, @@ -630,12 +636,12 @@ } /** - * soup_server_get_tls_database: + * soup_server_get_tls_database: (attributes org.gtk.Method.get_property=tls-database) * @server: a #SoupServer * - * Gets the @server SSL/TLS database + * Gets the @server SSL/TLS database. * - * Returns: (transfer none) (nullable): a #GTlsDatabase or %NULL + * Returns: (transfer none) (nullable): a #GTlsDatabase */ GTlsDatabase * soup_server_get_tls_database (SoupServer *server) @@ -649,11 +655,11 @@ } /** - * soup_server_set_tls_auth_mode: + * soup_server_set_tls_auth_mode: (attributes org.gtk.Method.set_property=tls-auth-mode) * @server: a #SoupServer * @mode: a #GTlsAuthenticationMode * - * Sets @server's #GTlsAuthenticationMode to use for SSL/TLS client authentication + * Sets @server's #GTlsAuthenticationMode to use for SSL/TLS client authentication. */ void soup_server_set_tls_auth_mode (SoupServer *server, @@ -672,10 +678,10 @@ } /** - * soup_server_get_tls_auth_mode: + * soup_server_get_tls_auth_mode: (attributes org.gtk.Method.get_property=tls-auth-mode) * @server: a #SoupServer * - * Gets the @server SSL/TLS client authentication mode + * Gets the @server SSL/TLS client authentication mode. * * Returns: a #GTlsAuthenticationMode */ @@ -697,17 +703,16 @@ * Checks whether @server is capable of https. * * In order for a server to run https, you must call - * soup_server_set_ssl_cert_file(), or set the - * #SoupServer:tls-certificate property, to provide it with a + * method@Server.set_tls_certificate, or set the + * property@Server:tls-certificate property, to provide it with a * certificate to use. * - * If you are using the deprecated single-listener APIs, then a return - * value of %TRUE indicates that the #SoupServer serves https - * exclusively. If you are using soup_server_listen(), etc, then a - * %TRUE return value merely indicates that the server is - * <emphasis>able</emphasis> to do https, regardless of whether it - * actually currently is or not. Use soup_server_get_uris() to see if - * it currently has any https listeners. + * If you are using the deprecated single-listener APIs, then a return value of + * %TRUE indicates that the #SoupServer serves https exclusively. If you are + * using method@Server.listen, etc, then a %TRUE return value merely indicates + * that the server is *able* to do https, regardless of whether it actually + * currently is or not. Use method@Server.get_uris to see if it currently has + * any https listeners. * * Returns: %TRUE if @server is configured to serve https. **/ @@ -732,7 +737,7 @@ * modifiying any of these sockets may cause @server to malfunction. * * Returns: (transfer container) (element-type Gio.Socket): a - * list of listening sockets. + * list of listening sockets. **/ GSList * soup_server_get_listeners (SoupServer *server) @@ -745,7 +750,7 @@ listeners = NULL; for (iter = priv->listeners; iter; iter = iter->next) - listeners = g_slist_prepend (listeners, soup_socket_get_gsocket (iter->data)); + listeners = g_slist_prepend (listeners, soup_listener_get_socket (iter->data)); /* priv->listeners has the sockets in reverse order from how * they were added, so listeners now has them back in the @@ -830,7 +835,7 @@ gboolean rejected = FALSE; char *auth_user; SoupMessageHeaders *headers; - SoupSocket *sock; + SoupServerConnection *conn; /* Add required response headers */ headers = soup_server_message_get_response_headers (msg); @@ -844,10 +849,10 @@ if (soup_server_message_get_status (msg) != 0) return; - sock = soup_server_message_get_soup_socket (msg); + conn = soup_server_message_get_connection (msg); uri = soup_server_message_get_uri (msg); - if ((soup_socket_is_ssl (sock) && !soup_uri_is_https (uri)) || - (!soup_socket_is_ssl (sock) && !soup_uri_is_http (uri))) { + if ((soup_server_connection_is_ssl (conn) && !soup_uri_is_https (uri)) || + (!soup_server_connection_is_ssl (conn) && !soup_uri_is_http (uri))) { soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL); return; } @@ -984,30 +989,73 @@ } static void -client_disconnected (SoupServer *server, - SoupServerMessage *msg) +message_connected (SoupServer *server, + SoupServerMessage *msg) +{ + soup_server_message_read_request (msg, + (SoupMessageIOCompletionFn)request_finished, + server); +} + +static void +client_disconnected (SoupServer *server, + SoupServerConnection *conn) { SoupServerPrivate *priv = soup_server_get_instance_private (server); - priv->clients = g_slist_remove (priv->clients, msg); + priv->clients = g_slist_remove (priv->clients, conn); + g_object_unref (conn); +} - if (soup_server_message_get_status (msg) != 0) - soup_server_message_io_finished (msg); +static void +request_started_cb (SoupServer *server, + SoupServerMessage *msg, + SoupServerConnection *conn) +{ + SoupServerPrivate *priv = soup_server_get_instance_private (server); + + g_signal_connect_object (msg, "got-headers", + G_CALLBACK (got_headers), + server, G_CONNECT_SWAPPED); + g_signal_connect_object (msg, "got-body", + G_CALLBACK (got_body), + server, G_CONNECT_SWAPPED); + + if (priv->server_header) { + SoupMessageHeaders *headers; + + headers = soup_server_message_get_response_headers (msg); + soup_message_headers_append_common (headers, SOUP_HEADER_SERVER, + priv->server_header); + } + + g_signal_emit (server, signalsREQUEST_STARTED, 0, msg); + + if (soup_server_message_get_io_data (msg)) { + message_connected (server, msg); + return; + } + + g_signal_connect_object (msg, "connected", + G_CALLBACK (message_connected), + server, G_CONNECT_SWAPPED); } static void -soup_server_accept_socket (SoupServer *server, - SoupSocket *sock) +soup_server_accept_connection (SoupServer *server, + SoupServerConnection *conn) { SoupServerPrivate *priv = soup_server_get_instance_private (server); - SoupServerMessage *msg; - - msg = soup_server_message_new (sock); - g_signal_connect_object (msg, "disconnected", - G_CALLBACK (client_disconnected), - server, G_CONNECT_SWAPPED); - priv->clients = g_slist_prepend (priv->clients, msg); - start_request (server, msg); + + priv->clients = g_slist_prepend (priv->clients, g_object_ref (conn)); + g_signal_connect_object (conn, "disconnected", + G_CALLBACK (client_disconnected), + server, G_CONNECT_SWAPPED); + g_signal_connect_object (conn, "request-started", + G_CALLBACK (request_started_cb), + server, G_CONNECT_SWAPPED); + + soup_server_connection_accepted (conn); } static void @@ -1016,13 +1064,11 @@ SoupServer *server) { SoupServerPrivate *priv = soup_server_get_instance_private (server); - SoupSocket *sock = soup_server_message_get_soup_socket (msg); + SoupServerConnection *conn = soup_server_message_get_connection (msg); gboolean failed; - if (completion == SOUP_MESSAGE_IO_STOLEN) { - g_object_unref (msg); + if (completion == SOUP_MESSAGE_IO_STOLEN) return; - } /* Complete the message, assuming it actually really started. */ if (soup_server_message_get_method (msg)) { @@ -1036,64 +1082,30 @@ } if (completion == SOUP_MESSAGE_IO_COMPLETE && - soup_socket_is_connected (sock) && + soup_server_connection_is_connected (conn) && soup_server_message_is_keepalive (msg) && - priv->listeners) { - g_object_ref (sock); - priv->clients = g_slist_remove (priv->clients, msg); - g_object_unref (msg); - - soup_server_accept_socket (server, sock); - g_object_unref (sock); + priv->listeners) return; - } - soup_socket_disconnect (sock); - g_object_unref (msg); -} - -static void -start_request (SoupServer *server, - SoupServerMessage *msg) -{ - SoupServerPrivate *priv = soup_server_get_instance_private (server); - - if (priv->server_header) { - SoupMessageHeaders *headers; - - headers = soup_server_message_get_response_headers (msg); - soup_message_headers_append_common (headers, SOUP_HEADER_SERVER, - priv->server_header); - } - - g_signal_connect_object (msg, "got-headers", - G_CALLBACK (got_headers), - server, G_CONNECT_SWAPPED); - g_signal_connect_object (msg, "got-body", - G_CALLBACK (got_body), - server, G_CONNECT_SWAPPED); - - g_signal_emit (server, signalsREQUEST_STARTED, 0, msg); - - soup_server_message_read_request (msg, - (SoupMessageIOCompletionFn)request_finished, - server); + if (soup_server_message_get_http_version (msg) < SOUP_HTTP_2_0) + soup_server_connection_disconnect (conn); } /** * soup_server_accept_iostream: * @server: a #SoupServer * @stream: a #GIOStream - * @local_addr: (nullable): the local #GSocketAddress associated with the @stream - * @remote_addr: (nullable): the remote #GSocketAddress associated with the @stream + * @local_addr: (nullable): the local #GSocketAddress associated with the + * @stream + * @remote_addr: (nullable): the remote #GSocketAddress associated with the + * @stream * @error: return location for a #GError * - * Add a new client stream to the @server. + * Adds a new client stream to the @server. * * Returns: %TRUE on success, %FALSE if the stream could not be - * accepted or any other error occurred (in which case @error will be - * set). - * + * accepted or any other error occurred (in which case @error will be + * set). **/ gboolean soup_server_accept_iostream (SoupServer *server, @@ -1102,29 +1114,24 @@ GSocketAddress *remote_addr, GError **error) { - SoupSocket *sock; - - sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, error, - "iostream", stream, - "local-address", local_addr, - "remote-connectable", remote_addr, - NULL); + SoupServerConnection *conn; - if (!sock) - return FALSE; - - soup_server_accept_socket (server, sock); - g_object_unref (sock); + conn = soup_server_connection_new_for_connection (stream, local_addr, remote_addr); + soup_server_accept_connection (server, conn); + g_object_unref (conn); return TRUE; } static void -new_connection (SoupSocket *listener, SoupSocket *sock, gpointer user_data) +new_connection (SoupListener *listener, + SoupServerConnection *conn, + SoupServer *server) { - SoupServer *server = user_data; + SoupServerPrivate *priv = soup_server_get_instance_private (server); - soup_server_accept_socket (server, sock); + soup_server_connection_set_advertise_http2 (conn, priv->http2_enabled); + soup_server_accept_connection (server, conn); } /** @@ -1133,11 +1140,11 @@ * * Closes and frees @server's listening sockets. * - * Note that if there are currently requests in progress on @server, - * that they will continue to be processed if @server's #GMainContext - * is still running. + * Note that if there are currently requests in progress on @server, that they + * will continue to be processed if @server's struct@GLib.MainContext is still + * running. * - * You can call soup_server_listen(), etc, after calling this function + * You can call method@Server.listen, etc, after calling this function * if you want to start listening again. **/ void @@ -1145,7 +1152,7 @@ { SoupServerPrivate *priv; GSList *listeners, *clients, *iter; - SoupSocket *listener; + SoupListener *listener; g_return_if_fail (SOUP_IS_SERVER (server)); priv = soup_server_get_instance_private (server); @@ -1156,15 +1163,15 @@ priv->listeners = NULL; for (iter = clients; iter; iter = iter->next) { - SoupServerMessage *msg = iter->data; + SoupServerConnection *conn = iter->data; - soup_socket_disconnect (soup_server_message_get_soup_socket (msg)); + soup_server_connection_disconnect (conn); } g_slist_free (clients); for (iter = listeners; iter; iter = iter->next) { listener = iter->data; - soup_socket_disconnect (listener); + soup_listener_disconnect (listener); g_object_unref (listener); } g_slist_free (listeners); @@ -1177,20 +1184,20 @@ * @SOUP_SERVER_LISTEN_IPV4_ONLY: Only listen on IPv4 interfaces. * @SOUP_SERVER_LISTEN_IPV6_ONLY: Only listen on IPv6 interfaces. * - * Options to pass to soup_server_listen(), etc. + * Options to pass to method@Server.listen, etc. * * %SOUP_SERVER_LISTEN_IPV4_ONLY and %SOUP_SERVER_LISTEN_IPV6_ONLY - * only make sense with soup_server_listen_all() and - * soup_server_listen_local(), not plain soup_server_listen() (which + * only make sense with method@Server.listen_all and + * method@Server.listen_local, not plain method@Server.listen (which * simply listens on whatever kind of socket you give it). And you * cannot specify both of them in a single call. - * */ static gboolean -soup_server_listen_internal (SoupServer *server, SoupSocket *listener, +soup_server_listen_internal (SoupServer *server, + SoupListener *listener, SoupServerListenOptions options, - GError **error) + GError **error) { SoupServerPrivate *priv = soup_server_get_instance_private (server); @@ -1214,23 +1221,9 @@ G_BINDING_SYNC_CREATE); } - if (soup_socket_get_gsocket (listener) == NULL) { - if (!soup_socket_listen (listener, error)) { - GInetSocketAddress *addr = soup_socket_get_local_address (listener); - GInetAddress *inet_addr = g_inet_socket_address_get_address (addr); - char *local_ip = g_inet_address_to_string (inet_addr); - - g_prefix_error (error, - _("Could not listen on address %s, port %d: "), - local_ip, - g_inet_socket_address_get_port (addr)); - g_free (local_ip); - return FALSE; - } - } - - g_signal_connect (listener, "new_connection", - G_CALLBACK (new_connection), server); + g_signal_connect (listener, "new-connection", + G_CALLBACK (new_connection), + server); /* Note: soup_server_listen_ipv4_ipv6() below relies on the * fact that this does g_slist_prepend(). @@ -1246,8 +1239,7 @@ * @options: listening options for this server * @error: return location for a #GError * - * This attempts to set up @server to listen for connections on - * @address. + * Attempts to set up @server to listen for connections on @address. * * If @options includes %SOUP_SERVER_LISTEN_HTTPS, and @server has * been configured for TLS, then @server will listen for https @@ -1257,18 +1249,16 @@ * any number of times on a server, if you want to listen on multiple * ports, or set up both http and https service. * - * After calling this method, @server will begin accepting and - * processing connections as soon as the appropriate #GMainContext is - * run. + * After calling this method, @server will begin accepting and processing + * connections as soon as the appropriate struct@GLib.MainContext is run. * - * Note that #SoupServer never makes use of dual IPv4/IPv6 sockets; if + * Note that this API does not make use of dual IPv4/IPv6 sockets; if * @address is an IPv6 address, it will only accept IPv6 connections. * You must configure IPv4 listening separately. * * Returns: %TRUE on success, %FALSE if @address could not be - * bound or any other error occurred (in which case @error will be - * set). - * + * bound or any other error occurred (in which case @error will be + * set). **/ gboolean soup_server_listen (SoupServer *server, GSocketAddress *address, @@ -1276,8 +1266,8 @@ GError **error) { SoupServerPrivate *priv; - SoupSocket *listener; - gboolean success, ipv6_only; + SoupListener *listener; + gboolean success; g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE); g_return_val_if_fail (!(options & SOUP_SERVER_LISTEN_IPV4_ONLY) && @@ -1286,10 +1276,9 @@ priv = soup_server_get_instance_private (server); g_return_val_if_fail (priv->disposed == FALSE, FALSE); - ipv6_only = g_socket_address_get_family (address) == G_SOCKET_FAMILY_IPV6; - listener = soup_socket_new ("local-address", address, - "ipv6-only", ipv6_only, - NULL); + listener = soup_listener_new_for_address (address, error); + if (!listener) + return FALSE; success = soup_server_listen_internal (server, listener, options, error); g_object_unref (listener); @@ -1308,7 +1297,7 @@ SoupServerPrivate *priv = soup_server_get_instance_private (server); GSocketAddress *addr4, *addr6; GError *my_error = NULL; - SoupSocket *v4sock; + SoupListener *v4sock; guint v4port; g_return_val_if_fail (iaddr4 != NULL || iaddr6 != NULL, FALSE); @@ -1325,7 +1314,7 @@ g_object_unref (addr4); v4sock = priv->listeners->data; - v4port = g_inet_socket_address_get_port (soup_socket_get_local_address (v4sock)); + v4port = g_inet_socket_address_get_port (soup_listener_get_address (v4sock)); } else { v4sock = NULL; v4port = port; @@ -1357,7 +1346,7 @@ if (v4sock) { priv->listeners = g_slist_remove (priv->listeners, v4sock); - soup_socket_disconnect (v4sock); + soup_listener_disconnect (v4sock); g_object_unref (v4sock); } @@ -1378,20 +1367,20 @@ * @options: listening options for this server * @error: return location for a #GError * - * This attempts to set up @server to listen for connections on all - * interfaces on the system. (That is, it listens on the addresses - * <literal>0.0.0.0</literal> and/or <literal>::</literal>, depending - * on whether @options includes %SOUP_SERVER_LISTEN_IPV4_ONLY, - * %SOUP_SERVER_LISTEN_IPV6_ONLY, or neither.) If @port is specified, - * @server will listen on that port. If it is 0, @server will find an - * unused port to listen on. (In that case, you can use - * soup_server_get_uris() to find out what port it ended up choosing.) + * Attempts to set up @server to listen for connections on all interfaces + * on the system. * - * See soup_server_listen() for more details. + * That is, it listens on the addresses `0.0.0.0` and/or `::`, depending on + * whether @options includes %SOUP_SERVER_LISTEN_IPV4_ONLY, + * %SOUP_SERVER_LISTEN_IPV6_ONLY, or neither.) If @port is specified, @server + * will listen on that port. If it is 0, @server will find an unused port to + * listen on. (In that case, you can use method@Server.get_uris to find out + * what port it ended up choosing. * - * Returns: %TRUE on success, %FALSE if @port could not be bound - * or any other error occurred (in which case @error will be set). + * See method@Server.listen for more details. * + * Returns: %TRUE on success, %FALSE if @port could not be bound + * or any other error occurred (in which case @error will be set). **/ gboolean soup_server_listen_all (SoupServer *server, guint port, @@ -1431,20 +1420,18 @@ * @options: listening options for this server * @error: return location for a #GError * - * This attempts to set up @server to listen for connections on - * "localhost" (that is, <literal>127.0.0.1</literal> and/or - * <literal>::1</literal>, depending on whether @options includes - * %SOUP_SERVER_LISTEN_IPV4_ONLY, %SOUP_SERVER_LISTEN_IPV6_ONLY, or - * neither). If @port is specified, @server will listen on that port. - * If it is 0, @server will find an unused port to listen on. (In that - * case, you can use soup_server_get_uris() to find out what port it - * ended up choosing.) + * Attempts to set up @server to listen for connections on "localhost". * - * See soup_server_listen() for more details. + * That is, `127.0.0.1` and/or `::1`, depending on whether @options includes + * %SOUP_SERVER_LISTEN_IPV4_ONLY, %SOUP_SERVER_LISTEN_IPV6_ONLY, or neither). If + * @port is specified, @server will listen on that port. If it is 0, @server + * will find an unused port to listen on. (In that case, you can use + * method@Server.get_uris to find out what port it ended up choosing. * - * Returns: %TRUE on success, %FALSE if @port could not be bound - * or any other error occurred (in which case @error will be set). + * See method@Server.listen for more details. * + * Returns: %TRUE on success, %FALSE if @port could not be bound + * or any other error occurred (in which case @error will be set). **/ gboolean soup_server_listen_local (SoupServer *server, guint port, @@ -1484,14 +1471,12 @@ * @options: listening options for this server * @error: return location for a #GError * - * This attempts to set up @server to listen for connections on - * @socket. + * Attempts to set up @server to listen for connections on @socket. * - * See soup_server_listen() for more details. + * See method@Server.listen for more details. * * Returns: %TRUE on success, %FALSE if an error occurred (in - * which case @error will be set). - * + * which case @error will be set). **/ gboolean soup_server_listen_socket (SoupServer *server, GSocket *socket, @@ -1499,7 +1484,7 @@ GError **error) { SoupServerPrivate *priv; - SoupSocket *listener; + SoupListener *listener; gboolean success; g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE); @@ -1510,10 +1495,7 @@ priv = soup_server_get_instance_private (server); g_return_val_if_fail (priv->disposed == FALSE, FALSE); - listener = g_initable_new (SOUP_TYPE_SOCKET, NULL, error, - "gsocket", socket, - "ipv6-only", TRUE, - NULL); + listener = soup_listener_new (socket, error); if (!listener) return FALSE; @@ -1528,24 +1510,24 @@ * @server: a #SoupServer * * Gets a list of URIs corresponding to the interfaces @server is - * listening on. These will contain IP addresses, not hostnames, and - * will also indicate whether the given listener is http or https. + * listening on. * - * Note that if you used soup_server_listen_all(), the returned URIs - * will use the addresses <literal>0.0.0.0</literal> and - * <literal>::</literal>, rather than actually returning separate URIs - * for each interface on the system. + * These will contain IP addresses, not hostnames, and will also indicate + * whether the given listener is http or https. * - * Returns: (transfer full) (element-type GUri): a list of - * #GUris, which you must free when you are done with it. + * Note that if you used method@Server.listen_all the returned URIs will use + * the addresses `0.0.0.0` and `::`, rather than actually returning separate + * URIs for each interface on the system. * + * Returns: (transfer full) (element-type GUri): a list of #GUris, which you + * must free when you are done with it. */ GSList * soup_server_get_uris (SoupServer *server) { SoupServerPrivate *priv; GSList *uris, *l; - SoupSocket *listener; + SoupListener *listener; GInetSocketAddress *addr; GInetAddress *inet_addr; char *ip; @@ -1557,7 +1539,7 @@ for (l = priv->listeners, uris = NULL; l; l = l->next) { listener = l->data; - addr = soup_socket_get_local_address (listener); + addr = soup_listener_get_address (listener); inet_addr = g_inet_socket_address_get_address (addr); ip = g_inet_address_to_string (inet_addr); port = g_inet_socket_address_get_port (addr); @@ -1566,7 +1548,7 @@ port = -1; uri = g_uri_build (SOUP_HTTP_URI_FLAGS, - soup_socket_is_ssl (listener) ? "https" : "http", + soup_listener_is_ssl (listener) ? "https" : "http", NULL, ip, port, "/", NULL, NULL); uris = g_slist_prepend (uris, uri); @@ -1584,29 +1566,28 @@ * @path: the path component of @msg's Request-URI * @query: (element-type utf8 utf8) (nullable): the parsed query * component of @msg's Request-URI - * @user_data: the data passed to soup_server_add_handler() or - * soup_server_add_early_handler(). + * @user_data: the data passed to method@Server.add_handler or + * method@Server.add_early_handler. * - * A callback used to handle requests to a #SoupServer. + * A callback used to handle requests to a class@Server. * * @path and @query contain the likewise-named components of the * Request-URI, subject to certain assumptions. By default, - * #SoupServer decodes all percent-encoding in the URI path, such that - * "/foo%<!-- -->2Fbar" is treated the same as "/foo/bar". If your + * class@Server decodes all percent-encoding in the URI path, such that + * `"/foo%2Fbar"` is treated the same as `"/foo/bar"`. If your * server is serving resources in some non-POSIX-filesystem namespace, * you may want to distinguish those as two distinct paths. In that - * case, you can set the SoupServer:raw-paths property when creating - * the #SoupServer, and it will leave those characters undecoded. - * - * @query contains the query component of the Request-URI parsed - * according to the rules for HTML form handling. Although this is the - * only commonly-used query string format in HTTP, there is nothing - * that actually requires that HTTP URIs use that format; if your - * server needs to use some other format, you can just ignore @query, - * and call soup_message_get_uri() and parse the URI's query field - * yourself. - * - * See soup_server_add_handler() and soup_server_add_early_handler() + * case, you can set the property@Server:raw-paths property when creating + * the class@Server, and it will leave those characters undecoded. + * + * @query contains the query component of the Request-URI parsed according to + * the rules for HTML form handling. Although this is the only commonly-used + * query string format in HTTP, there is nothing that actually requires that + * HTTP URIs use that format; if your server needs to use some other format, you + * can just ignore @query, and call method@Message.get_uri and parse the URI's + * query field yourself. + * + * See method@Server.add_handler and method@Server.add_early_handler * for details of what handlers can/should do. **/ @@ -1633,45 +1614,44 @@ * soup_server_add_handler: * @server: a #SoupServer * @path: (nullable): the toplevel path for the handler - * @callback: (scope notified) (destroy destroy): callback to invoke for requests under @path + * @callback: (scope notified) (destroy destroy): callback to invoke for + * requests under @path * @user_data: data for @callback * @destroy: destroy notifier to free @user_data * - * Adds a handler to @server for requests prefixed by @path. If @path is - * %NULL or "/", then this will be the default handler for all - * requests that don't have a more specific handler. (Note though that - * if you want to handle requests to the special "*" URI, you must - * explicitly register a handler for "*"; the default handler will not - * be used for that case.) + * Adds a handler to @server for requests prefixed by @path. + * + * If @path is %NULL or "/", then this will be the default handler for all + * requests that don't have a more specific handler. (Note though that if you + * want to handle requests to the special "*" URI, you must explicitly register + * a handler for "*"; the default handler will not be used for that case.) * * For requests under @path (that have not already been assigned a - * status code by a #SoupAuthDomain, an early server handler, or a + * status code by a class@AuthDomain, an early server handler, or a * signal handler), @callback will be invoked after receiving the - * request body; the #SoupServerMessage<!-- -->'s method, request-headers, + * request body; the class@ServerMessage's method, request-headers, * and request-body properties will be set. * - * After determining what to do with the request, the callback must at - * a minimum call soup_server_message_set_status() on the message to set the response - * status code. Additionally, it may set response headers and/or fill - * in the response body. + * After determining what to do with the request, the callback must at a minimum + * call method@ServerMessage.set_status on the message to set the response + * status code. Additionally, it may set response headers and/or fill in the + * response body. * * If the callback cannot fully fill in the response before returning * (eg, if it needs to wait for information from a database, or - * another network server), it should call soup_server_pause_message() + * another network server), it should call method@ServerMessage.pause * to tell @server to not send the response right away. When the - * response is ready, call soup_server_unpause_message() to cause it + * response is ready, call method@ServerMessage.unpause to cause it * to be sent. * - * To send the response body a bit at a time using "chunked" encoding, - * first call soup_message_headers_set_encoding() to set - * %SOUP_ENCODING_CHUNKED on the response-headers. Then call - * soup_message_body_append() (or soup_message_body_append_bytes)) - * to append each chunk as it becomes ready, and - * soup_server_unpause_message() to make sure it's running. (The - * server will automatically pause the message if it is using chunked - * encoding but no more chunks are available.) When you are done, call - * soup_message_body_complete() to indicate that no more chunks are - * coming. + * To send the response body a bit at a time using "chunked" encoding, first + * call method@MessageHeaders.set_encoding to set %SOUP_ENCODING_CHUNKED on + * the response-headers. Then call method@MessageBody.append (or + * method@MessageBody.append_bytes)) to append each chunk as it becomes ready, + * and method@ServerMessage.unpause to make sure it's running. (The server + * will automatically pause the message if it is using chunked encoding but no + * more chunks are available.) When you are done, call + * method@MessageBody.complete to indicate that no more chunks are coming. **/ void soup_server_add_handler (SoupServer *server, @@ -1698,38 +1678,37 @@ * soup_server_add_early_handler: * @server: a #SoupServer * @path: (nullable): the toplevel path for the handler - * @callback: (scope notified) (destroy destroy): callback to invoke for requests under @path + * @callback: (scope notified) (destroy destroy): callback to invoke for + * requests under @path * @user_data: data for @callback * @destroy: destroy notifier to free @user_data * - * Adds an "early" handler to @server for requests prefixed by @path. Note - * that "normal" and "early" handlers are matched up together, so if - * you add a normal handler for "/foo" and an early handler for - * "/foo/bar", then a request to "/foo/bar" (or any path below it) - * will run only the early handler. (But if you add both handlers at - * the same path, then both will get run.) + * Adds an "early" handler to @server for requests prefixed by @path. + * + * Note that "normal" and "early" handlers are matched up together, so if you + * add a normal handler for "/foo" and an early handler for "/foo/bar", then a + * request to "/foo/bar" (or any path below it) will run only the early handler. + * (But if you add both handlers at the same path, then both will get run.) * * For requests under @path (that have not already been assigned a - * status code by a #SoupAuthDomain or a signal handler), @callback + * status code by a class@AuthDomain or a signal handler), @callback * will be invoked after receiving the request headers, but before * receiving the request body; the message's method and * request-headers properties will be set. * - * Early handlers are generally used for processing requests with - * request bodies in a streaming fashion. If you determine that the - * request will contain a message body, normally you would call - * soup_message_body_set_accumulate() on the message's - * request-body to turn off request-body accumulation, - * and connect to the message's #SoupServerMessage::got-chunk signal to - * process each chunk as it comes in. + * Early handlers are generally used for processing requests with request bodies + * in a streaming fashion. If you determine that the request will contain a + * message body, normally you would call method@MessageBody.set_accumulate on + * the message's request-body to turn off request-body accumulation, and connect + * to the message's signal@ServerMessage::got-chunk signal to process each + * chunk as it comes in. * * To complete the message processing after the full message body has - * been read, you can either also connect to #SoupServerMessage::got-body, + * been read, you can either also connect to signal@ServerMessage::got-body, * or else you can register a non-early handler for @path as well. As * long as you have not set the status-code by the time - * #SoupServerMessage::got-body is emitted, the non-early handler will be + * signal@ServerMessage::got-body is emitted, the non-early handler will be * run as well. - * **/ void soup_server_add_early_handler (SoupServer *server, @@ -1760,13 +1739,13 @@ * @msg: the #SoupServerMessage * @user_data: the data passed to @soup_server_add_handler * - * A callback used to handle WebSocket requests to a #SoupServer. The - * callback will be invoked after sending the handshake response back - * to the client (and is only invoked if the handshake was - * successful). + * A callback used to handle WebSocket requests to a #SoupServer. + * + * The callback will be invoked after sending the handshake response back to the + * client (and is only invoked if the handshake was successful). * * @path contains the path of the Request-URI, subject to the same - * rules as #SoupServerCallback (qv). + * rules as callback@ServerCallback `(qv)`. **/ /** @@ -1776,13 +1755,15 @@ * @origin: (nullable): the origin of the connection * @protocols: (nullable) (array zero-terminated=1): the protocols * supported by this handler - * @callback: (scope notified) (destroy destroy): callback to invoke for successful WebSocket requests under @path + * @callback: (scope notified) (destroy destroy): callback to invoke for + * successful WebSocket requests under @path * @user_data: data for @callback * @destroy: destroy notifier to free @user_data * - * Adds a WebSocket handler to @server for requests prefixed by @path. (If - * @path is %NULL or "/", then this will be the default handler for - * all requests that don't have a more specific handler.) + * Adds a WebSocket handler to @server for requests prefixed by @path. + * + * If @path is %NULL or "/", then this will be the default handler for all + * requests that don't have a more specific handler. * * When a path has a WebSocket handler registered, @server will check * incoming requests for WebSocket handshakes after all other handlers @@ -1852,15 +1833,15 @@ * @server: a #SoupServer * @auth_domain: a #SoupAuthDomain * - * Adds an authentication domain to @server. Each auth domain will - * have the chance to require authentication for each request that - * comes in; normally auth domains will require authentication for - * requests on certain paths that they have been set up to watch, or - * that meet other criteria set by the caller. If an auth domain - * determines that a request requires authentication (and the request - * doesn't contain authentication), @server will automatically reject - * the request with an appropriate status (401 Unauthorized or 407 - * Proxy Authentication Required). If the request used the + * Adds an authentication domain to @server. + * + * Each auth domain will have the chance to require authentication for each + * request that comes in; normally auth domains will require authentication for + * requests on certain paths that they have been set up to watch, or that meet + * other criteria set by the caller. If an auth domain determines that a request + * requires authentication (and the request doesn't contain authentication), + * @server will automatically reject the request with an appropriate status (401 + * Unauthorized or 407 Proxy Authentication Required). If the request used the * SoupServer:100-continue Expectation, @server will reject it before the * request body is sent. **/ @@ -1900,22 +1881,26 @@ * @server: a #SoupServer * @msg: a #SoupServerMessage associated with @server. * - * Pauses I/O on @msg. This can be used when you need to return from - * the server handler without having the full response ready yet. Use - * soup_server_unpause_message() to resume I/O. + * Pauses I/O on @msg. + * + * This can be used when you need to return from the server handler without + * having the full response ready yet. Use method@Server.unpause_message to + * resume I/O. * - * This must only be called on a #SoupServerMessage which was created by the + * This must only be called on a class@ServerMessage which was created by the * #SoupServer and are currently doing I/O, such as those passed into a - * #SoupServerCallback or emitted in a #SoupServer::request-read signal. + * callback@ServerCallback or emitted in a signal@Server::request-read + * signal. + * + * Deprecated: 3.2: Use soup_server_message_pause() instead. **/ void soup_server_pause_message (SoupServer *server, SoupServerMessage *msg) { g_return_if_fail (SOUP_IS_SERVER (server)); - g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg)); - soup_server_message_io_pause (msg); + soup_server_message_pause (msg); } /** @@ -1923,24 +1908,27 @@ * @server: a #SoupServer * @msg: a #SoupServerMessage associated with @server. * - * Resumes I/O on @msg. Use this to resume after calling - * soup_server_pause_message(), or after adding a new chunk to a - * chunked response. + * Resumes I/O on @msg. + * + * Use this to resume after calling method@Server.pause_message, or after + * adding a new chunk to a chunked response. * * I/O won't actually resume until you return to the main loop. * - * This must only be called on a #SoupServerMessage which was created by the + * This must only be called on a class@ServerMessage which was created by the * #SoupServer and are currently doing I/O, such as those passed into a - * #SoupServerCallback or emitted in a #SoupServer::request-read signal. + * callback@ServerCallback or emitted in a signal@Server::request-read + * signal. + * + * Deprecated: 3.2: Use soup_server_message_unpause() instead. **/ void soup_server_unpause_message (SoupServer *server, SoupServerMessage *msg) { g_return_if_fail (SOUP_IS_SERVER (server)); - g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg)); - soup_server_message_io_unpause (msg); + soup_server_message_unpause (msg); } /** @@ -1949,13 +1937,13 @@ * @extension_type: a #GType * * Add support for a WebSocket extension of the given @extension_type. + * * When a WebSocket client requests an extension of @extension_type, - * a new #SoupWebsocketExtension of type @extension_type will be created + * a new class@WebsocketExtension of type @extension_type will be created * to handle the request. * - * Note that #SoupWebsocketExtensionDeflate is supported by default, use - * soup_server_remove_websocket_extension() if you want to disable it. - * + * Note that class@WebsocketExtensionDeflate is supported by default, use + * method@Server.remove_websocket_extension if you want to disable it. */ void soup_server_add_websocket_extension (SoupServer *server, GType extension_type) @@ -1980,7 +1968,6 @@ * * Removes support for WebSocket extension of type @extension_type (or any subclass of * @extension_type) from @server. - * */ void soup_server_remove_websocket_extension (SoupServer *server, GType extension_type) @@ -2005,3 +1992,12 @@ } } } + +void +soup_server_set_http2_enabled (SoupServer *server, + gboolean enabled) +{ + SoupServerPrivate *priv = soup_server_get_instance_private (server); + + priv->http2_enabled = enabled; +}
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/server/soup-server.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/server/soup-server.h
Changed
@@ -150,10 +150,10 @@ SoupAuthDomain *auth_domain); /* I/O */ -SOUP_AVAILABLE_IN_ALL +SOUP_DEPRECATED_IN_3_2_FOR(soup_server_message_pause) void soup_server_pause_message (SoupServer *server, SoupServerMessage *msg); -SOUP_AVAILABLE_IN_ALL +SOUP_DEPRECATED_IN_3_2_FOR(soup_server_message_unpause) void soup_server_unpause_message (SoupServer *server, SoupServerMessage *msg);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-client-message-io.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-client-message-io.c
Changed
@@ -140,3 +140,10 @@ { return io->funcs->get_cancellable (io, msg); } + +void +soup_client_message_io_owner_changed (SoupClientMessageIO *io) +{ + if (io->funcs->owner_changed) + io->funcs->owner_changed (io); +}
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-client-message-io.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-client-message-io.h
Changed
@@ -55,6 +55,7 @@ gboolean (*is_reusable) (SoupClientMessageIO *io); GCancellable *(*get_cancellable) (SoupClientMessageIO *io, SoupMessage *msg); + void (*owner_changed) (SoupClientMessageIO *io); } SoupClientMessageIOFuncs; struct _SoupClientMessageIO { @@ -105,3 +106,4 @@ gboolean soup_client_message_io_is_reusable (SoupClientMessageIO *io); GCancellable *soup_client_message_io_get_cancellable (SoupClientMessageIO *io, SoupMessage *msg); +void soup_client_message_io_owner_changed (SoupClientMessageIO *io);
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-connection-manager.c
Added
@@ -0,0 +1,558 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright 2022 Igalia S.L. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "soup-connection-manager.h" +#include "soup-message-private.h" +#include "soup-misc.h" +#include "soup-session-private.h" +#include "soup-uri-utils-private.h" +#include "soup.h" + +struct _SoupConnectionManager { + SoupSession *session; + + GMutex mutex; + GCond cond; + GSocketConnectable *remote_connectable; + guint max_conns; + guint max_conns_per_host; + guint num_conns; + + GHashTable *http_hosts; + GHashTable *https_hosts; + GHashTable *conns; + + guint64 last_connection_id; +}; + +typedef struct { + GUri *uri; + GHashTable *owner_map; + GNetworkAddress *addr; + + GList *conns; + guint num_conns; + + GSource *keep_alive_src; + SoupConnectionManager *conn_manager; +} SoupHost; + +#define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */ + +static SoupHost * +soup_host_new (GUri *uri, + GHashTable *owner_map) +{ + SoupHost *host; + const char *scheme = g_uri_get_scheme (uri); + + host = g_new0 (SoupHost, 1); + host->owner_map = owner_map; + if (g_strcmp0 (scheme, "http") != 0 && g_strcmp0 (scheme, "https") != 0) { + host->uri = soup_uri_copy (uri, + SOUP_URI_SCHEME, soup_uri_is_https (uri) ? "https" : "http", + SOUP_URI_NONE); + } else + host->uri = g_uri_ref (uri); + + host->addr = g_object_new (G_TYPE_NETWORK_ADDRESS, + "hostname", g_uri_get_host (host->uri), + "port", g_uri_get_port (host->uri), + "scheme", g_uri_get_scheme (host->uri), + NULL); + + g_hash_table_insert (host->owner_map, host->uri, host); + + return host; +} + +static void +soup_host_free (SoupHost *host) +{ + g_warn_if_fail (host->conns == NULL); + + if (host->keep_alive_src) { + g_source_destroy (host->keep_alive_src); + g_source_unref (host->keep_alive_src); + } + + g_uri_unref (host->uri); + g_object_unref (host->addr); + g_free (host); +} + +/* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal() + * because we want to ignore the protocol; http://example.com and + * webcal://example.com are the same host. + */ +static guint +soup_host_uri_hash (gconstpointer key) +{ + GUri *uri = (GUri*)key; + + g_warn_if_fail (uri != NULL && g_uri_get_host (uri) != NULL); + + return g_uri_get_port (uri) + soup_str_case_hash (g_uri_get_host (uri)); +} + +static gboolean +soup_host_uri_equal (gconstpointer v1, gconstpointer v2) +{ + GUri *one = (GUri*)v1; + GUri *two = (GUri*)v2; + + g_warn_if_fail (one != NULL && two != NULL); + + const char *one_host = g_uri_get_host (one); + const char *two_host = g_uri_get_host (two); + g_warn_if_fail (one_host != NULL && two_host != NULL); + + if (g_uri_get_port (one) != g_uri_get_port (two)) + return FALSE; + + return g_ascii_strcasecmp (one_host, two_host) == 0; +} + +static gboolean +free_unused_host (gpointer user_data) +{ + SoupHost *host = (SoupHost *)user_data; + + if (host->conns) + return FALSE; + + /* This will free the host in addition to removing it from the hash table */ + g_hash_table_remove (host->owner_map, host->uri); + + return FALSE; +} + +static void +soup_host_add_connection (SoupHost *host, + SoupConnection *conn) +{ + host->conns = g_list_prepend (host->conns, conn); + host->num_conns++; + + if (host->keep_alive_src) { + g_source_destroy (host->keep_alive_src); + g_source_unref (host->keep_alive_src); + host->keep_alive_src = NULL; + } +} + +static void +soup_host_remove_connection (SoupHost *host, + SoupConnection *conn) +{ + host->conns = g_list_remove (host->conns, conn); + host->num_conns--; + + /* Free the SoupHost (and its GNetworkAddress) if there + * has not been any new connection to the host during + * the last HOST_KEEP_ALIVE msecs. + */ + if (host->num_conns == 0) { + g_assert (host->keep_alive_src == NULL); + host->keep_alive_src = soup_add_timeout (g_main_context_get_thread_default (), + HOST_KEEP_ALIVE, + free_unused_host, + host); + } +} + +static SoupHost * +soup_connection_manager_get_host_for_message (SoupConnectionManager *manager, + SoupMessage *msg) +{ + GUri *uri = soup_message_get_uri (msg); + SoupHost *host; + GHashTable *map; + + map = soup_uri_is_https (uri) ? manager->https_hosts : manager->http_hosts; + host = g_hash_table_lookup (map, uri); + if (!host) + host = soup_host_new (uri, map); + + return host; +} + +SoupConnectionManager * +soup_connection_manager_new (SoupSession *session, + guint max_conns, + guint max_conns_per_host) +{ + SoupConnectionManager *manager; + + manager = g_new0 (SoupConnectionManager, 1); + manager->session = session; + manager->max_conns = max_conns; + manager->max_conns_per_host = max_conns_per_host; + manager->http_hosts = g_hash_table_new_full (soup_host_uri_hash, + soup_host_uri_equal, + NULL, + (GDestroyNotify)soup_host_free); + manager->https_hosts = g_hash_table_new_full (soup_host_uri_hash, + soup_host_uri_equal, + NULL, + (GDestroyNotify)soup_host_free); + manager->conns = g_hash_table_new (NULL, NULL); + g_mutex_init (&manager->mutex); + g_cond_init (&manager->cond); + + return manager; +} + +void +soup_connection_manager_free (SoupConnectionManager *manager) +{ + g_clear_object (&manager->remote_connectable); + g_hash_table_destroy (manager->http_hosts); + g_hash_table_destroy (manager->https_hosts); + g_hash_table_destroy (manager->conns); + g_mutex_clear (&manager->mutex); + g_cond_clear (&manager->cond); + + g_free (manager); +} + +void +soup_connection_manager_set_max_conns (SoupConnectionManager *manager, + guint max_conns) +{ + g_assert (manager->num_conns == 0); + manager->max_conns = max_conns; +} + +guint +soup_connection_manager_get_max_conns (SoupConnectionManager *manager) +{ + return manager->max_conns; +} + +void +soup_connection_manager_set_max_conns_per_host (SoupConnectionManager *manager, + guint max_conns_per_host) +{ + g_assert (manager->num_conns == 0); + manager->max_conns_per_host = max_conns_per_host; +} + +guint +soup_connection_manager_get_max_conns_per_host (SoupConnectionManager *manager) +{ + return manager->max_conns_per_host; +} + +void +soup_connection_manager_set_remote_connectable (SoupConnectionManager *manager, + GSocketConnectable *connectable) +{ + g_assert (manager->num_conns == 0); + manager->remote_connectable = connectable ? g_object_ref (connectable) : NULL; +} + +GSocketConnectable * +soup_connection_manager_get_remote_connectable (SoupConnectionManager *manager) +{ + return manager->remote_connectable; +} + +guint +soup_connection_manager_get_num_conns (SoupConnectionManager *manager) +{ + return manager->num_conns; +} + +static void +soup_connection_manager_drop_connection (SoupConnectionManager *manager, + SoupConnection *conn) +{ + g_signal_handlers_disconnect_by_data (conn, manager); + manager->num_conns--; + g_object_unref (conn); + + g_cond_broadcast (&manager->cond); +} + +static void +soup_connection_list_disconnect_all (GList *conns) +{ + GList *c; + + for (c = conns; c; c = g_list_next (c)) { + SoupConnection *conn = (SoupConnection *)c->data; + + soup_connection_disconnect (conn); + g_object_unref (conn); + } + g_list_free (conns); +} + +static GList * +soup_connection_manager_cleanup_locked (SoupConnectionManager *manager, + gboolean cleanup_idle) +{ + GList *conns = NULL; + GHashTableIter iter; + SoupConnection *conn; + SoupHost *host; + + g_hash_table_iter_init (&iter, manager->conns); + while (g_hash_table_iter_next (&iter, (gpointer *)&conn, (gpointer *)&host)) { + SoupConnectionState state; + + state = soup_connection_get_state (conn); + if (state == SOUP_CONNECTION_IDLE && (cleanup_idle || !soup_connection_is_idle_open (conn))) { + conns = g_list_prepend (conns, g_object_ref (conn)); + g_hash_table_iter_remove (&iter); + soup_host_remove_connection (host, conn); + soup_connection_manager_drop_connection (manager, conn); + } + } + + return conns; +} + +static void +connection_disconnected (SoupConnection *conn, + SoupConnectionManager *manager) +{ + SoupHost *host = NULL; + + g_mutex_lock (&manager->mutex); + g_hash_table_steal_extended (manager->conns, conn, NULL, (gpointer *)&host); + if (host) + soup_host_remove_connection (host, conn); + soup_connection_manager_drop_connection (manager, conn); + g_mutex_unlock (&manager->mutex); + + soup_session_kick_queue (manager->session); +} + +static void +connection_state_changed (SoupConnection *conn, + GParamSpec *param, + SoupConnectionManager *manager) +{ + if (soup_connection_get_state (conn) != SOUP_CONNECTION_IDLE) + return; + + g_mutex_lock (&manager->mutex); + g_cond_broadcast (&manager->cond); + g_mutex_unlock (&manager->mutex); + + soup_session_kick_queue (manager->session); +} + +static SoupConnection * +soup_connection_manager_get_connection_locked (SoupConnectionManager *manager, + SoupMessageQueueItem *item) +{ + SoupMessage *msg = item->msg; + gboolean need_new_connection; + SoupConnection *conn; + SoupSocketProperties *socket_props; + SoupHost *host; + guint8 force_http_version; + GList *l; + GSocketConnectable *remote_connectable; + gboolean try_cleanup = TRUE; + + need_new_connection = + (soup_message_query_flags (msg, SOUP_MESSAGE_NEW_CONNECTION)) || + (soup_message_is_misdirected_retry (msg)) || + (!soup_message_query_flags (msg, SOUP_MESSAGE_IDEMPOTENT) && + !SOUP_METHOD_IS_IDEMPOTENT (soup_message_get_method (msg))); + + host = soup_connection_manager_get_host_for_message (manager, msg); + + force_http_version = g_getenv ("SOUP_FORCE_HTTP1") ? SOUP_HTTP_1_1 : soup_message_get_force_http_version (msg); + while (TRUE) { + for (l = host->conns; l && l->data; l = g_list_next (l)) { + SoupHTTPVersion http_version; + + conn = (SoupConnection *)l->data; + + http_version = soup_connection_get_negotiated_protocol (conn); + if (force_http_version <= SOUP_HTTP_2_0 && http_version != force_http_version) + continue; + + switch (soup_connection_get_state (conn)) { + case SOUP_CONNECTION_IN_USE: + if (!need_new_connection && http_version == SOUP_HTTP_2_0 && soup_connection_get_owner (conn) == g_thread_self () && soup_connection_is_reusable (conn)) + return conn; + break; + case SOUP_CONNECTION_IDLE: + if (!need_new_connection && soup_connection_is_idle_open (conn)) + return conn; + break; + case SOUP_CONNECTION_CONNECTING: + if (soup_session_steal_preconnection (item->session, item, conn)) + return conn; + + /* Always wait if we have a pending connection as it may be + * an h2 connection which will be shared. http/1.x connections + * will only be slightly delayed. */ + if (force_http_version > SOUP_HTTP_1_1 && !need_new_connection && !item->connect_only && item->async && soup_connection_get_owner (conn) == g_thread_self ()) + return NULL; + default: + break; + } + } + + if (host->num_conns >= manager->max_conns_per_host) { + if (need_new_connection && try_cleanup) { + GList *conns; + + try_cleanup = FALSE; + conns = soup_connection_manager_cleanup_locked (manager, TRUE); + if (conns) { + /* The connection has already been removed and the signals disconnected so, + * it's ok to disconnect with the mutex locked. + */ + soup_connection_list_disconnect_all (conns); + continue; + } + } + + if (item->async) + return NULL; + + g_cond_wait (&manager->cond, &manager->mutex); + try_cleanup = TRUE; + continue; + } + + if (manager->num_conns >= manager->max_conns) { + if (try_cleanup) { + GList *conns; + + try_cleanup = FALSE; + conns = soup_connection_manager_cleanup_locked (manager, TRUE); + if (conns) { + /* The connection has already been removed and the signals disconnected so, + * it's ok to disconnect with the mutex locked. + */ + soup_connection_list_disconnect_all (conns); + continue; + } + } + + if (item->async) + return NULL; + + g_cond_wait (&manager->cond, &manager->mutex); + try_cleanup = TRUE; + continue; + } + + break; + } + + /* Create a new connection */ + remote_connectable = manager->remote_connectable ? manager->remote_connectable : G_SOCKET_CONNECTABLE (host->addr); + socket_props = soup_session_ensure_socket_props (item->session); + conn = g_object_new (SOUP_TYPE_CONNECTION, + "id", ++manager->last_connection_id, + "context", soup_session_get_context (item->session), + "remote-connectable", remote_connectable, + "ssl", soup_uri_is_https (host->uri), + "socket-properties", socket_props, + "force-http-version", force_http_version, + NULL); + + g_signal_connect (conn, "disconnected", + G_CALLBACK (connection_disconnected), + manager); + g_signal_connect (conn, "notify::state", + G_CALLBACK (connection_state_changed), + manager); + + g_hash_table_insert (manager->conns, conn, host); + + manager->num_conns++; + soup_host_add_connection (host, conn); + + return conn; +} + +SoupConnection * +soup_connection_manager_get_connection (SoupConnectionManager *manager, + SoupMessageQueueItem *item) +{ + SoupConnection *conn; + + soup_connection_manager_cleanup (manager, FALSE); + + conn = soup_message_get_connection (item->msg); + if (conn) { + g_warn_if_fail (soup_connection_get_state (conn) != SOUP_CONNECTION_DISCONNECTED); + g_object_unref (conn); + return conn; + } + + g_mutex_lock (&manager->mutex); + conn = soup_connection_manager_get_connection_locked (manager, item); + if (conn) + soup_message_set_connection (item->msg, conn); + g_mutex_unlock (&manager->mutex); + + return conn; +} + +gboolean +soup_connection_manager_cleanup (SoupConnectionManager *manager, + gboolean cleanup_idle) +{ + GList *conns; + + g_mutex_lock (&manager->mutex); + conns = soup_connection_manager_cleanup_locked (manager, cleanup_idle); + g_mutex_unlock (&manager->mutex); + + if (conns) { + soup_connection_list_disconnect_all (conns); + + return TRUE; + } + + return FALSE; +} + +GIOStream * +soup_connection_manager_steal_connection (SoupConnectionManager *manager, + SoupMessage *msg) +{ + SoupConnection *conn; + SoupHost *host; + GIOStream *stream; + + conn = soup_message_get_connection (msg); + if (!conn) + return NULL; + + if (soup_connection_get_state (conn) != SOUP_CONNECTION_IN_USE) { + g_object_unref (conn); + return NULL; + } + + g_mutex_lock (&manager->mutex); + host = soup_connection_manager_get_host_for_message (manager, msg); + g_hash_table_remove (manager->conns, conn); + soup_host_remove_connection (host, conn); + soup_connection_manager_drop_connection (manager, conn); + g_mutex_unlock (&manager->mutex); + + stream = soup_connection_steal_iostream (conn); + soup_message_set_connection (msg, NULL); + g_object_unref (conn); + + return stream; +}
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-connection-manager.h
Added
@@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright 2022 Igalia S.L. + */ + +#ifndef __SOUP_CONNECTION_MANAGER_H__ +#define __SOUP_CONNECTION_MANAGER_H__ 1 + +#include "soup-connection.h" +#include "soup-message-queue-item.h" +#include <gio/gio.h> + +typedef struct _SoupConnectionManager SoupConnectionManager; + +SoupConnectionManager *soup_connection_manager_new (SoupSession *session, + guint max_conns, + guint max_conns_per_host); +void soup_connection_manager_free (SoupConnectionManager *manager); +void soup_connection_manager_set_max_conns (SoupConnectionManager *manager, + guint max_conns); +guint soup_connection_manager_get_max_conns (SoupConnectionManager *manager); +void soup_connection_manager_set_max_conns_per_host (SoupConnectionManager *manager, + guint max_conns_per_host); +guint soup_connection_manager_get_max_conns_per_host (SoupConnectionManager *manager); +void soup_connection_manager_set_remote_connectable (SoupConnectionManager *manager, + GSocketConnectable *connectable); +GSocketConnectable *soup_connection_manager_get_remote_connectable (SoupConnectionManager *manager); +guint soup_connection_manager_get_num_conns (SoupConnectionManager *manager); +SoupConnection *soup_connection_manager_get_connection (SoupConnectionManager *manager, + SoupMessageQueueItem *item); +gboolean soup_connection_manager_cleanup (SoupConnectionManager *manager, + gboolean cleanup_idle); +GIOStream *soup_connection_manager_steal_connection (SoupConnectionManager *manager, + SoupMessage *msg); + +#endif /* __SOUP_CONNECTION_MANAGER_H__ */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-connection.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-connection.c
Changed
@@ -31,7 +31,7 @@ SoupSocketProperties *socket_props; guint64 id; GSocketAddress *remote_address; - gboolean force_http1; + guint8 force_http_version; GUri *proxy_uri; gboolean ssl; @@ -47,6 +47,7 @@ GTlsCertificate *tls_client_cert; GCancellable *cancellable; + GThread *owner; } SoupConnectionPrivate; G_DEFINE_FINAL_TYPE_WITH_PRIVATE (SoupConnection, soup_connection, G_TYPE_OBJECT) @@ -75,14 +76,15 @@ PROP_TLS_CERTIFICATE_ERRORS, PROP_TLS_PROTOCOL_VERSION, PROP_TLS_CIPHERSUITE_NAME, - PROP_FORCE_HTTP1, + PROP_FORCE_HTTP_VERSION, + PROP_CONTEXT, LAST_PROPERTY }; static GParamSpec *propertiesLAST_PROPERTY = { NULL, }; -static void stop_idle_timer (SoupConnectionPrivate *priv); +static gboolean idle_timeout (gpointer conn); /* Number of seconds after which we close a connection that hasn't yet * been used. @@ -95,6 +97,8 @@ SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); priv->http_version = SOUP_HTTP_1_1; + priv->force_http_version = G_MAXUINT8; + priv->owner = g_thread_self (); } static void @@ -132,7 +136,11 @@ SoupConnection *conn = SOUP_CONNECTION (object); SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - stop_idle_timer (priv); + if (priv->idle_timeout_src) { + g_source_destroy (priv->idle_timeout_src); + g_source_unref (priv->idle_timeout_src); + priv->idle_timeout_src = NULL; + } G_OBJECT_CLASS (soup_connection_parent_class)->dispose (object); } @@ -156,9 +164,16 @@ case PROP_ID: priv->id = g_value_get_uint64 (value); break; - case PROP_FORCE_HTTP1: - priv->force_http1 = g_value_get_boolean (value); + case PROP_FORCE_HTTP_VERSION: + priv->force_http_version = g_value_get_uchar (value); break; + case PROP_CONTEXT: + priv->idle_timeout_src = g_timeout_source_new (0); + g_source_set_ready_time (priv->idle_timeout_src, -1); + g_source_set_name (priv->idle_timeout_src, "Soup connection idle timeout"); + g_source_set_callback (priv->idle_timeout_src, idle_timeout, object, NULL); + g_source_attach (priv->idle_timeout_src, g_value_get_pointer (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -182,7 +197,7 @@ g_value_set_boxed (value, priv->socket_props); break; case PROP_STATE: - g_value_set_enum (value, priv->state); + g_value_set_enum (value, g_atomic_int_get (&priv->state)); break; case PROP_SSL: g_value_set_boolean (value, priv->ssl); @@ -202,8 +217,8 @@ case PROP_TLS_CIPHERSUITE_NAME: g_value_set_string (value, soup_connection_get_tls_ciphersuite_name (SOUP_CONNECTION (object))); break; - case PROP_FORCE_HTTP1: - g_value_set_boolean (value, priv->force_http1); + case PROP_FORCE_HTTP_VERSION: + g_value_set_uchar (value, priv->force_http_version); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -344,11 +359,18 @@ NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - propertiesPROP_FORCE_HTTP1 = - g_param_spec_boolean ("force-http1", - "Force HTTP 1.x", - "Force connection to use HTTP 1.x", - FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + propertiesPROP_FORCE_HTTP_VERSION = + g_param_spec_uchar ("force-http-version", + "Force HTTP version", + "Force connection to use a specific HTTP version", + 0, G_MAXUINT8, G_MAXUINT8, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + propertiesPROP_CONTEXT = + g_param_spec_pointer ("context", + "Context", + "The session main context", + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, LAST_PROPERTY, properties); @@ -369,7 +391,7 @@ idle_timeout (gpointer conn) { soup_connection_disconnect (conn); - return FALSE; + return G_SOURCE_REMOVE; } static void @@ -377,21 +399,14 @@ { SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - if (priv->socket_props->idle_timeout > 0 && !priv->idle_timeout_src) { - priv->idle_timeout_src = - soup_add_timeout (g_main_context_get_thread_default (), - priv->socket_props->idle_timeout * 1000, - idle_timeout, conn); - } -} + if (priv->socket_props->idle_timeout == 0) + return; -static void -stop_idle_timer (SoupConnectionPrivate *priv) -{ - if (priv->idle_timeout_src) { - g_source_destroy (priv->idle_timeout_src); - g_clear_pointer (&priv->idle_timeout_src, g_source_unref); - } + if (g_source_get_ready_time (priv->idle_timeout_src) >= 0) + return; + + g_source_set_ready_time (priv->idle_timeout_src, + g_get_monotonic_time () + (guint64)priv->socket_props->idle_timeout * G_USEC_PER_SEC); } static void @@ -400,11 +415,11 @@ { SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - if (priv->state == state) + if (g_atomic_int_get (&priv->state) == state) return; - priv->state = state; - if (priv->state == SOUP_CONNECTION_IDLE) + g_atomic_int_set (&priv->state, state); + if (state == SOUP_CONNECTION_IDLE) start_idle_timer (conn); g_object_notify_by_pspec (G_OBJECT (conn), propertiesPROP_STATE); @@ -512,13 +527,14 @@ G_CALLBACK (re_emit_socket_event), conn, 0); - if (!props->proxy_use_default) { - if (props->proxy_resolver) { - g_socket_client_set_proxy_resolver (client, props->proxy_resolver); - g_socket_client_add_application_proxy (client, "http"); - } else - g_socket_client_set_enable_proxy (client, FALSE); - } + if (!props->proxy_use_default && !props->proxy_resolver) { + g_socket_client_set_enable_proxy (client, FALSE); + } else { + if (props->proxy_resolver) + g_socket_client_set_proxy_resolver (client, props->proxy_resolver); + g_socket_client_add_application_proxy (client, "http"); + } + if (props->io_timeout) g_socket_client_set_timeout (client, props->io_timeout); if (props->local_addr) @@ -568,11 +584,23 @@ GPtrArray *advertised_protocols = g_ptr_array_sized_new (4); // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml - if (!priv->force_http1) + switch (priv->force_http_version) { + case SOUP_HTTP_1_0: + g_ptr_array_add (advertised_protocols, "http/1.0"); + break; + case SOUP_HTTP_1_1: + g_ptr_array_add (advertised_protocols, "http/1.1"); + break; + case SOUP_HTTP_2_0: g_ptr_array_add (advertised_protocols, "h2"); - - g_ptr_array_add (advertised_protocols, "http/1.1"); - g_ptr_array_add (advertised_protocols, "http/1.0"); + break; + default: + if (!priv->remote_address || !G_IS_PROXY_ADDRESS (priv->remote_address)) + g_ptr_array_add (advertised_protocols, "h2"); + g_ptr_array_add (advertised_protocols, "http/1.1"); + g_ptr_array_add (advertised_protocols, "http/1.0"); + break; + } g_ptr_array_add (advertised_protocols, NULL); tls_interaction = priv->socket_props->tls_interaction ? g_object_ref (priv->socket_props->tls_interaction) : soup_tls_interaction_new (conn); @@ -989,7 +1017,8 @@ * soup_connection_disconnect: * @conn: a connection * - * Disconnects @conn's socket and emits a %disconnected signal. + * Disconnects @conn's socket and emits a signal@Socket::disconnected signal. + * * After calling this, @conn will be essentially useless. **/ void @@ -997,7 +1026,7 @@ { SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - if (priv->state == SOUP_CONNECTION_DISCONNECTED) + if (g_atomic_int_get (&priv->state) == SOUP_CONNECTION_DISCONNECTED) return; soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED); @@ -1092,7 +1121,8 @@ { SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - g_assert (priv->state == SOUP_CONNECTION_IDLE); + if (g_atomic_int_get (&priv->state) != SOUP_CONNECTION_IDLE) + return FALSE; if (!g_socket_is_connected (soup_connection_get_socket (conn))) return FALSE; @@ -1108,7 +1138,7 @@ { SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - return priv->state; + return g_atomic_int_get (&priv->state); } void @@ -1117,25 +1147,29 @@ { SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - g_assert (in_use || priv->in_use > 0); + g_assert (in_use || g_atomic_int_get (&priv->in_use) > 0); - if (in_use) - priv->in_use++; - else - priv->in_use--; + if (in_use) { + g_atomic_int_inc (&priv->in_use); + if (g_atomic_int_compare_and_exchange (&priv->state, SOUP_CONNECTION_IDLE, SOUP_CONNECTION_IN_USE)) { + priv->owner = g_thread_self (); + soup_client_message_io_owner_changed (priv->io_data); + g_object_notify_by_pspec (G_OBJECT (conn), propertiesPROP_STATE); + } - if (priv->in_use > 0) { - if (priv->state == SOUP_CONNECTION_IDLE) - soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE); return; } - clear_proxy_msg (conn); + g_assert (g_atomic_int_get (&priv->state) != SOUP_CONNECTION_IDLE); - if (soup_connection_is_reusable (conn)) - soup_connection_set_state (conn, SOUP_CONNECTION_IDLE); - else - soup_connection_disconnect (conn); + if (g_atomic_int_dec_and_test (&priv->in_use)) { + clear_proxy_msg (conn); + + if (soup_connection_is_reusable (conn)) + soup_connection_set_state (conn, SOUP_CONNECTION_IDLE); + else + soup_connection_disconnect (conn); + } } SoupClientMessageIO * @@ -1144,10 +1178,10 @@ { SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - g_assert (priv->state == SOUP_CONNECTION_IN_USE); + g_assert (g_atomic_int_get (&priv->state) == SOUP_CONNECTION_IN_USE); priv->unused_timeout = 0; - stop_idle_timer (priv); + g_source_set_ready_time (priv->idle_timeout_src, -1); if (priv->proxy_uri && soup_message_get_method (msg) == SOUP_METHOD_CONNECT) set_proxy_msg (conn, msg); @@ -1335,3 +1369,11 @@ return priv->io_data && soup_client_message_io_is_reusable (priv->io_data); } + +GThread * +soup_connection_get_owner (SoupConnection *conn) +{ + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + + return priv->owner; +}
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-connection.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-connection.h
Changed
@@ -85,6 +85,7 @@ GSocketAddress *soup_connection_get_remote_address (SoupConnection *conn); SoupHTTPVersion soup_connection_get_negotiated_protocol (SoupConnection *conn); gboolean soup_connection_is_reusable (SoupConnection *conn); +GThread *soup_connection_get_owner (SoupConnection *conn); G_END_DECLS
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-date-utils.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-date-utils.c
Changed
@@ -27,15 +27,6 @@ #include "soup-date-utils-private.h" /** - * SECTION:soup-date-utils - * @section_id: SoupDateTime - * @title: DateTime Utilities - * @short_description: Functions to help working with #GDateTime and HTTP - * - * These are simple utility functions to help using #GDateTime. - */ - -/** * soup_date_time_is_past: * @date: a #GDateTime * @@ -58,11 +49,11 @@ /** * SoupDateFormat: * @SOUP_DATE_HTTP: RFC 1123 format, used by the HTTP "Date" header. Eg - * "Sun, 06 Nov 1994 08:49:37 GMT" + * "Sun, 06 Nov 1994 08:49:37 GMT". * @SOUP_DATE_COOKIE: The format for the "Expires" timestamp in the - * Netscape cookie specification. Eg, "Sun, 06-Nov-1994 08:49:37 GMT". + * Netscape cookie specification. Eg, "Sun, 06-Nov-1994 08:49:37 GMT". * - * Date formats that soup_date_time_to_string() can use. + * Date formats that func@date_time_to_string can use. * * @SOUP_DATE_HTTP and @SOUP_DATE_COOKIE always coerce the time to * UTC. @@ -88,7 +79,7 @@ * * Converts @date to a string in the format described by @format. * - * Return: (transfer full): @date as a string or %NULL + * Returns: (transfer full): @date as a string or %NULL **/ char * soup_date_time_to_string (GDateTime *date, @@ -307,13 +298,14 @@ * soup_date_time_new_from_http_string: * @date_string: The date as a string * - * Parses @date_string and tries to extract a date from it. This - * recognizes all of the "HTTP-date" formats from RFC 2616, RFC 2822 - * dates, and reasonable approximations thereof. (Eg, it is lenient about - * whitespace, leading "0"s, etc.) + * Parses @date_string and tries to extract a date from it. + * + * This recognizes all of the "HTTP-date" formats from RFC 2616, RFC 2822 dates, + * and reasonable approximations thereof. (Eg, it is lenient about whitespace, + * leading "0"s, etc.) * * Returns: (nullable): a new #GDateTime, or %NULL if @date_string - * could not be parsed. + * could not be parsed. **/ GDateTime * soup_date_time_new_from_http_string (const char *date_string)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-form.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-form.c
Changed
@@ -15,31 +15,19 @@ #include "soup.h" /** - * SECTION:soup-form - * @section_id: SoupForm - * @short_description: HTML form handling - * @see_also: #SoupMultipart - * - * libsoup contains several help methods for processing HTML forms as - * defined by the the HTML 4.01 specification(http://www.w3.org/TR/html401/interact/forms.html#h-17.13). - **/ - -/** * SOUP_FORM_MIME_TYPE_URLENCODED: * * A macro containing the value - * <literal>"application/x-www-form-urlencoded"</literal>; the default + * `application/x-www-form-urlencoded`; the default * MIME type for POSTing HTML form data. - * **/ /** * SOUP_FORM_MIME_TYPE_MULTIPART: * * A macro containing the value - * <literal>"multipart/form-data"</literal>; the MIME type used for + * `multipart/form-data`; the MIME type used for * posting form data that contains files to be uploaded. - * **/ #define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10) @@ -71,12 +59,13 @@ * soup_form_decode: * @encoded_form: data of type "application/x-www-form-urlencoded" * - * Decodes @form, which is an urlencoded dataset as defined in the - * HTML 4.01 spec. + * Decodes @form. + * + * which is an urlencoded dataset as defined in the HTML 4.01 spec. * * Returns: (element-type utf8 utf8) (transfer container): a hash - * table containing the name/value pairs from @encoded_form, which you - * can free with g_hash_table_destroy(). + * table containing the name/value pairs from @encoded_form, which you + * can free with func@GLib.HashTable.destroy. **/ GHashTable * soup_form_decode (const char *encoded_form) @@ -111,35 +100,35 @@ /** * soup_form_decode_multipart: * @multipart: a #SoupMultipart - * @file_control_name: (nullable): the name of the HTML file upload control, or %NULL - * @filename: (out) (optional): return location for the name of the uploaded file, or %NULL - * @content_type: (out) (optional): return location for the MIME type of the uploaded file, or %NULL - * @file: (out) (optional): return location for the uploaded file data, or %NULL + * @file_control_name: (nullable): the name of the HTML file upload control + * @filename: (out) (optional): return location for the name of the uploaded file + * @content_type: (out) (optional): return location for the MIME type of the uploaded file + * @file: (out) (optional): return location for the uploaded file data + * + * Decodes the "multipart/form-data" request in @multipart. * - * Decodes the "multipart/form-data" request in @multipart; this is a - * convenience method for the case when you have a single file upload - * control in a form. (Or when you don't have any file upload - * controls, but are still using "multipart/form-data" anyway.) Pass - * the name of the file upload control in @file_control_name, and - * soup_form_decode_multipart() will extract the uploaded file data - * into @filename, @content_type, and @file. All of the other form - * control data will be returned (as strings, as with - * soup_form_decode()) in the returned #GHashTable. + * this is a convenience method for the case when you have a single file upload + * control in a form. (Or when you don't have any file upload controls, but are + * still using "multipart/form-data" anyway.) Pass the name of the file upload + * control in @file_control_name, and func@form_decode_multipart will extract + * the uploaded file data into @filename, @content_type, and @file. All of the + * other form control data will be returned (as strings, as with + * func@form_decode in the returned struct@GLib.HashTable. * * You may pass %NULL for @filename, @content_type and/or @file if you do not - * care about those fields. soup_form_decode_multipart() may also + * care about those fields. func@form_decode_multipart may also * return %NULL in those fields if the client did not provide that * information. You must free the returned filename and content-type - * with g_free(), and the returned file data with g_bytes_unref(). + * with func@GLib.free, and the returned file data with method@Glib.Bytes.unref. * * If you have a form with more than one file upload control, you will - * need to decode it manually, using soup_multipart_new_from_message() - * and soup_multipart_get_part(). + * need to decode it manually, using ctor@Multipart.new_from_message + * and method@Multipart.get_part. * * Returns: (nullable) (element-type utf8 utf8) (transfer container): - * a hash table containing the name/value pairs (other than - * @file_control_name) from @msg, which you can free with - * g_hash_table_destroy(). On error, it will return %NULL. + * a hash table containing the name/value pairs (other than + * @file_control_name) from @msg, which you can free with + * func@GLib.HashTable.destroy. On error, it will return %NULL. */ GHashTable * soup_form_decode_multipart (SoupMultipart *multipart, @@ -234,20 +223,21 @@ * soup_form_encode: * @first_field: name of the first form field * @...: value of @first_field, followed by additional field names - * and values, terminated by %NULL. + * and values, terminated by %NULL. * * Encodes the given field names and values into a value of type - * "application/x-www-form-urlencoded", as defined in the HTML 4.01 - * spec. + * "application/x-www-form-urlencoded". + * + * Encodes as defined in the HTML 4.01 spec. * * This method requires you to know the names of the form fields (or * at the very least, the total number of fields) at compile time; for - * working with dynamic forms, use soup_form_encode_hash() or - * soup_form_encode_datalist(). + * working with dynamic forms, use func@form_encode_hash or + * func@form_encode_datalist. * - * Returns: the encoded form + * See also: ctor@Message.new_from_encoded_form. * - * See also: soup_message_new_from_encoded_form() + * Returns: the encoded form **/ char * soup_form_encode (const char *first_field, ...) @@ -265,20 +255,21 @@ /** * soup_form_encode_hash: * @form_data_set: (element-type utf8 utf8): a hash table containing - * name/value pairs (as strings) + * name/value pairs (as strings) * * Encodes @form_data_set into a value of type - * "application/x-www-form-urlencoded", as defined in the HTML 4.01 - * spec. + * "application/x-www-form-urlencoded". + * + * Encodes as defined in the HTML 4.01 spec. * * Note that the HTML spec states that "The control names/values are * listed in the order they appear in the document." Since this method * takes a hash table, it cannot enforce that; if you care about the - * ordering of the form fields, use soup_form_encode_datalist(). + * ordering of the form fields, use func@form_encode_datalist. * - * Returns: the encoded form + * See also: ctor@Message.new_from_encoded_form. * - * See also: soup_message_new_from_encoded_form() + * Returns: the encoded form **/ char * soup_form_encode_hash (GHashTable *form_data_set) @@ -304,13 +295,15 @@ * @form_data_set: a datalist containing name/value pairs * * Encodes @form_data_set into a value of type - * "application/x-www-form-urlencoded", as defined in the HTML 4.01 - * spec. Unlike soup_form_encode_hash(), this preserves the ordering - * of the form elements, which may be required in some situations. + * "application/x-www-form-urlencoded". * - * Returns: the encoded form + * Encodes as defined in the HTML 4.01 spec. Unlike func@form_encode_hash, + * this preserves the ordering of the form elements, which may be required in + * some situations. * - * See also: soup_message_new_from_encoded_form() + * See also: ctor@Message.new_from_encoded_form. + * + * Returns: the encoded form **/ char * soup_form_encode_datalist (GData **form_data_set) @@ -324,14 +317,16 @@ /** * soup_form_encode_valist: * @first_field: name of the first form field - * @args: pointer to additional values, as in soup_form_encode() + * @args: pointer to additional values, as in func@form_encode * - * See soup_form_encode(). This is mostly an internal method, used by - * various other methods such as soup_form_encode(). + * See func@form_encode. * - * Returns: the encoded form + * This is mostly an internal method, used by various other methods such as + * func@form_encode. * - * See also: soup_message_new_from_encoded_form() + * See also: ctor@Message.new_from_encoded_form. + * + * Returns: the encoded form **/ char * soup_form_encode_valist (const char *first_field, va_list args)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-headers.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-headers.c
Changed
@@ -18,15 +18,6 @@ #include "soup.h" /** - * SECTION:soup-headers - * @section_id: SoupHeaders - * @title: SoupHeaders - * @short_description: Functions to help working with HTTP Headers - * - * These are utility functions to help working with HTTP headers. - */ - -/** * soup_headers_parse: * @str: the header string (including the Request-Line or Status-Line, * but not the trailing blank line) @@ -34,14 +25,14 @@ * @dest: #SoupMessageHeaders to store the header values in * * Parses the headers of an HTTP request or response in @str and - * stores the results in @dest. Beware that @dest may be modified even - * on failure. + * stores the results in @dest. + * + * Beware that @dest may be modified even on failure. * * This is a low-level method; normally you would use - * soup_headers_parse_request() or soup_headers_parse_response(). + * func@headers_parse_request or func@headers_parse_response. * * Returns: success or failure - * **/ gboolean soup_headers_parse (const char *str, int len, SoupMessageHeaders *dest) @@ -170,11 +161,11 @@ * @len: length of @str * @req_headers: #SoupMessageHeaders to store the header values in * @req_method: (out) (optional): if non-%NULL, will be filled in with the - * request method + * request method * @req_path: (out) (optional): if non-%NULL, will be filled in with the - * request path + * request path * @ver: (out) (optional): if non-%NULL, will be filled in with the HTTP - * version + * version * * Parses the headers of an HTTP request in @str and stores the * results in @req_method, @req_path, @ver, and @req_headers. @@ -182,7 +173,7 @@ * Beware that @req_headers may be modified even on failure. * * Returns: %SOUP_STATUS_OK if the headers could be parsed, or an - * HTTP error to be returned to the client if they could not be. + * HTTP error to be returned to the client if they could not be. **/ guint soup_headers_parse_request (const char *str, @@ -280,15 +271,16 @@ * soup_headers_parse_status_line: * @status_line: an HTTP Status-Line * @ver: (out) (optional): if non-%NULL, will be filled in with the HTTP - * version + * version * @status_code: (out) (optional): if non-%NULL, will be filled in with - * the status code + * the status code * @reason_phrase: (out) (optional): if non-%NULL, will be filled in with - * the reason phrase + * the reason phrase * * Parses the HTTP Status-Line string in @status_line into @ver, - * @status_code, and @reason_phrase. @status_line must be terminated by - * either "\0" or "\r\n". + * @status_code, and @reason_phrase. + * + * @status_line must be terminated by either "\0" or "\r\n". * * Returns: %TRUE if @status_line was parsed successfully. **/ @@ -357,11 +349,11 @@ * @len: length of @str * @headers: #SoupMessageHeaders to store the header values in * @ver: (out) (optional): if non-%NULL, will be filled in with the HTTP - * version + * version * @status_code: (out) (optional): if non-%NULL, will be filled in with - * the status code + * the status code * @reason_phrase: (out) (optional): if non-%NULL, will be filled in with - * the reason phrase + * the reason phrase * * Parses the headers of an HTTP response in @str and stores the * results in @ver, @status_code, @reason_phrase, and @headers. @@ -488,12 +480,12 @@ * soup_header_parse_list: * @header: a header value * - * Parses a header whose content is described by RFC2616 as - * "#something", where "something" does not itself contain commas, - * except as part of quoted-strings. + * Parses a header whose content is described by RFC2616 as `#something`. + * + * "something" does not itself contain commas, except as part of quoted-strings. * * Returns: (transfer full) (element-type utf8): a #GSList of - * list elements, as allocated strings + * list elements, as allocated strings **/ GSList * soup_header_parse_list (const char *header) @@ -526,7 +518,7 @@ * soup_header_parse_quality_list: * @header: a header value * @unacceptable: (out) (optional) (transfer full) (element-type utf8): on - * return, will contain a list of unacceptable values + * return, will contain a list of unacceptable values * * Parses a header whose content is a list of items with optional * "qvalue"s (eg, Accept, Accept-Charset, Accept-Encoding, @@ -537,7 +529,7 @@ * the main list. * * Returns: (transfer full) (element-type utf8): a #GSList of - * acceptable values (as allocated strings), highest-qvalue first. + * acceptable values (as allocated strings), highest-qvalue first. **/ GSList * soup_header_parse_quality_list (const char *header, GSList **unacceptable) @@ -613,8 +605,8 @@ /** * soup_header_free_list: (skip) - * @list: a #GSList returned from soup_header_parse_list() or - * soup_header_parse_quality_list() + * @list: a #GSList returned from func@header_parse_list or + * func@header_parse_quality_list * * Frees @list. **/ @@ -627,12 +619,13 @@ /** * soup_header_contains: * @header: An HTTP header suitable for parsing with - * soup_header_parse_list() + * func@header_parse_list * @token: a token * * Parses @header to see if it contains the token @token (matched - * case-insensitively). Note that this can't be used with lists - * that have qvalues. + * case-insensitively). + * + * Note that this can't be used with lists that have qvalues. * * Returns: whether or not @header contains @token **/ @@ -786,7 +779,7 @@ * @header: a header value * * Parses a header which is a comma-delimited list of something like: - * <literal>token "=" ( token | quoted-string ) </literal>. + * `token "=" ( token | quoted-string ) `. * * Tokens that don't have an associated value will still be added to * the resulting hash table, but with a %NULL value. @@ -796,8 +789,8 @@ * header). * * Returns: (element-type utf8 utf8) (transfer full): a - * #GHashTable of list elements, which can be freed with - * soup_header_free_param_list(). + * #GHashTable of list elements, which can be freed with + * func@header_free_param_list. **/ GHashTable * soup_header_parse_param_list (const char *header) @@ -812,7 +805,7 @@ * @header: a header value * * Parses a header which is a semicolon-delimited list of something - * like: <literal>token "=" ( token | quoted-string ) </literal>. + * like: `token "=" ( token | quoted-string ) `. * * Tokens that don't have an associated value will still be added to * the resulting hash table, but with a %NULL value. @@ -822,9 +815,8 @@ * header). * * Returns: (element-type utf8 utf8) (transfer full): a - * #GHashTable of list elements, which can be freed with - * soup_header_free_param_list(). - * + * #GHashTable of list elements, which can be freed with + * func@header_free_param_list. **/ GHashTable * soup_header_parse_semi_param_list (const char *header) @@ -838,19 +830,19 @@ * soup_header_parse_param_list_strict: * @header: a header value * - * A strict version of soup_header_parse_param_list() + * A strict version of func@header_parse_param_list * that bails out if there are duplicate parameters. + * * Note that this function will treat RFC5987-encoded * parameters as duplicated if an ASCII version is also * present. For header fields that might contain * RFC5987-encoded parameters, use - * soup_header_parse_param_list() instead. + * func@header_parse_param_list instead. * * Returns: (element-type utf8 utf8) (transfer full) (nullable): - * a #GHashTable of list elements, which can be freed with - * soup_header_free_param_list() or %NULL if there are duplicate - * elements. - * + * a #GHashTable of list elements, which can be freed with + * func@header_free_param_list or %NULL if there are duplicate + * elements. **/ GHashTable * soup_header_parse_param_list_strict (const char *header) @@ -864,19 +856,19 @@ * soup_header_parse_semi_param_list_strict: * @header: a header value * - * A strict version of soup_header_parse_semi_param_list() + * A strict version of func@header_parse_semi_param_list * that bails out if there are duplicate parameters. + * * Note that this function will treat RFC5987-encoded * parameters as duplicated if an ASCII version is also * present. For header fields that might contain * RFC5987-encoded parameters, use - * soup_header_parse_semi_param_list() instead. + * func@header_parse_semi_param_list instead. * * Returns: (element-type utf8 utf8) (transfer full) (nullable): - * a #GHashTable of list elements, which can be freed with - * soup_header_free_param_list() or %NULL if there are duplicate - * elements. - * + * a #GHashTable of list elements, which can be freed with + * func@header_free_param_list or %NULL if there are duplicate + * elements. **/ GHashTable * soup_header_parse_semi_param_list_strict (const char *header) @@ -888,8 +880,8 @@ /** * soup_header_free_param_list: - * @param_list: (element-type utf8 utf8): a #GHashTable returned from soup_header_parse_param_list() - * or soup_header_parse_semi_param_list() + * @param_list: (element-type utf8 utf8): a #GHashTable returned from + * func@header_parse_param_list or func@header_parse_semi_param_list * * Frees @param_list. **/ @@ -972,12 +964,11 @@ * @name: a parameter name * @value: a parameter value * - * Appends something like <literal>@name="@value"</literal> to + * Appends something like `name="value"` to * @string, taking care to escape any quotes or backslashes in @value. * * If @value is (non-ASCII) UTF-8, this will instead use RFC 5987 - * encoding, just like soup_header_g_string_append_param(). - * + * encoding, just like func@header_g_string_append_param. **/ void soup_header_g_string_append_param_quoted (GString *string, @@ -997,9 +988,8 @@ * @name: a parameter name * @value: a parameter value, or %NULL * - * Appends something like <literal>@name=@value</literal> to @string, - * taking care to quote @value if needed, and if so, to escape any - * quotes or backslashes in @value. + * Appends something like `name=value` to @string, taking care to quote @value + * if needed, and if so, to escape any quotes or backslashes in @value. * * Alternatively, if @value is a non-ASCII UTF-8 string, it will be * appended using RFC5987 syntax. Although in theory this is supposed @@ -1008,7 +998,6 @@ * "filename" parameter. * * If @value is %NULL, this will just append @name to @string. - * **/ void soup_header_g_string_append_param (GString *string,
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-http2-utils.c
Added
@@ -0,0 +1,115 @@ +/* + * Copyright 2022 Igalia, S.L. + */ + +#include <glib.h> + +#include "soup-http2-utils.h" + +const char * +soup_http2_io_state_to_string (SoupHTTP2IOState state) +{ + switch (state) { + case STATE_NONE: + return "NONE"; + case STATE_WRITE_HEADERS: + return "WRITE_HEADERS"; + case STATE_WRITE_DATA: + return "WRITE_DATA"; + case STATE_WRITE_DONE: + return "WRITE_DONE"; + case STATE_READ_HEADERS: + return "READ_HEADERS"; + case STATE_READ_DATA_START: + return "READ_DATA_START"; + case STATE_READ_DATA: + return "READ_DATA"; + case STATE_READ_DONE: + return "READ_DONE"; + } + g_assert_not_reached (); + return ""; +} + +const char * +soup_http2_frame_type_to_string (nghttp2_frame_type type) +{ + switch (type) { + case NGHTTP2_DATA: + return "DATA"; + case NGHTTP2_HEADERS: + return "HEADERS"; + case NGHTTP2_PRIORITY: + return "PRIORITY"; + case NGHTTP2_RST_STREAM: + return "RST_STREAM"; + case NGHTTP2_SETTINGS: + return "SETTINGS"; + case NGHTTP2_PING: + return "PING"; + case NGHTTP2_GOAWAY: + return "GOAWAY"; + case NGHTTP2_WINDOW_UPDATE: + return "WINDOW_UPDATE"; + /* LCOV_EXCL_START */ + case NGHTTP2_PUSH_PROMISE: + return "PUSH_PROMISE"; + case NGHTTP2_CONTINUATION: + return "CONTINUATION"; + case NGHTTP2_ALTSVC: + return "ALTSVC"; + case NGHTTP2_ORIGIN: + return "ORIGIN"; + default: + g_warn_if_reached (); + return "UNKNOWN"; + /* LCOV_EXCL_STOP */ + } +} + +const char * +soup_http2_headers_category_to_string (nghttp2_headers_category catergory) +{ + switch (catergory) { + case NGHTTP2_HCAT_REQUEST: + return "REQUEST"; + case NGHTTP2_HCAT_RESPONSE: + return "RESPONSE"; + case NGHTTP2_HCAT_PUSH_RESPONSE: + return "PUSH_RESPONSE"; + case NGHTTP2_HCAT_HEADERS: + return "HEADERS"; + } + g_assert_not_reached (); + return ""; +} + +G_GNUC_PRINTF(1, 0) +static void +debug_nghttp2 (const char *format, + va_list args) +{ + char *message; + gsize len; + + if (g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, "nghttp2")) + return; + + message = g_strdup_vprintf (format, args); + len = strlen (message); + if (len >= 1 && messagelen - 1 == '\n') + messagelen - 1 = '\0'; + g_log ("nghttp2", G_LOG_LEVEL_DEBUG, "NGHTTP2 %s", message); + g_free (message); +} + +void +soup_http2_debug_init (void) +{ + static gsize nghttp2_debug_init = 0; + if (g_once_init_enter (&nghttp2_debug_init)) { + nghttp2_set_debug_vprintf_callback(debug_nghttp2); + g_once_init_leave (&nghttp2_debug_init, 1); + } + +}
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-http2-utils.h
Added
@@ -0,0 +1,49 @@ +/* + * Copyright 2022 Igalia, S.L. + */ + +#pragma once + +#include <nghttp2/nghttp2.h> + +#define NGCHECK(stm) \ + G_STMT_START { \ + int return_code = stm; \ + if (return_code == NGHTTP2_ERR_NOMEM) \ + g_abort (); \ + else if (return_code < 0) \ + g_debug ("Unhandled NGHTTP2 Error: %s", nghttp2_strerror (return_code)); \ + } G_STMT_END + +#define MAKE_NV(NAME, VALUE, VALUELEN) \ + { \ + (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), VALUELEN, NGHTTP2_NV_FLAG_NONE \ + } + +#define MAKE_NV2(NAME, VALUE) \ + { \ + (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), strlen (VALUE), NGHTTP2_NV_FLAG_NONE \ + } + +#define MAKE_NV3(NAME, VALUE, FLAGS) \ + { \ + (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), strlen (VALUE), FLAGS \ + } + + +typedef enum { + STATE_NONE, + STATE_WRITE_HEADERS, + STATE_WRITE_DATA, + STATE_WRITE_DONE, + STATE_READ_HEADERS, + STATE_READ_DATA_START, + STATE_READ_DATA, + STATE_READ_DONE, +} SoupHTTP2IOState; + +const char *soup_http2_io_state_to_string (SoupHTTP2IOState state); +const char *soup_http2_frame_type_to_string (nghttp2_frame_type type); +const char *soup_http2_headers_category_to_string (nghttp2_headers_category catergory); + +void soup_http2_debug_init (void);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-logger-private.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-logger-private.h
Changed
@@ -9,10 +9,6 @@ G_BEGIN_DECLS -void soup_logger_request_body_setup (SoupLogger *logger, - SoupMessage *msg, - SoupBodyOutputStream *stream); - void soup_logger_log_request_data (SoupLogger *logger, SoupMessage *msg, const char *buffer, gsize len); G_END_DECLS
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-logger.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-logger.c
Changed
@@ -23,24 +23,25 @@ #include "soup-session-feature-private.h" /** - * SECTION:soup-logger - * @short_description: Debug logging support + * SoupLogger: + * + * Debug logging support * - * #SoupLogger watches a #SoupSession and logs the HTTP traffic that + * #SoupLogger watches a class@Session and logs the HTTP traffic that * it generates, for debugging purposes. Many applications use an * environment variable to determine whether or not to use * #SoupLogger, and to determine the amount of debugging output. * - * To use #SoupLogger, first create a logger with soup_logger_new(), - * optionally configure it with soup_logger_set_request_filter(), - * soup_logger_set_response_filter(), and soup_logger_set_printer(), - * and then attach it to a session (or multiple sessions) with - * soup_session_add_feature(). + * To use #SoupLogger, first create a logger with ctor@Logger.new, optionally + * configure it with method@Logger.set_request_filter, + * method@Logger.set_response_filter, and method@Logger.set_printer, and + * then attach it to a session (or multiple sessions) with + * method@Session.add_feature. * - * By default, the debugging output is sent to - * <literal>stdout</literal>, and looks something like: + * By default, the debugging output is sent to `stdout`, and looks something + * like: * - * <informalexample><screen> + * ``` * > POST /unauth HTTP/1.1 * > Soup-Debug-Timestamp: 1200171744 * > Soup-Debug: SoupSession 1 (0x612190), SoupMessage 1 (0x617000), GSocket 1 (0x612220) @@ -53,45 +54,36 @@ * < Soup-Debug: SoupMessage 1 (0x617000) * < Date: Sun, 12 Jan 2008 21:02:24 GMT * < Content-Length: 0 - * </screen></informalexample> + * ``` * - * The <literal>Soup-Debug-Timestamp</literal> line gives the time (as - * a <type>time_t</type>) when the request was sent, or the response fully - * received. + * The `Soup-Debug-Timestamp` line gives the time (as a `time_t`) when the + * request was sent, or the response fully received. * - * The <literal>Soup-Debug</literal> line gives further debugging - * information about the #SoupSession, #SoupMessage, and #GSocket - * involved; the hex numbers are the addresses of the objects in - * question (which may be useful if you are running in a debugger). - * The decimal IDs are simply counters that uniquely identify objects - * across the lifetime of the #SoupLogger. In particular, this can be - * used to identify when multiple messages are sent across the same - * connection. + * The `Soup-Debug` line gives further debugging information about the + * class@Session, class@Message, and class@Gio.Socket involved; the hex + * numbers are the addresses of the objects in question (which may be useful if + * you are running in a debugger). The decimal IDs are simply counters that + * uniquely identify objects across the lifetime of the #SoupLogger. In + * particular, this can be used to identify when multiple messages are sent + * across the same connection. * * Currently, the request half of the message is logged just before * the first byte of the request gets written to the network (from the - * #SoupMessage::starting signal). - * - * The response is logged just after the last byte of the response - * body is read from the network (from the #SoupMessage::got-body or - * #SoupMessage::got-informational signal), which means that the - * #SoupMessage::got-headers signal, and anything triggered off it - * (such as #SoupMessage::authenticate) will be emitted - * <emphasis>before</emphasis> the response headers are actually - * logged. - * - * If the response doesn't happen to trigger the - * #SoupMessage::got-body nor #SoupMessage::got-informational signals - * due to, for example, a cancellation before receiving the last byte - * of the response body, the response will still be logged on the - * event of the #SoupMessage::finished signal. - **/ - -/** - * SoupLogger: + * signal@Message::starting signal). * - * Class implementing logging. - */ + * The response is logged just after the last byte of the response body is read + * from the network (from the signal@Message::got-body or + * signal@Message::got-informational signal), which means that the + * signal@Message::got-headers signal, and anything triggered off it (such as + * #SoupMessage::authenticate) will be emitted *before* the response headers are + * actually logged. + * + * If the response doesn't happen to trigger the signal@Message::got-body nor + * signal@Message::got-informational signals due to, for example, a + * cancellation before receiving the last byte of the response body, the + * response will still be logged on the event of the signal@Message::finished + * signal. + **/ struct _SoupLogger { GObject parent; @@ -99,9 +91,9 @@ typedef struct { GQuark tag; + GMutex mutex; GHashTable *ids; GHashTable *request_bodies; - GHashTable *request_messages; GHashTable *response_bodies; SoupSession *session; @@ -132,8 +124,6 @@ static GParamSpec *propertiesLAST_PROPERTY = { NULL, }; -static void body_ostream_done (gpointer data, GObject *bostream); - static void soup_logger_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data); static SoupContentProcessorInterface *soup_logger_default_content_processor_interface; @@ -156,12 +146,14 @@ if (!nread) return; + g_mutex_lock (&priv->mutex); body = g_hash_table_lookup (bodies, key); if (!body) { body = g_string_new (NULL); g_hash_table_insert (bodies, key, body); } + g_mutex_unlock (&priv->mutex); if (priv->max_body_size >= 0) { /* longer than max => we've written the extra ... */ @@ -255,13 +247,7 @@ priv->ids = g_hash_table_new (NULL, NULL); priv->request_bodies = g_hash_table_new_full (NULL, NULL, NULL, body_free); priv->response_bodies = g_hash_table_new_full (NULL, NULL, NULL, body_free); - priv->request_messages = g_hash_table_new (NULL, NULL); -} - -static void -body_ostream_drop_ref (gpointer key, gpointer value, gpointer data) -{ - g_object_weak_unref (key, body_ostream_done, data); + g_mutex_init (&priv->mutex); } static void @@ -270,13 +256,9 @@ SoupLogger *logger = SOUP_LOGGER (object); SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger); - g_hash_table_foreach (priv->request_messages, - body_ostream_drop_ref, priv); - g_hash_table_destroy (priv->ids); g_hash_table_destroy (priv->request_bodies); g_hash_table_destroy (priv->response_bodies); - g_hash_table_destroy (priv->request_messages); if (priv->request_filter_dnotify) priv->request_filter_dnotify (priv->request_filter_data); @@ -285,6 +267,8 @@ if (priv->printer_dnotify) priv->printer_dnotify (priv->printer_data); + g_mutex_clear (&priv->mutex); + G_OBJECT_CLASS (soup_logger_parent_class)->finalize (object); } @@ -341,8 +325,7 @@ /** * SoupLogger:level: * - * The level of logging output - * + * The level of logging output. */ propertiesPROP_LEVEL = g_param_spec_enum ("level", @@ -353,12 +336,11 @@ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** - * SoupLogger:max-body-size: + * SoupLogger:max-body-size: (attributes org.gtk.Property.get=soup_logger_get_max_body_size org.gtk.Property.set=soup_logger_set_max_body_size) * - * If #SoupLogger:level is %SOUP_LOGGER_LOG_BODY, this gives + * If property@Logger:level is %SOUP_LOGGER_LOG_BODY, this gives * the maximum number of bytes of the body that will be logged. * (-1 means "no limit".) - * **/ propertiesPROP_MAX_BODY_SIZE = g_param_spec_int ("max-body-size", @@ -378,7 +360,7 @@ * SoupLoggerLogLevel: * @SOUP_LOGGER_LOG_NONE: No logging * @SOUP_LOGGER_LOG_MINIMAL: Log the Request-Line or Status-Line and - * the Soup-Debug pseudo-headers + * the Soup-Debug pseudo-headers * @SOUP_LOGGER_LOG_HEADERS: Log the full request/response headers * @SOUP_LOGGER_LOG_BODY: Log the full headers and request/response bodies * @@ -392,8 +374,8 @@ * Creates a new #SoupLogger with the given debug level. * * If you need finer control over what message parts are and aren't - * logged, use soup_logger_set_request_filter() and - * soup_logger_set_response_filter(). + * logged, use method@Logger.set_request_filter and + * method@Logger.set_response_filter. * * Returns: a new #SoupLogger **/ @@ -407,16 +389,17 @@ * SoupLoggerFilter: * @logger: the #SoupLogger * @msg: the message being logged - * @user_data: the data passed to soup_logger_set_request_filter() - * or soup_logger_set_response_filter() + * @user_data: the data passed to method@Logger.set_request_filter + * or method@Logger.set_response_filter * - * The prototype for a logging filter. The filter callback will be - * invoked for each request or response, and should analyze it and - * return a #SoupLoggerLogLevel value indicating how much of the - * message to log. + * The prototype for a logging filter. * - * Returns: a #SoupLoggerLogLevel value indicating how much of - * the message to log + * The filter callback will be invoked for each request or response, and should + * analyze it and return a enum@LoggerLogLevel value indicating how much of + * the message to log. + * + * Returns: a enum@LoggerLogLevel value indicating how much of the message to + * log **/ /** @@ -427,10 +410,11 @@ * @destroy: a #GDestroyNotify to free @filter_data * * Sets up a filter to determine the log level for a given request. + * * For each HTTP request @logger will invoke @request_filter to * determine how much (if any) of that request to log. (If you do not * set a request filter, @logger will just always log requests at the - * level passed to soup_logger_new().) + * level passed to ctor@Logger.new.) **/ void soup_logger_set_request_filter (SoupLogger *logger, @@ -453,10 +437,11 @@ * @destroy: a #GDestroyNotify to free @filter_data * * Sets up a filter to determine the log level for a given response. + * * For each HTTP response @logger will invoke @response_filter to * determine how much (if any) of that response to log. (If you do not * set a response filter, @logger will just always log responses at - * the level passed to soup_logger_new().) + * the level passed to ctor@Logger.new.) **/ void soup_logger_set_response_filter (SoupLogger *logger, @@ -477,7 +462,7 @@ * @level: the level of the information being printed. * @direction: a single-character prefix to @data * @data: data to print - * @user_data: the data passed to soup_logger_set_printer() + * @user_data: the data passed to method@Logger.set_printer * * The prototype for a custom printing callback. * @@ -489,9 +474,9 @@ * * To get the effect of the default printer, you would do: * - * <informalexample><programlisting> - * printf ("%c %s\n", direction, data); - * </programlisting></informalexample> + * ```c + * printf ("%c %s\n", direction, data); + * ``` **/ /** @@ -502,7 +487,7 @@ * @destroy: a #GDestroyNotify to free @printer_data * * Sets up an alternate log printing routine, if you don't want - * the log to go to <literal>stdout</literal>. + * the log to go to `stdout`. **/ void soup_logger_set_printer (SoupLogger *logger, @@ -518,7 +503,7 @@ } /** - * soup_logger_set_max_body_size: + * soup_logger_set_max_body_size: (attributes org.gtk.Method.set_property=max-body-size) * @logger: a #SoupLogger * @max_body_size: the maximum body size to log * @@ -533,7 +518,7 @@ } /** - * soup_logger_get_max_body_size: + * soup_logger_get_max_body_size: (attributes org.gtk.Method.get_property=max-body-size) * @logger: a #SoupLogger * * Get the maximum body size for @logger. @@ -563,9 +548,11 @@ gpointer klass = G_OBJECT_GET_CLASS (object); gpointer id; + g_mutex_lock (&priv->mutex); id = g_hash_table_lookup (priv->ids, klass); id = (char *)id + 1; g_hash_table_insert (priv->ids, klass, id); + g_mutex_unlock (&priv->mutex); g_object_set_qdata (object, priv->tag, id); return GPOINTER_TO_UINT (id); @@ -772,6 +759,7 @@ finished (SoupMessage *msg, gpointer user_data) { SoupLogger *logger = user_data; + SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger); /* Do not print the response if we didn't print a request. This can happen if * msg is a preconnect request, for example. @@ -779,8 +767,10 @@ if (!soup_logger_get_id (logger, msg)) return; + g_mutex_lock (&priv->mutex); print_response (logger, msg); soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n"); + g_mutex_unlock (&priv->mutex); } static void @@ -791,6 +781,8 @@ SoupLoggerLogLevel log_level; GString *body = NULL; + g_mutex_lock (&priv->mutex); + if (priv->response_filter) log_level = priv->response_filter (logger, msg, priv->response_filter_data); @@ -801,8 +793,10 @@ print_response (logger, msg); soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n"); - if (!g_hash_table_steal_extended (priv->response_bodies, msg, NULL, (gpointer *)&body)) + if (!g_hash_table_steal_extended (priv->response_bodies, msg, NULL, (gpointer *)&body)) { + g_mutex_unlock (&priv->mutex); return; + } if (soup_message_get_status (msg) == SOUP_STATUS_CONTINUE) { soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>', @@ -817,57 +811,24 @@ } g_string_free (body, TRUE); + + g_mutex_unlock (&priv->mutex); } static void got_body (SoupMessage *msg, gpointer user_data) { SoupLogger *logger = user_data; + SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger); + + g_mutex_lock (&priv->mutex); g_signal_handlers_disconnect_by_func (msg, finished, logger); print_response (logger, msg); soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n"); -} - -static void -body_stream_wrote_data_cb (GOutputStream *stream, - const void *buffer, - guint count, - gboolean is_metadata, - SoupLogger *logger) -{ - SoupLoggerPrivate *priv; - SoupMessage *msg; - - if (is_metadata) - return; - - priv = soup_logger_get_instance_private (logger); - msg = g_hash_table_lookup (priv->request_messages, stream); - write_body (logger, buffer, count, msg, priv->request_bodies); -} - -static void -body_ostream_done (gpointer data, GObject *bostream) -{ - SoupLoggerPrivate *priv = data; - g_hash_table_remove (priv->request_messages, bostream); -} - -void -soup_logger_request_body_setup (SoupLogger *logger, - SoupMessage *msg, - SoupBodyOutputStream *stream) -{ - SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger); - - g_hash_table_insert (priv->request_messages, stream, msg); - g_signal_connect_object (stream, "wrote-data", - G_CALLBACK (body_stream_wrote_data_cb), - logger, 0); - g_object_weak_ref (G_OBJECT (stream), body_ostream_done, priv); + g_mutex_unlock (&priv->mutex); } static void @@ -878,7 +839,7 @@ gboolean restarted; guint msg_id; SoupConnection *conn; - GSocket *socket; + GSocket *socket = NULL; msg_id = soup_logger_get_id (logger, msg); if (msg_id) @@ -892,12 +853,17 @@ soup_logger_set_id (logger, priv->session); conn = soup_message_get_connection (msg); - socket = conn ? soup_connection_get_socket (conn) : NULL; + if (conn) { + socket = soup_connection_get_socket (conn); + g_object_unref (conn); + } if (socket && !soup_logger_get_id (logger, socket)) soup_logger_set_id (logger, socket); + g_mutex_lock (&priv->mutex); print_request (logger, msg, socket, restarted); soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n"); + g_mutex_unlock (&priv->mutex); } static void
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-message-headers.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-message-headers.c
Changed
@@ -16,15 +16,6 @@ #include "soup-misc.h" /** - * SECTION:soup-message-headers - * @short_description: HTTP message headers - * @see_also: #SoupMessage - * - * #SoupMessageHeaders represents the HTTP message headers associated - * with a request or response. - **/ - -/** * SoupMessageHeaders: * * The HTTP message headers associated with a request or response. @@ -36,7 +27,7 @@ * @SOUP_MESSAGE_HEADERS_RESPONSE: response headers * @SOUP_MESSAGE_HEADERS_MULTIPART: multipart body part headers * - * Value passed to soup_message_headers_new() to set certain default + * Value passed to ctor@MessageHeaders.new to set certain default * behaviors. **/ @@ -71,9 +62,11 @@ * soup_message_headers_new: * @type: the type of headers * - * Creates a #SoupMessageHeaders. (#SoupMessage does this - * automatically for its own headers. You would only need to use this - * method if you are manually parsing or generating message headers.) + * Creates a #SoupMessageHeaders. + * + * (class@Message does this automatically for its own headers. You would only + * need to use this method if you are manually parsing or generating message + * headers.) * * Returns: a new #SoupMessageHeaders **/ @@ -122,6 +115,7 @@ * @hdrs: a #SoupMessageHeaders * * Atomically decrements the reference count of @hdrs by one. + * * When the reference count reaches zero, the resources allocated by * @hdrs are freed */ @@ -140,7 +134,6 @@ * Gets the type of headers. * * Returns: the header's type. - * **/ SoupMessageHeadersType soup_message_headers_get_headers_type (SoupMessageHeaders *hdrs) @@ -256,7 +249,6 @@ * @hdrs: a #SoupMessageHeaders * * Removes all the headers listed in the Connection header. - * */ void soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs) @@ -300,10 +292,11 @@ * @name: the header name to add * @value: the new value of @name * - * Appends a new header with name @name and value @value to @hdrs. (If - * there is an existing header with name @name, then this creates a - * second one, which is only allowed for list-valued headers; see also - * soup_message_headers_replace().) + * Appends a new header with name @name and value @value to @hdrs. + * + * (If there is an existing header with name @name, then this creates a second + * one, which is only allowed for list-valued headers; see also + * method@MessageHeaders.replace.) * * The caller is expected to make sure that @name and @value are * syntactically correct. @@ -383,8 +376,9 @@ * @name: the header name to replace * @value: the new value of @name * - * Replaces the value of the header @name in @hdrs with @value. (See - * also soup_message_headers_append().) + * Replaces the value of the header @name in @hdrs with @value. + * + * See also method@MessageHeaders.append. * * The caller is expected to make sure that @name and @value are * syntactically correct. @@ -493,8 +487,9 @@ * @hdrs: a #SoupMessageHeaders * @name: the header name to remove * - * Removes @name from @hdrs. If there are multiple values for @name, - * they are all removed. + * Removes @name from @hdrs. + * + * If there are multiple values for @name, they are all removed. **/ void soup_message_headers_remove (SoupMessageHeaders *hdrs, const char *name) @@ -547,10 +542,11 @@ * @hdrs: a #SoupMessageHeaders * @name: (in): header name * - * Gets the value of header @name in @hdrs. Use this for headers whose - * values are <emphasis>not</emphasis> comma-delimited lists, and - * which therefore can only appear at most once in the headers. For - * list-valued headers, use soup_message_headers_get_list(). + * Gets the value of header @name in @hdrs. + * + * Use this for headers whose values are *not* comma-delimited lists, and which + * therefore can only appear at most once in the headers. For list-valued + * headers, use method@MessageHeaders.get_list. * * If @hdrs does erroneously contain multiple copies of the header, it * is not defined which one will be returned. (Ideally, it will return @@ -558,7 +554,6 @@ * implementations.) * * Returns: (nullable) (transfer none): the header's value or %NULL if not found. - * **/ const char * soup_message_headers_get_one (SoupMessageHeaders *hdrs, const char *name) @@ -603,11 +598,10 @@ * and contains a case-insensitive match for @token. * * (If @name is present in @hdrs, then this is equivalent to calling - * soup_header_contains() on its value.) + * func@header_contains on its value.) * * Returns: %TRUE if the header is present and contains @token, * %FALSE otherwise. - * **/ gboolean soup_message_headers_header_contains (SoupMessageHeaders *hdrs, const char *name, const char *token) @@ -642,7 +636,6 @@ * * Returns: %TRUE if the header is present and its value is * @value, %FALSE otherwise. - * **/ gboolean soup_message_headers_header_equals (SoupMessageHeaders *hdrs, const char *name, const char *value) @@ -700,13 +693,14 @@ * @hdrs: a #SoupMessageHeaders * @name: header name * - * Gets the value of header @name in @hdrs. Use this for headers whose - * values are comma-delimited lists, and which are therefore allowed - * to appear multiple times in the headers. For non-list-valued - * headers, use soup_message_headers_get_one(). + * Gets the value of header @name in @hdrs. + * + * Use this for headers whose values are comma-delimited lists, and which are + * therefore allowed to appear multiple times in the headers. For + * non-list-valued headers, use method@MessageHeaders.get_one. * * If @name appears multiple times in @hdrs, - * soup_message_headers_get_list() will concatenate all of the values + * method@MessageHeaders.get_list will concatenate all of the values * together, separated by commas. This is sometimes awkward to parse * (eg, WWW-Authenticate, Set-Cookie), but you have to be able to deal * with it anyway, because the HTTP spec explicitly states that this @@ -714,7 +708,6 @@ * same thing. * * Returns: (nullable) (transfer none): the header's value or %NULL if not found. - * **/ const char * soup_message_headers_get_list (SoupMessageHeaders *hdrs, const char *name) @@ -770,9 +763,8 @@ * An opaque type used to iterate over a %SoupMessageHeaders * structure. * - * After intializing the iterator with - * soup_message_headers_iter_init(), call - * soup_message_headers_iter_next() to fetch data from it. + * After intializing the iterator with func@MessageHeadersIter.init, call + * method@MessageHeadersIter.next to fetch data from it. * * You may not modify the headers while iterating over them. **/ @@ -786,7 +778,7 @@ /** * soup_message_headers_iter_init: * @iter: (out) (transfer none): a pointer to a %SoupMessageHeadersIter - * structure + * structure * @hdrs: a %SoupMessageHeaders * * Initializes @iter for iterating @hdrs. @@ -806,17 +798,19 @@ * soup_message_headers_iter_next: * @iter: (inout) (transfer none): a %SoupMessageHeadersIter * @name: (out) (transfer none): pointer to a variable to return - * the header name in + * the header name in * @value: (out) (transfer none): pointer to a variable to return - * the header value in + * the header value in * - * Yields the next name/value pair in the %SoupMessageHeaders being - * iterated by @iter. If @iter has already yielded the last header, - * then soup_message_headers_iter_next() will return %FALSE and @name - * and @value will be unchanged. + * Yields the next name/value pair in the struct@MessageHeaders being + * iterated by @iter. + * + * If @iter has already yielded the last header, then + * method@MessageHeadersIter.next will return %FALSE and @name and @value + * will be unchanged. * * Returns: %TRUE if another name and value were returned, %FALSE - * if the end of the headers has been reached. + * if the end of the headers has been reached. **/ gboolean soup_message_headers_iter_next (SoupMessageHeadersIter *iter, @@ -851,9 +845,9 @@ * SoupMessageHeadersForeachFunc: * @name: the header name * @value: the header value - * @user_data: the data passed to soup_message_headers_foreach() + * @user_data: the data passed to method@MessageHeaders.foreach * - * The callback passed to soup_message_headers_foreach(). + * The callback passed to method@MessageHeaders.foreach. **/ /** @@ -864,11 +858,11 @@ * * Calls @func once for each header value in @hdrs. * - * Beware that unlike soup_message_headers_get_list(), this processes the + * Beware that unlike method@MessageHeaders.get_list, this processes the * headers in exactly the way they were added, rather than * concatenating multiple same-named headers into a single value. * (This is intentional; it ensures that if you call - * soup_message_headers_append() multiple times with the same name, + * method@MessageHeaders.append multiple times with the same name, * then the I/O code will output multiple copies of the header when * sending the message to the remote implementation, which may be * required for interoperability in some cases.) @@ -903,13 +897,13 @@ * SoupEncoding: * @SOUP_ENCODING_UNRECOGNIZED: unknown / error * @SOUP_ENCODING_NONE: no body is present (which is not the same as a - * 0-length body, and only occurs in certain places) + * 0-length body, and only occurs in certain places) * @SOUP_ENCODING_CONTENT_LENGTH: Content-Length encoding * @SOUP_ENCODING_EOF: Response body ends when the connection is closed * @SOUP_ENCODING_CHUNKED: chunked encoding (currently only supported - * for response) + * for response) * @SOUP_ENCODING_BYTERANGES: multipart/byteranges (Reserved for future - * use: NOT CURRENTLY IMPLEMENTED) + * use: NOT CURRENTLY IMPLEMENTED) * * How a message body is encoded for transport **/ @@ -918,10 +912,11 @@ * soup_message_headers_get_encoding: * @hdrs: a #SoupMessageHeaders * - * Gets the message body encoding that @hdrs declare. This may not - * always correspond to the encoding used on the wire; eg, a HEAD - * response may declare a Content-Length or Transfer-Encoding, but - * it will never actually include a body. + * Gets the message body encoding that @hdrs declare. + * + * This may not always correspond to the encoding used on the wire; eg, a HEAD + * response may declare a Content-Length or Transfer-Encoding, but it will never + * actually include a body. * * Returns: the encoding declared by @hdrs. **/ @@ -960,9 +955,10 @@ * @hdrs: a #SoupMessageHeaders * @encoding: a #SoupEncoding * - * Sets the message body encoding that @hdrs will declare. In particular, - * you should use this if you are going to send a request or response in - * chunked encoding. + * Sets the message body encoding that @hdrs will declare. + * + * In particular, you should use this if you are going to send a request or + * response in chunked encoding. **/ void soup_message_headers_set_encoding (SoupMessageHeaders *hdrs, @@ -998,8 +994,9 @@ * soup_message_headers_get_content_length: * @hdrs: a #SoupMessageHeaders * - * Gets the message body length that @hdrs declare. This will only - * be non-0 if soup_message_headers_get_encoding() returns + * Gets the message body length that @hdrs declare. + * + * This will only be non-0 if method@MessageHeaders.get_encoding returns * %SOUP_ENCODING_CONTENT_LENGTH. * * Returns: the message body length declared by @hdrs. @@ -1029,7 +1026,7 @@ * Content-Length header for you immediately before sending the * headers. One situation in which this method is useful is when * generating the response to a HEAD request; Calling - * soup_message_headers_set_content_length() allows you to put the + * method@MessageHeaders.set_content_length allows you to put the * correct content length into the response without needing to waste * memory by filling in a response body which won't actually be sent. **/ @@ -1058,6 +1055,7 @@ * @hdrs: a #SoupMessageHeaders * * Gets the expectations declared by @hdrs's "Expect" header. + * * Currently this will either be %SOUP_EXPECTATION_CONTINUE or * %SOUP_EXPECTATION_UNRECOGNIZED. * @@ -1116,7 +1114,6 @@ * If @end is -1 and @start is negative, then it represents a "suffix * range", referring to the last -@start bytes of the resource body. * (Eg, the last 500 bytes would be @start = -500 and @end = -1.) - * **/ static int @@ -1229,24 +1226,24 @@ * @hdrs: a #SoupMessageHeaders * @total_length: the total_length of the response body * @ranges: (out) (array length=length): return location for an array - * of #SoupRange + * of #SoupRange * @length: the length of the returned array * * Parses @hdrs's Range header and returns an array of the requested - * byte ranges. The returned array must be freed with - * soup_message_headers_free_ranges(). + * byte ranges. + * + * The returned array must be freed with method@MessageHeaders.free_ranges. * * If @total_length is non-0, its value will be used to adjust the * returned ranges to have explicit start and end values, and the * returned ranges will be sorted and non-overlapping. If * @total_length is 0, then some ranges may have an end value of -1, - * as described under #SoupRange, and some of the ranges may be + * as described under struct@Range, and some of the ranges may be * redundant. * * Beware that even if given a @total_length, this function does not * check that the ranges are satisfiable. * - * <note><para> * #SoupServer has built-in handling for range requests. If your * server handler returns a %SOUP_STATUS_OK response containing the * complete response body (rather than pausing the message and @@ -1260,12 +1257,10 @@ * it all at once, or you do not already have the complete response * body available, and only want to generate the parts that were * actually requested by the client. - * </para></note> * * Returns: %TRUE if @hdrs contained a syntactically-valid - * "Range" header, %FALSE otherwise (in which case @range and @length - * will not be set). - * + * "Range" header, %FALSE otherwise (in which case @range and @length + * will not be set). **/ gboolean soup_message_headers_get_ranges (SoupMessageHeaders *hdrs, @@ -1284,8 +1279,7 @@ * @hdrs: a #SoupMessageHeaders * @ranges: an array of #SoupRange * - * Frees the array of ranges returned from soup_message_headers_get_ranges(). - * + * Frees the array of ranges returned from method@MessageHeaders.get_ranges. **/ void soup_message_headers_free_ranges (SoupMessageHeaders *hdrs, @@ -1300,10 +1294,10 @@ * @ranges: an array of #SoupRange * @length: the length of @range * - * Sets @hdrs's Range header to request the indicated ranges. (If you - * only want to request a single range, you can use - * soup_message_headers_set_range().) + * Sets @hdrs's Range header to request the indicated ranges. * + * If you only want to request a single range, you can use + * method@MessageHeaders.set_range. **/ void soup_message_headers_set_ranges (SoupMessageHeaders *hdrs, @@ -1340,11 +1334,11 @@ * @end: the end of the range to request * * Sets @hdrs's Range header to request the indicated range. - * @start and @end are interpreted as in a #SoupRange. * - * If you need to request multiple ranges, use - * soup_message_headers_set_ranges(). + * @start and @end are interpreted as in a struct@Range. * + * If you need to request multiple ranges, use + * method@MessageHeaders.set_ranges. **/ void soup_message_headers_set_range (SoupMessageHeaders *hdrs, @@ -1364,15 +1358,14 @@ * @start: (out): return value for the start of the range * @end: (out): return value for the end of the range * @total_length: (out) (optional): return value for the total length of the - * resource, or %NULL if you don't care. + * resource, or %NULL if you don't care. * * Parses @hdrs's Content-Range header and returns it in @start, * @end, and @total_length. If the total length field in the header * was specified as "*", then @total_length will be set to -1. * * Returns: %TRUE if @hdrs contained a "Content-Range" header - * containing a byte range which could be parsed, %FALSE otherwise. - * + * containing a byte range which could be parsed, %FALSE otherwise. **/ gboolean soup_message_headers_get_content_range (SoupMessageHeaders *hdrs, @@ -1419,15 +1412,13 @@ * @total_length: the total length of the resource, or -1 if unknown * * Sets @hdrs's Content-Range header according to the given values. + * * (Note that @total_length is the total length of the entire resource * that this is a range of, not simply @end - @start + 1.) * - * <note><para> - * #SoupServer has built-in handling for range requests, and you do + * class@Server has built-in handling for range requests, and you do * not normally need to call this function youself. See - * soup_message_headers_get_ranges() for more details. - * </para></note> - * + * method@MessageHeaders.get_ranges for more details. **/ void soup_message_headers_set_content_range (SoupMessageHeaders *hdrs, @@ -1532,14 +1523,14 @@ * %NULL * * Looks up the "Content-Type" header in @hdrs, parses it, and returns - * its value in *@content_type and *@params. @params can be %NULL if you - * are only interested in the content type itself. + * its value in *@content_type and *@params. * - * Returns: (nullable): a string with the value of the - * "Content-Type" header or %NULL if @hdrs does not contain that - * header or it cannot be parsed (in which case *@params will be - * unchanged). + * @params can be %NULL if you are only interested in the content type itself. * + * Returns: (nullable): a string with the value of the + * "Content-Type" header or %NULL if @hdrs does not contain that + * header or it cannot be parsed (in which case *@params will be + * unchanged). **/ const char * soup_message_headers_get_content_type (SoupMessageHeaders *hdrs, @@ -1557,12 +1548,11 @@ * soup_message_headers_set_content_type: * @hdrs: a #SoupMessageHeaders * @content_type: the MIME type - * @params: (nullable) (element-type utf8 utf8): additional - * parameters, or %NULL + * @params: (nullable) (element-type utf8 utf8): additional parameters * - * Sets the "Content-Type" header in @hdrs to @content_type, - * optionally with additional parameters specified in @params. + * Sets the "Content-Type" header in @hdrs to @content_type. * + * Accepts additional parameters specified in @params. **/ void soup_message_headers_set_content_type (SoupMessageHeaders *hdrs, @@ -1576,13 +1566,14 @@ * soup_message_headers_get_content_disposition: * @hdrs: a #SoupMessageHeaders * @disposition: (out) (transfer full): return location for the - * disposition-type, or %NULL + * disposition-type, or %NULL * @params: (out) (transfer full) (element-type utf8 utf8): return - * location for the Content-Disposition parameters, or %NULL + * location for the Content-Disposition parameters, or %NULL * * Looks up the "Content-Disposition" header in @hdrs, parses it, and - * returns its value in *@disposition and *@params. @params can be - * %NULL if you are only interested in the disposition-type. + * returns its value in *@disposition and *@params. + * + * @params can be %NULL if you are only interested in the disposition-type. * * In HTTP, the most common use of this header is to set a * disposition-type of "attachment", to suggest to the browser that a @@ -1594,13 +1585,12 @@ * test this yourself.) * * Content-Disposition is also used in "multipart/form-data", however - * this is handled automatically by #SoupMultipart and the associated + * this is handled automatically by struct@Multipart and the associated * form methods. * * Returns: %TRUE if @hdrs contains a "Content-Disposition" - * header, %FALSE if not (in which case *@disposition and *@params - * will be unchanged). - * + * header, %FALSE if not (in which case *@disposition and *@params + * will be unchanged). **/ gboolean soup_message_headers_get_content_disposition (SoupMessageHeaders *hdrs, @@ -1630,15 +1620,13 @@ * soup_message_headers_set_content_disposition: * @hdrs: a #SoupMessageHeaders * @disposition: the disposition-type - * @params: (nullable) (element-type utf8 utf8): additional - * parameters, or %NULL + * @params: (nullable) (element-type utf8 utf8): additional parameters * * Sets the "Content-Disposition" header in @hdrs to @disposition, * optionally with additional parameters specified in @params. * - * See soup_message_headers_get_content_disposition() for a discussion + * See method@MessageHeaders.get_content_disposition for a discussion * of how Content-Disposition is used in HTTP. - * **/ void soup_message_headers_set_content_disposition (SoupMessageHeaders *hdrs,
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-message-metrics.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-message-metrics.c
Changed
@@ -12,29 +12,21 @@ #include "soup-message-metrics-private.h" /** - * SECTION:soup-message-metrics - * @short_description: Message metrics - * @see_also: #SoupMessage + * SoupMessageMetrics: * - * Metrics collected while loading a #SoupMessage. + * Contains metrics collected while loading a class@Message either from the + * network or the disk cache. * - * Metrics are not collected by default for a #SoupMessage, you need to add the + * Metrics are not collected by default for a class@Message, you need to add the * flag %SOUP_MESSAGE_COLLECT_METRICS to enable the feature. - */ - -/** - * SoupMessageMetrics: - * - * SoupMessageMetrics contains metrics collected while loading a #SoupMessage - * either from the network or the disk cache. * * Temporal metrics are expressed as a monotonic time and always start with a * fetch start event and finish with response end. All other events are optional. * An event can be 0 because it hasn't happened yet, because it's optional or * because the load failed before the event reached. * - * Size metrics are expressed in bytes and aree updated while the #SoupMessage is - * being loaded. You can connect to different #SoupMessage signals to get the + * Size metrics are expressed in bytes and aree updated while the class@Message is + * being loaded. You can connect to different class@Message signals to get the * final result of every value. */ @@ -53,7 +45,6 @@ * Copies @metrics. * * Returns: a copy of @metrics - * **/ SoupMessageMetrics * soup_message_metrics_copy (SoupMessageMetrics *metrics) @@ -72,7 +63,7 @@ * soup_message_metrics_free: * @metrics: a #SoupMessageMetrics * - * Frees @metrics + * Frees @metrics. */ void soup_message_metrics_free (SoupMessageMetrics *metrics) @@ -86,7 +77,7 @@ * soup_message_metrics_get_fetch_start: * @metrics: a #SoupMessageMetrics * - * Get the time immediately before the #SoupMessage started to + * Get the time immediately before the class@Message started to * fetch a resource either from a remote server or local disk cache. * * Returns: the fetch start time @@ -103,10 +94,12 @@ * soup_message_metrics_get_dns_start: * @metrics: a #SoupMessageMetrics * - * Get the time immediately before the #SoupMessage started the - * domain lookup name for the resource. It will be 0 if no domain - * lookup was required to fetch the resource (a persistent connection - * was used or resource was loaded from the local disk cache). + * Get the time immediately before the class@Message started the + * domain lookup name for the resource. + * + * It will be 0 if no domain lookup was required to fetch the resource (a + * persistent connection was used or resource was loaded from the local disk + * cache). * * Returns: the domain lookup start time */ @@ -122,10 +115,12 @@ * soup_message_metrics_get_dns_end: * @metrics: a #SoupMessageMetrics * - * Get the time immediately after the #SoupMessage completed the - * domain lookup name for the resource. It will be 0 if no domain - * lookup was required to fetch the resource (a persistent connection - * was used or resource was loaded from the local disk cache). + * Get the time immediately after the class@Message completed the + * domain lookup name for the resource. + * + * It will be 0 if no domain lookup was required to fetch the resource (a + * persistent connection was used or resource was loaded from the local disk + * cache). * * Returns: the domain lookup end time */ @@ -141,10 +136,12 @@ * soup_message_metrics_get_connect_start: * @metrics: a #SoupMessageMetrics * - * Get the time immediately before the #SoupMessage started to - * establish the connection to the server. It will be 0 if no - * network connection was required to fetch the resource (a persistent - * connection was used or resource was loaded from the local disk cache). + * Get the time immediately before the class@Message started to + * establish the connection to the server. + * + * It will be 0 if no network connection was required to fetch the resource (a + * persistent connection was used or resource was loaded from the local disk + * cache). * * Returns: the connection start time */ @@ -160,11 +157,13 @@ * soup_message_metrics_get_connect_end: * @metrics: a #SoupMessageMetrics * - * Get the time immediately after the #SoupMessage completed the + * Get the time immediately after the class@Message completed the * connection to the server. This includes the time for the proxy - * negotiation and TLS handshake. It will be 0 if no network connection - * was required to fetch the resource (a persistent connection was used - * or resource was loaded from the local disk cache). + * negotiation and TLS handshake. + * + * It will be 0 if no network connection was required to fetch the resource (a + * persistent connection was used or resource was loaded from the local disk + * cache). * * Returns: the connection end time */ @@ -180,10 +179,12 @@ * soup_message_metrics_get_tls_start: * @metrics: a #SoupMessageMetrics * - * Get the time immediately before the #SoupMessage started the - * TLS handshake. It will be 0 if no TLS handshake was required - * to fetch the resource (connection was not secure, a persistent - * connection was used or resource was loaded from the local disk cache). + * Get the time immediately before the class@Message started the + * TLS handshake. + * + * It will be 0 if no TLS handshake was required to fetch the resource + * (connection was not secure, a persistent connection was used or resource was + * loaded from the local disk cache). * * Returns: the tls start time */ @@ -199,7 +200,7 @@ * soup_message_metrics_get_request_start: * @metrics: a #SoupMessageMetrics * - * Get the time immediately before the #SoupMessage started the + * Get the time immediately before the class@Message started the * request of the resource from the server or the local disk cache. * * Returns: the request start time @@ -216,7 +217,7 @@ * soup_message_metrics_get_response_start: * @metrics: a #SoupMessageMetrics * - * Get the time immediately after the #SoupMessage received the first + * Get the time immediately after the class@Message received the first * bytes of the response from the server or the local disk cache. * * Returns: the response start time @@ -233,8 +234,9 @@ * soup_message_metrics_get_response_end: * @metrics: a #SoupMessageMetrics * - * Get the time immediately after the #SoupMessage received the last + * Get the time immediately after the class@Message received the last * bytes of the response from the server or the local disk cache. + * * In case of load failure, this returns the time immediately before the * fetch is aborted. * @@ -253,7 +255,8 @@ * @metrics: a #SoupMessageMetrics * * Get the number of bytes sent to the network for the request headers. - * This value is available right before #SoupMessage::wrote-headers signal + * + * This value is available right before signal@Message::wrote-headers signal * is emitted, but you might get an intermediate value if called before. * * Returns: the request headers bytes sent @@ -271,9 +274,10 @@ * @metrics: a #SoupMessageMetrics * * Get the request body size in bytes. This is the size of the original body - * given to the request before any encoding is applied. This value is available - * right before #SoupMessage::wrote-body signal is emitted, but you might get - * an intermediate value if called before. + * given to the request before any encoding is applied. + * + * This value is available right before signal@Message::wrote-body signal is + * emitted, but you might get an intermediate value if called before. * * Returns: the request body size */ @@ -289,11 +293,13 @@ * soup_message_metrics_get_request_body_bytes_sent: * @metrics: a #SoupMessageMetrics * - * Get the number of bytes sent to the network for the request body. This is - * the size of the body sent, after encodings are applied, so it might be - * greater than the value returned by soup_message_metrics_get_request_body_size(). - * This value is available right before #SoupMessage::wrote-body signal is - * emitted, but you might get an intermediate value if called before. + * Get the number of bytes sent to the network for the request body. + * + * This is the size of the body sent, after encodings are applied, so it might + * be greater than the value returned by + * method@MessageMetrics.get_request_body_size. This value is available right + * before signal@Message::wrote-body signal is emitted, but you might get an + * intermediate value if called before. * * Returns: the request body bytes sent */ @@ -310,7 +316,8 @@ * @metrics: a #SoupMessageMetrics * * Get the number of bytes received from the network for the response headers. - * This value is available right before #SoupMessage::got-headers signal + * + * This value is available right before signal@Message::got-headers signal * is emitted, but you might get an intermediate value if called before. * For resources loaded from the disk cache this value is always 0. * @@ -328,11 +335,13 @@ * soup_message_metrics_get_response_body_size: * @metrics: a #SoupMessageMetrics * - * Get the response body size in bytes. This is the size of the body as given to the - * user after all encodings are applied, so it might be greater than the value - * returned by soup_message_metrics_get_response_body_bytes_received(). This value is - * available right before #SoupMessage::got-body signal is emitted, but you might get - * an intermediate value if called before. + * Get the response body size in bytes. + * + * This is the size of the body as given to the user after all encodings are + * applied, so it might be greater than the value returned by + * method@MessageMetrics.get_response_body_bytes_received. This value is + * available right before signal@Message::got-body signal is emitted, but you + * might get an intermediate value if called before. * * Returns: the response body size */ @@ -348,10 +357,11 @@ * soup_message_metrics_get_response_body_bytes_received: * @metrics: a #SoupMessageMetrics * - * Get the number of bytes received from the network for the response body. This value is - * available right before #SoupMessage::got-body signal is emitted, but you might get - * an intermediate value if called before. - * For resources loaded from the disk cache this value is always 0. + * Get the number of bytes received from the network for the response body. + * + * This value is available right before signal@Message::got-body signal is + * emitted, but you might get an intermediate value if called before. For + * resources loaded from the disk cache this value is always 0. * * Returns: the response body bytes received */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-message-private.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-message-private.h
Changed
@@ -147,10 +147,10 @@ void soup_message_force_keep_alive_if_needed (SoupMessage *msg); -void soup_message_set_force_http1 (SoupMessage *msg, - gboolean force_http1); +void soup_message_set_force_http_version (SoupMessage *msg, + guint8 version); -gboolean soup_message_get_force_http1 (SoupMessage *msg); +guint8 soup_message_get_force_http_version (SoupMessage *msg); void soup_message_set_is_misdirected_retry (SoupMessage *msg, gboolean is_misdirected_retry);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-message-queue-item.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-message-queue-item.c
Changed
@@ -24,6 +24,7 @@ item = g_atomic_rc_box_new0 (SoupMessageQueueItem); item->session = g_object_ref (session); item->msg = g_object_ref (msg); + item->context = g_main_context_ref_thread_default (); item->async = async; item->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new (); @@ -41,11 +42,13 @@ static void soup_message_queue_item_destroy (SoupMessageQueueItem *item) { - if (!g_error_matches (item->error, SOUP_SESSION_ERROR, SOUP_SESSION_ERROR_MESSAGE_ALREADY_IN_QUEUE)) + if (!g_error_matches (item->error, SOUP_SESSION_ERROR, SOUP_SESSION_ERROR_MESSAGE_ALREADY_IN_QUEUE)) { g_warn_if_fail (soup_message_get_connection (item->msg) == NULL); + } g_object_unref (item->session); g_object_unref (item->msg); + g_main_context_unref (item->context); g_object_unref (item->cancellable); g_clear_error (&item->error); g_clear_object (&item->task);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-message-queue-item.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-message-queue-item.h
Changed
@@ -29,6 +29,7 @@ struct _SoupMessageQueueItem { SoupSession *session; SoupMessage *msg; + GMainContext *context; GCancellable *cancellable; GError *error;
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-message.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-message.c
Changed
@@ -21,39 +21,30 @@ #include "content-sniffer/soup-content-sniffer-stream.h" /** - * SECTION:soup-message - * @short_description: An HTTP request and response. - * @see_also: #SoupMessageHeaders + * SoupMessage: + * + * Represents an HTTP message being sent or received. * * A #SoupMessage represents an HTTP message that is being sent or * received. * - * You would create a #SoupMessage with soup_message_new() or - * soup_message_new_from_uri(), set up its - * fields appropriately, and send it. + * You would create a #SoupMessage with ctor@Message.new or + * ctor@Message.new_from_uri, set up its fields appropriately, and send it. * - * Note that libsoup's terminology here does not quite match the HTTP - * specification: in RFC 2616, an "HTTP-message" is - * <emphasis>either</emphasis> a Request, <emphasis>or</emphasis> a - * Response. In libsoup, a #SoupMessage combines both the request and - * the response. - **/ - -/** - * SoupMessage: + * property@Message:status-code will normally be a enum@Status value, eg, + * %SOUP_STATUS_OK, though of course it might actually be an unknown status + * code. property@Message:reason-phrase is the actual text returned from the + * server, which may or may not correspond to the "standard" description of + * @status_code. At any rate, it is almost certainly not localized, and not very + * descriptive even if it is in the user's language; you should not use + * property@Message:reason-phrase in user-visible messages. Rather, you should + * look at property@Message:status-code, and determine an end-user-appropriate + * message based on that and on what you were trying to do. * - * Represents an HTTP message being sent or received. - * - * @status_code will normally be a #SoupStatus value, eg, - * %SOUP_STATUS_OK, though of course it might actually be an unknown - * status code. @reason_phrase is the actual text returned from the - * server, which may or may not correspond to the "standard" - * description of @status_code. At any rate, it is almost certainly - * not localized, and not very descriptive even if it is in the user's - * language; you should not use @reason_phrase in user-visible - * messages. Rather, you should look at @status_code, and determine an - * end-user-appropriate message based on that and on what you were - * trying to do. + * Note that libsoup's terminology here does not quite match the HTTP + * specification: in RFC 2616, an "HTTP-message" is *either* a Request, *or* a + * Response. In libsoup, a #SoupMessage combines both the request and the + * response. */ struct _SoupMessage { @@ -80,7 +71,7 @@ GUri *uri; SoupAuth *auth, *proxy_auth; - SoupConnection *connection; + GWeakRef connection; GHashTable *disabled_features; @@ -103,9 +94,9 @@ gboolean is_top_level_navigation; gboolean is_options_ping; gboolean is_preconnect; - gboolean force_http1; gboolean is_misdirected_retry; guint last_connection_id; + guint8 force_http_version; GSocketAddress *remote_address; SoupMessageMetrics *metrics; @@ -173,9 +164,12 @@ priv->http_version = priv->orig_http_version = SOUP_HTTP_1_1; priv->priority = SOUP_MESSAGE_PRIORITY_NORMAL; + priv->force_http_version = G_MAXUINT8; priv->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST); priv->response_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); + + g_weak_ref_init (&priv->connection, NULL); } static void @@ -197,6 +191,7 @@ g_clear_object (&priv->pending_tls_cert_password); soup_message_set_connection (msg, NULL); + g_weak_ref_clear (&priv->connection); g_clear_pointer (&priv->uri, g_uri_unref); g_clear_pointer (&priv->first_party, g_uri_unref); @@ -362,7 +357,6 @@ * * Emitted immediately after writing a portion of the message * body to the network. - * **/ signalsWROTE_BODY_DATA = g_signal_new ("wrote-body-data", @@ -395,10 +389,11 @@ * @msg: the message * * Emitted after receiving a 1xx (Informational) response for - * a (client-side) message. The response_headers will be - * filled in with the headers associated with the - * informational response; however, those header values will - * be erased after this signal is done. + * a (client-side) message. + * + * The response_headers will be filled in with the headers associated + * with the informational response; however, those header values will be + * erased after this signal is done. * * If you cancel or requeue @msg while processing this signal, * then the current HTTP I/O will be stopped after this signal @@ -419,17 +414,17 @@ * * Emitted after receiving the Status-Line and response headers. * - * See also soup_message_add_header_handler() and - * soup_message_add_status_code_handler(), which can be used - * to connect to a subset of emissions of this signal. + * See also method@Message.add_header_handler and + * method@Message.add_status_code_handler, which can be used to + * connect to a subset of emissions of this signal. * * If you cancel or requeue @msg while processing this signal, * then the current HTTP I/O will be stopped after this signal * emission finished, and @msg's connection will be closed. * (If you need to requeue a message--eg, after handling * authentication or redirection--it is usually better to - * requeue it from a #SoupMessage::got_body handler rather - * than a #SoupMessage::got_headers handler, so that the + * requeue it from a signal@Message::got-body handler rather + * than a signal@Message::got_headers handler, so that the * existing HTTP connection can be reused.) **/ signalsGOT_HEADERS = @@ -445,11 +440,7 @@ * SoupMessage::got-body: * @msg: the message * - * Emitted after receiving the complete message request body. - * - * See also soup_message_add_header_handler() and - * soup_message_add_status_code_handler(), which can be used - * to connect to a subset of emissions of this signal. + * Emitted after receiving the complete message response body. **/ signalsGOT_BODY = g_signal_new ("got-body", @@ -466,13 +457,13 @@ * @type: the content type that we got from sniffing * @params: (element-type utf8 utf8): a #GHashTable with the parameters * - * This signal is emitted after #SoupMessage::got-headers. + * This signal is emitted after signal@Message::got-headers. + * * If content sniffing is disabled, or no content sniffing will be * performed, due to the sniffer deciding to trust the * Content-Type sent by the server, this signal is emitted - * immediately after #SoupMessage::got-headers, and @type is + * immediately after signal@Message::got-headers, and @type is * %NULL. - * **/ signalsCONTENT_SNIFFED = g_signal_new ("content-sniffed", @@ -490,7 +481,6 @@ * @msg: the message * * Emitted just before a message is sent. - * */ signalsSTARTING = g_signal_new ("starting", @@ -506,9 +496,11 @@ * @msg: the message * * Emitted when a request that was already sent once is now - * being sent again (eg, because the first attempt received a + * being sent again. + * + * e.g. because the first attempt received a * redirection response, or because we needed to use - * authentication). + * authentication. **/ signalsRESTARTED = g_signal_new ("restarted", @@ -524,7 +516,8 @@ * @msg: the message * * Emitted when all HTTP processing is finished for a message. - * (After #SoupMessage::got_body). + * + * (After signal@Message::got_body). **/ signalsFINISHED = g_signal_new ("finished", @@ -541,22 +534,23 @@ * @auth: the #SoupAuth to authenticate * @retrying: %TRUE if this is the second (or later) attempt * - * Emitted when the message requires authentication. If - * credentials are available call soup_auth_authenticate() on - * @auth. If these credentials fail, the signal will be - * emitted again, with @retrying set to %TRUE, which will - * continue until you return without calling - * soup_auth_authenticate() on @auth. + * Emitted when the message requires authentication. + * + * If credentials are available call method@Auth.authenticate on + * @auth. If these credentials fail, the signal will be emitted again, + * with @retrying set to %TRUE, which will continue until you return + * without calling method@Auth.authenticate on @auth. * * Note that this may be emitted before @msg's body has been * fully read. * - * You can authenticate @auth asynchronously by calling g_object_ref() - * on @auth and returning %TRUE. The operation will complete once - * either soup_auth_authenticate() or soup_auth_cancel() are called. + * You can authenticate @auth asynchronously by calling + * method@GObject.Object.ref on @auth and returning %TRUE. The operation will + * complete once either method@Auth.authenticate or + * method@Auth.cancel are called. * * Returns: %TRUE to stop other handlers from being invoked - * or %FALSE to propagate the event further. + * or %FALSE to propagate the event further. **/ signalsAUTHENTICATE = g_signal_new ("authenticate", @@ -576,18 +570,17 @@ * @connection: the current state of the network connection * * Emitted to indicate that some network-related event - * related to @msg has occurred. This essentially proxies the - * #GSocketClient::event signal, but only for events that - * occur while @msg "owns" the connection; if @msg is sent on - * an existing persistent connection, then this signal will - * not be emitted. (If you want to force the message to be - * sent on a new connection, set the - * %SOUP_MESSAGE_NEW_CONNECTION flag on it.) + * related to @msg has occurred. + * + * This essentially proxies the signal@Gio.SocketClient::event signal, + * but only for events that occur while @msg "owns" the connection; if + * @msg is sent on an existing persistent connection, then this signal + * will not be emitted. (If you want to force the message to be sent on + * a new connection, set the %SOUP_MESSAGE_NEW_CONNECTION flag on it.) * - * See #GSocketClient::event for more information on what + * See signal@Gio.SocketClient::event for more information on what * the different values of @event correspond to, and what * @connection will be in each case. - * **/ signalsNETWORK_EVENT = g_signal_new ("network-event", @@ -608,12 +601,13 @@ * * Emitted during the @msg's connection TLS handshake * after an unacceptable TLS certificate has been received. + * * You can return %TRUE to accept @tls_certificate despite * @tls_errors. * * Returns: %TRUE to accept the TLS certificate and stop other - * handlers from being invoked, or %FALSE to propagate the - * event further. + * handlers from being invoked, or %FALSE to propagate the + * event further. */ signalsACCEPT_CERTIFICATE = g_signal_new ("accept-certificate", @@ -633,17 +627,18 @@ * * Emitted during the @msg's connection TLS handshake when * @tls_connection requests a certificate from the client. + * * You can set the client certificate by calling - * soup_message_set_tls_client_certificate() and returning %TRUE. - * It's possible to handle the request asynchornously by returning - * %TRUE and call soup_message_set_tls_client_certificate() later - * once the certificate is available. - * Note that this signal is not emitted if #SoupSession::tls-interaction - * was set, or if soup_message_set_tls_client_certificate() was called - * before the connection TLS handshake started. + * method@Message.set_tls_client_certificate and returning %TRUE. It's + * possible to handle the request asynchornously by returning %TRUE and + * call method@Message.set_tls_client_certificate later once the + * certificate is available. Note that this signal is not emitted if + * property@Session:tls-interaction was set, or if + * method@Message.set_tls_client_certificate was called before the + * connection TLS handshake started. * * Returns: %TRUE to handle the request, or %FALSE to make the connection - * fail with %G_TLS_ERROR_CERTIFICATE_REQUIRED. + * fail with %G_TLS_ERROR_CERTIFICATE_REQUIRED. */ signalsREQUEST_CERTIFICATE = g_signal_new ("request-certificate", @@ -662,18 +657,19 @@ * * Emitted during the @msg's connection TLS handshake when * @tls_connection requests a certificate password from the client. + * * You can set the certificate password on @password, then call - * soup_message_tls_client_certificate_password_request_complete() and return %TRUE - * to handle the signal synchronously. - * It's possible to handle the request asynchornously by calling g_object_ref() - * on @password, then returning %TRUE and call - * soup_message_tls_client_certificate_password_request_complete() later after - * setting the password on @password. - * Note that this signal is not emitted if #SoupSession::tls-interaction - * was set. + * method@Message.tls_client_certificate_password_request_complete and + * return %TRUE to handle the signal synchronously. It's possible to + * handle the request asynchornously by calling + * method@GObject.Object.ref on @password, then returning %TRUE and + * call + * method@Message.tls_client_certificate_password_request_complete + * later after setting the password on @password. Note that this signal + * is not emitted if property@Session:tls-interaction was set. * * Returns: %TRUE to handle the request, or %FALSE to make the connection - * fail with %G_TLS_ERROR_CERTIFICATE_REQUIRED. + * fail with %G_TLS_ERROR_CERTIFICATE_REQUIRED. */ signalsREQUEST_CERTIFICATE_PASSWORD = g_signal_new ("request-certificate-password", @@ -689,7 +685,7 @@ * SoupMessage::hsts-enforced: * @msg: the message * - * Emitted when #SoupHSTSEnforcer has upgraded the protocol + * Emitted when class@HSTSEnforcer has upgraded the protocol * for @msg to HTTPS as a result of matching its domain with * a HSTS policy. **/ @@ -702,6 +698,11 @@ NULL, G_TYPE_NONE, 0); + /** + * SoupMessage:method: (attributes org.gtk.Property.get=soup_message_get_method org.gtk.Property.set=soup_message_set_method) + * + * The message's HTTP method. + **/ /* properties */ propertiesPROP_METHOD = g_param_spec_string ("method", @@ -710,6 +711,11 @@ SOUP_METHOD_GET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * SoupMessage:uri: (attributes org.gtk.Property.get=soup_message_get_uri org.gtk.Property.set=soup_message_set_uri) + * + * The message's Request-URI. + **/ propertiesPROP_URI = g_param_spec_boxed ("uri", "URI", @@ -717,6 +723,11 @@ G_TYPE_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * SoupMessage:http-version: (attributes org.gtk.Property.get=soup_message_get_http_version) + * + * The HTTP protocol version to use. + **/ propertiesPROP_HTTP_VERSION = g_param_spec_enum ("http-version", "HTTP Version", @@ -725,6 +736,11 @@ SOUP_HTTP_1_1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** + * SoupMessage:flags: (attributes org.gtk.Property.get=soup_message_get_flags org.gtk.Property.set=soup_message_set_flags) + * + * Various message options. + **/ propertiesPROP_FLAGS = g_param_spec_flags ("flags", "Flags", @@ -733,6 +749,11 @@ 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * SoupMessage:status-code: + * + * The HTTP response status code. + **/ propertiesPROP_STATUS_CODE = g_param_spec_uint ("status-code", "Status code", @@ -740,6 +761,11 @@ 0, 999, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** + * SoupMessage:reason-phrase: (attributes org.gtk.Property.get=soup_message_get_reason_phrase) + * + * The HTTP response reason phrase. + **/ propertiesPROP_REASON_PHRASE = g_param_spec_string ("reason-phrase", "Reason phrase", @@ -748,11 +774,10 @@ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** - * SoupMessage:first-party: + * SoupMessage:first-party: (attributes org.gtk.Property.get=soup_message_get_first_party org.gtk.Property.set=soup_message_set_first_party) * - * The #GUri loaded in the application when the message was + * The struct@GLib.Uri loaded in the application when the message was * queued. - * */ propertiesPROP_FIRST_PARTY = g_param_spec_boxed ("first-party", @@ -762,10 +787,9 @@ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** - * SoupMessage:site-for-cookkies: + * SoupMessage:site-for-cookies: (attributes org.gtk.Property.get=soup_message_get_site_for_cookies org.gtk.Property.set=soup_message_set_site_for_cookies) * * Site used to compare cookies against. Used for SameSite cookie support. - * */ propertiesPROP_SITE_FOR_COOKIES = g_param_spec_boxed ("site-for-cookies", @@ -774,10 +798,9 @@ G_TYPE_URI, G_PARAM_READWRITE); /** - * SoupMessage:is-top-level-navigation: + * SoupMessage:is-top-level-navigation: (attributes org.gtk.Property.get=soup_message_get_is_top_level_navigation org.gtk.Property.set=soup_message_set_is_top_level_navigation) * * Set when the message is navigating between top level domains. - * */ propertiesPROP_IS_TOP_LEVEL_NAVIGATION = g_param_spec_boolean ("is-top-level-navigation", @@ -785,6 +808,11 @@ "If the current messsage is navigating between top-levels", FALSE, G_PARAM_READWRITE); + /** + * SoupMessage:request-headers: (attributes org.gtk.Property.get=soup_message_get_request_headers) + * + * The HTTP request headers. + */ propertiesPROP_REQUEST_HEADERS = g_param_spec_boxed ("request-headers", "Request Headers", @@ -792,6 +820,11 @@ SOUP_TYPE_MESSAGE_HEADERS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** + * SoupMessage:response-headers: (attributes org.gtk.Property.get=soup_message_get_response_headers) + * + * The HTTP response headers. + */ propertiesPROP_RESPONSE_HEADERS = g_param_spec_boxed ("response-headers", "Response Headers", @@ -800,10 +833,9 @@ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** - * SoupMessage:tls-peer-certificate: - * - * The peer's #GTlsCertificate associated with the message + * SoupMessage:tls-peer-certificate: (attributes org.gtk.Property.get=soup_message_get_tls_peer_certificate) * + * The peer's class@Gio.TlsCertificate associated with the message. */ propertiesPROP_TLS_PEER_CERTIFICATE = g_param_spec_object ("tls-peer-certificate", @@ -813,10 +845,9 @@ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** - * SoupMessage:tls-peer-certificate-errors: - * - * The verification errors on #SoupMessage:tls-peer-certificate + * SoupMessage:tls-peer-certificate-errors: (attributes org.gtk.Property.get=soup_message_get_tls_peer_certificate_errors) * + * The verification errors on property@Message:tls-peer-certificate. */ propertiesPROP_TLS_PEER_CERTIFICATE_ERRORS = g_param_spec_flags ("tls-peer-certificate-errors", @@ -826,7 +857,7 @@ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** - * SoupMessage:tls-protocol-version: + * SoupMessage:tls-protocol-version: (attributes org.gtk.Property.get=soup_message_get_tls_protocol_version) * * The TLS protocol version negotiated for the message connection. */ @@ -840,7 +871,7 @@ G_PARAM_STATIC_STRINGS); /** - * SoupMessage:tls-ciphersuite-name: + * SoupMessage:tls-ciphersuite-name: (attributes org.gtk.Property.get=soup_message_get_tls_ciphersuite_name) * * The Name of TLS ciphersuite negotiated for this message connection. */ @@ -853,10 +884,10 @@ G_PARAM_STATIC_STRINGS); /** - * SoupMessage:remote-address: - * - * The remote #GSocketAddress of the connection associated with the message + * SoupMessage:remote-address: (attributes org.gtk.Property.get=soup_message_get_remote_address) * + * The remote class@Gio.SocketAddress of the connection associated + * with the message. */ propertiesPROP_REMOTE_ADDRESS = g_param_spec_object ("remote-address", @@ -866,11 +897,10 @@ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** - SoupMessage:priority: + SoupMessage:priority: (attributes org.gtk.Property.get=soup_message_get_priority org.gtk.Property.set=soup_message_set_priority) * * Sets the priority of the #SoupMessage. See - * soup_message_set_priority() for further details. - * + * method@Message.set_priority for further details. **/ propertiesPROP_PRIORITY = g_param_spec_enum ("priority", @@ -882,12 +912,14 @@ G_PARAM_STATIC_STRINGS); /** - * SoupMessage:is-options-ping: + * SoupMessage:is-options-ping: (attributes org.gtk.Property.get=soup_message_get_is_options_ping org.gtk.Property.set=soup_message_set_is_options_ping) + * + * Whether the message is an OPTIONS ping. * * The #SoupMessage is intended to be used to send * `OPTIONS *` to a server. When set to %TRUE, the - * path of #SoupMessage:uri will be ignored and - * #SoupMessage:method set to %SOUP_METHOD_OPTIONS. + * path of property@Message:uri will be ignored and + * property@Message:method set to %SOUP_METHOD_OPTIONS. */ propertiesPROP_IS_OPTIONS_PING = g_param_spec_boolean ("is-options-ping", @@ -906,10 +938,10 @@ * @method: the HTTP method for the created request * @uri_string: the destination endpoint (as a string) * - * Creates a new empty #SoupMessage, which will connect to @uri + * Creates a new empty #SoupMessage, which will connect to @uri. * * Returns: (transfer full) (nullable): the new #SoupMessage (or %NULL if @uri - * could not be parsed). + * could not be parsed). */ SoupMessage * soup_message_new (const char *method, const char *uri_string) @@ -936,9 +968,9 @@ /** * soup_message_new_from_uri: * @method: the HTTP method for the created request - * @uri: the destination endpoint (as a #GUri) + * @uri: the destination endpoint * - * Creates a new empty #SoupMessage, which will connect to @uri + * Creates a new empty #SoupMessage, which will connect to @uri. * * Returns: (transfer full): the new #SoupMessage */ @@ -956,10 +988,10 @@ /** * soup_message_new_options_ping: - * @base_uri: the destination endpoint (as a #GUri) + * @base_uri: the destination endpoint * - * Creates a new #SoupMessage to send `OPTIONS *` to a server. The path of @base_uri - * will be ignored. + * Creates a new #SoupMessage to send `OPTIONS *` to a server. The path of + * @base_uri will be ignored. * * Returns: (transfer full): the new #SoupMessage */ @@ -985,12 +1017,13 @@ * to @uri via @method. If @method is "GET", it will include the form data * into @uri's query field, and if @method is "POST" or "PUT", it will be set as * request body. + * * This function takes the ownership of @encoded_form, that will be released - * with g_free() when no longer in use. See also soup_form_encode(), - * soup_form_encode_hash() and soup_form_encode_datalist(). + * with func@GLib.free when no longer in use. See also func@form_encode, + * func@form_encode_hash and func@form_encode_datalist. * - * Returns: (transfer full) (nullable): the new #SoupMessage, or %NULL if @uri_string - * could not be parsed or @method is not "GET, "POST" or "PUT" + * Returns: (transfer full) (nullable): the new #SoupMessage, or %NULL if + * @uri_string could not be parsed or @method is not "GET, "POST" or "PUT" */ SoupMessage * soup_message_new_from_encoded_form (const char *method, @@ -1033,14 +1066,14 @@ /** * soup_message_new_from_multipart: - * @uri_string: the destination endpoint (as a string) + * @uri_string: the destination endpoint * @multipart: a #SoupMultipart * * Creates a new #SoupMessage and sets it up to send @multipart to * @uri_string via POST. * * Returns: (transfer full) (nullable): the new #SoupMessage, or %NULL if @uri_string - * could not be parsed + * could not be parsed */ SoupMessage * soup_message_new_from_multipart (const char *uri_string, @@ -1078,6 +1111,7 @@ * @content_length: the byte length of @stream or -1 if unknown * * Set the request body of a #SoupMessage. + * * If @content_type is %NULL and @stream is not %NULL the Content-Type header will * not be changed if present. * The request body needs to be set again in case @msg is restarted @@ -1123,7 +1157,8 @@ * @content_type: (nullable): MIME Content-Type of the body, or %NULL if unknown * @bytes: (nullable): a #GBytes with the request body data * - * Set the request body of a #SoupMessage from #GBytes. + * Set the request body of a #SoupMessage from struct@GLib.Bytes. + * * If @content_type is %NULL and @bytes is not %NULL the Content-Type header will * not be changed if present. * The request body needs to be set again in case @msg is restarted @@ -1208,7 +1243,11 @@ void soup_message_finished (SoupMessage *msg) { + SoupMessagePrivate *priv = soup_message_get_instance_private (msg); + g_signal_emit (msg, signalsFINISHED, 0); + + priv->force_http_version = G_MAXUINT8; } gboolean @@ -1258,12 +1297,13 @@ * @callback: the header handler * @user_data: data to pass to @handler_cb * - * Adds a signal handler to @msg for @signal, as with - * g_signal_connect(), but the @callback will only be run if @msg's - * incoming messages headers (that is, the <literal>request_headers</literal>) - * contain a header named @header. + * Adds a signal handler to @msg for @signal. * - * Returns: the handler ID from g_signal_connect() + * Similar to func@GObject.signal_connect, but the @callback will only be run + * if @msg's incoming messages headers (that is, the `request_headers`) contain + * a header named @header. + * + * Returns: the handler ID from func@GObject.signal_connect **/ guint soup_message_add_header_handler (SoupMessage *msg, @@ -1315,14 +1355,15 @@ * @callback: the header handler * @user_data: data to pass to @handler_cb * - * Adds a signal handler to @msg for @signal, as with - * g_signal_connect(), but the @callback will only be run if @msg has - * the status @status_code. + * Adds a signal handler to @msg for @signal. + * + * Similar to func@GObject.signal_connect, but the @callback will only be run + * if @msg has the status @status_code. * * @signal must be a signal that will be emitted after @msg's status * is set (this means it can't be a "wrote" signal). * - * Returns: the handler ID from g_signal_connect() + * Returns: the handler ID from func@GObject.signal_connect **/ guint soup_message_add_status_code_handler (SoupMessage *msg, @@ -1410,8 +1451,17 @@ SoupMessagePrivate *priv = soup_message_get_instance_private (msg); if (priv->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) { + SoupConnection *connection = g_weak_ref_get (&priv->connection); + /* When loaded from the disk cache, the connection is NULL. */ - return priv->connection ? soup_connection_get_proxy_uri (priv->connection) : NULL; + if (connection) { + GUri *uri = soup_connection_get_proxy_uri (connection); + + g_object_unref (connection); + return uri; + } + + return NULL; } return priv->uri; @@ -1482,7 +1532,7 @@ { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - return priv->connection; + return g_weak_ref_get (&priv->connection); } static void @@ -1629,67 +1679,68 @@ SoupConnection *conn) { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); + SoupConnection *connection = g_weak_ref_get (&priv->connection); - if (priv->connection == conn) + if (connection == conn) { + g_clear_object (&connection); return; + } - if (priv->connection) { - g_signal_handlers_disconnect_by_data (priv->connection, msg); + if (connection) { + g_signal_handlers_disconnect_by_data (connection, msg); priv->io_data = NULL; if (priv->pending_tls_cert_request) { - soup_connection_complete_tls_certificate_request (priv->connection, + soup_connection_complete_tls_certificate_request (connection, priv->tls_client_certificate, g_steal_pointer (&priv->pending_tls_cert_request)); g_clear_object (&priv->tls_client_certificate); } - g_object_remove_weak_pointer (G_OBJECT (priv->connection), (gpointer*)&priv->connection); - soup_connection_set_in_use (priv->connection, FALSE); + soup_connection_set_in_use (connection, FALSE); + g_object_unref (connection); } - priv->connection = conn; - if (!priv->connection) + g_weak_ref_set (&priv->connection, conn); + if (!conn) return; - soup_connection_set_in_use (priv->connection, TRUE); - priv->last_connection_id = soup_connection_get_id (priv->connection); + soup_connection_set_in_use (conn, TRUE); + priv->last_connection_id = soup_connection_get_id (conn); - g_object_add_weak_pointer (G_OBJECT (priv->connection), (gpointer*)&priv->connection); soup_message_set_tls_peer_certificate (msg, - soup_connection_get_tls_certificate (priv->connection), - soup_connection_get_tls_certificate_errors (priv->connection)); + soup_connection_get_tls_certificate (conn), + soup_connection_get_tls_certificate_errors (conn)); soup_message_set_tls_protocol_version (msg, soup_connection_get_tls_protocol_version (conn)); soup_message_set_tls_ciphersuite_name (msg, soup_connection_get_tls_ciphersuite_name (conn)); - soup_message_set_remote_address (msg, soup_connection_get_remote_address (priv->connection)); + soup_message_set_remote_address (msg, soup_connection_get_remote_address (conn)); if (priv->tls_client_certificate) { - soup_connection_set_tls_client_certificate (priv->connection, - priv->tls_client_certificate); + soup_connection_set_tls_client_certificate (conn, priv->tls_client_certificate); g_clear_object (&priv->tls_client_certificate); } - g_signal_connect_object (priv->connection, "event", + g_signal_connect_object (conn, "event", G_CALLBACK (re_emit_connection_event), msg, G_CONNECT_SWAPPED); - g_signal_connect_object (priv->connection, "accept-certificate", + g_signal_connect_object (conn, "accept-certificate", G_CALLBACK (re_emit_accept_certificate), msg, G_CONNECT_SWAPPED); - g_signal_connect_object (priv->connection, "request-certificate", + g_signal_connect_object (conn, "request-certificate", G_CALLBACK (re_emit_request_certificate), msg, G_CONNECT_SWAPPED); - g_signal_connect_object (priv->connection, "request-certificate-password", + g_signal_connect_object (conn, "request-certificate-password", G_CALLBACK (re_emit_request_certificate_password), msg, G_CONNECT_SWAPPED); - g_signal_connect_object (priv->connection, "notify::tls-certificate", + g_signal_connect_object (conn, "notify::tls-certificate", G_CALLBACK (re_emit_tls_certificate_changed), msg, G_CONNECT_SWAPPED); - g_signal_connect_object (priv->connection, "notify::tls-protocol-version", + g_signal_connect_object (conn, "notify::tls-protocol-version", G_CALLBACK (connection_tls_protocol_version_changed), msg, G_CONNECT_SWAPPED); - g_signal_connect_object (priv->connection, "notify::tls-ciphersuite-name", + g_signal_connect_object (conn, "notify::tls-ciphersuite-name", G_CALLBACK (connection_tls_ciphersuite_name_changed), msg, G_CONNECT_SWAPPED); - g_signal_connect_object (priv->connection, "notify::remote-address", + g_signal_connect_object (conn, "notify::remote-address", G_CALLBACK (connection_remote_address_changed), msg, G_CONNECT_SWAPPED); } @@ -1710,18 +1761,20 @@ SoupMessagePrivate *preconnect_priv = soup_message_get_instance_private (preconnect_msg); SoupMessagePrivate *priv = soup_message_get_instance_private (msg); GTlsCertificate *client_certificate = NULL; + SoupConnection *connection; g_assert (preconnect_priv->is_preconnect); - g_assert (!priv->connection); + g_assert (!g_weak_ref_get (&priv->connection)); client_certificate = g_steal_pointer (&priv->tls_client_certificate); - soup_message_set_connection (msg, preconnect_priv->connection); + connection = g_weak_ref_get (&preconnect_priv->connection); + soup_message_set_connection (msg, connection); /* If connection has pending interactions, transfer them too */ g_assert (!priv->pending_tls_cert_request); priv->pending_tls_cert_request = g_steal_pointer (&preconnect_priv->pending_tls_cert_request); if (priv->pending_tls_cert_request) { if (client_certificate) { - soup_connection_complete_tls_certificate_request (priv->connection, + soup_connection_complete_tls_certificate_request (connection, client_certificate, g_steal_pointer (&priv->pending_tls_cert_request)); g_object_unref (client_certificate); @@ -1734,7 +1787,7 @@ g_clear_object (&priv->pending_tls_cert_request); } } else if (client_certificate) { - soup_connection_set_tls_client_certificate (priv->connection, client_certificate); + soup_connection_set_tls_client_certificate (connection, client_certificate); g_object_unref (client_certificate); } @@ -1750,6 +1803,7 @@ } soup_message_set_connection (preconnect_msg, NULL); + g_object_unref (connection); } gboolean @@ -1773,13 +1827,16 @@ * @msg: a #SoupMessage * * Cleans up all response data on @msg, so that the request can be sent - * again and receive a new response. (Eg, as a result of a redirect or + * again and receive a new response. + * + * (Eg, as a result of a redirect or * authorization request.) **/ void soup_message_cleanup_response (SoupMessage *msg) { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); + SoupConnection *connection; g_object_freeze_notify (G_OBJECT (msg)); @@ -1788,12 +1845,15 @@ soup_message_set_status (msg, SOUP_STATUS_NONE, NULL); soup_message_set_http_version (msg, priv->orig_http_version); - if (!priv->connection) { + connection = g_weak_ref_get (&priv->connection); + if (!connection) { soup_message_set_tls_peer_certificate (msg, NULL, 0); soup_message_set_tls_protocol_version (msg, G_TLS_PROTOCOL_VERSION_UNKNOWN); soup_message_set_tls_ciphersuite_name (msg, NULL); soup_message_set_remote_address (msg, NULL); priv->last_connection_id = 0; + } else { + g_object_unref (connection); } g_object_thaw_notify (G_OBJECT (msg)); @@ -1806,19 +1866,19 @@ * @SOUP_MESSAGE_NEW_CONNECTION: Requests that the message should be * sent on a newly-created connection, not reusing an existing * persistent connection. Note that messages with non-idempotent - * #SoupMessage:method<!-- -->s behave this way by default, unless + * property@Message:methods behave this way by default, unless * #SOUP_MESSAGE_IDEMPOTENT is set. * @SOUP_MESSAGE_IDEMPOTENT: The message is considered idempotent, - * regardless its #SoupMessage:method, and allows reuse of existing + * regardless its property@Message:method, and allows reuse of existing * idle connections, instead of always requiring a new one, unless * #SOUP_MESSAGE_NEW_CONNECTION is set. - * @SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE: The #SoupAuthManager should not use + * @SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE: The class@AuthManager should not use * the credentials cache for this message, neither to use cached credentials * to automatically authenticate this message nor to cache the credentials * after the message is successfully authenticated. This applies to both server - * and proxy authentication. Note that #SoupMessage::authenticate signal will + * and proxy authentication. Note that signal@Message::authenticate signal will * be emitted, if you want to disable authentication for a message use - * soup_message_disable_feature() passing #SOUP_TYPE_AUTH_MANAGER instead. + * method@Message.disable_feature passing #SOUP_TYPE_AUTH_MANAGER instead. * @SOUP_MESSAGE_COLLECT_METRICS: Metrics will be collected for this message. * * Various flags that can be set on a #SoupMessage to alter its @@ -1826,7 +1886,7 @@ **/ /** - * soup_message_set_flags: + * soup_message_set_flags: (attributes org.gtk.Method.set_property=flags) * @msg: a #SoupMessage * @flags: a set of #SoupMessageFlags values * @@ -1849,10 +1909,10 @@ } /** - * soup_message_get_flags: + * soup_message_get_flags: (attributes org.gtk.Method.get_property=flags) * @msg: a #SoupMessage * - * Gets the flags on @msg + * Gets the flags on @msg. * * Returns: the flags **/ @@ -1873,7 +1933,7 @@ * @msg: a #SoupMessage * @flags: a set of #SoupMessageFlags values * - * Adds @flags to the set of @msg's flags + * Adds @flags to the set of @msg's flags. */ void soup_message_add_flags (SoupMessage *msg, @@ -1892,7 +1952,7 @@ * @msg: a #SoupMessage * @flags: a set of #SoupMessageFlags values * - * Queries if @flags are present in the set of @msg's flags + * Queries if @flags are present in the set of @msg's flags. * * Returns: %TRUE if @flags are enabled in @msg */ @@ -1913,7 +1973,7 @@ * @msg: a #SoupMessage * @flags: a set of #SoupMessageFlags values * - * Removes @flags from the set of @msg's flags + * Removes @flags from the set of @msg's flags. */ void soup_message_remove_flags (SoupMessage *msg, @@ -1928,13 +1988,14 @@ } /** - * soup_message_set_http_version: + * soup_message_set_http_version: (attributes org.gtk.Method.set_property=https-version) * @msg: a #SoupMessage * @version: the HTTP version * - * Sets the HTTP version on @msg. The default version is - * %SOUP_HTTP_1_1. Setting it to %SOUP_HTTP_1_0 will prevent certain - * functionality from being used. + * Sets the HTTP version on @msg. + * + * The default version is %SOUP_HTTP_1_1. Setting it to %SOUP_HTTP_1_0 will + * prevent certain functionality from being used. **/ void soup_message_set_http_version (SoupMessage *msg, SoupHTTPVersion version) @@ -1951,11 +2012,13 @@ } /** - * soup_message_get_http_version: + * soup_message_get_http_version: (attributes org.gtk.Method.get_property=http-version) * @msg: a #SoupMessage * - * Gets the HTTP version of @msg. This is the minimum of the - * version from the request and the version from the response. + * Gets the HTTP version of @msg. + * + * This is the minimum of the version from the request and the version from the + * response. * * Returns: the HTTP version **/ @@ -1976,8 +2039,9 @@ * @msg: a #SoupMessage * * Determines whether or not @msg's connection can be kept alive for - * further requests after processing @msg, based on the HTTP version, - * Connection header, etc. + * further requests after processing @msg. + * + * The result is based on the HTTP version, Connection header, etc. * * Returns: %TRUE or %FALSE. **/ @@ -2025,12 +2089,14 @@ } /** - * soup_message_set_uri: + * soup_message_set_uri: (attributes org.gtk.Method.set_property=method) * @msg: a #SoupMessage * @uri: the new #GUri * - * Sets @msg's URI to @uri. If @msg has already been sent and you want - * to re-send it with the new URI, you need to send it again. + * Sets @msg's URI to @uri. + * + * If @msg has already been sent and you want to re-send it with the new URI, + * you need to send it again. **/ void soup_message_set_uri (SoupMessage *msg, GUri *uri) @@ -2061,10 +2127,10 @@ } /** - * soup_message_get_uri: + * soup_message_get_uri: (attributes org.gtk.Method.get_property=method) * @msg: a #SoupMessage * - * Gets @msg's URI + * Gets @msg's URI. * * Returns: (transfer none): the URI @msg is targeted for. **/ @@ -2085,8 +2151,9 @@ * @msg: a #SoupMessage * @status_code: an HTTP status code * - * Sets @msg's status code to @status_code. If @status_code is a - * known value, it will also set @msg's reason_phrase. + * Sets @msg's status code to @status_code. + * + * If @status_code is a known value, it will also set @msg's reason_phrase. **/ void soup_message_set_status (SoupMessage *msg, @@ -2118,8 +2185,9 @@ * @msg: a #SoupMessage * @feature_type: the #GType of a #SoupSessionFeature * - * This disables the actions of #SoupSessionFeature<!-- -->s with the - * given @feature_type (or a subclass of that type) on @msg, so that + * Disables the actions of iface@SessionFeatures with the + * given @feature_type (or a subclass of that type) on @msg. + * * @msg is processed as though the feature(s) hadn't been added to the * session. Eg, passing #SOUP_TYPE_CONTENT_SNIFFER for @feature_type * will disable Content-Type sniffing on the message. @@ -2128,7 +2196,6 @@ * a message that has already been queued is undefined. In particular, * you cannot call this on a message that is being requeued after a * redirect or authentication. - * **/ void soup_message_disable_feature (SoupMessage *msg, GType feature_type) @@ -2172,12 +2239,12 @@ * @msg: a #SoupMessage * @feature_type: the #GType of a #SoupSessionFeature * - * Get whether #SoupSessionFeature<!-- -->s of the given @feature_type + * Get whether iface@SessionFeatures of the given @feature_type * (or a subclass of that type) are disabled on @msg. - * See soup_message_disable_feature(). * - * Returns: %TRUE if feature is disabled, or %FALSE otherwise. + * See method@Message.disable_feature. * + * Returns: %TRUE if feature is disabled, or %FALSE otherwise. */ gboolean soup_message_is_feature_disabled (SoupMessage *msg, GType feature_type) @@ -2210,13 +2277,12 @@ } /** - * soup_message_get_first_party: + * soup_message_get_first_party: (attributes org.gtk.Method.get_property=first-party) * @msg: a #SoupMessage * - * Gets @msg's first-party #GUri + * Gets @msg's first-party struct@GLib.Uri. * * Returns: (transfer none): the @msg's first party #GUri - * **/ GUri * soup_message_get_first_party (SoupMessage *msg) @@ -2230,14 +2296,14 @@ } /** - * soup_message_set_first_party: + * soup_message_set_first_party: (attributes org.gtk.Method.set_property=first-party) * @msg: a #SoupMessage * @first_party: the #GUri for the @msg's first party * - * Sets @first_party as the main document #GUri for @msg. For - * details of when and how this is used refer to the documentation for - * #SoupCookieJarAcceptPolicy. + * Sets @first_party as the main document #GUri for @msg. * + * For details of when and how this is used refer to the documentation for + * enum@CookieJarAcceptPolicy. **/ void soup_message_set_first_party (SoupMessage *msg, @@ -2268,13 +2334,12 @@ } /** - * soup_message_get_site_for_cookies: + * soup_message_get_site_for_cookies: (attributes org.gtk.Method.get_property=site-for-cookies) * @msg: a #SoupMessage * - * Gets @msg's site for cookies #GUri + * Gets @msg's site for cookies #GUri. * * Returns: (transfer none): the @msg's site for cookies #GUri - * **/ GUri * soup_message_get_site_for_cookies (SoupMessage *msg) @@ -2288,19 +2353,19 @@ } /** - * soup_message_set_site_for_cookies: + * soup_message_set_site_for_cookies: (attributes org.gtk.Method.set_property=site-for-cookies) * @msg: a #SoupMessage * @site_for_cookies: (nullable): the #GUri for the @msg's site for cookies * * Sets @site_for_cookies as the policy URL for same-site cookies for @msg. * - * It is either the URL of the top-level document or %NULL depending on whether the registrable - * domain of this document's URL matches the registrable domain of its parent's/opener's - * URL. For the top-level document it is set to the document's URL. + * It is either the URL of the top-level document or %NULL depending on whether + * the registrable domain of this document's URL matches the registrable domain + * of its parent's/opener's URL. For the top-level document it is set to the + * document's URL. * * See the same-site spec(https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00) * for more information. - * **/ void soup_message_set_site_for_cookies (SoupMessage *msg, @@ -2332,13 +2397,14 @@ } /** - * soup_message_set_is_top_level_navigation: + * soup_message_set_is_top_level_navigation: (attributes org.gtk.Method.set_property=is-top-level-navigation) * @msg: a #SoupMessage * @is_top_level_navigation: if %TRUE indicate the current request is a top-level navigation * + * Sets whether the current request is a top-level navitation. + * * See the same-site spec(https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00) * for more information. - * **/ void soup_message_set_is_top_level_navigation (SoupMessage *msg, @@ -2358,12 +2424,14 @@ } /** - * soup_message_get_is_top_level_navigation: + * soup_message_get_is_top_level_navigation: (attributes org.gtk.Method.get_property=is-top-level-navigation) * @msg: a #SoupMessage * * Returns if this message is set as a top level navigation. + * * Used for same-site policy checks. * + * Returns: Whether the current request is a top-level navitation **/ gboolean soup_message_get_is_top_level_navigation (SoupMessage *msg) @@ -2380,12 +2448,13 @@ * soup_message_get_tls_peer_certificate: * @msg: a #SoupMessage * - * Gets the peer's #GTlsCertificate associated with @msg's connection. + * Gets the peer's class@Gio.TlsCertificate associated with @msg's connection. + * * Note that this is not set yet during the emission of - * SoupMessage::accept-certificate signal. + * signal@Message::accept-certificate signal. * * Returns: (transfer none) (nullable): @msg's TLS peer certificate, - * or %NULL if @msg's connection is not SSL. + * or %NULL if @msg's connection is not SSL. */ GTlsCertificate * soup_message_get_tls_peer_certificate (SoupMessage *msg) @@ -2405,7 +2474,7 @@ * * Gets the errors associated with validating @msg's TLS peer certificate. * Note that this is not set yet during the emission of - * SoupMessage::accept-certificate signal. + * signal@Message::accept-certificate signal. * * Returns: a #GTlsCertificateFlags with @msg's TLS peer certificate errors. */ @@ -2426,6 +2495,7 @@ * @msg: a #SoupMessage * * Gets the TLS protocol version negotiated for @msg's connection. + * * If the message connection is not SSL, %G_TLS_PROTOCOL_VERSION_UNKNOWN is returned. * * Returns: a #GTlsProtocolVersion @@ -2449,7 +2519,7 @@ * Gets the name of the TLS ciphersuite negotiated for @msg's connection. * * Returns: (transfer none): the name of the TLS ciphersuite, - * or %NULL if @msg's connection is not SSL. + * or %NULL if @msg's connection is not SSL. */ const char * soup_message_get_tls_ciphersuite_name (SoupMessage *msg) @@ -2470,33 +2540,37 @@ * * Sets the @certificate to be used by @msg's connection when a * client certificate is requested during the TLS handshake. - * You can call this as a response to #SoupMessage::request-certificate + * + * You can call this as a response to signal@Message::request-certificate * signal, or before the connection is started. If @certificate is %NULL * the handshake will continue without providing a GTlsCertificate. - * Note that the #GTlsCertificate set by this function will be ignored if - * #SoupSession::tls-interaction is not %NULL. + * Note that the class@Gio.TlsCertificate set by this function will be ignored if + * property@Session:tls-interaction is not %NULL. */ void soup_message_set_tls_client_certificate (SoupMessage *msg, GTlsCertificate *certificate) { SoupMessagePrivate *priv; + SoupConnection *connection; g_return_if_fail (SOUP_IS_MESSAGE (msg)); g_return_if_fail (certificate == NULL || G_IS_TLS_CERTIFICATE (certificate)); priv = soup_message_get_instance_private (msg); + connection = g_weak_ref_get (&priv->connection); if (priv->pending_tls_cert_request) { - g_assert (SOUP_IS_CONNECTION (priv->connection)); - soup_connection_complete_tls_certificate_request (priv->connection, + g_assert (SOUP_IS_CONNECTION (connection)); + soup_connection_complete_tls_certificate_request (connection, certificate, g_steal_pointer (&priv->pending_tls_cert_request)); + g_object_unref (connection); return; } - if (priv->connection) { - soup_connection_set_tls_client_certificate (priv->connection, - certificate); + if (connection) { + soup_connection_set_tls_client_certificate (connection, certificate); + g_object_unref (connection); return; } @@ -2513,13 +2587,15 @@ * * Completes a certificate password request. * - * You must call this as a response to #SoupMessage::request-certificate-password - * signal, to notify @msg that the #GTlsPassword has already been updated. + * You must call this as a response to + * signal@Message::request-certificate-password signal, to notify @msg that + * the class@Gio.TlsPassword has already been updated. */ void soup_message_tls_client_certificate_password_request_complete (SoupMessage *msg) { SoupMessagePrivate *priv; + SoupConnection *connection; g_return_if_fail (SOUP_IS_MESSAGE (msg)); @@ -2529,9 +2605,11 @@ return; } - g_assert (SOUP_IS_CONNECTION (priv->connection)); - soup_connection_complete_tls_certificate_password_request (priv->connection, + connection = g_weak_ref_get (&priv->connection); + g_assert (SOUP_IS_CONNECTION (connection)); + soup_connection_complete_tls_certificate_password_request (connection, g_steal_pointer (&priv->pending_tls_cert_pass_request)); + g_object_unref (connection); } /** @@ -2549,9 +2627,8 @@ * for very urgent #SoupMessage as they will be the first ones to be * attended. * - * Priorities that can be set on a #SoupMessage to instruct the - * message queue to process it before any other message with lower - * priority. + * Priorities that can be set on a class@Message to instruct the message queue + * to process it before any other message with lower priority. **/ /** @@ -2559,9 +2636,10 @@ * @msg: a #SoupMessage * @priority: the #SoupMessagePriority * - * Sets the priority of a message. Note that this won't have any - * effect unless used before the message is added to the session's - * message processing queue. + * Sets the priority of a message. + * + * Note that this won't have any effect unless used before the message is added + * to the session's message processing queue. * * The message will be placed just before any other previously added * message with lower priority (messages with the same priority are @@ -2570,7 +2648,6 @@ * Setting priorities does not currently work with synchronous messages * because in the synchronous/blocking case, priority ends up being determined * semi-randomly by thread scheduling. - * */ void soup_message_set_priority (SoupMessage *msg, @@ -2592,11 +2669,11 @@ * soup_message_get_priority: * @msg: a #SoupMessage * - * Retrieves the #SoupMessagePriority. If not set this value defaults - * to #SOUP_MESSAGE_PRIORITY_NORMAL. + * Retrieves the enum@MessagePriority. * - * Returns: the priority of the message. + * If not set this value defaults to #SOUP_MESSAGE_PRIORITY_NORMAL. * + * Returns: the priority of the message. */ SoupMessagePriority soup_message_get_priority (SoupMessage *msg) @@ -2626,7 +2703,12 @@ if (!priv->io_data) return; - g_assert (priv->connection != NULL); +#ifndef G_DISABLE_ASSERT + SoupConnection *connection = g_weak_ref_get (&priv->connection); + + g_assert (connection != NULL); + g_object_unref (connection); +#endif soup_client_message_io_finished (g_steal_pointer (&priv->io_data), msg); } @@ -2742,10 +2824,11 @@ gpointer user_data) { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); + SoupConnection *connection = g_weak_ref_get (&priv->connection); - priv->io_data = soup_connection_setup_message_io (priv->connection, msg); - soup_client_message_io_send_item (priv->io_data, item, - completion_cb, user_data); + priv->io_data = soup_connection_setup_message_io (connection, msg); + g_object_unref (connection); + soup_client_message_io_send_item (priv->io_data, item, completion_cb, user_data); } GInputStream * @@ -2810,7 +2893,7 @@ } /** - * soup_message_get_method: + * soup_message_get_method: (attributes org.gtk.Method.get_property=method) * @msg: The #SoupMessage * * Returns the method of this message. @@ -2846,12 +2929,12 @@ } /** - * soup_message_get_reason_phrase: + * soup_message_get_reason_phrase: (attributes org.gtk.Method.get_property=reason-phrase) * @msg: The #SoupMessage * * Returns the reason phrase for the status of this message. * - * Returns: Phrase or %NULL + * Returns: (nullable): the phrase */ const char * soup_message_get_reason_phrase (SoupMessage *msg) @@ -2864,7 +2947,7 @@ } /** - * soup_message_get_request_headers: + * soup_message_get_request_headers: (attributes org.gtk.Method.get_property=request-headers) * @msg: The #SoupMessage * * Returns the headers sent with the request. @@ -2882,7 +2965,7 @@ } /** - * soup_message_get_response_headers: + * soup_message_get_response_headers: (attributes org.gtk.Method.get_property=response-headers) * @msg: The #SoupMessage * * Returns the headers recieved with the response. @@ -2899,6 +2982,13 @@ return priv->response_headers; } +/** + * soup_message_set_reason_phrase: (attributes org.gtk.Method.set_property=reason-phrase) + * @msg: The #SoupMessage + * @reason_phrase: The reason phrase + * + * Sets the reason phrase for the status of this message. + */ void soup_message_set_reason_phrase (SoupMessage *msg, const char *reason_phrase) { @@ -2913,7 +3003,7 @@ } /** - * soup_message_set_method: + * soup_message_set_method: (attributes org.gtk.Method.set_property=method) * @msg: a #SoupMessage * @method: the value to set * @@ -2959,8 +3049,9 @@ * @is_options_ping: the value to set * * Set whether @msg is intended to be used to send `OPTIONS *` to a server. - * When set to %TRUE, the path of #SoupMessage:uri will be ignored and - * #SoupMessage:method set to %SOUP_METHOD_OPTIONS. + * + * When set to %TRUE, the path of property@Message:uri will be ignored and + * property@Message:method set to %SOUP_METHOD_OPTIONS. */ void soup_message_set_is_options_ping (SoupMessage *msg, @@ -2985,6 +3076,7 @@ * @msg: The #SoupMessage * * Returns the unique idenfier for the last connection used. + * * This may be 0 if it was a cached resource or it has not gotten * a connection yet. * @@ -3004,12 +3096,14 @@ * soup_message_get_remote_address: * @msg: The #SoupMessage * - * Get the remote #GSocketAddress of the connection associated with the message. - * The returned address can be %NULL if the connection hasn't been established yet, - * or the resource was loaded from the disk cache. - * In case of proxy connections, the remote address returned is a #GProxyAddress. - * If #SoupSession::remote-connetable is set the returned address id for the connection - * ot the session's remote connectable. + * Get the remote class@Gio.SocketAddress of the connection associated with + * the message. + * + * The returned address can be %NULL if the connection hasn't been established + * yet, or the resource was loaded from the disk cache. In case of proxy + * connections, the remote address returned is a class@Gio.ProxyAddress. If + * property@Session:remote-connectable is set the returned address id for the + * connection to the session's remote connectable. * * Returns: (transfer none) (nullable): a #GSocketAddress or %NULL if the connection * hasn't been established @@ -3029,10 +3123,12 @@ * soup_message_get_metrics: * @msg: The #SoupMessage * - * Get the #SoupMessageMetrics of @msg. If the flag %SOUP_MESSAGE_COLLECT_METRICS is not - * enabled for @msg this will return %NULL. + * Get the struct@MessageMetrics of @msg. + * + * If the flag %SOUP_MESSAGE_COLLECT_METRICS is not enabled for @msg this will + * return %NULL. * - * Returns: (transfer none) (nullable): a #SoupMessageMetrics, or %NULL + * Returns: (transfer none) (nullable): a #SoupMessageMetrics */ SoupMessageMetrics * soup_message_get_metrics (SoupMessage *msg) @@ -3126,8 +3222,10 @@ { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - if (priv->http_version == SOUP_HTTP_2_0) + if (priv->http_version == SOUP_HTTP_2_0) { + soup_message_headers_remove_common (priv->request_headers, SOUP_HEADER_HOST); return; + } if (soup_message_headers_get_one_common (priv->request_headers, SOUP_HEADER_HOST)) return; @@ -3140,8 +3238,10 @@ { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - if (priv->http_version == SOUP_HTTP_2_0) + if (priv->http_version == SOUP_HTTP_2_0) { + soup_message_headers_remove_common (priv->request_headers, SOUP_HEADER_CONNECTION); return; + } /* Force keep alive connections for HTTP 1.0. Performance will * improve when issuing multiple requests to the same host in @@ -3156,20 +3256,20 @@ } void -soup_message_set_force_http1 (SoupMessage *msg, - gboolean force_http1) +soup_message_set_force_http_version (SoupMessage *msg, + guint8 version) { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - priv->force_http1 = force_http1; + priv->force_http_version = version; } -gboolean -soup_message_get_force_http1 (SoupMessage *msg) +guint8 +soup_message_get_force_http_version (SoupMessage *msg) { SoupMessagePrivate *priv = soup_message_get_instance_private (msg); - return priv->force_http1; + return priv->force_http_version; } void
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-misc.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-misc.c
Changed
@@ -92,16 +92,16 @@ /** * soup_add_timeout: (skip) * @async_context: (nullable): the #GMainContext to dispatch the I/O - * watch in, or %NULL for the default context + * watch in, or %NULL for the default context * @interval: the timeout interval, in milliseconds * @function: the callback to invoke at timeout time * @data: user data to pass to @function * - * Adds a timeout as with g_timeout_add(), but using the given + * Adds a timeout as with func@GLib.timeout_add, but using the given * @async_context. * * Returns: (transfer full): a #GSource, which can be removed from @async_context - * with g_source_destroy(). + * with method@GLib.Source.destroy. **/ GSource * soup_add_timeout (GMainContext *async_context, @@ -114,6 +114,18 @@ return source; } +GMainContext * +soup_thread_default_context (void) +{ + GMainContext *context; + + context = g_main_context_get_thread_default (); + if (!context) + context = g_main_context_default (); + + return context; +} + /* 00 URI_UNRESERVED * 01 URI_PCT_ENCODED * 02 URI_GEN_DELIMS
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-misc.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-misc.h
Changed
@@ -24,6 +24,7 @@ guint interval, GSourceFunc function, gpointer data); +GMainContext *soup_thread_default_context (void); /* Misc utils */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-multipart-input-stream.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-multipart-input-stream.c
Changed
@@ -21,27 +21,21 @@ #define RESPONSE_BLOCK_SIZE 8192 /** - * SECTION:soup-multipart-input-stream - * @short_description: Multipart input handling stream + * SoupMultipartInputStream: + * + * Handles streams of multipart messages. * * This adds support for the multipart responses. For handling the - * multiple parts the user needs to wrap the #GInputStream obtained by - * sending the request with a #SoupMultipartInputStream and use - * soup_multipart_input_stream_next_part() before reading. Responses + * multiple parts the user needs to wrap the class@Gio.InputStream obtained by + * sending the request with a class@MultipartInputStream and use + * method@MultipartInputStream.next_part before reading. Responses * which are not wrapped will be treated like non-multipart responses. * - * Note that although #SoupMultipartInputStream is a #GInputStream, + * Note that although #SoupMultipartInputStream is a class@Gio.InputStream, * you should not read directly from it, and the results are undefined * if you do. - * **/ -/** - * SoupMultipartInputStream: - * - * Class for handling streams of multipart messages. - */ - enum { PROP_0, @@ -310,6 +304,11 @@ input_stream_class->read_fn = soup_multipart_input_stream_read; + /** + * SoupMultipartInputStream:message: + * + * The class@Message. + */ propertiesPROP_MESSAGE = g_param_spec_object ("message", "Message", @@ -433,13 +432,13 @@ * @base_stream: the #GInputStream returned by sending the request. * * Creates a new #SoupMultipartInputStream that wraps the - * #GInputStream obtained by sending the #SoupMessage. Reads should - * not be done directly through this object, use the input streams - * returned by soup_multipart_input_stream_next_part() or its async + * class@Gio.InputStream obtained by sending the class@Message. + * + * Reads should not be done directly through this object, use the input streams + * returned by method@MultipartInputStream.next_part or its async * counterpart instead. * * Returns: a new #SoupMultipartInputStream - * **/ SoupMultipartInputStream * soup_multipart_input_stream_new (SoupMessage *msg, @@ -457,21 +456,20 @@ * @cancellable: a #GCancellable * @error: a #GError * - * Obtains an input stream for the next part. When dealing with a - * multipart response the input stream needs to be wrapped in a - * #SoupMultipartInputStream and this function or its async - * counterpart need to be called to obtain the first part for - * reading. + * Obtains an input stream for the next part. + * + * When dealing with a multipart response the input stream needs to be wrapped + * in a #SoupMultipartInputStream and this function or its async counterpart + * need to be called to obtain the first part for reading. * * After calling this function, - * soup_multipart_input_stream_get_headers() can be used to obtain the + * method@MultipartInputStream.get_headers can be used to obtain the * headers for the first part. A read of 0 bytes indicates the end of * the part; a new call to this function should be done at that point, * to obtain the next part. * * Returns: (nullable) (transfer full): a new #GInputStream, or - * %NULL if there are no more parts - * + * %NULL if there are no more parts */ GInputStream * soup_multipart_input_stream_next_part (SoupMultipartInputStream *multipart, @@ -523,10 +521,9 @@ * @callback: callback to call when request is satisfied. * @data: data for @callback * - * Obtains a #GInputStream for the next request. See - * soup_multipart_input_stream_next_part() for details on the - * workflow. + * Obtains a class@Gio.InputStream for the next request. * + * See method@MultipartInputStream.next_part for details on the workflow. */ void soup_multipart_input_stream_next_part_async (SoupMultipartInputStream *multipart, @@ -563,9 +560,8 @@ * Finishes an asynchronous request for the next part. * * Returns: (nullable) (transfer full): a newly created - * #GInputStream for reading the next part or %NULL if there are no - * more parts. - * + * class@Gio.InputStream for reading the next part or %NULL if there are no + * more parts. */ GInputStream * soup_multipart_input_stream_next_part_finish (SoupMultipartInputStream *multipart, @@ -581,20 +577,19 @@ * soup_multipart_input_stream_get_headers: * @multipart: a #SoupMultipartInputStream. * - * Obtains the headers for the part currently being processed. Note - * that the #SoupMessageHeaders that are returned are owned by the - * #SoupMultipartInputStream and will be replaced when a call is made - * to soup_multipart_input_stream_next_part() or its async - * counterpart, so if keeping the headers is required, a copy must be - * made. + * Obtains the headers for the part currently being processed. + * + * Note that the struct@MessageHeaders that are returned are owned by the + * #SoupMultipartInputStream and will be replaced when a call is made to + * method@MultipartInputStream.next_part or its async counterpart, so if + * keeping the headers is required, a copy must be made. * - * Note that if a part had no headers at all an empty #SoupMessageHeaders + * Note that if a part had no headers at all an empty struct@MessageHeaders * will be returned. * * Returns: (nullable) (transfer none): a #SoupMessageHeaders - * containing the headers for the part currently being processed or - * %NULL if the headers failed to parse. - * + * containing the headers for the part currently being processed or + * %NULL if the headers failed to parse. */ SoupMessageHeaders * soup_multipart_input_stream_get_headers (SoupMultipartInputStream *multipart)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-multipart.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-multipart.c
Changed
@@ -17,20 +17,13 @@ #include "soup.h" /** - * SECTION:soup-multipart - * @short_description: multipart HTTP message bodies - * @see_also: #SoupMessageBody, #SoupMessageHeaders - * - * Functions to use multi-part HTTP messages. - **/ - -/** * SoupMultipart: * * Represents a multipart HTTP message body, parsed according to the - * syntax of RFC 2046. Of particular interest to HTTP are - * <literal>multipart/byte-ranges</literal> and - * <literal>multipart/form-data</literal>. + * syntax of RFC 2046. + * + * Of particular interest to HTTP are `multipart/byte-ranges` and + * `multipart/form-data`, * * Although the headers of a #SoupMultipart body part will contain the * full headers from that body part, libsoup does not interpret them @@ -81,12 +74,13 @@ * @mime_type: the MIME type of the multipart to create. * * Creates a new empty #SoupMultipart with a randomly-generated - * boundary string. Note that @mime_type must be the full MIME type, - * including "multipart/". + * boundary string. + * + * Note that @mime_type must be the full MIME type, including "multipart/". + * + * See also: ctor@Message.new_from_multipart. * * Returns: a new empty #SoupMultipart of the given @mime_type - * - * See also: soup_message_new_from_multipart() **/ SoupMultipart * soup_multipart_new (const char *mime_type) @@ -129,8 +123,7 @@ * Parses @headers and @body to form a new #SoupMultipart * * Returns: (nullable): a new #SoupMultipart (or %NULL if the - * message couldn't be parsed or wasn't multipart). - * + * message couldn't be parsed or wasn't multipart). **/ SoupMultipart * soup_multipart_new_from_message (SoupMessageHeaders *headers, @@ -224,10 +217,9 @@ * soup_multipart_get_length: * @multipart: a #SoupMultipart * - * Gets the number of body parts in @multipart + * Gets the number of body parts in @multipart. * * Returns: the number of body parts in @multipart - * **/ int soup_multipart_get_length (SoupMultipart *multipart) @@ -240,15 +232,14 @@ * @multipart: a #SoupMultipart * @part: the part number to get (counting from 0) * @headers: (out) (transfer none): return location for the MIME part - * headers + * headers * @body: (out) (transfer none): return location for the MIME part - * body + * body * * Gets the indicated body part from @multipart. * * Returns: %TRUE on success, %FALSE if @part is out of range (in - * which case @headers and @body won't be set) - * + * which case @headers and @body won't be set) **/ gboolean soup_multipart_get_part (SoupMultipart *multipart, int part, @@ -268,10 +259,10 @@ * @body: the MIME part body * * Adds a new MIME part to @multipart with the given headers and body. + * * (The multipart will make its own copies of @headers and @body, so * you should free your copies if you are not using them for anything * else.) - * **/ void soup_multipart_append_part (SoupMultipart *multipart, @@ -319,11 +310,10 @@ * @control_name: the name of the control associated with @data * @data: the body data * - * Adds a new MIME part containing @data to @multipart, using - * "Content-Disposition: form-data", as per the HTML forms - * specification. + * Adds a new MIME part containing @data to @multipart. * - **/ + * Uses "Content-Disposition: form-data", as per the HTML forms specification. + **/ void soup_multipart_append_form_string (SoupMultipart *multipart, const char *control_name, const char *data) @@ -344,11 +334,10 @@ * @content_type: the MIME type of the file, or %NULL if not known * @body: the file data * - * Adds a new MIME part containing @body to @multipart, using - * "Content-Disposition: form-data", as per the HTML forms - * specification. + * Adds a new MIME part containing @body to @multipart * - **/ + * Uses "Content-Disposition: form-data", as per the HTML forms specification. + **/ void soup_multipart_append_form_file (SoupMultipart *multipart, const char *control_name, const char *filename, @@ -384,7 +373,6 @@ * @dest_body: (out): the body of the HTTP message to serialize @multipart to * * Serializes @multipart to @dest_headers and @dest_body. - * **/ void soup_multipart_to_message (SoupMultipart *multipart, @@ -442,8 +430,7 @@ * soup_multipart_free: * @multipart: a #SoupMultipart * - * Frees @multipart - * + * Frees @multipart. **/ void soup_multipart_free (SoupMultipart *multipart)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-session-feature.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-session-feature.c
Changed
@@ -14,24 +14,17 @@ #include "soup-message-private.h" /** - * SECTION:soup-session-feature - * @short_description: Interface for miscellaneous session features + * SoupSessionFeature: + * + * Interface for miscellaneous class@Session features. * * #SoupSessionFeature is the interface used by classes that extend - * the functionality of a #SoupSession. Some features like HTTP + * the functionality of a class@Session. Some features like HTTP * authentication handling are implemented internally via - * #SoupSessionFeature<!-- -->s. Other features can be added to the session - * by the application. (Eg, #SoupLogger, #SoupCookieJar.) - * - * See soup_session_add_feature(), etc, to add a feature to a session. - **/ - -/** - * SoupSessionFeature: - * - * An object that implement some sort of optional feature for - * #SoupSession. + * `SoupSessionFeature`s. Other features can be added to the session + * by the application. (Eg, class@Logger, class@CookieJar.) * + * See method@Session.add_feature, etc, to add a feature to a session. **/ /** @@ -39,14 +32,13 @@ * @parent: The parent interface. * @attach: Perform setup when a feature is added to a session * @detach: Perform cleanup when a feature is removed from a session - * @request_queued: Proxies the session's #SoupSession::request_queued signal - * @request_unqueued: Proxies the session's #SoupSession::request_unqueued signal + * @request_queued: Proxies the session's signal@Session::request_queued signal + * @request_unqueued: Proxies the session's signal@Session::request_unqueued signal * @add_feature: adds a sub-feature to the main feature * @remove_feature: removes a sub-feature from the main feature * @has_feature: tests if the feature includes a sub-feature * - * The interface implemented by #SoupSessionFeature<!-- -->s. - * + * The interface implemented by iface@SessionFeatures. **/ G_DEFINE_INTERFACE (SoupSessionFeature, soup_session_feature, G_TYPE_OBJECT) @@ -124,12 +116,12 @@ * @type: the #GType of a "sub-feature" * * Adds a "sub-feature" of type @type to the base feature @feature. + * * This is used for features that can be extended with multiple * different types. Eg, the authentication manager can be extended - * with subtypes of #SoupAuth. + * with subtypes of class@Auth. * * Returns: %TRUE if @feature accepted @type as a subfeature. - * */ gboolean soup_session_feature_add_feature (SoupSessionFeature *feature, @@ -150,10 +142,11 @@ * @type: the #GType of a "sub-feature" * * Removes the "sub-feature" of type @type from the base feature - * @feature. See soup_session_feature_add_feature(). + * @feature. * - * Returns: %TRUE if @type was removed from @feature + * See method@SessionFeature.add_feature. * + * Returns: %TRUE if @type was removed from @feature */ gboolean soup_session_feature_remove_feature (SoupSessionFeature *feature, @@ -173,11 +166,11 @@ * @feature: the "base" feature * @type: the #GType of a "sub-feature" * - * Tests if @feature has a "sub-feature" of type @type. See - * soup_session_feature_add_feature(). + * Tests if @feature has a "sub-feature" of type @type. * - * Returns: %TRUE if @feature has a subfeature of type @type + * See method@SessionFeature.add_feature. * + * Returns: %TRUE if @feature has a subfeature of type @type */ gboolean soup_session_feature_has_feature (SoupSessionFeature *feature,
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-session-private.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-session-private.h
Changed
@@ -7,7 +7,10 @@ #define __SOUP_SESSION_PRIVATE_H__ 1 #include "soup-session.h" +#include "soup-connection.h" #include "soup-content-processor.h" +#include "soup-message-queue-item.h" +#include "soup-socket-properties.h" G_BEGIN_DECLS @@ -31,6 +34,16 @@ GSList *soup_session_get_features (SoupSession *session, GType feature_type); +gboolean soup_session_steal_preconnection (SoupSession *session, + SoupMessageQueueItem *item, + SoupConnection *conn); + +void soup_session_kick_queue (SoupSession *session); + +SoupSocketProperties *soup_session_ensure_socket_props (SoupSession *session); + +GMainContext *soup_session_get_context (SoupSession *session); + G_END_DECLS #endif /* __SOUP_SESSION_PRIVATE_H__ */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-session.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-session.c
Changed
@@ -16,7 +16,7 @@ #include "auth/soup-auth-manager.h" #include "auth/soup-auth-ntlm.h" #include "cache/soup-cache-private.h" -#include "soup-connection.h" +#include "soup-connection-manager.h" #include "soup-message-private.h" #include "soup-message-headers-private.h" #include "soup-misc.h" @@ -29,11 +29,10 @@ #include "websocket/soup-websocket-connection.h" #include "websocket/soup-websocket-extension-manager-private.h" -#define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */ - /** - * SECTION:soup-session - * @short_description: Soup session state object + * SoupSession: + * + * Soup session state object. * * #SoupSession is the object that controls client-side HTTP. A * #SoupSession encapsulates all of the state that libsoup is keeping @@ -45,54 +44,31 @@ * reason you might need multiple sessions is if you need to have * multiple independent authentication contexts. (Eg, you are * connecting to a server and authenticating as two different users at - * different times; the easiest way to ensure that each #SoupMessage + * different times; the easiest way to ensure that each class@Message * is sent with the authentication information you intended is to use * one session for the first user, and a second session for the other * user.) * * Additional #SoupSession functionality is provided by - * #SoupSessionFeature objects, which can be added to a session with - * soup_session_add_feature() or soup_session_add_feature_by_type() - * For example, #SoupLogger provides support for - * logging HTTP traffic, #SoupContentDecoder provides support for - * compressed response handling, and #SoupContentSniffer provides + * iface@SessionFeature objects, which can be added to a session with + * method@Session.add_feature or method@Session.add_feature_by_type + * For example, class@Logger provides support for + * logging HTTP traffic, class@ContentDecoder provides support for + * compressed response handling, and class@ContentSniffer provides * support for HTML5-style response body content sniffing. - * Additionally, subtypes of #SoupAuth can be added + * Additionally, subtypes of class@Auth can be added * as features, to add support for additional authentication types. * - * All #SoupSessions are created with a #SoupAuthManager, and support + * All `SoupSession`s are created with a class@AuthManager, and support * for %SOUP_TYPE_AUTH_BASIC and %SOUP_TYPE_AUTH_DIGEST. Additionally, * sessions using the plain #SoupSession class (rather than one of its deprecated - * subtypes) have a #SoupContentDecoder by default. + * subtypes) have a class@ContentDecoder by default. * * Note that all async methods will invoke their callbacks on the thread-default * context at the time of the function call. **/ -/** - * SoupSession: - * - * Class managing options and state for #SoupMessage<!-- -->s. - */ - -typedef struct { - GUri *uri; - GNetworkAddress *addr; - - GSList *connections; /* CONTAINS: SoupConnection */ - guint num_conns; - - guint num_messages; - - GSource *keep_alive_src; - SoupSession *session; -} SoupSessionHost; -static guint soup_host_uri_hash (gconstpointer key); -static gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2); - typedef struct { - gboolean disposed; - GTlsDatabase *tlsdb; GTlsInteraction *tls_interaction; gboolean tlsdb_use_default; @@ -105,45 +81,31 @@ SoupSocketProperties *socket_props; + GMainContext *context; + GMutex queue_mutex; GQueue *queue; - GSource *queue_source; - guint16 in_async_run_queue; + GMutex queue_sources_mutex; + GHashTable *queue_sources; + gint num_async_items; + guint in_async_run_queue; gboolean needs_queue_sort; char *user_agent; char *accept_language; gboolean accept_language_auto; - GSocketConnectable *remote_connectable; - GSList *features; - GHashTable *features_cache; - GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */ - GHashTable *conns; /* SoupConnection -> SoupSessionHost */ - guint num_conns; - guint max_conns, max_conns_per_host; - guint64 last_connection_id; + SoupConnectionManager *conn_manager; } SoupSessionPrivate; -static void free_host (SoupSessionHost *host); -static void connection_state_changed (GObject *object, GParamSpec *param, - gpointer user_data); -static void connection_disconnected (SoupConnection *conn, gpointer user_data); -static void drop_connection (SoupSession *session, SoupSessionHost *host, - SoupConnection *conn); - static void async_run_queue (SoupSession *session); static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item); -static void soup_session_kick_queue (SoupSession *session); - -static void -soup_session_process_queue_item (SoupSession *session, - SoupMessageQueueItem *item, - gboolean *should_cleanup, - gboolean loop); +static void soup_session_process_queue_item (SoupSession *session, + SoupMessageQueueItem *item, + gboolean loop); #define SOUP_SESSION_MAX_CONNS_DEFAULT 10 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2 @@ -184,12 +146,6 @@ static GParamSpec *propertiesLAST_PROPERTY = { NULL, }; /** - * SOUP_SESSION_ERROR: - * - * A #GError domain for #SoupSession<!-- -->-related errors. Used with - * #SoupSessionError. - */ -/** * SoupSessionError: * @SOUP_SESSION_ERROR_PARSING: the server's response could not * be parsed @@ -212,7 +168,8 @@ typedef struct { GSource source; - SoupSession* session; + GWeakRef session; + guint num_items; } SoupMessageQueueSource; static gboolean @@ -220,48 +177,127 @@ GSourceFunc callback, gpointer user_data) { - SoupSession *session = ((SoupMessageQueueSource *)source)->session; + SoupMessageQueueSource *queue_source = (SoupMessageQueueSource *)source; + SoupSession *session = g_weak_ref_get (&queue_source->session); + + if (!session) + return G_SOURCE_REMOVE; g_source_set_ready_time (source, -1); async_run_queue (session); + g_object_unref (session); + return G_SOURCE_CONTINUE; } +static void +queue_finalize (GSource *source) +{ + SoupMessageQueueSource *queue_source = (SoupMessageQueueSource *)source; + + g_weak_ref_clear (&queue_source->session); +} + static GSourceFuncs queue_source_funcs = { NULL, //queue_prepare, NULL, //queue_check, queue_dispatch, - NULL, NULL, NULL + queue_finalize, + NULL, NULL }; static void +soup_session_add_queue_source (SoupSession *session, + GMainContext *context) +{ + SoupSessionPrivate *priv = soup_session_get_instance_private (session); + SoupMessageQueueSource *queue_source; + + if (!priv->queue_sources) + priv->queue_sources = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_source_unref); + + queue_source = g_hash_table_lookup (priv->queue_sources, context); + if (!queue_source) { + GSource *source; + + source = g_source_new (&queue_source_funcs, sizeof (SoupMessageQueueSource)); + queue_source = (SoupMessageQueueSource *)source; + g_weak_ref_init (&queue_source->session, session); + queue_source->num_items = 0; + g_source_set_name (source, "SoupMessageQueue"); + g_source_set_can_recurse (source, TRUE); + g_source_attach (source, context); + g_hash_table_insert (priv->queue_sources, context, source); + } + + queue_source->num_items++; +} + +static void +soup_session_add_queue_source_for_item (SoupSession *session, + SoupMessageQueueItem *item) +{ + SoupSessionPrivate *priv = soup_session_get_instance_private (session); + + if (!item->async) + return; + + g_mutex_lock (&priv->queue_sources_mutex); + soup_session_add_queue_source (session, item->context); + g_mutex_unlock (&priv->queue_sources_mutex); +} + +static void +soup_session_remove_queue_source (SoupSession *session, + GMainContext *context) +{ + SoupSessionPrivate *priv = soup_session_get_instance_private (session); + SoupMessageQueueSource *queue_source; + + queue_source = g_hash_table_lookup (priv->queue_sources, context); + if (!queue_source) + return; + + if (--queue_source->num_items > 0) + return; + + g_source_destroy ((GSource *)queue_source); + g_hash_table_remove (priv->queue_sources, context); +} + +static void +soup_session_remove_queue_source_for_item (SoupSession *session, + SoupMessageQueueItem *item) +{ + SoupSessionPrivate *priv = soup_session_get_instance_private (session); + + if (!item->async) + return; + + if (item->context == priv->context) + return; + + g_mutex_lock (&priv->queue_sources_mutex); + soup_session_remove_queue_source (session, item->context); + g_mutex_unlock (&priv->queue_sources_mutex); +} + +static void soup_session_init (SoupSession *session) { SoupSessionPrivate *priv = soup_session_get_instance_private (session); SoupAuthManager *auth_manager; - SoupMessageQueueSource *source; + priv->context = g_main_context_ref_thread_default (); + g_mutex_init (&priv->queue_mutex); priv->queue = g_queue_new (); - priv->queue_source = g_source_new (&queue_source_funcs, sizeof (SoupMessageQueueSource)); - source = (SoupMessageQueueSource *)priv->queue_source; - source->session = session; - g_source_set_name (priv->queue_source, "SoupMessageQueue"); - g_source_set_can_recurse (priv->queue_source, TRUE); - g_source_attach (priv->queue_source, g_main_context_get_thread_default ()); - priv->io_timeout = priv->idle_timeout = 60; + g_mutex_init (&priv->queue_sources_mutex); - priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash, - soup_host_uri_equal, - NULL, (GDestroyNotify)free_host); - priv->https_hosts = g_hash_table_new_full (soup_host_uri_hash, - soup_host_uri_equal, - NULL, (GDestroyNotify)free_host); - priv->conns = g_hash_table_new (NULL, NULL); - - priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT; - priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT; + priv->io_timeout = priv->idle_timeout = 60; - priv->features_cache = g_hash_table_new (NULL, NULL); + priv->conn_manager = soup_connection_manager_new (session, + SOUP_SESSION_MAX_CONNS_DEFAULT, + SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT); auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER, NULL); soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager), @@ -282,19 +318,26 @@ } static void +destroy_queue_source (gpointer key, + GSource *source) +{ + g_source_destroy (source); +} + +static void soup_session_dispose (GObject *object) { SoupSession *session = SOUP_SESSION (object); SoupSessionPrivate *priv = soup_session_get_instance_private (session); - priv->disposed = TRUE; soup_session_abort (session); - g_warn_if_fail (g_hash_table_size (priv->conns) == 0); + g_warn_if_fail (soup_connection_manager_get_num_conns (priv->conn_manager) == 0); while (priv->features) soup_session_remove_feature (session, priv->features->data); - g_source_destroy (priv->queue_source); + if (priv->queue_sources) + g_hash_table_foreach (priv->queue_sources, (GHFunc)destroy_queue_source, NULL); G_OBJECT_CLASS (soup_session_parent_class)->dispose (object); } @@ -307,13 +350,12 @@ g_warn_if_fail (g_queue_is_empty (priv->queue)); g_queue_free (priv->queue); - g_source_unref (priv->queue_source); - - g_clear_object (&priv->remote_connectable); + g_mutex_clear (&priv->queue_mutex); + g_clear_pointer (&priv->queue_sources, g_hash_table_destroy); + g_mutex_clear (&priv->queue_sources_mutex); + g_main_context_unref (priv->context); - g_hash_table_destroy (priv->http_hosts); - g_hash_table_destroy (priv->https_hosts); - g_hash_table_destroy (priv->conns); + g_clear_pointer (&priv->conn_manager, soup_connection_manager_free); g_free (priv->user_agent); g_free (priv->accept_language); @@ -323,8 +365,6 @@ g_clear_object (&priv->local_addr); - g_hash_table_destroy (priv->features_cache); - g_clear_object (&priv->proxy_resolver); g_clear_pointer (&priv->socket_props, soup_socket_properties_unref); @@ -332,13 +372,21 @@ G_OBJECT_CLASS (soup_session_parent_class)->finalize (object); } -static void -ensure_socket_props (SoupSession *session) +GMainContext * +soup_session_get_context (SoupSession *session) +{ + SoupSessionPrivate *priv = soup_session_get_instance_private (session); + + return priv->context; +} + +SoupSocketProperties * +soup_session_ensure_socket_props (SoupSession *session) { SoupSessionPrivate *priv = soup_session_get_instance_private (session); if (priv->socket_props) - return; + return priv->socket_props; priv->socket_props = soup_socket_properties_new (priv->local_addr, priv->tls_interaction, @@ -348,6 +396,8 @@ soup_socket_properties_set_proxy_resolver (priv->socket_props, priv->proxy_resolver); if (!priv->tlsdb_use_default) soup_socket_properties_set_tls_database (priv->socket_props, priv->tlsdb); + + return priv->socket_props; } static void @@ -360,7 +410,7 @@ soup_socket_properties_unref (priv->socket_props); priv->socket_props = NULL; - ensure_socket_props (session); + soup_session_ensure_socket_props (session); } static void @@ -379,10 +429,10 @@ soup_session_set_proxy_resolver (session, g_value_get_object (value)); break; case PROP_MAX_CONNS: - priv->max_conns = g_value_get_int (value); + soup_connection_manager_set_max_conns (priv->conn_manager, g_value_get_int (value)); break; case PROP_MAX_CONNS_PER_HOST: - priv->max_conns_per_host = g_value_get_int (value); + soup_connection_manager_set_max_conns_per_host (priv->conn_manager, g_value_get_int (value)); break; case PROP_TLS_DATABASE: soup_session_set_tls_database (session, g_value_get_object (value)); @@ -403,7 +453,7 @@ soup_session_set_accept_language_auto (session, g_value_get_boolean (value)); break; case PROP_REMOTE_CONNECTABLE: - priv->remote_connectable = g_value_dup_object (value); + soup_connection_manager_set_remote_connectable (priv->conn_manager, g_value_get_object (value)); break; case PROP_IDLE_TIMEOUT: soup_session_set_idle_timeout (session, g_value_get_uint (value)); @@ -468,8 +518,7 @@ * * Creates a #SoupSession with the default options. * - * Returns: the new session. - * + * Returns: (transfer full): the new session. */ SoupSession * soup_session_new (void) @@ -485,7 +534,6 @@ * Creates a #SoupSession with the specified options. * * Returns: the new session. - * */ SoupSession * soup_session_new_with_options (const char *optname1, @@ -503,12 +551,13 @@ } /** - * soup_session_get_local_address: + * soup_session_get_local_address: (attributes org.gtk.Method.get_property=local-address) * @session: a #SoupSession * - * Get the #GInetSocketAddress to use for the client side of connections in @session. + * Get the class@Gio.InetSocketAddress to use for the client side of + * connections in @session. * - * Returns: (transfer none) (nullable): a #GInetSocketAddress or %NULL + * Returns: (transfer none) (nullable): a #GInetSocketAddress */ GInetSocketAddress * soup_session_get_local_address (SoupSession *session) @@ -522,7 +571,7 @@ } /** - * soup_session_get_max_conns: + * soup_session_get_max_conns: (attributes org.gtk.Method.set_property=max-conns) * @session: a #SoupSession * * Get the maximum number of connections that @session can open at once. @@ -537,14 +586,15 @@ g_return_val_if_fail (SOUP_IS_SESSION (session), 0); priv = soup_session_get_instance_private (session); - return priv->max_conns; + return soup_connection_manager_get_max_conns (priv->conn_manager); } /** - * soup_session_get_max_conns_per_host: + * soup_session_get_max_conns_per_host: (attributes org.gtk.Method.get_property=max-conns-per-host) * @session: a #SoupSession * - * Get the maximum number of connections that @session can open at once to a given host. + * Get the maximum number of connections that @session can open at once to a + * given host. * * Returns: the maximum number of connections per host */ @@ -556,16 +606,18 @@ g_return_val_if_fail (SOUP_IS_SESSION (session), 0); priv = soup_session_get_instance_private (session); - return priv->max_conns_per_host; + return soup_connection_manager_get_max_conns_per_host (priv->conn_manager); } /** - * soup_session_set_proxy_resolver: + * soup_session_set_proxy_resolver: (attributes org.gtk.Method.set_property=proxy-resolver) * @session: a #SoupSession * @proxy_resolver: (nullable): a #GProxyResolver or %NULL * - * Set a #GProxyResolver to be used by @session on new connections. If @proxy_resolver - * is %NULL then no proxies will be used. See #SoupSession:proxy-resolver for more information. + * Set a iface@Gio.ProxyResolver to be used by @session on new connections. + * + * If @proxy_resolver is %NULL then no proxies will be used. See + * property@Session:proxy-resolver for more information. */ void soup_session_set_proxy_resolver (SoupSession *session, @@ -588,13 +640,13 @@ } /** - * soup_session_get_proxy_resolver: + * soup_session_get_proxy_resolver: (attributes org.gtk.Method.get_property=proxy-resolver) * @session: a #SoupSession * - * Get the #GProxyResolver currently used by @session. + * Get the iface@Gio.ProxyResolver currently used by @session. * * Returns: (transfer none) (nullable): a #GProxyResolver or %NULL if proxies - * are disabled in @session + * are disabled in @session */ GProxyResolver * soup_session_get_proxy_resolver (SoupSession *session) @@ -608,13 +660,14 @@ } /** - * soup_session_set_tls_database: + * soup_session_set_tls_database: (attributes org.gtk.Method.set_property=tls-database) * @session: a #SoupSession - * @tls_database: (nullable): a #GTlsDatabase or %NULL + * @tls_database: (nullable): a #GTlsDatabase + * + * Set a class@GIo.TlsDatabase to be used by @session on new connections. * - * Set a #GTlsDatabase to be used by @session on new connections. If @tls_database - * is %NULL then certificate validation will always fail. See #SoupSession:tls-database - * for more information. + * If @tls_database is %NULL then certificate validation will always fail. See + * property@Session:tls-database for more information. */ void soup_session_set_tls_database (SoupSession *session, @@ -637,12 +690,12 @@ } /** - * soup_session_get_tls_database: + * soup_session_get_tls_database: (attributes org.gtk.Method.get_property=tls-database) * @session: a #SoupSession * - * Get the #GTlsDatabase currently used by @session. + * Get the class@Gio.TlsDatabase currently used by @session. * - * Returns: (transfer none) (nullable): a #GTlsDatabase or %NULL + * Returns: (transfer none) (nullable): a #GTlsDatabase */ GTlsDatabase * soup_session_get_tls_database (SoupSession *session) @@ -659,13 +712,16 @@ } /** - * soup_session_set_tls_interaction: + * soup_session_set_tls_interaction: (attributes org.gtk.Method.set_property=tls-interaction) * @session: a #SoupSession - * @tls_interaction: (nullable): a #GTlsInteraction or %NULL + * @tls_interaction: (nullable): a #GTlsInteraction * - * Set a #GTlsInteraction to be used by @session on new connections. If @tls_interaction - * is %NULL then client certificate validation will always fail. See #SoupSession:tls-interaction - * for more information. + * Set a class@Gio.TlsInteraction to be used by @session on new connections. + * + * If @tls_interaction is %NULL then client certificate validation will always + * fail. + * + * See property@Session:tls-interaction for more information. */ void soup_session_set_tls_interaction (SoupSession *session, @@ -687,12 +743,12 @@ } /** - * soup_session_get_tls_interaction: + * soup_session_get_tls_interaction: (attributes org.gtk.Method.get_property=tls-interaction) * @session: a #SoupSession * - * Get the #GTlsInteraction currently used by @session. + * Get the class@Gio.TlsInteraction currently used by @session. * - * Returns: (transfer none) (nullable): a #GTlsInteraction or %NULL + * Returns: (transfer none) (nullable): a #GTlsInteraction */ GTlsInteraction * soup_session_get_tls_interaction (SoupSession *session) @@ -706,12 +762,14 @@ } /** - * soup_session_set_timeout: + * soup_session_set_timeout: (attributes org.gtk.Method.set_property=timeout) * @session: a #SoupSession * @timeout: a timeout in seconds * * Set a timeout in seconds for socket I/O operations to be used by @session - * on new connections. See #SoupSession:timeout for more information. + * on new connections. + * + * See property@Session:timeout for more information. */ void soup_session_set_timeout (SoupSession *session, @@ -731,10 +789,11 @@ } /** - * soup_session_get_timeout: + * soup_session_get_timeout: (attributes org.gtk.Method.get_property=timeout) * @session: a #SoupSession * - * Get the timeout in seconds for socket I/O operations currently used by @session. + * Get the timeout in seconds for socket I/O operations currently used by + * @session. * * Returns: the timeout in seconds */ @@ -750,12 +809,14 @@ } /** - * soup_session_set_idle_timeout: + * soup_session_set_idle_timeout: (attributes org.gtk.Method.set_property=idle-timeout) * @session: a #SoupSession * @timeout: a timeout in seconds * * Set a timeout in seconds for idle connection lifetime to be used by @session - * on new connections. See #SoupSession:idle-timeout for more information. + * on new connections. + * + * See property@Session:idle-timeout for more information. */ void soup_session_set_idle_timeout (SoupSession *session, @@ -775,10 +836,11 @@ } /** - * soup_session_get_idle_timeout: + * soup_session_get_idle_timeout: (attributes org.gtk.Method.get_property=idle-timeout) * @session: a #SoupSession * - * Get the timeout in seconds for idle connection lifetime currently used by @session. + * Get the timeout in seconds for idle connection lifetime currently used by + * @session. * * Returns: the timeout in seconds */ @@ -794,15 +856,17 @@ } /** - * soup_session_set_user_agent: + * soup_session_set_user_agent: (attributes org.gtk.Method.set_property=user-agent) * @session: a #SoupSession * @user_agent: the user agent string * - * Set the value to use for the "User-Agent" header on #SoupMessage<!-- -->s sent from @session. - * If @user_agent has trailing whitespace, @session will append its own product token - * (eg, "<literal>libsoup/3.0.0</literal>") to the end of the header for you. - * If @user_agent is %NULL then no "User-Agent" will be included in requests. See #SoupSession:user-agent - * for more information. + * Set the value to use for the "User-Agent" header on class@Messages sent + * from @session. + * + * If @user_agent has trailing whitespace, @session will append its own product + * token (eg, `libsoup/3.0.0`) to the end of the header for you. If @user_agent + * is %NULL then no "User-Agent" will be included in requests. See + * property@Session:user-agent for more information. */ void soup_session_set_user_agent (SoupSession *session, @@ -844,12 +908,12 @@ } /** - * soup_session_get_user_agent: + * soup_session_get_user_agent: (attributes org.gtk.Method.get_property=user-agent) * @session: a #SoupSession * * Get the value used by @session for the "User-Agent" header on new requests. * - * Returns: (transfer none) (nullable): the user agent string or %NULL + * Returns: (transfer none) (nullable): the user agent string */ const char * soup_session_get_user_agent (SoupSession *session) @@ -863,13 +927,15 @@ } /** - * soup_session_set_accept_language: + * soup_session_set_accept_language: (attributes org.gtk.Method.set_property=accept-language) * @session: a #SoupSession * @accept_language: the languages string * - * Set the value to use for the "Accept-Language" header on #SoupMessage<!-- -->s sent from @session. - * If @accept_language is %NULL then no "Accept-Language" will be included in requests. See #SoupSession:accept-language - * for more information. + * Set the value to use for the "Accept-Language" header on class@Messages + * sent from @session. + * + * If @accept_language is %NULL then no "Accept-Language" will be included in + * requests. See property@Session:accept-language for more information. */ void soup_session_set_accept_language (SoupSession *session, @@ -894,12 +960,13 @@ } /** - * soup_session_get_accept_language: + * soup_session_get_accept_language: (attributes org.gtk.Method.get_property=accept-language) * @session: a #SoupSession * - * Get the value used by @session for the "Accept-Language" header on new requests. + * Get the value used by @session for the "Accept-Language" header on new + * requests. * - * Returns: (transfer none) (nullable): the accept language string or %NULL + * Returns: (transfer none) (nullable): the accept language string */ const char * soup_session_get_accept_language (SoupSession *session) @@ -913,13 +980,15 @@ } /** - * soup_session_set_accept_language_auto: + * soup_session_set_accept_language_auto: (attributes org.gtk.Method.set_property=accept-language-auto) * @session: a #SoupSession * @accept_language_auto: the value to set * - * Set whether @session will automatically set the "Accept-Language" header on requests using - * a value generated from system languages based on g_get_language_names(). See #SoupSession:accept-language-auto - * for more information. + * Set whether @session will automatically set the "Accept-Language" header on + * requests using a value generated from system languages based on + * func@GLib.get_language_names. + * + * See property@Session:accept-language-auto for more information. */ void soup_session_set_accept_language_auto (SoupSession *session, @@ -946,12 +1015,14 @@ } /** - * soup_session_get_accept_language_auto: + * soup_session_get_accept_language_auto: (attributes org.gtk.Method.get_property=accept-language-auto) * @session: a #SoupSession * - * Get whether @session automatically sets the "Accept-Language" header on new requests. + * Gets whether @session automatically sets the "Accept-Language" header on new + * requests. * - * Returns: %TRUE if @session sets "Accept-Language" header automatically, or %FALSE otherwise. + * Returns: %TRUE if @session sets "Accept-Language" header automatically, or + * %FALSE otherwise. */ gboolean soup_session_get_accept_language_auto (SoupSession *session) @@ -965,12 +1036,12 @@ } /** - * soup_session_get_remote_connectable: + * soup_session_get_remote_connectable: (attributes org.gtk.Method.get_property=remote-connectable) * @session: a #SoupSession * - * Get the remote connectable if one set. + * Gets the remote connectable if one set. * - * Returns: (transfer none) (nullable): the #GSocketConnectable or %NULL + * Returns: (transfer none) (nullable): the #GSocketConnectable */ GSocketConnectable * soup_session_get_remote_connectable (SoupSession *session) @@ -980,122 +1051,7 @@ g_return_val_if_fail (SOUP_IS_SESSION (session), NULL); priv = soup_session_get_instance_private (session); - return priv->remote_connectable; -} - -/* Hosts */ - -/* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal() - * because we want to ignore the protocol; http://example.com and - * webcal://example.com are the same host. - */ -static guint -soup_host_uri_hash (gconstpointer key) -{ - GUri *uri = (GUri*)key; - - g_return_val_if_fail (uri != NULL && g_uri_get_host (uri) != NULL, 0); - - return g_uri_get_port (uri) + soup_str_case_hash (g_uri_get_host (uri)); -} - -static gboolean -soup_host_uri_equal (gconstpointer v1, gconstpointer v2) -{ - GUri *one = (GUri*)v1; - GUri *two = (GUri*)v2; - - g_return_val_if_fail (one != NULL && two != NULL, one == two); - - const char *one_host = g_uri_get_host (one); - const char *two_host = g_uri_get_host (two); - g_return_val_if_fail (one_host != NULL && two_host != NULL, one_host == two_host); - - if (g_uri_get_port (one) != g_uri_get_port (two)) - return FALSE; - - return g_ascii_strcasecmp (one_host, two_host) == 0; -} - -static SoupSessionHost * -soup_session_host_new (SoupSession *session, GUri *uri) -{ - SoupSessionHost *host; - const char *scheme = g_uri_get_scheme (uri); - - host = g_slice_new0 (SoupSessionHost); - if (g_strcmp0 (scheme, "http") && - g_strcmp0 (scheme, "https")) { - host->uri = soup_uri_copy (uri, - SOUP_URI_SCHEME, soup_uri_is_https (uri) ? - "https" : "http", - SOUP_URI_NONE); - } else - host->uri = g_uri_ref (uri); - - host->addr = g_object_new (G_TYPE_NETWORK_ADDRESS, - "hostname", g_uri_get_host (host->uri), - "port", g_uri_get_port (host->uri), - "scheme", g_uri_get_scheme (host->uri), - NULL); - host->keep_alive_src = NULL; - host->session = session; - - return host; -} - -static SoupSessionHost * -get_host_for_uri (SoupSession *session, GUri *uri) -{ - SoupSessionPrivate *priv = soup_session_get_instance_private (session); - SoupSessionHost *host; - gboolean https; - GUri *uri_tmp = NULL; - - https = soup_uri_is_https (uri); - if (https) - host = g_hash_table_lookup (priv->https_hosts, uri); - else - host = g_hash_table_lookup (priv->http_hosts, uri); - if (host) - return host; - - if (!soup_uri_is_http (uri) && !soup_uri_is_https (uri)) { - uri = uri_tmp = soup_uri_copy (uri, - SOUP_URI_SCHEME, https ? "https" : "http", - SOUP_URI_NONE); - } - host = soup_session_host_new (session, uri); - if (uri_tmp) - g_uri_unref (uri_tmp); - - if (https) - g_hash_table_insert (priv->https_hosts, host->uri, host); - else - g_hash_table_insert (priv->http_hosts, host->uri, host); - - return host; -} - -static SoupSessionHost * -get_host_for_message (SoupSession *session, SoupMessage *msg) -{ - return get_host_for_uri (session, soup_message_get_uri (msg)); -} - -static void -free_host (SoupSessionHost *host) -{ - g_warn_if_fail (host->connections == NULL); - - if (host->keep_alive_src) { - g_source_destroy (host->keep_alive_src); - g_source_unref (host->keep_alive_src); - } - - g_uri_unref (host->uri); - g_object_unref (host->addr); - g_slice_free (SoupSessionHost, host); + return soup_connection_manager_get_remote_connectable (priv->conn_manager); } static SoupMessageQueueItem * @@ -1106,7 +1062,9 @@ SoupSessionPrivate *priv = soup_session_get_instance_private (session); GList *link; + g_mutex_lock (&priv->queue_mutex); link = g_queue_find_custom (priv->queue, data, compare_func); + g_mutex_unlock (&priv->queue_mutex); return link ? (SoupMessageQueueItem *)link->data : NULL; } @@ -1128,7 +1086,13 @@ lookup_connection (SoupMessageQueueItem *item, SoupConnection *conn) { - return soup_message_get_connection (item->msg) == conn ? 0 : 1; + SoupConnection *connection = soup_message_get_connection (item->msg); + int retval; + + retval = connection == conn ? 0 : 1; + g_clear_object (&connection); + + return retval; } static SoupMessageQueueItem * @@ -1237,9 +1201,8 @@ * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS. * * Returns: %TRUE if a redirection was applied, %FALSE if not - * (eg, because there was no Location header, or it could not be - * parsed). - * + * (eg, because there was no Location header, or it could not be + * parsed). */ static gboolean soup_session_redirect_message (SoupSession *session, @@ -1319,6 +1282,7 @@ SOUP_STATUS_IS_REDIRECTION (soup_message_get_status (msg)))) { soup_message_set_connection (item->msg, NULL); } + g_clear_object (&conn); soup_message_cleanup_response (msg); } @@ -1341,13 +1305,15 @@ { SoupSessionPrivate *priv = soup_session_get_instance_private (item->session); - if (priv->in_async_run_queue) { - priv->needs_queue_sort = TRUE; + if (g_atomic_int_get (&priv->in_async_run_queue)) { + g_atomic_int_set (&priv->needs_queue_sort, TRUE); return; } + g_mutex_lock (&priv->queue_mutex); g_queue_sort (priv->queue, (GCompareDataFunc)compare_queue_item, NULL); - priv->needs_queue_sort = FALSE; + g_mutex_unlock (&priv->queue_mutex); + g_atomic_int_set (&priv->needs_queue_sort, FALSE); } static SoupMessageQueueItem * @@ -1358,7 +1324,6 @@ { SoupSessionPrivate *priv = soup_session_get_instance_private (session); SoupMessageQueueItem *item; - SoupSessionHost *host; GSList *f; soup_message_set_metrics_timestamp (msg, SOUP_MESSAGE_METRICS_FETCH_START); @@ -1366,12 +1331,16 @@ soup_message_set_is_preconnect (msg, FALSE); item = soup_message_queue_item_new (session, msg, async, cancellable); + g_mutex_lock (&priv->queue_mutex); g_queue_insert_sorted (priv->queue, soup_message_queue_item_ref (item), (GCompareDataFunc)compare_queue_item, NULL); + g_mutex_unlock (&priv->queue_mutex); - host = get_host_for_message (session, item->msg); - host->num_messages++; + soup_session_add_queue_source_for_item (session, item); + + if (async) + g_atomic_int_inc (&priv->num_async_items); if (!soup_message_query_flags (msg, SOUP_MESSAGE_NO_REDIRECT)) { soup_message_add_header_handler ( @@ -1405,6 +1374,9 @@ SoupSessionPrivate *priv = soup_session_get_instance_private (session); SoupMessageHeaders *request_headers; const char *method; + SoupConnection *conn; + + g_assert (item->context == soup_thread_default_context ()); request_headers = soup_message_get_request_headers (item->msg); if (priv->user_agent) @@ -1413,7 +1385,9 @@ if (priv->accept_language && !soup_message_headers_get_list_common (request_headers, SOUP_HEADER_ACCEPT_LANGUAGE)) soup_message_headers_append_common (request_headers, SOUP_HEADER_ACCEPT_LANGUAGE, priv->accept_language); - soup_message_set_http_version (item->msg, soup_connection_get_negotiated_protocol (soup_message_get_connection (item->msg))); + conn = soup_message_get_connection (item->msg); + soup_message_set_http_version (item->msg, soup_connection_get_negotiated_protocol (conn)); + g_object_unref (conn); soup_message_force_keep_alive_if_needed (item->msg); soup_message_update_request_host_if_needed (item->msg); @@ -1436,121 +1410,11 @@ soup_message_send_item (item->msg, item, completion_cb, item); } -static gboolean -soup_session_cleanup_connections (SoupSession *session, - gboolean cleanup_idle) -{ - SoupSessionPrivate *priv = soup_session_get_instance_private (session); - GSList *conns = NULL, *c; - GHashTableIter iter; - gpointer conn, host; - SoupConnectionState state; - - g_hash_table_iter_init (&iter, priv->conns); - while (g_hash_table_iter_next (&iter, &conn, &host)) { - state = soup_connection_get_state (conn); - if (state == SOUP_CONNECTION_IDLE && - (cleanup_idle || !soup_connection_is_idle_open (conn))) { - conns = g_slist_prepend (conns, g_object_ref (conn)); - g_hash_table_iter_remove (&iter); - drop_connection (session, host, conn); - } - } - - if (!conns) - return FALSE; - - for (c = conns; c; c = c->next) { - conn = c->data; - soup_connection_disconnect (conn); - g_object_unref (conn); - } - g_slist_free (conns); - - return TRUE; -} - -static gboolean -free_unused_host (gpointer user_data) -{ - SoupSessionHost *host = (SoupSessionHost *) user_data; - SoupSessionPrivate *priv = soup_session_get_instance_private (host->session); - GUri *uri = host->uri; - - if (host->connections) - return FALSE; - - /* This will free the host in addition to removing it from the - * hash table - */ - if (soup_uri_is_https (uri)) - g_hash_table_remove (priv->https_hosts, uri); - else - g_hash_table_remove (priv->http_hosts, uri); - - return FALSE; -} - -static void -drop_connection (SoupSession *session, SoupSessionHost *host, SoupConnection *conn) -{ - SoupSessionPrivate *priv = soup_session_get_instance_private (session); - - if (host) { - host->connections = g_slist_remove (host->connections, conn); - host->num_conns--; - - /* Free the SoupHost (and its GNetworkAddress) if there - * has not been any new connection to the host during - * the last HOST_KEEP_ALIVE msecs. - */ - if (host->num_conns == 0) { - g_assert (host->keep_alive_src == NULL); - host->keep_alive_src = soup_add_timeout (g_main_context_get_thread_default (), - HOST_KEEP_ALIVE, - free_unused_host, - host); - } - } - - g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session); - g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session); - priv->num_conns--; - - g_object_unref (conn); -} - -static void -connection_disconnected (SoupConnection *conn, gpointer user_data) -{ - SoupSession *session = user_data; - SoupSessionPrivate *priv = soup_session_get_instance_private (session); - SoupSessionHost *host; - - host = g_hash_table_lookup (priv->conns, conn); - if (host) - g_hash_table_remove (priv->conns, conn); - drop_connection (session, host, conn); - - soup_session_kick_queue (session); -} - -static void -connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data) -{ - SoupSession *session = user_data; - SoupConnection *conn = SOUP_CONNECTION (object); - - if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE && soup_connection_is_idle_open (conn)) - soup_session_kick_queue (session); -} - static void soup_session_unqueue_item (SoupSession *session, SoupMessageQueueItem *item) { SoupSessionPrivate *priv = soup_session_get_instance_private (session); - SoupSessionHost *host; GSList *f; soup_message_set_connection (item->msg, NULL); @@ -1560,10 +1424,14 @@ return; } + g_mutex_lock (&priv->queue_mutex); g_queue_remove (priv->queue, item); + g_mutex_unlock (&priv->queue_mutex); + + soup_session_remove_queue_source_for_item (session, item); - host = get_host_for_message (session, item->msg); - host->num_messages--; + if (item->async) + g_atomic_int_dec_and_test (&priv->num_async_items); /* g_signal_handlers_disconnect_by_func doesn't work if you * have a metamarshal, meaning it doesn't work with @@ -1587,6 +1455,8 @@ { SoupMessageQueueItem *item = user_data; + g_assert (item->context == soup_thread_default_context ()); + if (item->async) soup_session_kick_queue (item->session); @@ -1603,7 +1473,7 @@ item->state = SOUP_MESSAGE_FINISHING; if (!item->async) - soup_session_process_queue_item (item->session, item, NULL, TRUE); + soup_session_process_queue_item (item->session, item, TRUE); } } @@ -1624,7 +1494,10 @@ item->error = error; if (!SOUP_STATUS_IS_SUCCESSFUL (status) || item->error) { - soup_connection_disconnect (soup_message_get_connection (item->msg)); + SoupConnection *conn = soup_message_get_connection (item->msg); + + soup_connection_disconnect (conn); + g_object_unref (conn); soup_message_set_connection (item->msg, NULL); if (!error && soup_message_get_status (item->msg) == SOUP_STATUS_NONE) soup_message_set_status (item->msg, status, NULL); @@ -1655,12 +1528,20 @@ SoupSession *session = tunnel_item->session; guint status; + g_assert (tunnel_item->context == soup_thread_default_context ()); + if (tunnel_item->state == SOUP_MESSAGE_REQUEUED) tunnel_item->state = SOUP_MESSAGE_RESTARTING; if (tunnel_item->state == SOUP_MESSAGE_RESTARTING) { + SoupConnection *conn; + soup_message_restarted (msg); - if (soup_message_get_connection (tunnel_item->msg)) { + + conn = soup_message_get_connection (tunnel_item->msg); + if (conn) { + g_object_unref (conn); + g_clear_object (&tunnel_item->error); tunnel_item->state = SOUP_MESSAGE_RUNNING; soup_session_send_queue_item (session, tunnel_item, (SoupMessageIOCompletionFn)tunnel_message_completed); @@ -1675,21 +1556,26 @@ soup_session_unqueue_item (session, tunnel_item); status = soup_message_get_status (tunnel_item->msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (status) || item->state == SOUP_MESSAGE_RESTARTING) { - tunnel_complete (tunnel_item, status, NULL); + if (!SOUP_STATUS_IS_SUCCESSFUL (status) || tunnel_item->error || item->state == SOUP_MESSAGE_RESTARTING) { + tunnel_complete (tunnel_item, status, g_steal_pointer (&tunnel_item->error)); return; } if (tunnel_item->async) { - soup_connection_tunnel_handshake_async (soup_message_get_connection (item->msg), + SoupConnection *conn = soup_message_get_connection (item->msg); + + soup_connection_tunnel_handshake_async (conn, item->io_priority, item->cancellable, (GAsyncReadyCallback)tunnel_handshake_complete, tunnel_item); + g_object_unref (conn); } else { + SoupConnection *conn = soup_message_get_connection (item->msg); GError *error = NULL; - soup_connection_tunnel_handshake (soup_message_get_connection (item->msg), item->cancellable, &error); + soup_connection_tunnel_handshake (conn, item->cancellable, &error); + g_object_unref (conn); tunnel_complete (tunnel_item, SOUP_STATUS_OK, error); } } @@ -1700,6 +1586,7 @@ SoupSession *session = item->session; SoupMessageQueueItem *tunnel_item; SoupMessage *msg; + SoupConnection *conn; item->state = SOUP_MESSAGE_TUNNELING; @@ -1711,7 +1598,9 @@ item->cancellable); tunnel_item->io_priority = item->io_priority; tunnel_item->related = soup_message_queue_item_ref (item); - soup_message_set_connection (tunnel_item->msg, soup_message_get_connection (item->msg)); + conn = soup_message_get_connection (item->msg); + soup_message_set_connection (tunnel_item->msg, conn); + g_clear_object (&conn); tunnel_item->state = SOUP_MESSAGE_RUNNING; soup_session_send_queue_item (session, tunnel_item, @@ -1754,7 +1643,7 @@ /* Complete the preconnect successfully, since it was stolen. */ item->state = SOUP_MESSAGE_FINISHING; item->related = NULL; - soup_session_process_queue_item (item->session, item, NULL, FALSE); + soup_session_process_queue_item (item->session, item, FALSE); soup_message_queue_item_unref (item); item = new_item; @@ -1770,10 +1659,10 @@ soup_message_queue_item_unref (item); } -static gboolean -steal_preconnection (SoupSession *session, - SoupMessageQueueItem *item, - SoupConnection *conn) +gboolean +soup_session_steal_preconnection (SoupSession *session, + SoupMessageQueueItem *item, + SoupConnection *conn) { SoupMessageQueueItem *preconnect_item; @@ -1797,158 +1686,16 @@ return TRUE; } -static SoupConnection * -get_connection_for_host (SoupSession *session, - SoupMessageQueueItem *item, - SoupSessionHost *host, - gboolean need_new_connection, - gboolean *try_cleanup) -{ - SoupSessionPrivate *priv = soup_session_get_instance_private (session); - GSocketConnectable *remote_connectable; - gboolean force_http1; - SoupConnection *conn; - GSList *conns; - - if (priv->disposed) - return NULL; - - conn = soup_message_get_connection (item->msg); - if (conn) { - g_return_val_if_fail (soup_connection_get_state (conn) != SOUP_CONNECTION_DISCONNECTED, FALSE); - return conn; - } - - if (g_getenv ("SOUP_FORCE_HTTP1")) - force_http1 = TRUE; - else - force_http1 = soup_message_get_force_http1 (item->msg); - - for (conns = host->connections; conns; conns = conns->next) { - SoupHTTPVersion http_version; - - conn = conns->data; - - http_version = soup_connection_get_negotiated_protocol (conn); - if (force_http1 && http_version > SOUP_HTTP_1_1) - continue; - - switch (soup_connection_get_state (conn)) { - case SOUP_CONNECTION_IN_USE: - if (!need_new_connection && http_version == SOUP_HTTP_2_0 && soup_connection_is_reusable (conn)) - return conn; - break; - case SOUP_CONNECTION_IDLE: - if (!need_new_connection && soup_connection_is_idle_open (conn)) - return conn; - break; - case SOUP_CONNECTION_CONNECTING: - if (steal_preconnection (session, item, conn)) - return conn; - - /* Always wait if we have a pending connection as it may be - * an h2 connection which will be shared. http/1.x connections - * will only be slightly delayed. */ - if (!force_http1 && !need_new_connection && !item->connect_only) - return NULL; - default: - break; - } - } - - if (host->num_conns >= priv->max_conns_per_host) { - if (need_new_connection) - *try_cleanup = TRUE; - return NULL; - } - - if (priv->num_conns >= priv->max_conns) { - *try_cleanup = TRUE; - return NULL; - } - - if (priv->remote_connectable == NULL) { - remote_connectable = - g_object_new (G_TYPE_NETWORK_ADDRESS, - "hostname", g_uri_get_host (host->uri), - "port", g_uri_get_port (host->uri), - "scheme", g_uri_get_scheme (host->uri), - NULL); - } else { - remote_connectable = g_object_ref (priv->remote_connectable); - } - - ensure_socket_props (session); - conn = g_object_new (SOUP_TYPE_CONNECTION, - "id", ++priv->last_connection_id, - "remote-connectable", remote_connectable, - "ssl", soup_uri_is_https (host->uri), - "socket-properties", priv->socket_props, - "force-http1", force_http1, - NULL); - g_object_unref (remote_connectable); - - g_signal_connect (conn, "disconnected", - G_CALLBACK (connection_disconnected), - session); - g_signal_connect (conn, "notify::state", - G_CALLBACK (connection_state_changed), - session); - - g_hash_table_insert (priv->conns, conn, host); - - priv->num_conns++; - host->num_conns++; - host->connections = g_slist_prepend (host->connections, conn); - - if (host->keep_alive_src) { - g_source_destroy (host->keep_alive_src); - g_source_unref (host->keep_alive_src); - host->keep_alive_src = NULL; - } - - return conn; -} - static gboolean -get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup) +soup_session_ensure_item_connection (SoupSession *session, + SoupMessageQueueItem *item) { - SoupSession *session = item->session; - SoupSessionHost *host; - SoupConnection *conn = NULL; - gboolean my_should_cleanup = FALSE; - gboolean need_new_connection; - - soup_session_cleanup_connections (session, FALSE); - - need_new_connection = - (soup_message_query_flags (item->msg, SOUP_MESSAGE_NEW_CONNECTION)) || - (soup_message_is_misdirected_retry (item->msg)) || - (!soup_message_query_flags (item->msg, SOUP_MESSAGE_IDEMPOTENT) && - !SOUP_METHOD_IS_IDEMPOTENT (soup_message_get_method (item->msg))); - - host = get_host_for_message (session, item->msg); - while (TRUE) { - conn = get_connection_for_host (session, item, host, - need_new_connection, - &my_should_cleanup); - if (conn || item->async) - break; - - if (my_should_cleanup) { - soup_session_cleanup_connections (session, TRUE); - my_should_cleanup = FALSE; - continue; - } - } + SoupSessionPrivate *priv = soup_session_get_instance_private (session); + SoupConnection *conn; - if (!conn) { - if (should_cleanup) - *should_cleanup = my_should_cleanup; + conn = soup_connection_manager_get_connection (priv->conn_manager, item); + if (!conn) return FALSE; - } - - soup_message_set_connection (item->msg, conn); switch (soup_connection_get_state (conn)) { case SOUP_CONNECTION_IN_USE: @@ -1986,10 +1733,10 @@ static void soup_session_process_queue_item (SoupSession *session, SoupMessageQueueItem *item, - gboolean *should_cleanup, gboolean loop) { g_assert (item->session == session); + g_assert (item->context == soup_thread_default_context ()); do { if (item->paused) @@ -1997,17 +1744,20 @@ switch (item->state) { case SOUP_MESSAGE_STARTING: - if (!get_connection (item, should_cleanup)) + if (!soup_session_ensure_item_connection (session, item)) return; break; - case SOUP_MESSAGE_CONNECTED: - if (soup_connection_is_tunnelled (soup_message_get_connection (item->msg))) + case SOUP_MESSAGE_CONNECTED: { + SoupConnection *conn = soup_message_get_connection (item->msg); + + if (soup_connection_is_tunnelled (conn)) tunnel_connect (item); else item->state = SOUP_MESSAGE_READY; + g_object_unref (conn); break; - + } case SOUP_MESSAGE_READY: if (item->connect_only) { item->state = SOUP_MESSAGE_FINISHING; @@ -2067,50 +1817,51 @@ } static void -process_queue_item (SoupMessageQueueItem *item, - gboolean *should_cleanup) +collect_queue_item (SoupMessageQueueItem *item, + GList **items) { if (!item->async) return; + if (item->context != soup_thread_default_context ()) + return; + /* CONNECT messages are handled specially */ if (soup_message_get_method (item->msg) == SOUP_METHOD_CONNECT) return; - soup_session_process_queue_item (item->session, item, should_cleanup, TRUE); + *items = g_list_prepend (*items, item); } static void async_run_queue (SoupSession *session) { SoupSessionPrivate *priv = soup_session_get_instance_private (session); - gboolean try_cleanup = TRUE, should_cleanup = FALSE; + GList *items = NULL; + GList *i; - g_object_ref (session); - priv->in_async_run_queue++; - soup_session_cleanup_connections (session, FALSE); - - try_again: - g_queue_foreach (priv->queue, (GFunc)process_queue_item, &should_cleanup); - - if (try_cleanup && should_cleanup) { - /* There is at least one message in the queue that - * could be sent if we cleanupd an idle connection from - * some other server. - */ - if (soup_session_cleanup_connections (session, TRUE)) { - try_cleanup = should_cleanup = FALSE; - goto try_again; - } - } + g_atomic_int_inc (&priv->in_async_run_queue); + soup_connection_manager_cleanup (priv->conn_manager, FALSE); - priv->in_async_run_queue--; - if (!priv->in_async_run_queue && priv->needs_queue_sort) { - g_queue_sort (priv->queue, (GCompareDataFunc)compare_queue_item, NULL); - priv->needs_queue_sort = FALSE; + g_mutex_lock (&priv->queue_mutex); + g_queue_foreach (priv->queue, (GFunc)collect_queue_item, &items); + g_mutex_unlock (&priv->queue_mutex); + + items = g_list_reverse (items); + + for (i = items; i != NULL; i = g_list_next (i)) { + SoupMessageQueueItem *item = (SoupMessageQueueItem *)i->data; + soup_session_process_queue_item (item->session, item, TRUE); } - g_object_unref (session); + g_list_free (items); + + if (g_atomic_int_dec_and_test (&priv->in_async_run_queue) && g_atomic_int_get (&priv->needs_queue_sort)) { + g_mutex_lock (&priv->queue_mutex); + g_queue_sort (priv->queue, (GCompareDataFunc)compare_queue_item, NULL); + g_mutex_unlock (&priv->queue_mutex); + g_atomic_int_set (&priv->needs_queue_sort, FALSE); + } } /** @@ -2135,8 +1886,7 @@ * @session: a #SoupSession * @msg: a #SoupMessage currently running on @session * - * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to - * resume I/O. + * Pauses HTTP I/O on @msg. Call method@Session.unpause_message to resume I/O. **/ void soup_session_pause_message (SoupSession *session, @@ -2157,11 +1907,24 @@ } static void +kick_queue_source (gpointer key, + GSource *source) +{ + g_source_set_ready_time (source, 0); +} + +void soup_session_kick_queue (SoupSession *session) { SoupSessionPrivate *priv = soup_session_get_instance_private (session); - g_source_set_ready_time (priv->queue_source, 0); + if (g_atomic_int_get (&priv->num_async_items) <= 0) + return; + + g_mutex_lock (&priv->queue_sources_mutex); + if (priv->queue_sources) + g_hash_table_foreach (priv->queue_sources, (GHFunc)kick_queue_source, NULL); + g_mutex_unlock (&priv->queue_sources_mutex); } /** @@ -2170,7 +1933,7 @@ * @msg: a #SoupMessage currently running on @session * * Resumes HTTP I/O on @msg. Use this to resume after calling - * soup_session_pause_message(). + * method@Session.pause_message. * * If @msg is being sent via blocking I/O, this will resume reading or * writing immediately. If @msg is using non-blocking I/O, then @@ -2215,42 +1978,23 @@ * * Cancels all pending requests in @session and closes all idle * persistent connections. - * */ void soup_session_abort (SoupSession *session) { SoupSessionPrivate *priv; - GSList *conns, *c; - GHashTableIter iter; - gpointer conn, host; g_return_if_fail (SOUP_IS_SESSION (session)); + priv = soup_session_get_instance_private (session); /* Cancel everything */ + g_mutex_lock (&priv->queue_mutex); g_queue_foreach (priv->queue, (GFunc)soup_message_queue_item_cancel, NULL); + g_mutex_unlock (&priv->queue_mutex); /* Close all idle connections */ - conns = NULL; - g_hash_table_iter_init (&iter, priv->conns); - while (g_hash_table_iter_next (&iter, &conn, &host)) { - SoupConnectionState state; - - state = soup_connection_get_state (conn); - if (state == SOUP_CONNECTION_IDLE) { - conns = g_slist_prepend (conns, g_object_ref (conn)); - g_hash_table_iter_remove (&iter); - drop_connection (session, host, conn); - } - } - - for (c = conns; c; c = c->next) { - soup_connection_disconnect (c->data); - g_object_unref (c->data); - } - - g_slist_free (conns); + soup_connection_manager_cleanup (priv->conn_manager, TRUE); } static gboolean @@ -2271,11 +2015,10 @@ * @feature: an object that implements #SoupSessionFeature * * Adds @feature's functionality to @session. You cannot add multiple - * features of the same #GType to a session. + * features of the same alias@GLib.Type to a session. * * See the main #SoupSession documentation for information on what * features are present in sessions by default. - * **/ void soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature) @@ -2291,7 +2034,6 @@ return; priv->features = g_slist_prepend (priv->features, g_object_ref (feature)); - g_hash_table_remove_all (priv->features_cache); soup_session_feature_attach (feature, session); } @@ -2301,18 +2043,17 @@ * @feature_type: a #GType * * If @feature_type is the type of a class that implements - * #SoupSessionFeature, this creates a new feature of that type and - * adds it to @session as with soup_session_add_feature(). You can use + * iface@SessionFeature, this creates a new feature of that type and + * adds it to @session as with method@Session.add_feature. You can use * this when you don't need to customize the new feature in any way. * Adding multiple features of the same @feature_type is not allowed. * - * If @feature_type is not a #SoupSessionFeature type, this gives each + * If @feature_type is not a iface@SessionFeature type, this gives each * existing feature on @session the chance to accept @feature_type as - * a "subfeature". This can be used to add new #SoupAuth types, for instance. + * a "subfeature". This can be used to add new class@Auth types, for instance. * * See the main #SoupSession documentation for information on what * features are present in sessions by default. - * **/ void soup_session_add_feature_by_type (SoupSession *session, GType feature_type) @@ -2349,7 +2090,6 @@ * @feature: a feature that has previously been added to @session * * Removes @feature's functionality from @session. - * **/ void soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature) @@ -2361,7 +2101,6 @@ priv = soup_session_get_instance_private (session); if (g_slist_find (priv->features, feature)) { priv->features = g_slist_remove (priv->features, feature); - g_hash_table_remove_all (priv->features_cache); soup_session_feature_detach (feature, session); g_object_unref (feature); } @@ -2373,10 +2112,7 @@ * @feature_type: a #GType * * Removes all features of type @feature_type (or any subclass of - * @feature_type) from @session. You can also remove standard features - * from the session at construct time by using the - * SoupSession:remove-feature-by-type property. - * + * @feature_type) from @session. **/ void soup_session_remove_feature_by_type (SoupSession *session, GType feature_type) @@ -2411,11 +2147,10 @@ * @feature_type: the #GType of the class of features to check for * * Tests if @session has at a feature of type @feature_type (which can - * be the type of either a #SoupSessionFeature, or else a subtype of - * some class managed by another feature, such as #SoupAuth). + * be the type of either a iface@SessionFeature, or else a subtype of + * some class managed by another feature, such as class@Auth). * * Returns: %TRUE or %FALSE - * **/ gboolean soup_session_has_feature (SoupSession *session, @@ -2453,8 +2188,7 @@ * for @feature_type.) * * Returns: (transfer container) (element-type Soup.SessionFeature): - * a list of features. You must free the list, but not its contents - * + * a list of features. You must free the list, but not its contents **/ GSList * soup_session_get_features (SoupSession *session, GType feature_type) @@ -2479,9 +2213,8 @@ * * Gets the feature in @session of type @feature_type. * - * Returns: (nullable) (transfer none): a #SoupSessionFeature, or - * %NULL. The feature is owned by @session. - * + * Returns: (nullable) (transfer none): a #SoupSessionFeature, or %NULL. The + * feature is owned by @session. **/ SoupSessionFeature * soup_session_get_feature (SoupSession *session, GType feature_type) @@ -2494,19 +2227,10 @@ priv = soup_session_get_instance_private (session); - feature = g_hash_table_lookup (priv->features_cache, - GSIZE_TO_POINTER (feature_type)); - if (feature) - return feature; - for (f = priv->features; f; f = f->next) { feature = f->data; - if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) { - g_hash_table_insert (priv->features_cache, - GSIZE_TO_POINTER (feature_type), - feature); + if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) return feature; - } } return NULL; } @@ -2520,9 +2244,8 @@ * Gets the feature in @session of type @feature_type, provided * that it is not disabled for @msg. * - * Returns: (nullable) (transfer none): a #SoupSessionFeature, or %NULL. The - * feature is owned by @session. - * + * Returns: (nullable) (transfer none): a #SoupSessionFeature. The feature is + * owned by @session. **/ SoupSessionFeature * soup_session_get_feature_for_message (SoupSession *session, GType feature_type, @@ -2605,17 +2328,17 @@ * * Emitted when a request is queued on @session. * - * When sending a request, first #SoupSession::request_queued + * When sending a request, first signal@Session::request-queued * is emitted, indicating that the session has become aware of * the request. * * After a connection is available to send the request various - * #SoupMessage signals are emitted as the message is + * class@Message signals are emitted as the message is * processed. If the message is requeued, it will emit - * #SoupMessage::restarted, which will then be followed by other - * #SoupMessage signals when the message is re-sent. + * signal@Message::restarted, which will then be followed by other + * class@Message signals when the message is re-sent. * - * Eventually, the message will emit #SoupMessage::finished. + * Eventually, the message will emit signal@Message::finished. * Normally, this signals the completion of message * processing. However, it is possible that the application * will requeue the message from the "finished" handler. @@ -2623,15 +2346,14 @@ * * Eventually, a message will reach "finished" and not be * requeued. At that point, the session will emit - * #SoupSession::request_unqueued to indicate that it is done + * signal@Session::request-unqueued to indicate that it is done * with the message. * - * To sum up: #SoupSession::request_queued and - * #SoupSession::request_unqueued are guaranteed to be emitted - * exactly once, but #SoupMessage::finished (and all of the - * other #SoupMessage signals) may be invoked multiple times - * for a given message. - * + * To sum up: signal@Session::request-queued and + * signal@Session::request-unqueued are guaranteed to be emitted + * exactly once, but signal@Message::finished (and all of the other + * class@Message signals) may be invoked multiple times for a given + * message. **/ signalsREQUEST_QUEUED = g_signal_new ("request-queued", @@ -2649,10 +2371,10 @@ * @msg: the request that was unqueued * * Emitted when a request is removed from @session's queue, - * indicating that @session is done with it. See - * #SoupSession::request_queued for a detailed description of the - * message lifecycle within a session. + * indicating that @session is done with it. * + * See signal@Session::request-queued for a detailed description of + * the message lifecycle within a session. **/ signalsREQUEST_UNQUEUED = g_signal_new ("request-unqueued", @@ -2666,16 +2388,15 @@ /* properties */ /** - * SoupSession:proxy-resolver: + * SoupSession:proxy-resolver: (attributes org.gtk.Property.get=soup_session_get_proxy_resolver org.gtk.Property.set=soup_session_set_proxy_resolver) * - * A #GProxyResolver to use with this session. + * A iface@Gio.ProxyResolver to use with this session. * * If no proxy resolver is set, then the default proxy resolver - * will be used. See g_proxy_resolver_get_default(). + * will be used. See func@Gio.ProxyResolver.get_default. * You can set it to %NULL if you don't want to use proxies, or - * set it to your own #GProxyResolver if you want to control + * set it to your own iface@Gio.ProxyResolver if you want to control * what proxies get used. - * */ propertiesPROP_PROXY_RESOLVER = g_param_spec_object ("proxy-resolver", @@ -2685,7 +2406,7 @@ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** - * SoupSession:max-conns: + * SoupSession:max-conns: (attributes org.gtk.Property.get=soup_session_get_max_conns) * * The maximum number of connections that the session can open at once. */ @@ -2701,9 +2422,10 @@ G_PARAM_STATIC_STRINGS); /** - * SoupSession:max-conns-per-host: + * SoupSession:max-conns-per-host: (attributes org.gtk.Property.get=soup_session_get_max_conns_per_host) * - * The maximum number of connections that the session can open at once to a given host. + * The maximum number of connections that the session can open at once + * to a given host. */ propertiesPROP_MAX_CONNS_PER_HOST = g_param_spec_int ("max-conns-per-host", @@ -2716,17 +2438,16 @@ G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** - * SoupSession:idle-timeout: + * SoupSession:idle-timeout: (attributes org.gtk.Property.get=soup_session_get_idle_timeout org.gtk.Property.set=soup_session_set_idle_timeout) * * Connection lifetime (in seconds) when idle. Any connection * left idle longer than this will be closed. * * Although you can change this property at any time, it will * only affect newly-created connections, not currently-open - * ones. You can call soup_session_abort() after setting this + * ones. You can call method@Session.abort after setting this * if you want to ensure that all future connections will have * this timeout value. - * **/ propertiesPROP_IDLE_TIMEOUT = g_param_spec_uint ("idle-timeout", @@ -2737,14 +2458,13 @@ G_PARAM_STATIC_STRINGS); /** - * SoupSession:tls-database: + * SoupSession:tls-database: (attributes org.gtk.Property.get=soup_session_get_tls_database org.gtk.Property.set=soup_session_set_tls_database) * - * Sets the #GTlsDatabase to use for validating SSL/TLS + * Sets the class@Gio.TlsDatabase to use for validating SSL/TLS * certificates. * * If no certificate database is set, then the default database will be - * used. See g_tls_backend_get_default_database(). - * + * used. See method@Gio.TlsBackend.get_default_database. **/ propertiesPROP_TLS_DATABASE = g_param_spec_object ("tls-database", @@ -2755,7 +2475,7 @@ G_PARAM_STATIC_STRINGS); /** - * SoupSession:timeout: + * SoupSession:timeout: (attributes org.gtk.Property.get=soup_session_get_timeout org.gtk.Property.set=soup_session_set_timeout) * * The timeout (in seconds) for socket I/O operations * (including connecting to a server, and waiting for a reply @@ -2763,11 +2483,11 @@ * * Although you can change this property at any time, it will * only affect newly-created connections, not currently-open - * ones. You can call soup_session_abort() after setting this + * ones. You can call method@Session.abort after setting this * if you want to ensure that all future connections will have * this timeout value. * - * Not to be confused with #SoupSession:idle-timeout (which is + * Not to be confused with property@Session:idle-timeout (which is * the length of time that idle persistent connections will be * kept open). */ @@ -2780,10 +2500,12 @@ G_PARAM_STATIC_STRINGS); /** - * SoupSession:user-agent: + * SoupSession:user-agent: (attributes org.gtk.Property.get=soup_session_get_user_agent org.gtk.Property.set=soup_session_set_user_agent) + * + * User-Agent string. * * If non-%NULL, the value to use for the "User-Agent" header - * on #SoupMessage<!-- -->s sent from this session. + * on class@Messages sent from this session. * * RFC 2616 says: "The User-Agent request-header field * contains information about the user agent originating the @@ -2801,9 +2523,9 @@ * followed by a version string. You may also put comments, * enclosed in parentheses, between or after the tokens. * - * If you set a #SoupSession:user_agent property that has trailing + * If you set a property@Session:user-agent property that has trailing * whitespace, #SoupSession will append its own product token - * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the + * (eg, `libsoup/2.3.2`) to the end of the * header for you. **/ propertiesPROP_USER_AGENT = @@ -2815,13 +2537,12 @@ G_PARAM_STATIC_STRINGS); /** - * SoupSession:accept-language: + * SoupSession:accept-language: (attributes org.gtk.Property.get=soup_session_get_accept_language org.gtk.Property.set=soup_session_set_accept_language) * * If non-%NULL, the value to use for the "Accept-Language" header - * on #SoupMessage<!-- -->s sent from this session. - * - * Setting this will disable #SoupSession:accept-language-auto. + * on class@Messages sent from this session. * + * Setting this will disable property@Session:accept-language-auto. **/ propertiesPROP_ACCEPT_LANGUAGE = g_param_spec_string ("accept-language", @@ -2832,15 +2553,14 @@ G_PARAM_STATIC_STRINGS); /** - * SoupSession:accept-language-auto: + * SoupSession:accept-language-auto: (attributes org.gtk.Property.get=soup_session_get_accept_language_auto org.gtk.Property.set=soup_session_set_accept_language_auto) * * If %TRUE, #SoupSession will automatically set the string - * for the "Accept-Language" header on every #SoupMessage - * sent, based on the return value of g_get_language_names(). + * for the "Accept-Language" header on every class@Message + * sent, based on the return value of func@GLib.get_language_names. * * Setting this will override any previous value of - * #SoupSession:accept-language. - * + * property@Session:accept-language. **/ propertiesPROP_ACCEPT_LANGUAGE_AUTO = g_param_spec_boolean ("accept-language-auto", @@ -2851,7 +2571,7 @@ G_PARAM_STATIC_STRINGS); /** - * SoupSession:remote-connectable: + * SoupSession:remote-connectable: (attributes org.gtk.Property.get=soup_session_get_remote_connectable) * * Sets a socket to make outgoing connections on. This will override the default * behaviour of opening TCP/IP sockets to the hosts specified in the URIs. @@ -2859,8 +2579,7 @@ * This function is not required for common HTTP usage, but only when connecting * to a HTTP service that is not using standard TCP/IP sockets. An example of * this is a local service that uses HTTP over UNIX-domain sockets, in that case - * a #GUnixSocketAddress can be passed to this function. - * + * a class@Gio.UnixSocketAddress can be passed to this function. **/ propertiesPROP_REMOTE_CONNECTABLE = g_param_spec_object ("remote-connectable", @@ -2871,14 +2590,13 @@ G_PARAM_STATIC_STRINGS); /** - * SoupSession:local-address: + * SoupSession:local-address: (attributes org.gtk.Property.get=soup_session_get_local_address) * - * Sets the #GInetSocketAddress to use for the client side of + * Sets the class@Gio.InetSocketAddress to use for the client side of * the connection. * * Use this property if you want for instance to bind the * local socket to a specific IP address. - * **/ propertiesPROP_LOCAL_ADDRESS = g_param_spec_object ("local-address", @@ -2889,12 +2607,12 @@ G_PARAM_STATIC_STRINGS); /** - * SoupSession:tls-interaction: + * SoupSession:tls-interaction: (attributes org.gtk.Property.get=soup_session_get_tls_interaction org.gtk.Property.set=soup_session_set_tls_interaction) * - * A #GTlsInteraction object that will be passed on to any - * #GTlsConnections created by the session. (This can be used to - * provide client-side certificates, for example.) + * A class@Gio.TlsInteraction object that will be passed on to any + * class@Gio.TlsConnections created by the session. * + * This can be used to provide client-side certificates, for example. **/ propertiesPROP_TLS_INTERACTION = g_param_spec_object ("tls-interaction", @@ -3101,7 +2819,7 @@ soup_message_io_finished (msg); item->paused = FALSE; item->state = SOUP_MESSAGE_FINISHING; - soup_session_process_queue_item (item->session, item, NULL, FALSE); + soup_session_process_queue_item (item->session, item, FALSE); } async_send_request_return_result (item, NULL, error); } @@ -3335,14 +3053,14 @@ * @callback: (scope async): the callback to invoke * @user_data: data for @callback * - * Asynchronously sends @msg and waits for the beginning of a - * response. When @callback is called, then either @msg has been sent, - * and its response headers received, or else an error has occurred. - * Call soup_session_send_finish() to get a #GInputStream for reading - * the response body. + * Asynchronously sends @msg and waits for the beginning of a response. * - * See soup_session_send() for more details on the general semantics. + * When @callback is called, then either @msg has been sent, and its response + * headers received, or else an error has occurred. Call + * method@Session.send_finish to get a class@Gio.InputStream for reading the + * response body. * + * See method@Session.send for more details on the general semantics. */ void soup_session_send_async (SoupSession *session, @@ -3381,13 +3099,13 @@ * @result: the #GAsyncResult passed to your callback * @error: return location for a #GError, or %NULL * - * Gets the response to a soup_session_send_async() call and (if - * successful), returns a #GInputStream that can be used to read the + * Gets the response to a method@Session.send_async call. + * + * If successful returns a class@Gio.InputStream that can be used to read the * response body. * * Returns: (transfer full): a #GInputStream for reading the * response body, or %NULL on error. - * */ GInputStream * soup_session_send_finish (SoupSession *session, @@ -3410,7 +3128,7 @@ item->state = SOUP_MESSAGE_FINISHING; if (item->state != SOUP_MESSAGE_FINISHED) - soup_session_process_queue_item (session, item, NULL, FALSE); + soup_session_process_queue_item (session, item, FALSE); } } @@ -3425,27 +3143,26 @@ * @error: return location for a #GError, or %NULL * * Synchronously sends @msg and waits for the beginning of a response. - * On success, a #GInputStream will be returned which you can use to + * + * On success, a class@Gio.InputStream will be returned which you can use to * read the response body. ("Success" here means only that an HTTP * response was received and understood; it does not necessarily mean * that a 2xx class status code was received.) * * If non-%NULL, @cancellable can be used to cancel the request; - * soup_session_send() will return a %G_IO_ERROR_CANCELLED error. Note - * that with requests that have side effects (eg, - * <literal>POST</literal>, <literal>PUT</literal>, - * <literal>DELETE</literal>) it is possible that you might cancel the - * request after the server acts on it, but before it returns a - * response, leaving the remote resource in an unknown state. + * method@Session.send will return a %G_IO_ERROR_CANCELLED error. Note that + * with requests that have side effects (eg, `POST`, `PUT`, `DELETE`) it is + * possible that you might cancel the request after the server acts on it, but + * before it returns a response, leaving the remote resource in an unknown + * state. * * If @msg is requeued due to a redirect or authentication, the - * initial (3xx/401/407) response body will be suppressed, and - * soup_session_send() will only return once a final response has been + * initial (`3xx/401/407`) response body will be suppressed, and + * method@Session.send will only return once a final response has been * received. * * Returns: (transfer full): a #GInputStream for reading the * response body, or %NULL on error. - * */ GInputStream * soup_session_send (SoupSession *session, @@ -3474,7 +3191,7 @@ while (!stream) { /* Get a connection, etc */ - soup_session_process_queue_item (session, item, NULL, TRUE); + soup_session_process_queue_item (session, item, TRUE); if (item->state != SOUP_MESSAGE_RUNNING) break; @@ -3547,7 +3264,7 @@ item->state = SOUP_MESSAGE_FINISHING; item->paused = FALSE; if (item->state != SOUP_MESSAGE_FINISHED) - soup_session_process_queue_item (session, item, NULL, TRUE); + soup_session_process_queue_item (session, item, TRUE); } soup_message_queue_item_unref (item); @@ -3615,14 +3332,14 @@ * @user_data: data for @callback * * Asynchronously sends @msg and reads the response body. + * * When @callback is called, then either @msg has been sent, and its response - * body read, or else an error has occurred. - * This function should only be used when the resource to be retrieved - * is not too long and can be stored in memory. - * Call soup_session_send_and_read_finish() to get a #GBytes with the - * response body. + * body read, or else an error has occurred. This function should only be used + * when the resource to be retrieved is not too long and can be stored in + * memory. Call method@Session.send_and_read_finish to get a + * struct@GLib.Bytes with the response body. * - * See soup_session_send() for more details on the general semantics. + * See method@Session.send for more details on the general semantics. */ void soup_session_send_and_read_async (SoupSession *session, @@ -3653,8 +3370,9 @@ * @result: the #GAsyncResult passed to your callback * @error: return location for a #GError, or %NULL * - * Gets the response to a soup_session_send_and_read_async() call and (if - * successful), returns a #GBytes with the response body. + * Gets the response to a method@Session.send_and_read_async. + * + * If successful, returns a struct@GLib.Bytes with the response body. * * Returns: (transfer full): a #GBytes, or %NULL on error. */ @@ -3677,11 +3395,12 @@ * @error: return location for a #GError, or %NULL * * Synchronously sends @msg and reads the response body. - * On success, a #GBytes will be returned with the response body. + * + * On success, a struct@GLib.Bytes will be returned with the response body. * This function should only be used when the resource to be retrieved * is not too long and can be stored in memory. * - * See soup_session_send() for more details on the general semantics. + * See method@Session.send for more details on the general semantics. * * Returns: (transfer full): a #GBytes, or %NULL on error. */ @@ -3718,12 +3437,12 @@ * @session: a #SoupSession * @result: the #GAsyncResult passed to your callback * - * Gets the #SoupMessage of the @result asynchronous operation - * This is useful to get the #SoupMessage of an asynchronous - * operation started by @session from its #GAsyncReadyCallback. + * Gets the class@Message of the @result asynchronous operation This is useful + * to get the class@Message of an asynchronous operation started by @session + * from its callback@Gio.AsyncReadyCallback. * * Returns: (transfer none) (nullable): a #SoupMessage or - * %NULL if @result is not a valid @session async operation result. + * %NULL if @result is not a valid @session async operation result. */ SoupMessage * soup_session_get_async_result_message (SoupSession *session, @@ -3738,36 +3457,16 @@ return item ? item->msg : NULL; } -static GIOStream * -steal_connection (SoupSession *session, - SoupMessageQueueItem *item) -{ - SoupSessionPrivate *priv = soup_session_get_instance_private (session); - SoupConnection *conn; - SoupSessionHost *host; - GIOStream *stream; - - conn = g_object_ref (soup_message_get_connection (item->msg)); - host = get_host_for_message (session, item->msg); - g_hash_table_remove (priv->conns, conn); - drop_connection (session, host, conn); - - stream = soup_connection_steal_iostream (conn); - soup_message_set_connection (item->msg, NULL); - g_object_unref (conn); - - return stream; -} - /** * soup_session_steal_connection: * @session: a #SoupSession * @msg: the message whose connection is to be stolen * * "Steals" the HTTP connection associated with @msg from @session. + * * This happens immediately, regardless of the current state of the * connection, and @msg's callback will not be called. You can steal - * the connection from a #SoupMessage signal handler if you need to + * the connection from a class@Message signal handler if you need to * wait for part or all of the response to be received first. * * Calling this function may cause @msg to be freed if you are not @@ -3777,25 +3476,14 @@ * with @msg (or %NULL if @msg was no longer associated with a * connection). No guarantees are made about what kind of #GIOStream * is returned. - * */ static GIOStream * soup_session_steal_connection (SoupSession *session, SoupMessage *msg) { - SoupMessageQueueItem *item; - GIOStream *stream = NULL; - SoupConnection *conn; - - item = soup_session_lookup_queue_item (session, msg); - if (!item) - return NULL; - - conn = soup_message_get_connection (item->msg); - if (conn && soup_connection_get_state (conn) == SOUP_CONNECTION_IN_USE) - stream = steal_connection (session, item); + SoupSessionPrivate *priv = soup_session_get_instance_private (session); - return stream; + return soup_connection_manager_steal_connection (priv->conn_manager, msg); } static GPtrArray * @@ -3881,8 +3569,8 @@ * @callback: (scope async): the callback to invoke * @user_data: data for @callback * - * Asynchronously creates a #SoupWebsocketConnection to communicate - * with a remote server. + * Asynchronously creates a class@WebsocketConnection to communicate with a + * remote server. * * All necessary WebSocket-related headers will be added to @msg, and * it will then be sent and asynchronously processed normally @@ -3891,15 +3579,13 @@ * If the server returns "101 Switching Protocols", then @msg's status * code and response headers will be updated, and then the WebSocket * handshake will be completed. On success, - * soup_session_websocket_connect_finish() will return a new - * #SoupWebsocketConnection. On failure it will return a #GError. + * method@Session.websocket_connect_finish will return a new + * class@WebsocketConnection. On failure it will return a #GError. * - * If the server returns a status other than "101 Switching - * Protocols", then @msg will contain the complete response headers - * and body from the server's response, and - * soup_session_websocket_connect_finish() will return + * If the server returns a status other than "101 Switching Protocols", then + * @msg will contain the complete response headers and body from the server's + * response, and method@Session.websocket_connect_finish will return * %SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET. - * */ void soup_session_websocket_connect_async (SoupSession *session, @@ -3935,7 +3621,7 @@ /* WebSocket negotiation over HTTP/2 is not currently supported * and in practice all websocket servers support HTTP1.x with * HTTP/2 not providing a tangible benefit */ - soup_message_set_force_http1 (msg, TRUE); + soup_message_set_force_http_version (msg, SOUP_HTTP_1_1); item = soup_session_append_queue_item (session, msg, TRUE, cancellable); item->io_priority = io_priority; @@ -3958,14 +3644,14 @@ * @result: the #GAsyncResult passed to your callback * @error: return location for a #GError, or %NULL * - * Gets the #SoupWebsocketConnection response to a - * soup_session_websocket_connect_async() call and (if successful), - * returns a #SoupWebsocketConnection that can be used to communicate - * with the server. + * Gets the class@WebsocketConnection response to a + * method@Session.websocket_connect_async call. + * + * If successful, returns a class@WebsocketConnection that can be used to + * communicate with the server. * * Returns: (transfer full): a new #SoupWebsocketConnection, or * %NULL on error. - * */ SoupWebsocketConnection * soup_session_websocket_connect_finish (SoupSession *session, @@ -4016,12 +3702,15 @@ * @callback: (nullable) (scope async): the callback to invoke when the operation finishes * @user_data: data for @progress_callback and @callback * - * Start a preconnection to @msg. Once the connection is done, it will remain in idle state so that - * it can be reused by future requests. If there's already an idle connection for the given @msg - * host, the operation finishes successfully without creating a new connection. If a new request - * for the given @msg host is made while the preconnect is still ongoing, the request will take - * the ownership of the connection and the preconnect operation will finish successfully (if - * there's a connection error it will be handled by the request). + * Start a preconnection to @msg. + * + * Once the connection is done, it will remain in idle state so that it can be + * reused by future requests. If there's already an idle connection for the + * given @msg host, the operation finishes successfully without creating a new + * connection. If a new request for the given @msg host is made while the + * preconnect is still ongoing, the request will take the ownership of the + * connection and the preconnect operation will finish successfully (if there's + * a connection error it will be handled by the request). * * The operation finishes when the connection is done or an error occurred. */ @@ -4064,7 +3753,7 @@ * @result: the #GAsyncResult passed to your callback * @error: return location for a #GError, or %NULL * - * Complete a preconnect async operation started with soup_session_preconnect_async(). + * Complete a preconnect async operation started with method@Session.preconnect_async. * * Return value: %TRUE if the preconnect succeeded, or %FALSE in case of error. */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-session.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-session.h
Changed
@@ -33,6 +33,12 @@ void (*_soup_reserved8) (void); }; +/** + * soup_session_error_quark: + * Registers error quark for SoupSession if needed. + * + * Returns: Error quark for SoupSession. + */ SOUP_AVAILABLE_IN_ALL GQuark soup_session_error_quark (void); #define SOUP_SESSION_ERROR soup_session_error_quark ()
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-status.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-status.c
Changed
@@ -13,14 +13,6 @@ #include "soup.h" /** - * SECTION:soup-status - * @section_id: SoupStatus - * @short_description: HTTP (and libsoup) status codes - * - * HTTP (and libsoup) status codes. - **/ - -/** * SOUP_STATUS_IS_INFORMATIONAL: * @status: an HTTP status code * @@ -224,10 +216,10 @@ * * Looks up the stock HTTP description of @status_code. * - * <emphasis>There is no reason for you to ever use this - * function.</emphasis> If you wanted the textual description for the - * #SoupMessage:status_code of a given #SoupMessage, you should just - * look at the message's #SoupMessage:reason_phrase. However, you + * *There is no reason for you to ever use this + * function.* If you wanted the textual description for the + * property@Message:status-code of a given class@Message, you should just + * look at the message's property@Message:reason-phrase. However, you * should only do that for use in debugging messages; HTTP reason * phrases are not localized, and are not generally very descriptive * anyway, and so they should never be presented to the user directly.
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-tld.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-tld.c
Changed
@@ -17,16 +17,6 @@ #include "soup-tld.h" #include "soup.h" -/** - * SECTION:soup-tld - * @section_id: SoupTLD - * @short_description: Top-Level Domain Utilities - * - * These functions can be used to parse hostnames to attempt to determine - * what part of the name belongs to the domain owner, and what part is - * simply a "public suffix" such as ".com". - */ - static const char *soup_tld_get_base_domain_internal (const char *hostname, GError **error); @@ -36,10 +26,11 @@ * @error: return location for a #GError, or %NULL to ignore * errors. See #SoupTLDError for the available error codes * - * Finds the base domain for a given @hostname. The base domain is - * composed by the top level domain (such as .org, .com, .co.uk, etc) - * plus the second level domain, for example for myhost.mydomain.com - * it will return mydomain.com. + * Finds the base domain for a given @hostname + * + * The base domain is composed by the top level domain (such as .org, .com, + * .co.uk, etc) plus the second level domain, for example for + * myhost.mydomain.com it will return mydomain.com. * * Note that %NULL will be returned for private URLs (those not ending * with any well known TLD) because choosing a base domain for them @@ -51,8 +42,7 @@ * format). * * Returns: a pointer to the start of the base domain in @hostname. If - * an error occurs, %NULL will be returned and @error set. - * + * an error occurs, %NULL will be returned and @error set. **/ const char * soup_tld_get_base_domain (const char *hostname, GError **error) @@ -85,7 +75,6 @@ * UTF-8 or ASCII format. * * Returns: %TRUE if it is a public domain, %FALSE otherwise. - * **/ gboolean soup_tld_domain_is_public_suffix (const char *domain) @@ -106,7 +95,6 @@ * SOUP_TLD_ERROR: * * The #GError domain for soup-tld-related errors. - * */ /** * SoupTLDError: @@ -117,14 +105,13 @@ * public suffix). * @SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS: The passed-in hostname * did not have enough components. Eg, calling - * soup_tld_get_base_domain() on <literal>"co.uk"</literal>. + * func@tld_get_base_domain on <literal>"co.uk"</literal>. * @SOUP_TLD_ERROR_NO_BASE_DOMAIN: The passed-in hostname has * no recognized public suffix. * @SOUP_TLD_ERROR_NO_PSL_DATA: The Public Suffix List was not * available. * * Error codes for %SOUP_TLD_ERROR. - * */ G_DEFINE_QUARK (soup-tld-error-quark, soup_tld_error)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-tld.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-tld.h
Changed
@@ -16,7 +16,13 @@ SOUP_AVAILABLE_IN_ALL gboolean soup_tld_domain_is_public_suffix (const char *domain); -/* Errors */ + +/** + * soup_tld_error_quark: + * Registers error quark for soup_tld_get_base_domain() if needed. + * + * Returns: Error quark for Soup TLD functions. + */ SOUP_AVAILABLE_IN_ALL GQuark soup_tld_error_quark (void); #define SOUP_TLD_ERROR soup_tld_error_quark()
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-uri-utils.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-uri-utils.c
Changed
@@ -33,15 +33,6 @@ #include "soup-misc.h" /** - * SECTION:soup-uri-utils - * @section_id: SoupURIUtils - * @title: URI Utilities - * @short_description: Functions to help working with #GUri and HTTP - * - * Utility functions and defines to help working with URIs. - */ - -/** * SOUP_HTTP_URI_FLAGS: * * The set of #GUriFlags libsoup expects all #GUri to use. @@ -99,7 +90,7 @@ * @uri1: a #GUri * @uri2: another #GUri * - * Tests whether or not @uri1 and @uri2 are equal in all parts + * Tests whether or not @uri1 and @uri2 are equal in all parts. * * Returns: %TRUE if equal otherwise %FALSE **/ @@ -150,9 +141,10 @@ * soup_uri_uses_default_port: * @uri: a #GUri * - * Tests if @uri uses the default port for its scheme. (Eg, 80 for - * http.) (This only works for http, https and ftp; libsoup does not know - * the default ports of other protocols.) + * Tests if @uri uses the default port for its scheme. + * + * (Eg, 80 for http.) (This only works for http, https and ftp; libsoup does not + * know the default ports of other protocols.) * * Returns: %TRUE or %FALSE **/ @@ -278,7 +270,7 @@ /** * soup_uri_decode_data_uri: * @uri: a data URI, in string form - * @content_type: (out) (nullable) (transfer full): location to store content type, or %NULL + * @content_type: (out) (nullable) (transfer full): location to store content type * * Decodes the given data URI and returns its contents and @content_type. * @@ -367,7 +359,7 @@ * @SOUP_URI_QUERY: the URI query component * @SOUP_URI_FRAGMENT: the URI fragment component * - * Enum values passed to soup_uri_copy() to indicate the components of + * Enum values passed to func@uri_copy to indicate the components of * the URI that should be updated with the given values. */ @@ -378,7 +370,7 @@ * @...: value of @first_component followed by additional * components and values, terminated by %SOUP_URI_NONE * - * Return a copy of @uri with the given components updated + * Return a copy of @uri with the given components updated. * * Returns: (transfer full): a new #GUri */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-version.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-version.c
Changed
@@ -12,26 +12,18 @@ #include "soup-version.h" /** - * SECTION:soup-version - * @section_id: SoupVersion - * @short_description: Variables and functions to check the libsoup version - * - * Variables and functions to check the libsoup version. - **/ - -/** * SOUP_MAJOR_VERSION: * - * Like soup_get_major_version(), but from the headers used at - * application compile time, rather than from the library linked - * against at application run time. + * Like func@get_major_version, but from the headers used at application + * compile time, rather than from the library linked against at application run + * time. * */ /** * SOUP_MINOR_VERSION: * - * Like soup_get_minor_version(), but from the headers used at + * Like func@get_minor_version, but from the headers used at * application compile time, rather than from the library linked * against at application run time. * @@ -40,7 +32,7 @@ /** * SOUP_MICRO_VERSION: * - * Like soup_get_micro_version(), but from the headers used at + * Like func@get_micro_version, but from the headers used at * application compile time, rather than from the library linked * against at application run time. * @@ -54,16 +46,16 @@ * * Macro to test the version of libsoup being compiled against. * - * Returns: %TRUE if the version of the libsoup header files + * Returns %TRUE if the version of the libsoup header files * is the same as or newer than the passed-in version. - * */ /** * soup_get_major_version: * * Returns the major version number of the libsoup library. - * (e.g. in libsoup version 2.42.0 this is 2.) + * + * e.g. in libsoup version 2.42.0 this is 2. * * This function is in the library, so it represents the libsoup library * your code is running against. Contrast with the #SOUP_MAJOR_VERSION @@ -71,7 +63,6 @@ * have included when compiling your code. * * Returns: the major version number of the libsoup library - * */ guint soup_get_major_version (void) @@ -83,7 +74,8 @@ * soup_get_minor_version: * * Returns the minor version number of the libsoup library. - * (e.g. in libsoup version 2.42.0 this is 42.) + * + * e.g. in libsoup version 2.42.0 this is 42. * * This function is in the library, so it represents the libsoup library * your code is running against. Contrast with the #SOUP_MINOR_VERSION @@ -91,7 +83,6 @@ * have included when compiling your code. * * Returns: the minor version number of the libsoup library - * */ guint soup_get_minor_version (void) @@ -103,7 +94,8 @@ * soup_get_micro_version: * * Returns the micro version number of the libsoup library. - * (e.g. in libsoup version 2.42.0 this is 0.) + * + * e.g. in libsoup version 2.42.0 this is 0. * * This function is in the library, so it represents the libsoup library * your code is running against. Contrast with the #SOUP_MICRO_VERSION @@ -111,7 +103,6 @@ * have included when compiling your code. * * Returns: the micro version number of the libsoup library - * */ guint soup_get_micro_version (void) @@ -125,14 +116,14 @@ * @minor: the minor version to check * @micro: the micro version to check * - * Like SOUP_CHECK_VERSION, but the check for soup_check_version is - * at runtime instead of compile time. This is useful for compiling - * against older versions of libsoup, but using features from newer - * versions. + * Like func@CHECK_VERSION, but the check for soup_check_version is + * at runtime instead of compile time. * - * Returns: %TRUE if the version of the libsoup currently loaded - * is the same as or newer than the passed-in version. + * This is useful for compiling against older versions of libsoup, but using + * features from newer versions. * + * Returns: %TRUE if the version of the libsoup currently loaded + * is the same as or newer than the passed-in version. */ gboolean soup_check_version (guint major, @@ -146,7 +137,9 @@ * SOUP_VERSION_MIN_REQUIRED: * * A macro that should be defined by the user prior to including - * libsoup.h. The definition should be one of the predefined libsoup + * `libsoup.h`. + * + * The definition should be one of the predefined libsoup * version macros: %SOUP_VERSION_2_24, %SOUP_VERSION_2_26, ... * * This macro defines the earliest version of libsoup that the package @@ -156,14 +149,15 @@ * functions, then using functions that were deprecated in version * %SOUP_VERSION_MIN_REQUIRED or earlier will cause warnings (but * using functions deprecated in later releases will not). - * */ /** * SOUP_VERSION_MAX_ALLOWED: * * A macro that should be defined by the user prior to including - * libsoup.h. The definition should be one of the predefined libsoup + * libsoup.h. + * + * The definition should be one of the predefined libsoup * version macros: %SOUP_VERSION_2_24, %SOUP_VERSION_2_26, ... * * This macro defines the latest version of the libsoup API that the @@ -173,8 +167,7 @@ * functions, then using functions added after version * %SOUP_VERSION_MAX_ALLOWED will cause warnings. * - * Unless you are using SOUP_CHECK_VERSION() or the like to compile + * Unless you are using func@CHECK_VERSION or the like to compile * different code depending on the libsoup version, then this should be * set to the same value as %SOUP_VERSION_MIN_REQUIRED. - * */
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/soup-version.h.in -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/soup-version.h.in
Changed
@@ -91,6 +91,52 @@ #error "SOUP_VERSION_MIN_REQUIRED must be >= SOUP_VERSION_3_0" #endif +/** + * SOUP_DISABLE_DEPRECATION_WARNINGS: + * + * A macro that should be defined before including the `soup.h` header. + * + * If this symbol is defined, no compiler warnings will be produced for + * uses of deprecated libsoup APIs. + */ + +/** + * SOUP_DEPRECATED: + * + * Marks a symbol as deprecated. + * + * You should use `SOUP_DEPRECATED_IN_*` in order to handle versioning. + */ + +/** + * SOUP_DEPRECATED_FOR: + * @f: the symbol that replaces the deprecated one + * + * Marks a symbol as deprecated in favor of another symbol. + * + * You should use `SOUP_DEPRECATED_FOR_IN_*` in order to handle versioning. + */ + +/** + * SOUP_UNAVAILABLE + * @maj: the major version that introduced the symbol + * @min: the minor version that introduced the symbol + * + * Marks a symbol unavailable before the given major and minor version. + * + * You should use `SOUP_AVAILABLE_IN_*` in order to handle versioning. + */ + +#ifdef SOUP_DISABLE_DEPRECATION_WARNINGS +#define SOUP_DEPRECATED _SOUP_EXTERN +#define SOUP_DEPRECATED_FOR(f) _SOUP_EXTERN +#define SOUP_UNAVAILABLE(maj,min) _SOUP_EXTERN +#else +#define SOUP_DEPRECATED G_DEPRECATED _SOUP_EXTERN +#define SOUP_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f) _SOUP_EXTERN +#define SOUP_UNAVAILABLE(maj,min) G_UNAVAILABLE(maj,min) _SOUP_EXTERN +#endif + #define SOUP_AVAILABLE_IN_ALL _SOUP_EXTERN {version_attributes}
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/websocket/soup-websocket-connection.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/websocket/soup-websocket-connection.c
Changed
@@ -29,48 +29,40 @@ #include "soup-websocket-extension.h" /* - * SECTION:websocketconnection - * @title: SoupWebsocketConnection - * @short_description: A WebSocket connection + * SoupWebsocketConnection: + * + * A WebSocket connection. * * A #SoupWebsocketConnection is a WebSocket connection to a peer. * This API is modeled after the W3C API for interacting with * WebSockets. * - * The #SoupWebsocketConnection:state property will indicate the + * The property@WebsocketConnection:state property will indicate the * state of the connection. * - * Use soup_websocket_connection_send() to send a message to the peer. - * When a message is received the #SoupWebsocketConnection::message + * Use method@WebsocketConnection.send to send a message to the peer. + * When a message is received the signal@WebsocketConnection::message * signal will fire. * - * The soup_websocket_connection_close() function will perform an + * The method@WebsocketConnection.close function will perform an * orderly close of the connection. The - * #SoupWebsocketConnection::closed signal will fire once the + * signal@WebsocketConnection::closed signal will fire once the * connection closes, whether it was initiated by this side or the * peer. * - * Connect to the #SoupWebsocketConnection::closing signal to detect + * Connect to the signal@WebsocketConnection::closing signal to detect * when either peer begins closing the connection. */ /** - * SoupWebsocketConnection: - * - * A class representing a WebSocket connection. - * - */ - -/** * SoupWebsocketConnectionClass: - * @message: default handler for the #SoupWebsocketConnection::message signal - * @error: default handler for the #SoupWebsocketConnection::error signal - * @closing: the default handler for the #SoupWebsocketConnection:closing signal - * @closed: default handler for the #SoupWebsocketConnection::closed signal - * @pong: default handler for the #SoupWebsocketConnection::pong signal - * - * The abstract base class for #SoupWebsocketConnection + * @message: default handler for the signal@WebsocketConnection::message signal + * @error: default handler for the signal@WebsocketConnection::error signal + * @closing: the default handler for the signal@WebsocketConnection:closing signal + * @closed: default handler for the signal@WebsocketConnection::closed signal + * @pong: default handler for the signal@WebsocketConnection::pong signal * + * The abstract base class for class@WebsocketConnection. */ enum { @@ -727,7 +719,7 @@ code = 0; break; default: - if (code < 3000) { + if (code < 3000 || code >= 5000) { g_debug ("Wrong closing code %d received", code); protocol_error_and_close (self); return; @@ -774,6 +766,18 @@ break; } + /* 1005, 1006 and 1015 are reserved values and MUST NOT be set as a status code in a Close control frame by an endpoint */ + switch (priv->peer_close_code) { + case SOUP_WEBSOCKET_CLOSE_NO_STATUS: + case SOUP_WEBSOCKET_CLOSE_ABNORMAL: + case SOUP_WEBSOCKET_CLOSE_TLS_HANDSHAKE: + g_debug ("received a broken close frame containing reserved status code %u", priv->peer_close_code); + protocol_error_and_close (self); + return; + default: + break; + } + if (len > 2) { data += 2; len -= 2; @@ -1486,7 +1490,6 @@ * over. * * The input and output streams must be pollable streams. - * */ propertiesPROP_IO_STREAM = g_param_spec_object ("io-stream", @@ -1501,7 +1504,6 @@ * SoupWebsocketConnection:connection-type: * * The type of connection (client/server). - * */ propertiesPROP_CONNECTION_TYPE = g_param_spec_enum ("connection-type", @@ -1520,7 +1522,6 @@ * * For servers this represents the address of the WebSocket, * and for clients it is the address connected to. - * */ propertiesPROP_URI = g_param_spec_boxed ("uri", @@ -1535,7 +1536,6 @@ * SoupWebsocketConnection:origin: * * The client's Origin. - * */ propertiesPROP_ORIGIN = g_param_spec_string ("origin", @@ -1551,7 +1551,6 @@ * * The chosen protocol, or %NULL if a protocol was not agreed * upon. - * */ propertiesPROP_PROTOCOL = g_param_spec_string ("protocol", @@ -1566,7 +1565,6 @@ * SoupWebsocketConnection:state: * * The current state of the WebSocket. - * */ propertiesPROP_STATE = g_param_spec_enum ("state", @@ -1580,9 +1578,9 @@ /** * SoupWebsocketConnection:max-incoming-payload-size: * - * The maximum payload size for incoming packets the protocol expects - * or 0 to not limit it. + * The maximum payload size for incoming packets. * + * The protocol expects or 0 to not limit it. */ propertiesPROP_MAX_INCOMING_PAYLOAD_SIZE = g_param_spec_uint64 ("max-incoming-payload-size", @@ -1599,9 +1597,9 @@ * SoupWebsocketConnection:keepalive-interval: * * Interval in seconds on when to send a ping message which will - * serve as a keepalive message. If set to 0 the keepalive message is - * disabled. + * serve as a keepalive message. * + * If set to 0 the keepalive message is disabled. */ propertiesPROP_KEEPALIVE_INTERVAL = g_param_spec_uint ("keepalive-interval", @@ -1617,8 +1615,7 @@ /** * SoupWebsocketConnection:extensions: * - * List of #SoupWebsocketExtension objects that are active in the connection. - * + * List of class@WebsocketExtension objects that are active in the connection. */ propertiesPROP_EXTENSIONS = g_param_spec_pointer ("extensions", @@ -1639,9 +1636,8 @@ * Emitted when we receive a message from the peer. * * As a convenience, the @message data will always be - * NUL-terminated, but the NUL byte will not be included in + * %NULL-terminated, but the NUL byte will not be included in * the length count. - * */ signalsMESSAGE = g_signal_new ("message", SOUP_TYPE_WEBSOCKET_CONNECTION, @@ -1655,10 +1651,10 @@ * @self: the WebSocket * @error: the error that occured * - * Emitted when an error occurred on the WebSocket. This may - * be fired multiple times. Fatal errors will be followed by - * the #SoupWebsocketConnection::closed signal being emitted. + * Emitted when an error occurred on the WebSocket. * + * This may be fired multiple times. Fatal errors will be followed by + * the signal@WebsocketConnection::closed signal being emitted. */ signalsERROR = g_signal_new ("error", SOUP_TYPE_WEBSOCKET_CONNECTION, @@ -1672,7 +1668,6 @@ * @self: the WebSocket * * This signal will be emitted during an orderly close. - * */ signalsCLOSING = g_signal_new ("closing", SOUP_TYPE_WEBSOCKET_CONNECTION, @@ -1685,13 +1680,13 @@ * SoupWebsocketConnection::closed: * @self: the WebSocket * - * Emitted when the connection has completely closed, either - * due to an orderly close from the peer, one initiated via - * soup_websocket_connection_close() or a fatal error + * Emitted when the connection has completely closed. + * + * This happens either due to an orderly close from the peer, one + * initiated via method@WebsocketConnection.close or a fatal error * condition that caused a close. * * This signal will be emitted once. - * */ signalsCLOSED = g_signal_new ("closed", SOUP_TYPE_WEBSOCKET_CONNECTION, @@ -1709,9 +1704,8 @@ * unsolicited) from the peer. * * As a convenience, the @message data will always be - * NUL-terminated, but the NUL byte will not be included in + * %NULL-terminated, but the NUL byte will not be included in * the length count. - * */ signalsPONG = g_signal_new ("pong", SOUP_TYPE_WEBSOCKET_CONNECTION, @@ -1731,6 +1725,7 @@ * @extensions: (element-type SoupWebsocketExtension) (transfer full): a #GList of #SoupWebsocketExtension objects * * Creates a #SoupWebsocketConnection on @stream with the given active @extensions. + * * This should be called after completing the handshake to begin using the WebSocket * protocol. * @@ -1765,7 +1760,6 @@ * Get the I/O stream the WebSocket is communicating over. * * Returns: (transfer none): the WebSocket's I/O stream. - * */ GIOStream * soup_websocket_connection_get_io_stream (SoupWebsocketConnection *self) @@ -1784,7 +1778,6 @@ * Get the connection type (client/server) of the connection. * * Returns: the connection type - * */ SoupWebsocketConnectionType soup_websocket_connection_get_connection_type (SoupWebsocketConnection *self) @@ -1806,7 +1799,6 @@ * for clients it is the address connected to. * * Returns: (transfer none): the URI - * */ GUri * soup_websocket_connection_get_uri (SoupWebsocketConnection *self) @@ -1824,8 +1816,7 @@ * * Get the origin of the WebSocket. * - * Returns: (nullable): the origin, or %NULL - * + * Returns: (nullable): the origin */ const char * soup_websocket_connection_get_origin (SoupWebsocketConnection *self) @@ -1843,8 +1834,7 @@ * * Get the protocol chosen via negotiation with the peer. * - * Returns: (nullable): the chosen protocol, or %NULL - * + * Returns: (nullable): the chosen protocol */ const char * soup_websocket_connection_get_protocol (SoupWebsocketConnection *self) @@ -1863,7 +1853,6 @@ * Get the extensions chosen via negotiation with the peer. * * Returns: (element-type SoupWebsocketExtension) (transfer none): a #GList of #SoupWebsocketExtension objects - * */ GList * soup_websocket_connection_get_extensions (SoupWebsocketConnection *self) @@ -1882,7 +1871,6 @@ * Get the current state of the WebSocket. * * Returns: the state - * */ SoupWebsocketState soup_websocket_connection_get_state (SoupWebsocketConnection *self) @@ -1907,11 +1895,10 @@ * * This only becomes valid once the WebSocket is in the * %SOUP_WEBSOCKET_STATE_CLOSED state. The value will often be in the - * #SoupWebsocketCloseCode enumeration, but may also be an application + * enum@WebsocketCloseCode enumeration, but may also be an application * defined close code. * * Returns: the close code or zero. - * */ gushort soup_websocket_connection_get_close_code (SoupWebsocketConnection *self) @@ -1934,7 +1921,6 @@ * the main loop is run, so copy it if you need to keep it around. * * Returns: the close data or %NULL - * */ const char * soup_websocket_connection_get_close_data (SoupWebsocketConnection *self) @@ -1951,13 +1937,13 @@ * @self: the WebSocket * @text: the message contents * - * Send a %NULL-terminated text (UTF-8) message to the peer. If you need - * to send text messages containing %NULL characters use - * soup_websocket_connection_send_message() instead. + * Send a %NULL-terminated text (UTF-8) message to the peer. + * + * If you need to send text messages containing %NULL characters use + * method@WebsocketConnection.send_message instead. * * The message is queued to be sent and will be sent when the main loop * is run. - * */ void soup_websocket_connection_send_text (SoupWebsocketConnection *self, @@ -1981,11 +1967,12 @@ * @data: (array length=length) (element-type guint8) (nullable): the message contents * @length: the length of @data * - * Send a binary message to the peer. If @length is 0, @data may be %NULL. + * Send a binary message to the peer. + * + * If @length is 0, @data may be %NULL. * * The message is queued to be sent and will be sent when the main loop * is run. - * */ void soup_websocket_connection_send_binary (SoupWebsocketConnection *self, @@ -2010,7 +1997,6 @@ * * The message is queued to be sent and will be sent when the main loop * is run. - * */ void soup_websocket_connection_send_message (SoupWebsocketConnection *self, @@ -2038,7 +2024,7 @@ * * Close the connection in an orderly fashion. * - * Note that until the #SoupWebsocketConnection::closed signal fires, the connection + * Note that until the signal@WebsocketConnection::closed signal fires, the connection * is not yet completely closed. The close message is not even sent until the * main loop runs. * @@ -2046,7 +2032,6 @@ * If @code is %SOUP_WEBSOCKET_CLOSE_NO_STATUS a close message with no body * (without code and data) is sent. * Note that the @data must be UTF-8 valid. - * */ void soup_websocket_connection_close (SoupWebsocketConnection *self, @@ -2076,7 +2061,6 @@ * Gets the maximum payload size allowed for incoming packets. * * Returns: the maximum payload size. - * */ guint64 soup_websocket_connection_get_max_incoming_payload_size (SoupWebsocketConnection *self) @@ -2093,9 +2077,9 @@ * @self: the WebSocket * @max_incoming_payload_size: the maximum payload size * - * Sets the maximum payload size allowed for incoming packets. It - * does not limit the outgoing packet size. + * Sets the maximum payload size allowed for incoming packets. * + * It does not limit the outgoing packet size. */ void soup_websocket_connection_set_max_incoming_payload_size (SoupWebsocketConnection *self, @@ -2118,7 +2102,6 @@ * Gets the keepalive interval in seconds or 0 if disabled. * * Returns: the keepalive interval. - * */ guint soup_websocket_connection_get_keepalive_interval (SoupWebsocketConnection *self) @@ -2150,8 +2133,9 @@ * @interval: the interval to send a ping message or 0 to disable it * * Sets the interval in seconds on when to send a ping message which will serve - * as a keepalive message. If set to 0 the keepalive message is disabled. + * as a keepalive message. * + * If set to 0 the keepalive message is disabled. */ void soup_websocket_connection_set_keepalive_interval (SoupWebsocketConnection *self,
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/websocket/soup-websocket-extension-deflate.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/websocket/soup-websocket-extension-deflate.c
Changed
@@ -69,19 +69,11 @@ /** * SoupWebsocketExtensionDeflate: * - * A SoupWebsocketExtensionDeflate is a #SoupWebsocketExtension + * A SoupWebsocketExtensionDeflate is a class@WebsocketExtension * implementing permessage-deflate (RFC 7692). * - * This extension is used by default in a #SoupSession when #SoupWebsocketExtensionManager - * feature is present, and always used by #SoupServer. - * - */ - -/** - * SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE: - * - * A #GType corresponding to permessage-deflate WebSocket extension. - * + * This extension is used by default in a class@Session when class@WebsocketExtensionManager + * feature is present, and always used by class@Server. */ G_DEFINE_FINAL_TYPE_WITH_PRIVATE (SoupWebsocketExtensionDeflate, soup_websocket_extension_deflate, SOUP_TYPE_WEBSOCKET_EXTENSION)
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/websocket/soup-websocket-extension-manager.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/websocket/soup-websocket-extension-manager.c
Changed
@@ -35,24 +35,22 @@ /** * SoupWebsocketExtensionManager: * - * SoupWebsocketExtensionManager is the #SoupSessionFeature that handles WebSockets - * extensions for a #SoupSession. + * SoupWebsocketExtensionManager is the iface@SessionFeature that handles WebSockets + * extensions for a class@Session. * - * A SoupWebsocketExtensionManager is added to the session by default, and normally + * A #SoupWebsocketExtensionManager is added to the session by default, and normally * you don't need to worry about it at all. However, if you want to * disable WebSocket extensions, you can remove the feature from the - * session with soup_session_remove_feature_by_type(), or disable it on - * individual requests with soup_message_disable_feature(). - * + * session with method@Session.remove_feature_by_type or disable it on + * individual requests with method@Message.disable_feature. **/ /** * SOUP_TYPE_WEBSOCKET_EXTENSION_MANAGER: * - * The #GType of #SoupWebsocketExtensionManager; you can use this with - * soup_session_remove_feature_by_type() or - * soup_message_disable_feature(). - * + * The #GType of class@WebsocketExtensionManager; you can use this with + * method@Session.remove_feature_by_type or + * method@Message.disable_feature. */ static void soup_websocket_extension_manager_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/websocket/soup-websocket-extension.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/websocket/soup-websocket-extension.c
Changed
@@ -27,18 +27,11 @@ #include "soup-websocket-extension.h" /** - * SECTION:soup-websocket-extension - * @short_description: a WebSocket extension - * @see_also: #SoupSession, #SoupWebsocketExtensionManager - * - * SoupWebsocketExtension is the base class for WebSocket extension objects. - * - */ - -/** * SoupWebsocketExtension: * - * Class for impelementing websocket extensions. + * A WebSocket extension + * + * #SoupWebsocketExtension is the base class for WebSocket extension objects. */ /** @@ -55,8 +48,7 @@ * @process_incoming_message: called to process the payload data of a message * after it's received. Reserved bits of the header should be cleared. * - * The class structure for the SoupWebsocketExtension. - * + * The class structure for the #SoupWebsocketExtension. */ G_DEFINE_ABSTRACT_TYPE (SoupWebsocketExtension, soup_websocket_extension, G_TYPE_OBJECT) @@ -75,10 +67,10 @@ * soup_websocket_extension_configure: * @extension: a #SoupWebsocketExtension * @connection_type: either %SOUP_WEBSOCKET_CONNECTION_CLIENT or %SOUP_WEBSOCKET_CONNECTION_SERVER - * @params: (nullable): the parameters, or %NULL + * @params: (nullable): the parameters * @error: return location for a #GError * - * Configures @extension with the given @params + * Configures @extension with the given @params. * * Returns: %TRUE if extension could be configured with the given parameters, or %FALSE otherwise */ @@ -105,11 +97,12 @@ * soup_websocket_extension_get_request_params: * @extension: a #SoupWebsocketExtension * - * Get the parameters strings to be included in the request header. If the extension - * doesn't include any parameter in the request, this function returns %NULL. + * Get the parameters strings to be included in the request header. * - * Returns: (nullable) (transfer full): a new allocated string with the parameters + * If the extension doesn't include any parameter in the request, this function + * returns %NULL. * + * Returns: (nullable) (transfer full): a new allocated string with the parameters */ char * soup_websocket_extension_get_request_params (SoupWebsocketExtension *extension) @@ -129,11 +122,12 @@ * soup_websocket_extension_get_response_params: * @extension: a #SoupWebsocketExtension * - * Get the parameters strings to be included in the response header. If the extension - * doesn't include any parameter in the response, this function returns %NULL. + * Get the parameters strings to be included in the response header. * - * Returns: (nullable) (transfer full): a new allocated string with the parameters + * If the extension doesn't include any parameter in the response, this function + * returns %NULL. * + * Returns: (nullable) (transfer full): a new allocated string with the parameters */ char * soup_websocket_extension_get_response_params (SoupWebsocketExtension *extension) @@ -156,14 +150,15 @@ * @payload: (transfer full): the payload data * @error: return location for a #GError * - * Process a message before it's sent. If the payload isn't changed the given - * @payload is just returned, otherwise g_bytes_unref() is called on the given - * @payload and a new #GBytes is returned with the new data. + * Process a message before it's sent. + * + * If the payload isn't changed the given @payload is just returned, otherwise + * method@Glib.Bytes.unref is called on the given @payload and a new + * struct@GLib.Bytes is returned with the new data. * * Extensions using reserved bits of the header will change them in @header. * * Returns: (transfer full): the message payload data, or %NULL in case of error - * */ GBytes * soup_websocket_extension_process_outgoing_message (SoupWebsocketExtension *extension, @@ -192,14 +187,15 @@ * @payload: (transfer full): the payload data * @error: return location for a #GError * - * Process a message after it's received. If the payload isn't changed the given - * @payload is just returned, otherwise g_bytes_unref() is called on the given - * @payload and a new #GBytes is returned with the new data. + * Process a message after it's received. + * + * If the payload isn't changed the given @payload is just returned, otherwise + * method@GLib.Bytes.unref is called on the given @payload and a new + * struct@GLib.Bytes is returned with the new data. * * Extensions using reserved bits of the header will reset them in @header. * * Returns: (transfer full): the message payload data, or %NULL in case of error - * */ GBytes * soup_websocket_extension_process_incoming_message (SoupWebsocketExtension *extension,
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/websocket/soup-websocket.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/websocket/soup-websocket.c
Changed
@@ -34,42 +34,37 @@ #define FIXED_DIGEST_LEN 20 /** - * SECTION:soup-websocket - * @section_id: SoupWebsocket - * @short_description: The WebSocket Protocol - * @see_also: soup_session_websocket_connect_async(), - * soup_server_add_websocket_handler() + * SoupWebsocketConnection: * - * #SoupWebsocketConnection provides support for the <ulink - * url="http://tools.ietf.org/html/rfc6455">WebSocket</ulink> protocol. + * The WebSocket Protocol * - * To connect to a WebSocket server, create a #SoupSession and call - * soup_session_websocket_connect_async(). To accept WebSocket - * connections, create a #SoupServer and add a handler to it with - * soup_server_add_websocket_handler(). + * Provides support for the WebSocket(http://tools.ietf.org/html/rfc6455) + * protocol. * - * (Lower-level support is available via - * soup_websocket_client_prepare_handshake() and - * soup_websocket_client_verify_handshake(), for handling the client - * side of the WebSocket handshake, and - * soup_websocket_server_process_handshake() for handling the server - * side.) - * - * #SoupWebsocketConnection handles the details of WebSocket - * communication. You can use soup_websocket_connection_send_text() - * and soup_websocket_connection_send_binary() to send data, and the - * #SoupWebsocketConnection::message signal to receive data. - * (#SoupWebsocketConnection currently only supports asynchronous - * I/O.) + * To connect to a WebSocket server, create a class@Session and call + * method@Session.websocket_connect_async. To accept WebSocket + * connections, create a class@Server and add a handler to it with + * method@Server.add_websocket_handler. * + * (Lower-level support is available via + * func@websocket_client_prepare_handshake and + * func@websocket_client_verify_handshake, for handling the client side of the + * WebSocket handshake, and func@websocket_server_process_handshake for + * handling the server side.) + * + * #SoupWebsocketConnection handles the details of WebSocket communication. You + * can use method@WebsocketConnection.send_text and + * method@WebsocketConnection.send_binary to send data, and the + * signal@WebsocketConnection::message signal to receive data. + * (#SoupWebsocketConnection currently only supports asynchronous I/O.) */ /** * SOUP_WEBSOCKET_ERROR: * - * A #GError domain for WebSocket-related errors. Used with - * #SoupWebsocketError. + * A struct@GLib.Error domain for WebSocket-related errors. * + * Used with error@WebsocketError. */ /** @@ -83,7 +78,6 @@ * because the "Origin" header was not an allowed value. * * WebSocket-related errors. - * */ /** @@ -92,8 +86,7 @@ * @SOUP_WEBSOCKET_CONNECTION_CLIENT: a client-side connection * @SOUP_WEBSOCKET_CONNECTION_SERVER: a server-side connection * - * The type of a #SoupWebsocketConnection. - * + * The type of a class@WebsocketConnection. */ /** @@ -101,9 +94,7 @@ * @SOUP_WEBSOCKET_DATA_TEXT: UTF-8 text * @SOUP_WEBSOCKET_DATA_BINARY: binary data * - * The type of data contained in a #SoupWebsocketConnection::message - * signal. - * + * The type of data contained in a signal@WebsocketConnection::message signal. */ /** @@ -132,10 +123,10 @@ * the TLS handshake failed; must not be sent. * * Pre-defined close codes that can be passed to - * soup_websocket_connection_close() or received from - * soup_websocket_connection_get_close_code(). (However, other codes - * are also allowed.) + * method@WebsocketConnection.close or received from + * method@WebsocketConnection.get_close_code. * + * However, other codes are also allowed. */ /** @@ -146,7 +137,6 @@ * @SOUP_WEBSOCKET_STATE_CLOSED: the connection is completely closed down * * The state of the WebSocket connection. - * */ G_DEFINE_QUARK (soup-websocket-error-quark, soup_websocket_error) @@ -248,12 +238,13 @@ * * Adds the necessary headers to @msg to request a WebSocket * handshake including supported WebSocket extensions. + * * The message body and non-WebSocket-related headers are * not modified. * * This is a low-level function; if you use - * soup_session_websocket_connect_async() to create a WebSocket - * connection, it will call this for you. + * method@Session.websocket_connect_async to create a WebSocket connection, it + * will call this for you. */ void soup_websocket_client_prepare_handshake (SoupMessage *msg, @@ -545,9 +536,9 @@ * only requests containing valid supported extensions in * "Sec-WebSocket-Extensions" header will be accepted. * - * Normally soup_websocket_server_process_handshake() + * Normally func@websocket_server_process_handshake * will take care of this for you, and if you use - * soup_server_add_websocket_handler() to handle accepting WebSocket + * method@Server.add_websocket_handler to handle accepting WebSocket * connections, it will call that for you. However, this function may * be useful if you need to perform more complicated validation; eg, * accepting multiple different Origins, or handling different protocols @@ -687,7 +678,7 @@ * will be returned in @accepted_extensions parameter if non-%NULL. * * This is a low-level function; if you use - * soup_server_add_websocket_handler() to handle accepting WebSocket + * method@Server.add_websocket_handler to handle accepting WebSocket * connections, it will call this for you. * * Returns: %TRUE if @msg contained a valid WebSocket handshake @@ -802,7 +793,7 @@ * extensions are returned in @accepted_extensions parameter if non-%NULL. * * This is a low-level function; if you use - * soup_session_websocket_connect_async() to create a WebSocket + * method@Session.websocket_connect_async to create a WebSocket * connection, it will call this for you. * * Returns: %TRUE if @msg contains a completed valid WebSocket
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/libsoup/websocket/soup-websocket.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/libsoup/websocket/soup-websocket.h
Changed
@@ -24,6 +24,12 @@ G_BEGIN_DECLS +/** + * soup_websocket_error_quark: + * Registers error quark for SoupWebsocket if needed. + * + * Returns: Error quark for SoupWebsocket. + */ #define SOUP_WEBSOCKET_ERROR (soup_websocket_error_quark ()) SOUP_AVAILABLE_IN_ALL GQuark soup_websocket_error_quark (void) G_GNUC_CONST;
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/meson.build -> _service:tar_scm:libsoup-3.2.2.tar.xz/meson.build
Changed
@@ -1,5 +1,5 @@ project('libsoup', 'c', - version: '3.0.6', + version: '3.2.2', meson_version : '>= 0.54', license : 'LGPL-2.0-or-later', default_options : @@ -11,23 +11,25 @@ soup_version = meson.project_version() -# Before making a release, the libversion string should be modified. -# -# * Bump the first component if binary compatibility has been broken; or -# * Bump the second component if new APIs are added; or -# * Bump the third component otherwise. -# -# When bumping the first component version, set the second and third components -# to 0. When bumping the second version, set the third one to zero. -libversion = '0.0.5' +# Before making a release, the soversion string should be modified. +# The string is of the form C, R, A. +# - If interfaces have been changed or added, but binary compatibility has +# been preserved, change to C+1, 0, A+1. +# - If binary compatibility has been broken (eg removed or changed interfaces) +# change to C+1, 0, 0 +# - If the interface is the same as the previous version, use C, R+1, A. +soup_version_info = 6, 0, 6 + +# Turn C, R, A into an actual usable *soversion*. +soup_soversion_major = soup_version_info0 - soup_version_info2 # Current-Age +soup_soversion_minor = soup_version_info2 # Age +soup_soversion_micro = soup_version_info1 # Revision +libversion = '@0@.@1@.@2@'.format(soup_soversion_major, soup_soversion_minor, soup_soversion_micro) + +darwin_versions = soup_soversion_major + soup_soversion_minor + 1, '@0@.@1@'.format(soup_soversion_major + soup_soversion_minor + 1, soup_soversion_micro) + apiversion = '3.0' -soversion = libversion.split('.')0 libsoup_api_name = '@0@-@1@'.format(meson.project_name(), apiversion) -libversion_arr = libversion.split('.') -darwin_version_major = libversion_arr0.to_int() -darwin_version_minor = libversion_arr1.to_int() -darwin_version_micro = libversion_arr2.to_int() -darwin_versions = darwin_version_major + darwin_version_minor + 1, '@0@.@1@'.format(darwin_version_major + darwin_version_minor + 1, darwin_version_micro) host_system = host_machine.system() @@ -108,7 +110,12 @@ glib_deps = glib_dep, gmodule_dep, gobject_dep, gio_dep +cdata = configuration_data() + libnghttp2_dep = dependency('libnghttp2') +if cc.has_function('nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation', prefix : '#include <nghttp2/nghttp2.h>', dependencies : libnghttp2_dep) + cdata.set('HAVE_NGHTTP2_OPTION_SET_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION', '1') +endif sqlite_dep = dependency('sqlite3', required: false) @@ -124,8 +131,6 @@ sqlite_dep = dependency('sqlite3') endif -cdata = configuration_data() - brotlidec_dep = dependency('libbrotlidec', required : get_option('brotli')) if brotlidec_dep.found() cdata.set('WITH_BROTLI', true) @@ -266,10 +271,11 @@ cdata.set('APACHE_MODULE_DIR', apache_modules_dirs0) message('Apache SSL module directory: ' + apache_modules_dirs1) cdata.set('APACHE_SSL_MODULE_DIR', apache_modules_dirs1) - message('Apache PHP module file: ' + apache_modules_dirs2) - cdata.set('APACHE_PHP_MODULE_FILE', apache_modules_dirs2) - message('Apache mod_unixd module directory: ' + (apache_modules_dirs3 != '' ? apache_modules_dirs3 : '(none)')) - cdata.set('IF_HAVE_MOD_UNIXD', apache_modules_dirs3 != '' ? '' : '#') + message('Apache mod_unixd module directory: ' + (apache_modules_dirs2 != '' ? apache_modules_dirs2 : '(none)')) + cdata.set('IF_HAVE_MOD_UNIXD', apache_modules_dirs2 != '' ? '' : '#') + message('Apache HTTP/2 module directory: ' + apache_modules_dirs3) + cdata.set('IF_HAVE_MOD_HTTP2', apache_modules_dirs3 != '' ? '' : '#') + cdata.set('APACHE_HTTP2_MODULE_DIR', apache_modules_dirs3) cdata.set('HAVE_APACHE', have_apache) else message('Failed to locate necessary Apache modules for full test coverage') @@ -284,27 +290,9 @@ have_autobahn = find_program('wstest', required: get_option('autobahn')).found() endif -# Quart server used for HTTP/2 tests -quart_found = false - -if not get_option('http2_tests').disabled() - pymod = import('python') - python = pymod.find_installation('python3') - if python.found() - ret = run_command(python, '-c', 'import importlib\nassert(importlib.find_loader("quart"))', check: false) - if ret.returncode() == 0 - quart_found = true - endif - endif - message('Python module quart found: @0@'.format(quart_found.to_string('YES', 'NO'))) - if get_option('http2_tests').enabled() and not quart_found - error('quart is required for http2 tests') - endif -endif - gnutls_dep = dependency('gnutls', version: '>= 3.6.0', required : get_option('pkcs11_tests')) -if not have_apache or not quart_found or not have_autobahn or not gnutls_dep.found() +if not have_apache or not have_autobahn or not gnutls_dep.found() warning('Some regression tests will not be compiled due to missing libraries or modules. Please check the logs for more details.') endif @@ -402,13 +390,11 @@ subdir('fuzzing') if get_option('tests') subdir('tests') - endif - -if get_option('gtk_doc') - srcdir = include_directories('libsoup') - subdir('docs/reference') endif +srcdir = include_directories('libsoup') +subdir('docs/reference') + summary({ 'prefix' : get_option('prefix'), 'libdir' : get_option('libdir'), @@ -424,7 +410,7 @@ 'Translations' : xgettext.found(), 'GIR' : enable_introspection, 'VAPI' : enable_vapi, - 'Documentation' : get_option('gtk_doc'), + 'Documentation' : have_docs, }, section : 'Features' ) @@ -432,7 +418,7 @@ summary({ 'All tests' : get_option('tests'), 'Tests requiring Apache' : have_apache, - 'Tests requiring Quart' : quart_found, + 'Documentation tests' : get_option('doc_tests'), 'Fuzzing tests' : get_option('fuzzing').enabled(), 'Autobahn tests' : have_autobahn, 'PKCS #11 tests' : gnutls_dep.found(),
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/meson_options.txt -> _service:tar_scm:libsoup-3.2.2.tar.xz/meson_options.txt
Changed
@@ -47,10 +47,16 @@ description : 'Build Vala bindings' ) -option('gtk_doc', +option('docs', + type: 'feature', + value: 'auto', + description: 'Enable generating the API reference' +) + +option('doc_tests', type: 'boolean', value: false, - description: 'Enable generating the API reference' + description: 'Enable strict validation of API docs' ) option('tests', @@ -83,12 +89,6 @@ description: 'enable fuzzing support' ) -option('http2_tests', - type: 'feature', - value: 'auto', - description: 'enable HTTP/2 tests depending on quart' -) - option('pkcs11_tests', type: 'feature', value: 'auto',
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/po/LINGUAS -> _service:tar_scm:libsoup-3.2.2.tar.xz/po/LINGUAS
Changed
@@ -30,6 +30,7 @@ id it ja +ka kn ko lt
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/po/POTFILES.in -> _service:tar_scm:libsoup-3.2.2.tar.xz/po/POTFILES.in
Changed
@@ -7,9 +7,10 @@ libsoup/http1/soup-message-io-data.c libsoup/http2/soup-body-input-stream-http2.c libsoup/http2/soup-client-message-io-http2.c +libsoup/server/http1/soup-server-message-io-http1.c +libsoup/server/http2/soup-server-message-io-http2.c +libsoup/server/soup-listener.c libsoup/server/soup-server.c -libsoup/server/soup-server-io.c -libsoup/server/soup-socket.c libsoup/soup-session.c libsoup/soup-tld.c libsoup/websocket/soup-websocket.c
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/po/ka.po
Added
@@ -0,0 +1,193 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: libsoup\n" +"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libsoup/issues\n" +"POT-Creation-Date: 2022-06-02 11:25+0000\n" +"PO-Revision-Date: 2022-06-06 16:49+0200\n" +"Last-Translator: Temuri Doghonadze <temuri.doghonadze@gmail.com>\n" +"Language-Team: Georgian <(nothing)>\n" +"Language: ka\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Poedit 3.0.1\n" + +#: libsoup/cache/soup-cache-input-stream.c:70 +msgid "Network stream unexpectedly closed" +msgstr "ქსელური ნაკადი მოულოდნელად დაიხურა" + +#: libsoup/cache/soup-cache-input-stream.c:252 +msgid "Failed to completely cache the resource" +msgstr "რესურსის მთლიანად დაქეშვა შეუძლებელია" + +#: libsoup/content-decoder/soup-converter-wrapper.c:197 +#, c-format +msgid "Output buffer is too small" +msgstr "გამოტანის ბუფერი მეტისმეტად პატარაა" + +#: libsoup/http1/soup-body-input-stream.c:155 +#: libsoup/http1/soup-body-input-stream.c:187 +#: libsoup/http1/soup-body-input-stream.c:220 +#: libsoup/http1/soup-message-io-data.c:77 +msgid "Connection terminated unexpectedly" +msgstr "შეერთება მოულოდნელად დაიხურა" + +#: libsoup/http1/soup-body-input-stream.c:471 +msgid "Invalid seek request" +msgstr "გადახვევის არასწორი მოთხოვნა" + +#: libsoup/http1/soup-body-input-stream.c:499 +msgid "Cannot truncate SoupBodyInputStream" +msgstr "SoupBodyInputStream-ის წაკვეთის შეცდომა" + +#: libsoup/http1/soup-client-message-io-http1.c:312 +#: libsoup/http1/soup-client-message-io-http1.c:756 +#: libsoup/http2/soup-body-input-stream-http2.c:221 +#: libsoup/server/soup-server-io.c:363 libsoup/server/soup-server-io.c:828 +msgid "Operation would block" +msgstr "ოპერაცია დაიბლოკებოდა" + +#: libsoup/http1/soup-client-message-io-http1.c:456 +msgid "Could not parse HTTP response" +msgstr "HTTP პასუხის დამუშავების შეცდომა" + +#: libsoup/http1/soup-client-message-io-http1.c:479 +msgid "Unrecognized HTTP response encoding" +msgstr "HTTP პასუხის უცნობი კოდირება" + +#: libsoup/http1/soup-client-message-io-http1.c:715 +#: libsoup/http1/soup-client-message-io-http1.c:741 +#: libsoup/http2/soup-client-message-io-http2.c:1606 +#: libsoup/http2/soup-client-message-io-http2.c:1630 +msgid "Operation was cancelled" +msgstr "ოპერაცია გაუქმდა" + +#: libsoup/http1/soup-message-io-data.c:105 +msgid "Header too big" +msgstr "თავსართი ძალიან დიდია" + +#: libsoup/server/soup-server.c:1203 +msgid "Can’t create a TLS server without a TLS certificate" +msgstr "TLS სერვერის შექმნა შეუძლებელია TLS სერტიფიკატის გარეშე" + +#: libsoup/server/soup-server.c:1225 +#, c-format +msgid "Could not listen on address %s, port %d: " +msgstr "მისამართზე: %s, პორტზე %d მოსმენა შეუძლებელია: " + +#: libsoup/server/soup-socket.c:123 +msgid "Could not import existing socket: " +msgstr "არსებული სოკეტის შემოტანა შეუძლებელია: " + +#: libsoup/server/soup-socket.c:132 +msgid "Can’t import unconnected socket" +msgstr "დაუკავშირებელი სოკეტის შემოტანა შეუძლებელია" + +#: libsoup/soup-session.c:1180 +msgid "Location header is missing or empty in response headers" +msgstr "პასუხის თავსართებში თავსართი \"Location\" არ არსებობს ან ცარიელია" + +#: libsoup/soup-session.c:1194 +#, c-format +msgid "Invalid URI “%s” in Location response header" +msgstr "\"Location\" თავსართის არასწორი URI: \"%s\"" + +#: libsoup/soup-session.c:1214 +msgid "Too many redirects" +msgstr "მეტისმეტად ბევრი გადამისამართება" + +#: libsoup/soup-session.c:1219 +msgid "Message was restarted too many times" +msgstr "შეტყობინება მეტისმეტად ბევრჯერ გაეშვა" + +#: libsoup/soup-session.c:3310 libsoup/soup-session.c:3458 +msgid "Message is already in session queue" +msgstr "შეტყობინება უკვე სესიის რიგშია" + +#: libsoup/soup-session.c:3821 +msgid "The server did not accept the WebSocket handshake." +msgstr "სერვერი WebSocket-ის ტიპის კავშირს არ დაეთანხმა." + +#: libsoup/soup-tld.c:129 +msgid "No public-suffix list available." +msgstr "Public-suffix სია მიუწვდომელია." + +#: libsoup/soup-tld.c:139 libsoup/soup-tld.c:155 +msgid "Invalid hostname" +msgstr "ჰოსტის არასწორი სახელი" + +#: libsoup/soup-tld.c:146 +msgid "Hostname is an IP address" +msgstr "ჰოსის სახელი IP მისამართს წარმოადგენს" + +#: libsoup/soup-tld.c:167 +msgid "Hostname has no base domain" +msgstr "ჰოსტის სახელს ბაზისური დომენი არ გააჩნია" + +#: libsoup/soup-tld.c:175 +msgid "Not enough domains" +msgstr "არასაკმარისი დომენები" + +#: libsoup/websocket/soup-websocket.c:390 +#: libsoup/websocket/soup-websocket.c:434 +#: libsoup/websocket/soup-websocket.c:450 +msgid "Server requested unsupported extension" +msgstr "სერვერმა მხარდაუჭერელი გაფართოება მოითხოვა" + +#: libsoup/websocket/soup-websocket.c:413 +#: libsoup/websocket/soup-websocket.c:605 +#, c-format +msgid "Incorrect WebSocket “%s” header" +msgstr "WebSocket-ის არასწორი თავსართი: \"%s\"" + +#: libsoup/websocket/soup-websocket.c:414 +#: libsoup/websocket/soup-websocket.c:869 +#, c-format +msgid "Server returned incorrect “%s” key" +msgstr "სერვერმა არასწორი გასაღებს დააბრუნა: \"%s\"" + +#: libsoup/websocket/soup-websocket.c:477 +#, c-format +msgid "Duplicated parameter in “%s” WebSocket extension header" +msgstr "დუბლრებული პარამეტრი WebSocket-ის გაფართოების თავსართში: \"%s\"" + +#: libsoup/websocket/soup-websocket.c:478 +#, c-format +msgid "Server returned a duplicated parameter in “%s” WebSocket extension header" +msgstr "სერვერმა დააბრუნა დუბლრებული პარამეტრი WebSocket-ის გაფართოების თავსართში: \"%s\"" + +#: libsoup/websocket/soup-websocket.c:568 +#: libsoup/websocket/soup-websocket.c:578 +msgid "WebSocket handshake expected" +msgstr "მოსალოდნელი იყო WebSocket-ის ტიპის შეერთება" + +#: libsoup/websocket/soup-websocket.c:586 +msgid "Unsupported WebSocket version" +msgstr "WebSocket-ის მხარდაუჭერელი ვერსია" + +#: libsoup/websocket/soup-websocket.c:595 +msgid "Invalid WebSocket key" +msgstr "WebSocket-ის არასწორი გასაღები" + +#: libsoup/websocket/soup-websocket.c:614 +msgid "Unsupported WebSocket subprotocol" +msgstr "WebSocket-ის მხარდაუჭერელი ქვეპროტოკოლი" + +#: libsoup/websocket/soup-websocket.c:820 +msgid "Server rejected WebSocket handshake" +msgstr "სერვერმა უარჰყო WebSocket-ის ტიპის შეერთება" + +#: libsoup/websocket/soup-websocket.c:828 +#: libsoup/websocket/soup-websocket.c:837 +msgid "Server ignored WebSocket handshake" +msgstr "სერვერმა დააიგნორა WebSocket-ის ტიპის შეერთება" + +#: libsoup/websocket/soup-websocket.c:849 +msgid "Server requested unsupported protocol" +msgstr "სერვერმა მხარდაუჭერელი პროტოკოლი მოითხოვა"
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/po/sk.po -> _service:tar_scm:libsoup-3.2.2.tar.xz/po/sk.po
Changed
@@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: libsoup\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libsoup/issues\n" -"POT-Creation-Date: 2020-06-26 17:08+0000\n" -"PO-Revision-Date: 2020-09-06 17:36+0200\n" +"POT-Creation-Date: 2021-06-11 18:40+0000\n" +"PO-Revision-Date: 2022-04-01 14:04+0200\n" "Last-Translator: Dušan Kazik <prescott66@gmail.com>\n" "Language-Team: Slovak <gnome-sk-list@gnome.org>\n" "Language: sk\n" @@ -16,195 +16,218 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n" -"X-Generator: Poedit 2.4.1\n" +"X-Generator: Poedit 3.0.1\n" -#: libsoup/soup-body-input-stream.c:139 libsoup/soup-body-input-stream.c:170 -#: libsoup/soup-body-input-stream.c:203 libsoup/soup-message-io.c:236 -msgid "Connection terminated unexpectedly" -msgstr "Pripojenie bolo neočakávane ukončené" - -#: libsoup/soup-body-input-stream.c:459 -msgid "Invalid seek request" -msgstr "Neplatná požiadavka na posunutie" - -# struct -#: libsoup/soup-body-input-stream.c:487 -msgid "Cannot truncate SoupBodyInputStream" -msgstr "SoupBodyInputStream sa nedá skrátiť" - -#: libsoup/soup-cache-input-stream.c:76 +#: libsoup/cache/soup-cache-input-stream.c:70 msgid "Network stream unexpectedly closed" msgstr "Sieťový prúd bol neočakávane uzavretý" -#: libsoup/soup-cache-input-stream.c:291 +#: libsoup/cache/soup-cache-input-stream.c:252 msgid "Failed to completely cache the resource" msgstr "Úplné načítanie zdroja do dočasnej pamäte zlyhalo" -#: libsoup/soup-directory-input-stream.c:231 -msgid "Name" -msgstr "Názov" - -#: libsoup/soup-directory-input-stream.c:232 -msgid "Size" -msgstr "Veľkosť" - -#: libsoup/soup-directory-input-stream.c:233 -msgid "Date Modified" -msgstr "Dátum zmeny" - # PM: buffer by som preložil ako schránka # PK: schranka je clipboard, buffer je jednoznacne vyrovnavacia pamet -#: libsoup/soup-converter-wrapper.c:189 +#: libsoup/content-decoder/soup-converter-wrapper.c:197 #, c-format msgid "Output buffer is too small" msgstr "Výstupná vyrovnávacia pamäť je príliš malá" -#: libsoup/soup-message-client-io.c:39 -msgid "Could not parse HTTP response" -msgstr "Nepodarilo sa analyzovať odpoveď HTTP" +#: libsoup/http1/soup-body-input-stream.c:155 +#: libsoup/http1/soup-body-input-stream.c:187 +#: libsoup/http1/soup-body-input-stream.c:220 +#: libsoup/http1/soup-message-io-data.c:77 +msgid "Connection terminated unexpectedly" +msgstr "Pripojenie bolo neočakávane ukončené" -#: libsoup/soup-message-client-io.c:62 -msgid "Unrecognized HTTP response encoding" -msgstr "Nerozpoznané kódovanie odpovede HTTP" +#: libsoup/http1/soup-body-input-stream.c:471 +msgid "Invalid seek request" +msgstr "Neplatná požiadavka na posunutie" -#: libsoup/soup-message-io.c:261 -msgid "Header too big" -msgstr "Záhlavie je príliš veľké" +# struct +#: libsoup/http1/soup-body-input-stream.c:499 +msgid "Cannot truncate SoupBodyInputStream" +msgstr "SoupBodyInputStream sa nedá skrátiť" # PK: tu neviem ako to povedat, malo by ist o to, ze proste ta operacia neni async -#: libsoup/soup-message-io.c:393 libsoup/soup-message-io.c:1016 +#: libsoup/http1/soup-client-message-io-http1.c:312 +#: libsoup/http1/soup-client-message-io-http1.c:756 +#: libsoup/http2/soup-body-input-stream-http2.c:221 +#: libsoup/server/soup-server-io.c:354 libsoup/server/soup-server-io.c:819 msgid "Operation would block" msgstr "Operácia by blokovala spracovanie" -#: libsoup/soup-message-io.c:968 libsoup/soup-message-io.c:1001 -msgid "Operation was cancelled" -msgstr "Operácia bola zrušená" +#: libsoup/http1/soup-client-message-io-http1.c:456 +msgid "Could not parse HTTP response" +msgstr "Nepodarilo sa analyzovať odpoveď HTTP" -#: libsoup/soup-message-server-io.c:63 -msgid "Could not parse HTTP request" -msgstr "Nepodarilo sa analyzovať požiadavku HTTP" +#: libsoup/http1/soup-client-message-io-http1.c:479 +msgid "Unrecognized HTTP response encoding" +msgstr "Nerozpoznané kódovanie odpovede HTTP" -# error -#: libsoup/soup-request.c:141 -#, c-format -msgid "No URI provided" -msgstr "Nebol poskytnutý identifikátor URI" +#: libsoup/http1/soup-client-message-io-http1.c:715 +#: libsoup/http1/soup-client-message-io-http1.c:741 +#: libsoup/http2/soup-client-message-io-http2.c:1426 +#: libsoup/http2/soup-client-message-io-http2.c:1450 +msgid "Operation was cancelled" +msgstr "Operácia bola zrušená" -# first %s - scheme (http, ftp, ...) -#: libsoup/soup-request.c:151 -#, c-format -msgid "Invalid “%s” URI: %s" -msgstr "Neplatná schéma „%s“ identifikátora URI: %s" +#: libsoup/http1/soup-message-io-data.c:105 +msgid "Header too big" +msgstr "Záhlavie je príliš veľké" -#: libsoup/soup-server.c:1810 +#: libsoup/server/soup-server.c:1072 msgid "Can’t create a TLS server without a TLS certificate" msgstr "Nedá sa vytvoriť TLS server bez TLS certifikátu" -#: libsoup/soup-server.c:1827 +#: libsoup/server/soup-server.c:1088 #, c-format msgid "Could not listen on address %s, port %d: " msgstr "Nepodarilo sa počúvať na adrese %s, porte %d: " -#: libsoup/soup-session.c:4570 -#, c-format -msgid "Could not parse URI “%s”" -msgstr "Nepodarilo sa analyzovať identifikátor URI „%s“" +#: libsoup/server/soup-socket.c:116 +msgid "Could not import existing socket: " +msgstr "Nepodarilo sa importovať existujúci soket: " -#: libsoup/soup-session.c:4607 -#, c-format -msgid "Unsupported URI scheme “%s”" -msgstr "Nepodporovaná schéma „%s“ pre identifikátor URI" +#: libsoup/server/soup-socket.c:125 +msgid "Can’t import unconnected socket" +msgstr "Nedá sa importovať nepripojený soket" + +#: libsoup/soup-session.c:1166 +msgid "Location header is missing or empty in response headers" +msgstr "" +"Hlavička s umiestnením chýba alebo je prázdna v hlavičkách s odpoveďami" -#: libsoup/soup-session.c:4629 +#: libsoup/soup-session.c:1180 #, c-format -msgid "Not an HTTP URI" -msgstr "Nie je HTTP URI" +msgid "Invalid URI “%s” in Location response header" +msgstr "Neplatný URI „%s“ v hlavičke odpovede s umiestnením" + +#: libsoup/soup-session.c:1200 +msgid "Too many redirects" +msgstr "Príliš veľa presmerovaní" + +#: libsoup/soup-session.c:1205 +msgid "Message was restarted too many times" +msgstr "Správa bola reštartovaná príliš veľakrát" -#: libsoup/soup-session.c:4840 +#: libsoup/soup-session.c:3315 libsoup/soup-session.c:3464 +msgid "Message is already in session queue" +msgstr "Správa už je vo fronte relácie" + +#: libsoup/soup-session.c:3825 msgid "The server did not accept the WebSocket handshake." msgstr "Server neprijal vyjednanie protokolu WebSocket." -#: libsoup/soup-socket.c:148 -msgid "Can’t import non-socket as SoupSocket" -msgstr "Nedá sa importovať ne-soket ako SoupSocket" +#: libsoup/soup-tld.c:142 +msgid "No public-suffix list available." +msgstr "Nie je dostupný žiadny zoznam verejných prípon." -#: libsoup/soup-socket.c:166 -msgid "Could not import existing socket: " -msgstr "Nepodarilo sa importovať existujúci soket: " +#: libsoup/soup-tld.c:152 libsoup/soup-tld.c:168 +msgid "Invalid hostname" +msgstr "Neplatný názov hostiteľa" -#: libsoup/soup-socket.c:175 -msgid "Can’t import unconnected socket" -msgstr "Nedá sa importovať nepripojený soket" +#: libsoup/soup-tld.c:159 +msgid "Hostname is an IP address" +msgstr "Názov hostiteľa je adresa IP" + +#: libsoup/soup-tld.c:180 +msgid "Hostname has no base domain" +msgstr "Názov hostiteľa neobsahuje základnú doménu" + +#: libsoup/soup-tld.c:188 +msgid "Not enough domains" +msgstr "Nedostatočný počet domén" -#: libsoup/soup-websocket.c:479 libsoup/soup-websocket.c:523 -#: libsoup/soup-websocket.c:539 +#: libsoup/websocket/soup-websocket.c:399 +#: libsoup/websocket/soup-websocket.c:443 +#: libsoup/websocket/soup-websocket.c:459 msgid "Server requested unsupported extension" msgstr "Server si vyžiadal nepodporované rozšírenie" -#: libsoup/soup-websocket.c:502 libsoup/soup-websocket.c:694 +#: libsoup/websocket/soup-websocket.c:422 +#: libsoup/websocket/soup-websocket.c:614 #, c-format msgid "Incorrect WebSocket “%s” header" msgstr "Nesprávna hlavička „%s“ protokolu Websocket" -#: libsoup/soup-websocket.c:503 libsoup/soup-websocket.c:1024 +#: libsoup/websocket/soup-websocket.c:423 +#: libsoup/websocket/soup-websocket.c:878 #, c-format msgid "Server returned incorrect “%s” key" msgstr "Server vrátil nesprávny kľúč „%s“" -#: libsoup/soup-websocket.c:566 +#: libsoup/websocket/soup-websocket.c:486 #, c-format msgid "Duplicated parameter in “%s” WebSocket extension header" msgstr "Duplikovaný parameter v hlavičke rozšírenia protokolu WebSocket „%s“" -#: libsoup/soup-websocket.c:567 +#: libsoup/websocket/soup-websocket.c:487 #, c-format -msgid "Server returned a duplicated parameter in “%s” WebSocket extension header" -msgstr "Server vrátil duplikovaný parameter v hlavičke rozšírenia protokolu WebSocket „%s“" +msgid "" +"Server returned a duplicated parameter in “%s” WebSocket extension header" +msgstr "" +"Server vrátil duplikovaný parameter v hlavičke rozšírenia protokolu " +"WebSocket „%s“" -#: libsoup/soup-websocket.c:658 libsoup/soup-websocket.c:667 +#: libsoup/websocket/soup-websocket.c:577 +#: libsoup/websocket/soup-websocket.c:587 msgid "WebSocket handshake expected" msgstr "Očakávalo sa vyjednanie protokolu WebSocket" -#: libsoup/soup-websocket.c:675 +#: libsoup/websocket/soup-websocket.c:595 msgid "Unsupported WebSocket version" msgstr "Nepodporovaná verzia protokolu WebSocket" -#: libsoup/soup-websocket.c:684 +#: libsoup/websocket/soup-websocket.c:604 msgid "Invalid WebSocket key" msgstr "Neplatný kľúč protokolu WebSocket" -#: libsoup/soup-websocket.c:703 +#: libsoup/websocket/soup-websocket.c:623 msgid "Unsupported WebSocket subprotocol" msgstr "Nepodporovaný podprotokol protokolu WebSocket" -#: libsoup/soup-websocket.c:975 +#: libsoup/websocket/soup-websocket.c:829 msgid "Server rejected WebSocket handshake" msgstr "Server odmietol vyjednanie protokolu WebSocket" -#: libsoup/soup-websocket.c:983 libsoup/soup-websocket.c:992 +#: libsoup/websocket/soup-websocket.c:837 +#: libsoup/websocket/soup-websocket.c:846 msgid "Server ignored WebSocket handshake" msgstr "Server ignoroval vyjednanie protokolu WebSocket" -#: libsoup/soup-websocket.c:1004 +#: libsoup/websocket/soup-websocket.c:858 msgid "Server requested unsupported protocol" msgstr "Server si vyžiadal nepodporovaný protokol" -#: libsoup/soup-tld.c:150 -msgid "No public-suffix list available." -msgstr "Nie je dostupný žiadny zoznam verejných prípon." +#~ msgid "Name" +#~ msgstr "Názov" -#: libsoup/soup-tld.c:160 libsoup/soup-tld.c:176 -msgid "Invalid hostname" -msgstr "Neplatný názov hostiteľa" +#~ msgid "Size" +#~ msgstr "Veľkosť" -#: libsoup/soup-tld.c:167 -msgid "Hostname is an IP address" -msgstr "Názov hostiteľa je adresa IP" +#~ msgid "Date Modified" +#~ msgstr "Dátum zmeny" -#: libsoup/soup-tld.c:188 -msgid "Hostname has no base domain" -msgstr "Názov hostiteľa neobsahuje základnú doménu" +#~ msgid "Could not parse HTTP request" +#~ msgstr "Nepodarilo sa analyzovať požiadavku HTTP" -#: libsoup/soup-tld.c:196 -msgid "Not enough domains" -msgstr "Nedostatočný počet domén" +# error +#~ msgid "No URI provided" +#~ msgstr "Nebol poskytnutý identifikátor URI" + +# first %s - scheme (http, ftp, ...) +#~ msgid "Invalid “%s” URI: %s" +#~ msgstr "Neplatná schéma „%s“ identifikátora URI: %s" + +#~ msgid "Could not parse URI “%s”" +#~ msgstr "Nepodarilo sa analyzovať identifikátor URI „%s“" + +#~ msgid "Unsupported URI scheme “%s”" +#~ msgstr "Nepodporovaná schéma „%s“ pre identifikátor URI" + +#~ msgid "Not an HTTP URI" +#~ msgstr "Nie je HTTP URI" + +#~ msgid "Can’t import non-socket as SoupSocket" +#~ msgstr "Nedá sa importovať ne-soket ako SoupSocket"
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/po/tr.po -> _service:tar_scm:libsoup-3.2.2.tar.xz/po/tr.po
Changed
@@ -1,5 +1,5 @@ # Turkish translation for libsoup. -# Copyright (C) 2012-2019 libsoup's COPYRIGHT HOLDER +# Copyright (C) 2012-2022 libsoup's COPYRIGHT HOLDER # This file is distributed under the same license as the libsoup package. # # Ozan Çağlayan <ozancag@gmail.com>, 2013. @@ -13,7 +13,7 @@ msgstr "" "Project-Id-Version: libsoup master\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libsoup/issues\n" -"POT-Creation-Date: 2021-08-09 09:48+0000\n" +"POT-Creation-Date: 2022-08-13 12:28+0000\n" "PO-Revision-Date: 2021-09-09 22:15+0300\n" "Last-Translator: Emin Tufan Çetin <etcetin@gmail.com>\n" "Language-Team: Türkçe <gnome-turk@gnome.org>\n" @@ -53,25 +53,26 @@ msgid "Cannot truncate SoupBodyInputStream" msgstr "SoupBodyInputStream kesilemiyor" -#: libsoup/http1/soup-client-message-io-http1.c:312 -#: libsoup/http1/soup-client-message-io-http1.c:756 +#: libsoup/http1/soup-client-message-io-http1.c:321 +#: libsoup/http1/soup-client-message-io-http1.c:762 #: libsoup/http2/soup-body-input-stream-http2.c:221 -#: libsoup/server/soup-server-io.c:363 libsoup/server/soup-server-io.c:828 +#: libsoup/server/http1/soup-server-message-io-http1.c:416 +#: libsoup/server/http1/soup-server-message-io-http1.c:889 msgid "Operation would block" msgstr "İşlem engellenebilir" -#: libsoup/http1/soup-client-message-io-http1.c:456 +#: libsoup/http1/soup-client-message-io-http1.c:462 msgid "Could not parse HTTP response" msgstr "HTTP yanıtı ayrıştırılamadı" -#: libsoup/http1/soup-client-message-io-http1.c:479 +#: libsoup/http1/soup-client-message-io-http1.c:485 msgid "Unrecognized HTTP response encoding" msgstr "HTTP yanıtı tanınmayan biçimde kodlanmış" -#: libsoup/http1/soup-client-message-io-http1.c:715 -#: libsoup/http1/soup-client-message-io-http1.c:741 -#: libsoup/http2/soup-client-message-io-http2.c:1456 -#: libsoup/http2/soup-client-message-io-http2.c:1480 +#: libsoup/http1/soup-client-message-io-http1.c:721 +#: libsoup/http1/soup-client-message-io-http1.c:747 +#: libsoup/http2/soup-client-message-io-http2.c:1544 +#: libsoup/http2/soup-client-message-io-http2.c:1568 msgid "Operation was cancelled" msgstr "İşlem iptal edildi" @@ -79,152 +80,118 @@ msgid "Header too big" msgstr "Başlık çok büyük" -#: libsoup/server/soup-server.c:1201 -msgid "Can’t create a TLS server without a TLS certificate" -msgstr "TLS sertifikası olmadan TLS sunucusu oluşturulamaz" - -#: libsoup/server/soup-server.c:1223 -#, c-format -msgid "Could not listen on address %s, port %d: " -msgstr "%s adresinin %d bağlantı noktası dinlenemedi: " - -#: libsoup/server/soup-socket.c:121 +#: libsoup/server/soup-listener.c:261 msgid "Could not import existing socket: " msgstr "Var olan yuva içe aktarılamadı: " -#: libsoup/server/soup-socket.c:130 +#: libsoup/server/soup-listener.c:267 msgid "Can’t import unconnected socket" msgstr "Bağlı olmayan yuva içe aktarılamıyor" -#: libsoup/soup-session.c:1166 +#: libsoup/server/soup-server.c:1209 +msgid "Can’t create a TLS server without a TLS certificate" +msgstr "TLS sertifikası olmadan TLS sunucusu oluşturulamaz" + +#: libsoup/soup-session.c:1116 msgid "Location header is missing or empty in response headers" msgstr "Konum başlığı eksik veya yanıt başlıklarında boş" -#: libsoup/soup-session.c:1180 +#: libsoup/soup-session.c:1130 #, c-format msgid "Invalid URI “%s” in Location response header" msgstr "Konum yanıt başlığında geçersiz URI “%s”" -#: libsoup/soup-session.c:1200 +#: libsoup/soup-session.c:1150 msgid "Too many redirects" msgstr "Çok sayıda yönlendirme" -#: libsoup/soup-session.c:1205 +#: libsoup/soup-session.c:1155 msgid "Message was restarted too many times" msgstr "İleti birçok kez yeniden başladı" -#: libsoup/soup-session.c:3317 libsoup/soup-session.c:3466 +#: libsoup/soup-session.c:3011 libsoup/soup-session.c:3159 msgid "Message is already in session queue" msgstr "İleti zaten oturum sırasında" -#: libsoup/soup-session.c:3827 +#: libsoup/soup-session.c:3491 msgid "The server did not accept the WebSocket handshake." msgstr "Sunucu, WebSocket el sıkışmasını kabul etmedi." -#: libsoup/soup-tld.c:142 +#: libsoup/soup-tld.c:129 msgid "No public-suffix list available." msgstr "Uygun halka açık son ek listesi yok." -#: libsoup/soup-tld.c:152 libsoup/soup-tld.c:168 +#: libsoup/soup-tld.c:139 libsoup/soup-tld.c:155 msgid "Invalid hostname" msgstr "Geçersiz makine adı" -#: libsoup/soup-tld.c:159 +#: libsoup/soup-tld.c:146 msgid "Hostname is an IP address" msgstr "Makine adı bir IP adresi" -#: libsoup/soup-tld.c:180 +#: libsoup/soup-tld.c:167 msgid "Hostname has no base domain" msgstr "Ana makinenin temel etki alanı yok" -#: libsoup/soup-tld.c:188 +#: libsoup/soup-tld.c:175 msgid "Not enough domains" msgstr "Yeterli etki alanı yok" -#: libsoup/websocket/soup-websocket.c:399 -#: libsoup/websocket/soup-websocket.c:443 -#: libsoup/websocket/soup-websocket.c:459 +#: libsoup/websocket/soup-websocket.c:390 +#: libsoup/websocket/soup-websocket.c:434 +#: libsoup/websocket/soup-websocket.c:450 msgid "Server requested unsupported extension" msgstr "Sunucu, desteklenmeyen eklenti isteğinde bulundu" -#: libsoup/websocket/soup-websocket.c:422 -#: libsoup/websocket/soup-websocket.c:614 +#: libsoup/websocket/soup-websocket.c:413 +#: libsoup/websocket/soup-websocket.c:605 #, c-format msgid "Incorrect WebSocket “%s” header" msgstr "Hatalı WebSocket “%s” başlığı" -#: libsoup/websocket/soup-websocket.c:423 -#: libsoup/websocket/soup-websocket.c:878 +#: libsoup/websocket/soup-websocket.c:414 +#: libsoup/websocket/soup-websocket.c:869 #, c-format msgid "Server returned incorrect “%s” key" msgstr "Sunucu, geçersiz “%s” anahtarı döndürdü" -#: libsoup/websocket/soup-websocket.c:486 +#: libsoup/websocket/soup-websocket.c:477 #, c-format msgid "Duplicated parameter in “%s” WebSocket extension header" msgstr "“%s” WebSocket eklenti başlığında yinelenen parametre" -#: libsoup/websocket/soup-websocket.c:487 +#: libsoup/websocket/soup-websocket.c:478 #, c-format -msgid "Server returned a duplicated parameter in “%s” WebSocket extension header" +msgid "" +"Server returned a duplicated parameter in “%s” WebSocket extension header" msgstr "Sunucu, “%s” WebSocket eklenti başlığında yinelenen parametre döndürdü" -#: libsoup/websocket/soup-websocket.c:577 -#: libsoup/websocket/soup-websocket.c:587 +#: libsoup/websocket/soup-websocket.c:568 +#: libsoup/websocket/soup-websocket.c:578 msgid "WebSocket handshake expected" msgstr "WebSocket el sıkışması bekleniyor" -#: libsoup/websocket/soup-websocket.c:595 +#: libsoup/websocket/soup-websocket.c:586 msgid "Unsupported WebSocket version" msgstr "Desteklenmeyen WebSocket sürümü" -#: libsoup/websocket/soup-websocket.c:604 +#: libsoup/websocket/soup-websocket.c:595 msgid "Invalid WebSocket key" msgstr "Geçersiz WebSocket anahtarı" -#: libsoup/websocket/soup-websocket.c:623 +#: libsoup/websocket/soup-websocket.c:614 msgid "Unsupported WebSocket subprotocol" msgstr "Desteklenmeyen WebSocket alt iletişim kuralı" -#: libsoup/websocket/soup-websocket.c:829 +#: libsoup/websocket/soup-websocket.c:820 msgid "Server rejected WebSocket handshake" msgstr "Sunucu, WebSocket el sıkışmasını reddetti" +#: libsoup/websocket/soup-websocket.c:828 #: libsoup/websocket/soup-websocket.c:837 -#: libsoup/websocket/soup-websocket.c:846 msgid "Server ignored WebSocket handshake" msgstr "Sunucu, WebSocket el sıkışmasını yoksaydı" -#: libsoup/websocket/soup-websocket.c:858 +#: libsoup/websocket/soup-websocket.c:849 msgid "Server requested unsupported protocol" msgstr "Sunucu, desteklenmeyen iletişim kuralı isteğinde bulundu" - -#~ msgid "Could not parse URI “%s”: %s" -#~ msgstr "URI “%s” ayrıştırılamadı: %s" - -#~ msgid "Unsupported URI scheme “%s”" -#~ msgstr "Desteklenmeyen URI şeması “%s”" - -#~ msgid "Invalid “%s” URI: %s" -#~ msgstr "Geçersiz “%s” URI: %s" - -#~ msgid "Name" -#~ msgstr "Ad" - -#~ msgid "Size" -#~ msgstr "Boyut" - -#~ msgid "Date Modified" -#~ msgstr "Değiştirilme Tarihi" - -#~ msgid "Could not parse HTTP request" -#~ msgstr "HTTP isteği ayrıştırılamadı" - -#~ msgid "No URI provided" -#~ msgstr "URI verilmedi" - -#~ msgid "Not an HTTP URI" -#~ msgstr "HTTP URI değil" - -#~ msgid "Can’t import non-socket as SoupSocket" -#~ msgstr "Non-socket, SoupSocket olarak içe aktarılamıyor"
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/subprojects/gi-docgen.wrap
Added
@@ -0,0 +1,6 @@ +wrap-git +directory=gi-docgen +url=https://gitlab.gnome.org/GNOME/gi-docgen.git +push-url=ssh://git@gitlab.gnome.org:GNOME/gi-docgen.git +revision=main +depth=1
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/auth-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/auth-test.c
Changed
@@ -803,6 +803,42 @@ soup_test_session_abort_unref (session); } +static gboolean +sync_bad_password_authenticate (SoupMessage *msg, + SoupAuth *auth, + gboolean retrying) +{ + if (retrying) + return FALSE; + + soup_auth_authenticate (auth, "user1", "wrong"); + return TRUE; +} + +static void +do_sync_auth_bad_password_test (void) +{ + SoupSession *session; + SoupMessage *msg; + char *uri; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (NULL); + uri = g_strconcat (base_uri, "Basic/realm1/", NULL); + + msg = soup_message_new ("GET", uri); + g_free (uri); + g_signal_connect (msg, "authenticate", + G_CALLBACK (sync_bad_password_authenticate), + NULL); + soup_test_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED); + g_object_unref (msg); + + soup_test_session_abort_unref (session); +} + typedef struct { const char *password; struct { @@ -1849,6 +1885,7 @@ g_test_add_func ("/auth/async-auth/bad-password", do_async_auth_bad_password_test); g_test_add_func ("/auth/async-auth/no-password", do_async_auth_no_password_test); g_test_add_func ("/auth/async-auth/cancel", do_async_auth_cancel_test); + g_test_add_func ("/auth/sync-auth/bad-password", do_sync_auth_bad_password_test); g_test_add_func ("/auth/select-auth", do_select_auth_test); g_test_add_func ("/auth/auth-close", do_auth_close_test); g_test_add_func ("/auth/infinite-auth", do_infinite_auth_test);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/cache-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/cache-test.c
Changed
@@ -4,6 +4,7 @@ */ #include "test-utils.h" +#include <glib/gstdio.h> static void server_callback (SoupServer *server, @@ -861,6 +862,239 @@ g_free (cache_dir); } +typedef struct { + SoupSession *session; + GUri *uri; + gboolean must_revalidate; + gboolean is_expired; + gboolean hit_network; + gboolean validated; + GError *error; +} ThreadTestRequest; + +static void +threads_message_starting (SoupMessage *msg, + ThreadTestRequest *request) +{ + SoupMessageHeaders *request_headers; + + if (request->validated) + return; + + request_headers = soup_message_get_request_headers (msg); + request->validated = !!soup_message_headers_get_one (request_headers, "If-Modified-Since"); +} + +static void +threads_request_queued (SoupSession *session, + SoupMessage *msg, + ThreadTestRequest *request) +{ + if (soup_message_get_uri (msg) != request->uri) + return; + + g_signal_connect (msg, "starting", + G_CALLBACK (threads_message_starting), + request); +} + +static void +task_async_function (GTask *task, + GObject *source, + gpointer task_data, + GCancellable *cancellable) +{ + GMainContext *context; + SoupMessage *msg; + SoupMessageHeaders *request_headers; + GInputStream *stream; + ThreadTestRequest *request = (ThreadTestRequest *)task_data; + + context = g_main_context_new (); + g_main_context_push_thread_default (context); + + msg = soup_message_new_from_uri ("GET", request->uri); + g_signal_connect (request->session, "request-queued", + G_CALLBACK (threads_request_queued), + request); + request_headers = soup_message_get_request_headers (msg); + if (request->must_revalidate) { + soup_message_headers_append (request_headers, + "Test-Set-Last-Modified", + request->is_expired ? "Sat, 02 Jan 2010 00:00:00 GMT" : "Fri, 01 Jan 2010 00:00:00 GMT"); + soup_message_headers_append (request_headers, + "Test-Set-Expires", "Sat, 02 Jan 2011 00:00:00 GMT"); + soup_message_headers_append (request_headers, + "Test-Set-Cache-Control", "must-revalidate"); + } else { + soup_message_headers_append (request_headers, + "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT"); + } + + stream = soup_test_request_send (request->session, msg, NULL, 0, &request->error); + if (stream) { + request->hit_network = is_network_stream (stream); + soup_test_request_read_all (stream, NULL, &request->error); + g_object_unref (stream); + } + + g_signal_handlers_disconnect_by_data (request->session, request); + + g_object_unref (msg); + + /* Continue iterating to ensure the item is unqueued and the connection released */ + while (g_main_context_pending (context)) + g_main_context_iteration (context, TRUE); + + /* Cache writes are G_PRIORITY_LOW, so they won't have happened yet */ + soup_cache_flush ((SoupCache *)soup_session_get_feature (request->session, SOUP_TYPE_CACHE)); + + g_task_return_boolean (task, TRUE); + + g_main_context_pop_thread_default (context); + g_main_context_unref (context); +} + +static void +task_finished_cb (SoupSession *session, + GAsyncResult *result, + guint *finished_count) +{ + g_assert_true (g_task_propagate_boolean (G_TASK (result), NULL)); + g_atomic_int_inc (finished_count); +} + +static void +do_threads_test (gconstpointer data) +{ + GUri *base_uri = (GUri *)data; + SoupSession *session; + SoupCache *cache; + char *cache_dir; + ThreadTestRequest requests4; + guint i; + guint finished_count = 0; + + session = soup_test_session_new (NULL); + + cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL); + cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER); + soup_session_add_feature (session, SOUP_SESSION_FEATURE (cache)); + + requests0.session = session; + requests0.uri = g_uri_parse_relative (base_uri, "/1", SOUP_HTTP_URI_FLAGS, NULL); + requests0.must_revalidate = FALSE; + requests0.is_expired = FALSE; + requests1.session = session; + requests1.uri = g_uri_parse_relative (base_uri, "/2", SOUP_HTTP_URI_FLAGS, NULL); + requests1.must_revalidate = TRUE; + requests1.is_expired = FALSE; + requests2.session = session; + requests2.uri = g_uri_parse_relative (base_uri, "/3", SOUP_HTTP_URI_FLAGS, NULL); + requests2.must_revalidate = FALSE; + requests2.is_expired = FALSE; + requests3.session = session; + requests3.uri = g_uri_parse_relative (base_uri, "/4", SOUP_HTTP_URI_FLAGS, NULL); + requests3.must_revalidate = TRUE; + requests3.is_expired = FALSE; + + for (i = 0; i < 4; i++) { + GTask *task; + + requestsi.hit_network = FALSE; + requestsi.validated = FALSE; + requestsi.error = NULL; + + task = g_task_new (NULL, NULL, (GAsyncReadyCallback)task_finished_cb, &finished_count); + g_task_set_task_data (task, &requestsi, NULL); + g_task_run_in_thread (task, (GTaskThreadFunc)task_async_function); + g_object_unref (task); + } + + while (g_atomic_int_get (&finished_count) != 4) + g_main_context_iteration (NULL, TRUE); + + /* Initial requests hit the network */ + for (i = 0; i < 4; i++) { + g_assert_true (requestsi.hit_network); + g_assert_false (requestsi.validated); + g_assert_no_error (requestsi.error); + } + + finished_count = 0; + for (i = 0; i < 4; i++) { + GTask *task; + + requestsi.hit_network = FALSE; + requestsi.validated = FALSE; + requestsi.error = NULL; + + task = g_task_new (NULL, NULL, (GAsyncReadyCallback)task_finished_cb, &finished_count); + g_task_set_task_data (task, &requestsi, NULL); + g_task_run_in_thread (task, (GTaskThreadFunc)task_async_function); + g_object_unref (task); + } + + while (g_atomic_int_get (&finished_count) != 4) + g_main_context_iteration (NULL, TRUE); + + /* None of the requests hit the ntwork */ + for (i = 0; i < 4; i++) { + g_assert_false (requestsi.hit_network); + g_assert_no_error (requestsi.error); + } + + /* The ones including must-revalidate are validated */ + g_assert_false (requests0.validated); + g_assert_true (requests1.validated); + g_assert_false (requests2.validated); + g_assert_true (requests3.validated); + + /* Try again making the validations fail */ + requests1.is_expired = TRUE; + requests3.is_expired = TRUE; + + finished_count = 0; + for (i = 0; i < 4; i++) { + GTask *task; + + requestsi.hit_network = FALSE; + requestsi.validated = FALSE; + requestsi.error = NULL; + + task = g_task_new (NULL, NULL, (GAsyncReadyCallback)task_finished_cb, &finished_count); + g_task_set_task_data (task, &requestsi, NULL); + g_task_run_in_thread (task, (GTaskThreadFunc)task_async_function); + g_object_unref (task); + } + + while (g_atomic_int_get (&finished_count) != 4) + g_main_context_iteration (NULL, TRUE); + + /* None of the requests failed */ + for (i = 0; i < 4; i++) + g_assert_no_error (requestsi.error); + + /* The ones including must-revalidate are validated and hit the network this time */ + g_assert_false (requests0.validated); + g_assert_false (requests0.hit_network); + g_assert_true (requests1.validated); + g_assert_true (requests1.hit_network); + g_assert_false (requests2.validated); + g_assert_false (requests2.hit_network); + g_assert_true (requests3.validated); + g_assert_true (requests3.hit_network); + + for (i = 0; i < 4; i++) + g_uri_unref (requestsi.uri); + + soup_test_session_abort_unref (session); + soup_cache_clear (cache); + g_rmdir (cache_dir); + g_object_unref (cache); + g_free (cache_dir); +} + int main (int argc, char **argv) { @@ -880,6 +1114,7 @@ g_test_add_data_func ("/cache/headers", base_uri, do_headers_test); g_test_add_data_func ("/cache/leaks", base_uri, do_leaks_test); g_test_add_data_func ("/cache/metrics", base_uri, do_metrics_test); + g_test_add_data_func ("/cache/threads", base_uri, do_threads_test); ret = g_test_run ();
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/connection-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/connection-test.c
Changed
@@ -6,13 +6,14 @@ #include "test-utils.h" #include "soup-connection.h" -#include "soup-socket.h" +#include "soup-server-connection.h" #include "soup-server-message-private.h" #include <gio/gnetworking.h> static SoupServer *server; static GUri *base_uri; +static GUri *base_https_uri; static GMutex server_mutex; static void @@ -24,17 +25,17 @@ } static void -close_socket (SoupServerMessage *msg, - SoupSocket *sock) +close_socket (SoupServerMessage *msg, + SoupServerConnection *conn) { - GSocket *gsocket; + GSocket *socket; int sockfd; /* Actually calling soup_socket_disconnect() here would cause * us to leak memory, so just shutdown the socket instead. */ - gsocket = soup_socket_get_gsocket (sock); - sockfd = g_socket_get_fd (gsocket); + socket = soup_server_connection_get_socket (conn); + sockfd = g_socket_get_fd (socket); #ifdef G_OS_WIN32 shutdown (sockfd, SD_SEND); #else @@ -50,41 +51,39 @@ } static gboolean -timeout_socket (GObject *pollable, - SoupSocket *sock) +timeout_socket (GObject *pollable, + SoupServerConnection *conn) { - soup_socket_disconnect (sock); + soup_server_connection_disconnect (conn); return FALSE; } static void -timeout_request_started (SoupServer *server, - SoupServerMessage *msg, - gpointer user_data) +timeout_request_finished (SoupServer *server, + SoupServerMessage *msg, + gpointer user_data) { - SoupSocket *sock; - GMainContext *context = g_main_context_get_thread_default (); + SoupServerConnection *conn; GIOStream *iostream; GInputStream *istream; GSource *source; - g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL); + g_signal_handlers_disconnect_by_func (server, timeout_request_finished, NULL); - sock = soup_server_message_get_soup_socket (msg); - iostream = soup_socket_get_iostream (sock); + conn = soup_server_message_get_connection (msg); + iostream = soup_server_connection_get_iostream (conn); istream = g_io_stream_get_input_stream (iostream); source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (istream), NULL); - g_source_set_callback (source, (GSourceFunc)timeout_socket, sock, NULL); + g_source_set_callback (source, (GSourceFunc)timeout_socket, conn, NULL); g_source_attach (source, g_main_context_get_thread_default ()); g_source_unref (source); g_mutex_unlock (&server_mutex); - while (soup_socket_is_connected (sock)) - g_main_context_iteration (context, TRUE); } static void -setup_timeout_persistent (SoupServer *server, SoupSocket *sock) +setup_timeout_persistent (SoupServer *server, + SoupServerConnection *conn) { /* In order for the test to work correctly, we have to * close the connection *after* the client side writes @@ -101,8 +100,8 @@ * 3. Close the socket. */ g_mutex_lock (&server_mutex); - g_signal_connect (server, "request-started", - G_CALLBACK (timeout_request_started), NULL); + g_signal_connect (server, "request-finished", + G_CALLBACK (timeout_request_finished), NULL); } static void @@ -143,16 +142,16 @@ "Connection", "close"); if (too_long) { - SoupSocket *sock; + SoupServerConnection *conn; /* soup-message-io will wait for us to add * another chunk after the first, to fill out * the declared Content-Length. Instead, we * forcibly close the socket at that point. */ - sock = soup_server_message_get_soup_socket (msg); + conn = soup_server_message_get_connection (msg); g_signal_connect (msg, "wrote-chunk", - G_CALLBACK (close_socket), sock); + G_CALLBACK (close_socket), conn); } else if (no_close) { /* Remove the 'Connection: close' after writing * the headers, so that when we check it after @@ -166,10 +165,10 @@ } if (!strcmp (path, "/timeout-persistent")) { - SoupSocket *sock; + SoupServerConnection *conn; - sock = soup_server_message_get_soup_socket (msg); - setup_timeout_persistent (server, sock); + conn = soup_server_message_get_connection (msg); + setup_timeout_persistent (server, conn); } soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); @@ -260,14 +259,17 @@ } static void -do_timeout_test_for_session (SoupSession *session) +do_timeout_test_for_base_uri (GUri *base_uri) { + SoupSession *session; SoupMessage *msg; GSocket *sockets4 = { NULL, NULL, NULL, NULL }; GUri *timeout_uri; int i; GBytes *body; + session = soup_test_session_new (NULL); + g_signal_connect (session, "request-queued", G_CALLBACK (request_queued_socket_collector), &sockets); @@ -310,23 +312,24 @@ for (i = 0; socketsi; i++) g_object_unref (socketsi); + + soup_test_session_abort_unref (session); } static void do_persistent_connection_timeout_test (void) { - SoupSession *session; - g_test_bug ("631525"); - debug_printf (1, " Normal session, message API\n"); - session = soup_test_session_new (NULL); - do_timeout_test_for_session (session); - soup_test_session_abort_unref (session); + debug_printf (1, " HTTP/1\n"); + do_timeout_test_for_base_uri (base_uri); + + debug_printf (1, " HTTP/2\n"); + do_timeout_test_for_base_uri (base_https_uri); } static void -do_persistent_connection_timeout_test_with_cancellation (void) +do_persistent_connection_timeout_test_with_cancellation_for_base_uri (GUri *base_uri) { SoupSession *session; SoupMessage *msg; @@ -409,6 +412,16 @@ soup_test_session_abort_unref (session); } +static void +do_persistent_connection_timeout_test_with_cancellation (void) +{ + debug_printf (1, " HTTP/1\n"); + do_persistent_connection_timeout_test_with_cancellation_for_base_uri (base_uri); + + debug_printf (1, " HTTP/2\n"); + do_persistent_connection_timeout_test_with_cancellation_for_base_uri (base_https_uri); +} + static GMainLoop *max_conns_loop; static int msgs_done; static guint quit_loop_timeout; @@ -1591,6 +1604,99 @@ soup_test_session_abort_unref (session); } +static void +force_http2_test_network_event (SoupMessage *msg, + GSocketClientEvent event, + GIOStream *connection, + SoupConnection **conn) +{ + if (event != G_SOCKET_CLIENT_RESOLVING) + return; + + *conn = soup_message_get_connection (msg); +} + +static void +do_connection_force_http2_test (void) +{ + SoupSession *session; + SoupMessage *msg; + SoupConnection *conn1 = NULL; + SoupConnection *conn2 = NULL; + GBytes *body; + + SOUP_TEST_SKIP_IF_NO_TLS; + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (NULL); + + msg = soup_message_new ("GET", HTTPS_SERVER); + g_signal_connect (msg, "network-event", + G_CALLBACK (force_http2_test_network_event), + &conn1); + body = soup_session_send_and_read (session, msg, NULL, NULL); + g_assert_nonnull (conn1); + g_assert_cmpint (soup_connection_get_state (conn1), ==, SOUP_CONNECTION_IDLE); + g_assert_cmpint (soup_connection_get_negotiated_protocol (conn1), ==, SOUP_HTTP_1_1); + g_object_unref (msg); + g_bytes_unref (body); + + /* With HTTP/2 forced, a new connection must be created */ + msg = soup_message_new ("GET", HTTPS_SERVER); + g_signal_connect (msg, "network-event", + G_CALLBACK (force_http2_test_network_event), + &conn2); + soup_message_set_force_http_version (msg, SOUP_HTTP_2_0); + body = soup_session_send_and_read (session, msg, NULL, NULL); + g_assert_nonnull (conn2); + g_assert_cmpint (soup_connection_get_state (conn2), ==, SOUP_CONNECTION_IDLE); + g_assert_cmpint (soup_connection_get_negotiated_protocol (conn2), ==, SOUP_HTTP_2_0); + g_assert_false (conn1 == conn2); + g_object_unref (msg); + g_bytes_unref (body); + + soup_test_session_abort_unref (session); +} + +static void +message_restarted (SoupMessage *msg, + gboolean *was_restarted) +{ + *was_restarted = TRUE; +} + +static void +do_connection_http_1_1_required_test (void) +{ + SoupSession *session; + SoupMessage *msg; + GBytes *body; + gboolean was_restarted = FALSE; + GError *error = NULL; + + SOUP_TEST_SKIP_IF_NO_TLS; + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (NULL); + + msg = soup_message_new ("GET", "https://127.0.0.1:47525/client-cert"); + soup_message_set_force_http_version (msg, SOUP_HTTP_2_0); + g_signal_connect (msg, "restarted", + G_CALLBACK (message_restarted), &was_restarted); + body = soup_test_session_async_send (session, msg, NULL, &error); + g_assert_no_error (error); + g_assert_cmpuint (soup_message_get_status (msg), ==, 403); + g_assert_true (was_restarted); + g_assert_nonnull (body); + g_bytes_unref (body); + g_object_unref (msg); + + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); + + soup_test_session_abort_unref (session); +} + int main (int argc, char **argv) { @@ -1599,9 +1705,10 @@ test_init (argc, argv, NULL); apache_init (); - server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); - soup_server_add_handler (server, NULL, server_callback, "http", NULL); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD | SOUP_TEST_SERVER_HTTP2); + soup_server_add_handler (server, NULL, server_callback, NULL, NULL); base_uri = soup_test_server_get_uri (server, "http", NULL); + base_https_uri = soup_test_server_get_uri (server, "https", NULL); g_test_add_func ("/connection/content-length-framing", do_content_length_framing_test); g_test_add_func ("/connection/persistent-connection-timeout", do_persistent_connection_timeout_test); @@ -1614,6 +1721,8 @@ g_test_add_func ("/connection/event", do_connection_event_test); g_test_add_func ("/connection/preconnect", do_connection_preconnect_test); g_test_add_func ("/connection/metrics", do_connection_metrics_test); + g_test_add_func ("/connection/force-http2", do_connection_force_http2_test); + g_test_add_func ("/connection/http2/http-1-1-required", do_connection_http_1_1_required_test); ret = g_test_run ();
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/context-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/context-test.c
Changed
@@ -9,7 +9,6 @@ static char *base_uri; typedef struct { - SoupServer *server; SoupServerMessage *msg; GSource *timeout; } SlowData; @@ -35,7 +34,7 @@ soup_message_body_append (response_body, SOUP_MEMORY_STATIC, "OK\r\n", 4); soup_message_body_complete (response_body); - soup_server_unpause_message (sd->server, sd->msg); + soup_server_message_unpause (sd->msg); g_object_unref (sd->msg); return FALSE; @@ -67,10 +66,9 @@ soup_message_headers_set_encoding (response_headers, SOUP_ENCODING_CHUNKED); g_object_ref (msg); - soup_server_pause_message (server, msg); + soup_server_message_pause (msg); sd = g_new (SlowData, 1); - sd->server = server; sd->msg = msg; sd->timeout = soup_add_timeout ( g_main_context_get_thread_default (), @@ -132,11 +130,11 @@ GAsyncResult *result, GMainContext *async_context) { - GInputStream *stream; + GBytes *body; g_assert_true (async_context == g_main_context_get_thread_default ()); - stream = soup_session_send_finish (session, result, NULL); - g_clear_object (&stream); + body = soup_session_send_and_read_finish (session, result, NULL); + g_clear_pointer (&body, g_bytes_unref); } static void @@ -178,9 +176,9 @@ msg = soup_message_new ("GET", uri); loop = g_main_loop_new (async_context, FALSE); g_signal_connect (msg, "finished", G_CALLBACK (message_finished), loop); - soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL, - (GAsyncReadyCallback)message_send_cb, - async_context); + soup_session_send_and_read_async (session, msg, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)message_send_cb, + async_context); g_main_loop_run (loop); /* We need one more iteration, because SoupMessage::finished is emitted * right before the message is unqueued. @@ -217,7 +215,7 @@ SoupSession *session; char *uri; SoupMessage *msg; - GInputStream *stream; + GBytes *body; GMainLoop *loop; idle = g_idle_add_full (G_PRIORITY_HIGH, idle_test2_fail, NULL, NULL); @@ -231,18 +229,18 @@ debug_printf (1, " send_message\n"); msg = soup_message_new ("GET", uri); - stream = soup_session_send (session, msg, NULL, NULL); + body = soup_session_send_and_read (session, msg, NULL, NULL); soup_test_assert_message_status (msg, SOUP_STATUS_OK); - g_object_unref (stream); + g_bytes_unref (body); g_object_unref (msg); debug_printf (1, " queue_message\n"); msg = soup_message_new ("GET", uri); loop = g_main_loop_new (async_context, FALSE); g_signal_connect (msg, "finished", G_CALLBACK (message_finished), loop); - soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL, - (GAsyncReadyCallback)message_send_cb, - async_context); + soup_session_send_and_read_async (session, msg, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)message_send_cb, + async_context); g_main_loop_run (loop); /* We need one more iteration, because SoupMessage::finished is emitted * right before the message is unqueued.
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/cookies-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/cookies-test.c
Changed
@@ -419,6 +419,92 @@ g_uri_unref (uri); } +typedef struct { + SoupSession *session; + const char *cookie; +} ThreadTestData; + +static void +task_sync_function (GTask *task, + GObject *source, + ThreadTestData *data, + GCancellable *cancellable) +{ + SoupMessage *msg; + GBytes *body; + + msg = soup_message_new_from_uri ("GET", first_party_uri); + soup_message_headers_append (soup_message_get_request_headers (msg), + "Echo-Set-Cookie", data->cookie); + body = soup_session_send_and_read (data->session, msg, NULL, NULL); + g_assert_nonnull (body); + g_bytes_unref (body); + g_object_unref (msg); + + g_task_return_boolean (task, TRUE); +} + +static void +task_finished_cb (SoupSession *session, + GAsyncResult *result, + guint *finished_count) +{ + g_assert_true (g_task_propagate_boolean (G_TASK (result), NULL)); + g_atomic_int_inc (finished_count); +} + +static gint +find_cookie (SoupCookie *cookie, + const char *name) +{ + return g_strcmp0 (soup_cookie_get_name (cookie), name); +} + +static void +do_cookies_threads_test (void) +{ + SoupSession *session; + SoupCookieJar *jar; + guint n_msgs = 4; + guint finished_count = 0; + guint i; + const char *values4 = { "one=1", "two=2", "three=3", "four=4" }; + GSList *cookies; + + session = soup_test_session_new (NULL); + soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR); + jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR)); + + for (i = 0; i < n_msgs; i++) { + GTask *task; + ThreadTestData *data; + + data = g_new (ThreadTestData, 1); + data->session = session; + data->cookie = valuesi; + + task = g_task_new (NULL, NULL, (GAsyncReadyCallback)task_finished_cb, &finished_count); + g_task_set_task_data (task, data, g_free); + g_task_run_in_thread (task, (GTaskThreadFunc)task_sync_function); + g_object_unref (task); + } + + while (g_atomic_int_get (&finished_count) != n_msgs) + g_main_context_iteration (NULL, TRUE); + + cookies = soup_cookie_jar_get_cookie_list (jar, first_party_uri, TRUE); + g_assert_cmpuint (g_slist_length (cookies), ==, 4); + g_assert_nonnull (g_slist_find_custom (cookies, "one", (GCompareFunc)find_cookie)); + g_assert_nonnull (g_slist_find_custom (cookies, "two", (GCompareFunc)find_cookie)); + g_assert_nonnull (g_slist_find_custom (cookies, "three", (GCompareFunc)find_cookie)); + g_assert_nonnull (g_slist_find_custom (cookies, "four", (GCompareFunc)find_cookie)); + + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); + + soup_test_session_abort_unref (session); +} + int main (int argc, char **argv) { @@ -443,6 +529,7 @@ g_test_add_func ("/cookies/get-cookies/empty-host", do_get_cookies_empty_host_test); g_test_add_func ("/cookies/remove-feature", do_remove_feature_test); g_test_add_func ("/cookies/secure-cookies", do_cookies_strict_secure_test); + g_test_add_func ("/cookies/threads", do_cookies_threads_test); ret = g_test_run ();
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/http2-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/http2-test.c
Changed
@@ -20,13 +20,20 @@ #include "test-utils.h" #include "soup-connection.h" #include "soup-message-private.h" +#include "soup-message-headers-private.h" +#include "soup-server-message-private.h" #include "soup-body-input-stream-http2.h" +#include <gio/gnetworking.h> + +static GUri *base_uri; typedef struct { SoupSession *session; - SoupMessage *msg; } Test; +#define LARGE_N_CHARS 24 +#define LARGE_CHARS_REPEAT 1024 + static void setup_session (Test *test, gconstpointer data) { @@ -42,56 +49,74 @@ static void do_basic_async_test (Test *test, gconstpointer data) { - test->msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/"); + SoupMessage *msg; + GBytes *response; GError *error = NULL; - GBytes *response = soup_test_session_async_send (test->session, test->msg, NULL, &error); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); + g_assert_cmpuint (soup_message_get_http_version (msg), ==, SOUP_HTTP_1_1); + + response = soup_test_session_async_send (test->session, msg, NULL, &error); g_assert_no_error (error); + g_assert_cmpuint (soup_message_get_http_version (msg), ==, SOUP_HTTP_2_0); g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Hello world"); g_bytes_unref (response); - g_object_unref (test->msg); + g_object_unref (msg); } static void do_basic_sync_test (Test *test, gconstpointer data) { - test->msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/"); + SoupMessage *msg; + GBytes *response; GError *error = NULL; - GBytes *response = soup_session_send_and_read (test->session, test->msg, NULL, &error); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); + g_assert_cmpuint (soup_message_get_http_version (msg), ==, SOUP_HTTP_1_1); + response = soup_session_send_and_read (test->session, msg, NULL, &error); g_assert_no_error (error); + g_assert_cmpuint (soup_message_get_http_version (msg), ==, SOUP_HTTP_2_0); g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Hello world"); g_bytes_unref (response); - g_object_unref (test->msg); + g_object_unref (msg); } static void do_no_content_async_test (Test *test, gconstpointer data) { - test->msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/no-content"); + GUri *uri; + SoupMessage *msg; + GBytes *response; GError *error = NULL; - GBytes *response = soup_test_session_async_send (test->session, test->msg, NULL, &error); + uri = g_uri_parse_relative (base_uri, "/no-content", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); + response = soup_test_session_async_send (test->session, msg, NULL, &error); g_assert_no_error (error); - g_assert_cmpuint (soup_message_get_status (test->msg), ==, 204); + g_assert_cmpuint (soup_message_get_status (msg), ==, 204); g_assert_cmpuint (g_bytes_get_size (response), ==, 0); + g_uri_unref (uri); g_bytes_unref (response); - g_object_unref (test->msg); + g_object_unref (msg); } static void do_large_test (Test *test, gconstpointer data) { gboolean async = GPOINTER_TO_INT (data); - SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/large"); + GUri *uri; + SoupMessage *msg; GBytes *response; GError *error = NULL; + uri = g_uri_parse_relative (base_uri, "/large", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); + /* This is both large and read in chunks */ if (async) response = soup_test_session_async_send (test->session, msg, NULL, &error); @@ -99,9 +124,9 @@ response = soup_session_send_and_read (test->session, msg, NULL, &error); g_assert_no_error (error); - /* Size hardcoded to match http2-server.py's response */ - g_assert_cmpuint (g_bytes_get_size (response), ==, (1024 * 24) + 1); + g_assert_cmpuint (g_bytes_get_size (response), ==, (LARGE_N_CHARS * LARGE_CHARS_REPEAT) + 1); + g_uri_unref (uri); g_bytes_unref (response); g_object_unref (msg); } @@ -144,15 +169,18 @@ do_multi_message_async_test (Test *test, gconstpointer data) { GMainContext *async_context = g_main_context_ref_thread_default (); + GUri *uri1, *uri2; + SoupMessage *msg1, *msg2; + GBytes *response1 = NULL; + GBytes *response2 = NULL; - SoupMessage *msg1 = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/echo_query?body%201"); + uri1 = g_uri_parse_relative (base_uri, "echo_query?body%201", SOUP_HTTP_URI_FLAGS, NULL); + msg1 = soup_message_new_from_uri (SOUP_METHOD_GET, uri1); soup_message_set_http_version (msg1, SOUP_HTTP_2_0); - SoupMessage *msg2 = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/echo_query?body%202"); + uri2 = g_uri_parse_relative (base_uri, "echo_query?body%202", SOUP_HTTP_URI_FLAGS, NULL); + msg2 = soup_message_new_from_uri (SOUP_METHOD_GET, uri2); soup_message_set_http_version (msg2, SOUP_HTTP_2_0); - - GBytes *response1 = NULL; - GBytes *response2 = NULL; soup_session_send_async (test->session, msg1, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response1); soup_session_send_async (test->session, msg2, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response2); @@ -173,6 +201,8 @@ g_bytes_unref (response2); g_object_unref (msg1); g_object_unref (msg2); + g_uri_unref (uri1); + g_uri_unref (uri2); g_main_context_unref (async_context); } @@ -208,21 +238,19 @@ static void do_cancellation_test (Test *test, gconstpointer data) { + GUri *uri; SoupMessage *msg; GMainContext *async_context = g_main_context_ref_thread_default (); GCancellable *cancellable = g_cancellable_new (); gboolean done = FALSE; - msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/large"); + uri = g_uri_parse_relative (base_uri, "/large", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); soup_session_send_and_read_async (test->session, msg, G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback)on_send_and_read_cancelled_complete, &done); - /* Just iterate until a partial read is happening */ - for (guint i = 100000; i; i--) - g_main_context_iteration (async_context, FALSE); - - /* Then cancel everything */ - g_cancellable_cancel (cancellable); + /* Cancel right after getting the headers */ + g_signal_connect_swapped (msg, "got-headers", G_CALLBACK (g_cancellable_cancel), cancellable); while (!done) g_main_context_iteration (async_context, FALSE); @@ -230,13 +258,14 @@ g_object_unref (msg); done = FALSE; - msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/large"); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); soup_session_send_and_read_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, (GAsyncReadyCallback)on_send_and_read_complete, &done); while (!done) g_main_context_iteration (async_context, FALSE); + g_uri_unref (uri); g_object_unref (msg); g_object_unref (cancellable); g_main_context_unref (async_context); @@ -258,7 +287,7 @@ if (cancelled_by_session) flags |= SOUP_TEST_REQUEST_CANCEL_BY_SESSION; - msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/"); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); cancellable = g_cancellable_new (); istream = soup_test_request_send (session, msg, cancellable, flags, &error); g_assert_no_error (error); @@ -307,13 +336,17 @@ static void do_post_sync_test (Test *test, gconstpointer data) { + GUri *uri; + SoupMessage *msg; + GInputStream *response; GBytes *bytes = g_bytes_new_static ("body 1", sizeof ("body 1")); - test->msg = soup_message_new (SOUP_METHOD_POST, "https://127.0.0.1:5000/echo_post"); - soup_message_set_request_body_from_bytes (test->msg, "text/plain", bytes); - GError *error = NULL; - GInputStream *response = soup_session_send (test->session, test->msg, NULL, &error); + uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri); + soup_message_set_request_body_from_bytes (msg, "text/plain", bytes); + + response = soup_session_send (test->session, msg, NULL, &error); g_assert_no_error (error); g_assert_nonnull (response); @@ -323,27 +356,31 @@ g_bytes_unref (response_bytes); g_object_unref (response); g_bytes_unref (bytes); - g_object_unref (test->msg); - + g_object_unref (msg); + g_uri_unref (uri); } static void do_post_large_sync_test (Test *test, gconstpointer data) { - guint large_size = 1000000; + GUri *uri; + SoupMessage *msg; + GInputStream *response; + guint large_size = 10000; char *large_data; unsigned int i; + GError *error = NULL; large_data = g_malloc (large_size); for (i = 0; i < large_size; i++) large_datai = i & 0xFF; GBytes *bytes = g_bytes_new_take (large_data, large_size); - test->msg = soup_message_new (SOUP_METHOD_POST, "https://127.0.0.1:5000/echo_post"); - soup_message_set_request_body_from_bytes (test->msg, "text/plain", bytes); - GError *error = NULL; - GInputStream *response = soup_session_send (test->session, test->msg, NULL, &error); + uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri); + soup_message_set_request_body_from_bytes (msg, "text/plain", bytes); + response = soup_session_send (test->session, msg, NULL, &error); g_assert_no_error (error); g_assert_nonnull (response); @@ -353,24 +390,27 @@ g_bytes_unref (response_bytes); g_object_unref (response); g_bytes_unref (bytes); - g_object_unref (test->msg); + g_object_unref (msg); + g_uri_unref (uri); } static void do_post_async_test (Test *test, gconstpointer data) { + GUri *uri; + SoupMessage *msg; + GBytes *response = NULL; GMainContext *async_context = g_main_context_ref_thread_default (); - GBytes *bytes = g_bytes_new_static ("body 1", sizeof ("body 1")); - test->msg = soup_message_new (SOUP_METHOD_POST, "https://127.0.0.1:5000/echo_post"); - soup_message_set_request_body_from_bytes (test->msg, "text/plain", bytes); - GBytes *response = NULL; - soup_session_send_async (test->session, test->msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response); + uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri); + soup_message_set_request_body_from_bytes (msg, "text/plain", bytes); - while (!response) { + soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response); + + while (!response) g_main_context_iteration (async_context, TRUE); - } g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "body 1"); @@ -380,14 +420,18 @@ g_bytes_unref (response); g_bytes_unref (bytes); g_main_context_unref (async_context); - g_object_unref (test->msg); + g_object_unref (msg); + g_uri_unref (uri); } static void do_post_large_async_test (Test *test, gconstpointer data) { + GUri *uri; + SoupMessage *msg; + GBytes *response = NULL; GMainContext *async_context = g_main_context_ref_thread_default (); - guint large_size = 1000000; + guint large_size = 10000; char *large_data; unsigned int i; @@ -395,15 +439,15 @@ for (i = 0; i < large_size; i++) large_datai = i & 0xFF; GBytes *bytes = g_bytes_new_take (large_data, large_size); - test->msg = soup_message_new (SOUP_METHOD_POST, "https://127.0.0.1:5000/echo_post"); - soup_message_set_request_body_from_bytes (test->msg, "text/plain", bytes); - GBytes *response = NULL; - soup_session_send_async (test->session, test->msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response); + uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri); + soup_message_set_request_body_from_bytes (msg, "text/plain", bytes); - while (!response) { + soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response); + + while (!response) g_main_context_iteration (async_context, TRUE); - } g_assert_true (g_bytes_equal (bytes, response)); @@ -413,22 +457,26 @@ g_bytes_unref (response); g_bytes_unref (bytes); g_main_context_unref (async_context); - g_object_unref (test->msg); + g_object_unref (msg); + g_uri_unref (uri); } static void do_post_blocked_async_test (Test *test, gconstpointer data) { + GUri *uri; + SoupMessage *msg; + GBytes *response = NULL; GMainContext *async_context = g_main_context_ref_thread_default (); GInputStream *in_stream = soup_body_input_stream_http2_new (); soup_body_input_stream_http2_add_data (SOUP_BODY_INPUT_STREAM_HTTP2 (in_stream), (guint8*)"Part 1 -", 8); - test->msg = soup_message_new (SOUP_METHOD_POST, "https://127.0.0.1:5000/echo_post"); - soup_message_set_request_body (test->msg, "text/plain", in_stream, 8 + 8); + uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri); + soup_message_set_request_body (msg, "text/plain", in_stream, 8 + 8); - GBytes *response = NULL; - soup_session_send_async (test->session, test->msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response); + soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response); while (!response) { // Let it iterate for a bit waiting on blocked data @@ -447,23 +495,27 @@ g_bytes_unref (response); g_object_unref (in_stream); g_main_context_unref (async_context); - g_object_unref (test->msg); + g_object_unref (msg); + g_uri_unref (uri); } static void do_post_file_async_test (Test *test, gconstpointer data) { + GUri *uri; + SoupMessage *msg; + GBytes *response = NULL; GMainContext *async_context = g_main_context_ref_thread_default (); GFile *in_file = g_file_new_for_path (g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL)); GFileInputStream *in_stream = g_file_read (in_file, NULL, NULL); g_assert_nonnull (in_stream); - test->msg = soup_message_new (SOUP_METHOD_POST, "https://127.0.0.1:5000/echo_post"); - soup_message_set_request_body (test->msg, "application/x-x509-ca-cert", G_INPUT_STREAM (in_stream), -1); + uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri); + soup_message_set_request_body (msg, "application/x-x509-ca-cert", G_INPUT_STREAM (in_stream), -1); - GBytes *response = NULL; - soup_session_send_async (test->session, test->msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response); + soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_complete, &response); while (!response) g_main_context_iteration (async_context, TRUE); @@ -477,7 +529,8 @@ g_object_unref (in_stream); g_object_unref (in_file); g_main_context_unref (async_context); - g_object_unref (test->msg); + g_object_unref (msg); + g_uri_unref (uri); } static gboolean @@ -500,18 +553,22 @@ static void do_paused_async_test (Test *test, gconstpointer data) { - - test->msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/auth"); - g_signal_connect (test->msg, "authenticate", G_CALLBACK (on_authenticate), NULL); - + GUri *uri; + SoupMessage *msg; + GBytes *response; GError *error = NULL; - GBytes *response = soup_test_session_async_send (test->session, test->msg, NULL, &error); + + uri = g_uri_parse_relative (base_uri, "/auth", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); + g_signal_connect (msg, "authenticate", G_CALLBACK (on_authenticate), NULL); + response = soup_test_session_async_send (test->session, msg, NULL, &error); g_assert_no_error (error); g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Authenticated"); g_bytes_unref (response); - g_object_unref (test->msg); + g_object_unref (msg); + g_uri_unref (uri); } static SoupConnection *last_connection; @@ -556,6 +613,7 @@ { GMainContext *async_context; guint complete_count = 0; + GUri *uri; if (g_getenv ("ASAN_OPTIONS")) { g_test_skip ("Flakey on asan GitLab runner"); @@ -564,10 +622,11 @@ async_context = g_main_context_ref_thread_default (); + uri = g_uri_parse_relative (base_uri, "/slow", SOUP_HTTP_URI_FLAGS, NULL); #define N_TESTS 100 for (unsigned int i = 0; i < N_TESTS; ++i) { - SoupMessage *msg = soup_message_new ("GET", "https://127.0.0.1:5000/slow"); + SoupMessage *msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_ready, &complete_count); g_object_unref (msg); } @@ -581,7 +640,7 @@ /* After no messages reference the connection it should be IDLE and reusable */ g_assert_cmpuint (soup_connection_get_state (last_connection), ==, SOUP_CONNECTION_IDLE); - SoupMessage *msg = soup_message_new ("GET", "https://127.0.0.1:5000/slow"); + SoupMessage *msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); soup_session_send_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, on_send_ready, &complete_count); g_object_unref (msg); @@ -591,22 +650,28 @@ while (g_main_context_pending (async_context)) g_main_context_iteration (async_context, FALSE); + g_uri_unref (uri); g_main_context_unref (async_context); } static void do_misdirected_request_test (Test *test, gconstpointer data) { - test->msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/misdirected_request"); + GUri *uri; + SoupMessage *msg; + GBytes *response; GError *error = NULL; - GBytes *response = soup_test_session_async_send (test->session, test->msg, NULL, &error); + uri = g_uri_parse_relative (base_uri, "/misdirected_request", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); + response = soup_test_session_async_send (test->session, msg, NULL, &error); g_assert_no_error (error); g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Success!"); g_bytes_unref (response); - g_object_unref (test->msg); + g_object_unref (msg); + g_uri_unref (uri); } static void @@ -627,42 +692,50 @@ static void do_logging_test (Test *test, gconstpointer data) { + GUri *uri; + SoupMessage *msg; + GBytes *response; + GError *error = NULL; + GBytes *bytes = g_bytes_new_static ("Test", sizeof ("Test")); gboolean has_logged_body = FALSE; SoupLogger *logger = soup_logger_new (SOUP_LOGGER_LOG_BODY); soup_logger_set_printer (logger, log_printer, &has_logged_body, NULL); soup_session_add_feature (test->session, SOUP_SESSION_FEATURE (logger)); - GBytes *bytes = g_bytes_new_static ("Test", sizeof ("Test")); - test->msg = soup_message_new (SOUP_METHOD_POST, "https://127.0.0.1:5000/echo_post"); - soup_message_set_request_body_from_bytes (test->msg, "text/plain", bytes); - GError *error = NULL; - - GBytes *response = soup_test_session_async_send (test->session, test->msg, NULL, &error); + uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri); + soup_message_set_request_body_from_bytes (msg, "text/plain", bytes); + response = soup_test_session_async_send (test->session, msg, NULL, &error); g_assert_no_error (error); g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Test"); g_assert_true (has_logged_body); g_bytes_unref (response); - g_object_unref (test->msg); + g_object_unref (msg); + g_uri_unref (uri); } static void do_metrics_size_test (Test *test, gconstpointer data) { + GUri *uri; + SoupMessage *msg; + GBytes *response; + GError *error = NULL; GBytes *bytes = g_bytes_new_static ("Test", sizeof ("Test")); - test->msg = soup_message_new (SOUP_METHOD_POST, "https://127.0.0.1:5000/echo_post"); - soup_message_set_request_body_from_bytes (test->msg, "text/plain", bytes); - soup_message_add_flags (test->msg, SOUP_MESSAGE_COLLECT_METRICS); - GError *error = NULL; - GBytes *response = soup_test_session_async_send (test->session, test->msg, NULL, &error); + uri = g_uri_parse_relative (base_uri, "/echo_post", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_POST, uri); + soup_message_set_request_body_from_bytes (msg, "text/plain", bytes); + soup_message_add_flags (msg, SOUP_MESSAGE_COLLECT_METRICS); + response = soup_test_session_async_send (test->session, msg, NULL, &error); g_assert_no_error (error); g_assert_cmpstr (g_bytes_get_data (response, NULL), ==, "Test"); - SoupMessageMetrics *metrics = soup_message_get_metrics (test->msg); + SoupMessageMetrics *metrics = soup_message_get_metrics (msg); g_assert_nonnull (metrics); g_assert_cmpuint (soup_message_metrics_get_request_header_bytes_sent (metrics), >, 0); @@ -675,7 +748,8 @@ g_bytes_unref (response); g_bytes_unref (bytes); - g_object_unref (test->msg); + g_object_unref (msg); + g_uri_unref (uri); } static void @@ -765,7 +839,7 @@ gboolean got_body_called = FALSE; guint network_event_called = 0; - msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/"); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); soup_message_add_flags (msg, SOUP_MESSAGE_COLLECT_METRICS); g_signal_connect (msg, "starting", G_CALLBACK (metrics_test_message_starting_cb), @@ -837,7 +911,7 @@ do_preconnect_test (Test *test, gconstpointer data) { GMainContext *async_context = g_main_context_ref_thread_default (); - SoupMessage *msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/"); + SoupMessage *msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); GError *error = NULL; SoupConnection *conn = NULL; guint32 connection_id; @@ -853,7 +927,7 @@ g_assert_cmpuint (soup_connection_get_state (conn), ==, SOUP_CONNECTION_IDLE); g_object_unref (msg); - msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/"); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); GBytes *response = soup_test_session_async_send (test->session, msg, NULL, &error); g_assert_no_error (error); @@ -877,7 +951,7 @@ GBytes *body; GError *error = NULL; - msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/"); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); request_headers = soup_message_get_request_headers (msg); soup_message_headers_append (request_headers, invalid_headersi, "Value"); body = soup_test_session_async_send (test->session, msg, NULL, &error); @@ -889,14 +963,64 @@ } static void -content_sniffed (SoupMessage *msg, - char *content_type, - GHashTable *params) +do_invalid_header_received_test (Test *test, gconstpointer data) { - soup_test_assert (g_object_get_data (G_OBJECT (msg), "got-chunk") == NULL, - "got-chunk got emitted before content-sniffed"); + gboolean async = GPOINTER_TO_INT (data); + GUri *uri; + SoupMessage *msg; + GBytes *response; + GError *error = NULL; + + uri = g_uri_parse_relative (base_uri, "/invalid-header", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); + + if (async) + response = soup_test_session_async_send (test->session, msg, NULL, &error); + else + response = soup_session_send_and_read (test->session, msg, NULL, &error); + + g_assert_null (response); + g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_assert_cmpstr (error->message, ==, "HTTP/2 Error: PROTOCOL_ERROR"); + g_clear_error (&error); + g_uri_unref (uri); + g_object_unref (msg); +} +#ifdef HAVE_NGHTTP2_OPTION_SET_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION +static void +do_invalid_header_rfc9113_received_test (Test *test, gconstpointer data) +{ + gboolean async = GPOINTER_TO_INT (data); + GUri *uri; + SoupMessage *msg; + GBytes *response; + GError *error = NULL; + + uri = g_uri_parse_relative (base_uri, "/invalid-header-rfc9113", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); + + if (async) + response = soup_test_session_async_send (test->session, msg, NULL, &error); + else + response = soup_session_send_and_read (test->session, msg, NULL, &error); + + g_assert_nonnull (response); + g_assert_no_error (error); + g_clear_error (&error); + g_object_unref (msg); + g_uri_unref (uri); +} +#endif + +static void +content_sniffed (SoupMessage *msg, + const char *content_type, + GHashTable *params, + char **sniffed_type) +{ g_object_set_data (G_OBJECT (msg), "content-sniffed", GINT_TO_POINTER (TRUE)); + *sniffed_type = g_strdup (content_type); } static void @@ -922,19 +1046,22 @@ static void do_one_sniffer_test (SoupSession *session, - const char *uri, + const char *path, gsize expected_size, gboolean should_sniff, GMainContext *async_context) { + GUri *uri; SoupMessage *msg; GInputStream *stream = NULL; GBytes *bytes; + char *sniffed_type = NULL; - msg = soup_message_new (SOUP_METHOD_GET, uri); + uri = g_uri_parse_relative (base_uri, path, SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); g_object_connect (msg, "signal::got-headers", got_headers, NULL, - "signal::content-sniffed", content_sniffed, NULL, + "signal::content-sniffed", content_sniffed, &sniffed_type, NULL); if (async_context) { soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL, @@ -954,29 +1081,35 @@ if (should_sniff) { soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") != NULL, "content-sniffed did not get emitted"); + g_assert_cmpstr (sniffed_type, ==, "text/plain"); } else { soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") == NULL, "content-sniffed got emitted without a sniffer"); + g_assert_null (sniffed_type); } bytes = read_stream_to_bytes_sync (stream); g_assert_cmpuint (g_bytes_get_size (bytes), ==, expected_size); + g_free (sniffed_type); g_object_unref (stream); g_bytes_unref (bytes); g_object_unref (msg); + g_uri_unref (uri); } static void do_sniffer_async_test (Test *test, gconstpointer data) { GMainContext *async_context = g_main_context_ref_thread_default (); + gboolean should_content_sniff = GPOINTER_TO_INT (data); - soup_session_add_feature_by_type (test->session, SOUP_TYPE_CONTENT_SNIFFER); + if (should_content_sniff) + soup_session_add_feature_by_type (test->session, SOUP_TYPE_CONTENT_SNIFFER); - do_one_sniffer_test (test->session, "https://127.0.0.1:5000/", 11, TRUE, async_context); - do_one_sniffer_test (test->session, "https://127.0.0.1:5000/large", (1024 * 24) + 1, TRUE, async_context); - do_one_sniffer_test (test->session, "https://127.0.0.1:5000/no-content", 0, FALSE, async_context); + do_one_sniffer_test (test->session, "/", 11, should_content_sniff, async_context); + do_one_sniffer_test (test->session, "/large", (LARGE_N_CHARS * LARGE_CHARS_REPEAT) + 1, should_content_sniff, async_context); + do_one_sniffer_test (test->session, "/no-content", 0, should_content_sniff, async_context); while (g_main_context_pending (async_context)) g_main_context_iteration (async_context, FALSE); @@ -987,43 +1120,212 @@ static void do_sniffer_sync_test (Test *test, gconstpointer data) { - soup_session_add_feature_by_type (test->session, SOUP_TYPE_CONTENT_SNIFFER); + gboolean should_content_sniff = GPOINTER_TO_INT (data); - do_one_sniffer_test (test->session, "https://127.0.0.1:5000/", 11, TRUE, NULL); - do_one_sniffer_test (test->session, "https://127.0.0.1:5000/large", (1024 * 24) + 1, TRUE, NULL); - do_one_sniffer_test (test->session, "https://127.0.0.1:5000/no-content", 0, FALSE, NULL); + if (should_content_sniff) + soup_session_add_feature_by_type (test->session, SOUP_TYPE_CONTENT_SNIFFER); + + do_one_sniffer_test (test->session, "/", 11, should_content_sniff, NULL); + do_one_sniffer_test (test->session, "/large", (LARGE_N_CHARS * LARGE_CHARS_REPEAT) + 1, should_content_sniff, NULL); + do_one_sniffer_test (test->session, "/no-content", 0, should_content_sniff, NULL); } static void do_timeout_test (Test *test, gconstpointer data) { + GUri *uri; SoupMessage *msg; GBytes *response; GError *error = NULL; soup_session_set_timeout (test->session, 2); - msg = soup_message_new (SOUP_METHOD_GET, "https://127.0.0.1:5000/timeout"); + uri = g_uri_parse_relative (base_uri, "/timeout", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); response = soup_test_session_async_send (test->session, msg, NULL, &error); g_assert_null (response); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); g_object_unref (msg); + g_uri_unref (uri); while (g_main_context_pending (NULL)) g_main_context_iteration (NULL, FALSE); } +static void +do_connection_closed_test (Test *test, gconstpointer data) +{ + GUri *uri; + SoupMessage *msg; + GInputStream *stream; + GError *error = NULL; + + uri = g_uri_parse_relative (base_uri, "/close", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); + stream = soup_session_send (test->session, msg, NULL, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT); + g_clear_error (&error); + g_clear_object (&stream); + g_object_unref (msg); + g_uri_unref (uri); +} + +static gboolean +unpause_message (SoupServerMessage *msg) +{ + soup_server_message_unpause (msg); + return FALSE; +} + +static void +server_handler (SoupServer *server, + SoupServerMessage *msg, + const char *path, + GHashTable *query, + gpointer user_data) +{ + g_assert_cmpuint (soup_server_message_get_http_version (msg), ==, SOUP_HTTP_2_0); + + if (strcmp (path, "/") == 0 || strcmp (path, "/slow") == 0 || strcmp (path, "/timeout") == 0) { + gboolean is_slow = path1 == 's'; + gboolean is_timeout = path1 == 't'; + + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + soup_server_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + "Hello world", 11); + if (is_slow || is_timeout) { + GSource *timeout; + + soup_server_message_pause (msg); + timeout = soup_add_timeout (g_main_context_get_thread_default (), + is_timeout ? 4000 : 1000, + (GSourceFunc)unpause_message, msg); + g_source_unref (timeout); + } + } else if (strcmp (path, "/no-content") == 0) { + soup_server_message_set_status (msg, SOUP_STATUS_NO_CONTENT, NULL); + } else if (strcmp (path, "/large") == 0) { + int i, j; + SoupMessageBody *response_body; + char letter = 'A'; + + /* Send increasing letters just to aid debugging */ + response_body = soup_server_message_get_response_body (msg); + for (i = 0; i < LARGE_N_CHARS; i++, letter++) { + GString *chunk = g_string_new (NULL); + + for (j = 0; j < LARGE_CHARS_REPEAT; j++) + chunk = g_string_append_c (chunk, letter); + soup_message_body_append_bytes (response_body, g_string_free_to_bytes (chunk)); + } + soup_message_body_append (response_body, SOUP_MEMORY_STATIC, "\0", 1); + + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + } else if (strcmp (path, "/echo_query") == 0) { + const char *query_str = g_uri_get_query (soup_server_message_get_uri (msg)); + + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + soup_server_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + query_str, strlen (query_str)); + } else if (strcmp (path, "/echo_post") == 0) { + SoupMessageBody *request_body; + + request_body = soup_server_message_get_request_body (msg); + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + soup_server_message_set_response (msg, "text/plain", + SOUP_MEMORY_COPY, + request_body->data, + request_body->length); + } else if (strcmp (path, "/misdirected_request") == 0) { + static SoupServerConnection *conn = NULL; + + if (!conn) { + conn = soup_server_message_get_connection (msg); + soup_server_message_set_status (msg, SOUP_STATUS_MISDIRECTED_REQUEST, NULL); + } else { + /* Message is retried on a different connection */ + g_assert_false (conn == soup_server_message_get_connection (msg)); + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + soup_server_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + "Success!", 8); + } + } else if (strcmp (path, "/auth") == 0) { + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + soup_server_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + "Authenticated", 13); + } else if (strcmp (path, "/invalid-header") == 0) { + SoupMessageHeaders *response_headers; + + response_headers = soup_server_message_get_response_headers (msg); + /* Use soup_message_headers_append_common to skip the validation check. */ + soup_message_headers_append_common (response_headers, SOUP_HEADER_CONTENT_TYPE, "\r"); + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + } else if (strcmp (path, "/invalid-header-rfc9113") == 0) { + SoupMessageHeaders *response_headers; + + response_headers = soup_server_message_get_response_headers (msg); + soup_message_headers_append (response_headers, "Invalid-Header-Value", "foo "); + soup_server_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + "Success!", 8); + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + } else if (strcmp (path, "/close") == 0) { + SoupServerConnection *conn; + int fd; + + conn = soup_server_message_get_connection (msg); + fd = g_socket_get_fd (soup_server_connection_get_socket (conn)); +#ifdef G_OS_WIN32 + shutdown (fd, SD_SEND); +#else + shutdown (fd, SHUT_WR); +#endif + + soup_server_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + "Success!", 8); + } +} + +static gboolean +server_basic_auth_callback (SoupAuthDomain *auth_domain, + SoupServerMessage *msg, + const char *username, + const char *password, + gpointer data) +{ + if (strcmp (username, "username") != 0) + return FALSE; + + return strcmp (password, "password") == 0; +} + int main (int argc, char **argv) { + SoupServer *server; + SoupAuthDomain *auth; int ret; test_init (argc, argv, NULL); - if (!quart_init ()) { - test_cleanup (); - return 1; - } + if (!tls_available) + return 0; + + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD | SOUP_TEST_SERVER_HTTP2); + auth = soup_auth_domain_basic_new ("realm", "http2-test", + "auth-callback", server_basic_auth_callback, + NULL); + soup_auth_domain_add_path (auth, "/auth"); + soup_server_add_auth_domain (server, auth); + g_object_unref (auth); + + soup_server_add_handler (server, NULL, server_handler, NULL, NULL); + base_uri = soup_test_server_get_uri (server, "https", "127.0.0.1"); g_test_add ("/http2/basic/async", Test, NULL, setup_session, @@ -1113,11 +1415,37 @@ setup_session, do_invalid_header_test, teardown_session); - g_test_add ("/http2/sniffer/async", Test, NULL, + g_test_add ("/http2/invalid-header-received/async", Test, GINT_TO_POINTER (TRUE), + setup_session, + do_invalid_header_received_test, + teardown_session); + g_test_add ("/http2/invalid-header-received/sync", Test, GINT_TO_POINTER (FALSE), + setup_session, + do_invalid_header_received_test, + teardown_session); +#ifdef HAVE_NGHTTP2_OPTION_SET_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION + g_test_add ("/http2/invalid-header-rfc9113-received/async", Test, GINT_TO_POINTER (TRUE), + setup_session, + do_invalid_header_rfc9113_received_test, + teardown_session); + g_test_add ("/http2/invalid-header-rfc9113-received/sync", Test, GINT_TO_POINTER (FALSE), + setup_session, + do_invalid_header_rfc9113_received_test, + teardown_session); +#endif + g_test_add ("/http2/sniffer/with-sniffer/async", Test, GINT_TO_POINTER (TRUE), setup_session, do_sniffer_async_test, teardown_session); - g_test_add ("/http2/sniffer/sync", Test, NULL, + g_test_add ("/http2/sniffer/no-sniffer/async", Test, GINT_TO_POINTER (FALSE), + setup_session, + do_sniffer_async_test, + teardown_session); + g_test_add ("/http2/sniffer/with-sniffer/sync", Test, GINT_TO_POINTER (TRUE), + setup_session, + do_sniffer_sync_test, + teardown_session); + g_test_add ("/http2/sniffer/no-sniffer/sync", Test, GINT_TO_POINTER (FALSE), setup_session, do_sniffer_sync_test, teardown_session); @@ -1125,9 +1453,16 @@ setup_session, do_timeout_test, teardown_session); + g_test_add ("/http2/connection-closed", Test, NULL, + setup_session, + do_connection_closed_test, + teardown_session); ret = g_test_run (); + g_uri_unref (base_uri); + soup_test_server_quit_unref (server); + test_cleanup (); return ret;
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/httpd.conf.in -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/httpd.conf.in
Changed
@@ -13,7 +13,7 @@ # Change this to "./error.log" if it's failing and you don't know why ErrorLog /dev/null -LoadModule mpm_prefork_module @APACHE_MODULE_DIR@/mod_mpm_prefork.so +LoadModule mpm_event_module @APACHE_MODULE_DIR@/mod_mpm_event.so LoadModule alias_module @APACHE_MODULE_DIR@/mod_alias.so LoadModule auth_basic_module @APACHE_MODULE_DIR@/mod_auth_basic.so LoadModule auth_digest_module @APACHE_MODULE_DIR@/mod_auth_digest.so @@ -29,12 +29,15 @@ LoadModule proxy_connect_module @APACHE_MODULE_DIR@/mod_proxy_connect.so LoadModule ssl_module @APACHE_SSL_MODULE_DIR@/mod_ssl.so @IF_HAVE_MOD_UNIXD@LoadModule unixd_module @APACHE_SSL_MODULE_DIR@/mod_unixd.so +@IF_HAVE_MOD_HTTP2@LoadModule http2_module @APACHE_HTTP2_MODULE_DIR@/mod_http2.so DirectoryIndex index.txt TypesConfig /dev/null -AddType application/x-httpd-php .php Redirect permanent /redirected /index.txt +# Prefer http1 for now because most of the tests expect http1 behavior. +Protocols http/1.1 h2 + # Proxy #1: unauthenticated Listen 127.0.0.1:47526 <VirtualHost 127.0.0.1:47526> @@ -281,3 +284,7 @@ # test RFC2069-style Digest AuthDigestQop none </Location> + +<Location /client-cert> + SSLVerifyClient require +</Location> \ No newline at end of file
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/meson.build -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/meson.build
Changed
@@ -78,12 +78,14 @@ {'name': 'date'}, {'name': 'forms'}, {'name': 'header-parsing'}, + {'name': 'http2'}, {'name': 'http2-body-stream'}, {'name': 'hsts'}, {'name': 'hsts-db'}, {'name': 'logger'}, {'name': 'misc'}, {'name': 'multipart'}, + {'name': 'multithread'}, {'name': 'no-ssl'}, {'name': 'ntlm'}, {'name': 'redirect'}, @@ -93,7 +95,6 @@ {'name': 'server-auth'}, {'name': 'server'}, {'name': 'sniffing'}, - {'name': 'socket'}, {'name': 'ssl', 'dependencies': gnutls_dep, 'depends': mock_pkcs11_module, @@ -107,16 +108,6 @@ 'dependencies': libz_dep}, -if quart_found - configure_file(input : 'http2-server.py', - output : 'http2-server.py', - copy : true, - install : installed_tests_enabled, - install_dir : installed_tests_execdir) - - tests += {'name': 'http2'} -endif - if brotlidec_dep.found() tests += {'name': 'brotli-decompressor'}
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/misc-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/misc-test.c
Changed
@@ -21,9 +21,7 @@ static gboolean timeout_finish_message (gpointer msg) { - SoupServer *server = g_object_get_data (G_OBJECT (msg), "server"); - - soup_server_unpause_message (server, msg); + soup_server_message_unpause (msg); return FALSE; } @@ -37,7 +35,7 @@ const char *method = soup_server_message_get_method (msg); GUri *uri = soup_server_message_get_uri (msg); - if (method != SOUP_METHOD_GET && method != SOUP_METHOD_POST) { + if (method != SOUP_METHOD_GET && method != SOUP_METHOD_POST && method != SOUP_METHOD_PUT) { soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL); return; } @@ -47,10 +45,28 @@ return; } + if (!strcmp (path, "/session")) { + SoupMessageHeaders *request_headers; + const char *session_id; + + request_headers = soup_server_message_get_request_headers (msg); + session_id = soup_message_headers_get_one (request_headers, "X-SoupTest-Session-Id"); + if (!session_id) { + SoupMessageHeaders *response_headers; + + response_headers = soup_server_message_get_response_headers (msg); + soup_message_headers_replace (response_headers, "X-SoupTest-Session-Id", "session-1"); + soup_server_message_set_status (msg, SOUP_STATUS_CONFLICT, NULL); + } else { + soup_server_message_set_status (msg, SOUP_STATUS_CREATED, NULL); + } + + return; + } + if (!strcmp (path, "/slow")) { GSource *timeout; - soup_server_pause_message (server, msg); - g_object_set_data (G_OBJECT (msg), "server", server); + soup_server_message_pause (msg); timeout = soup_add_timeout (g_main_context_get_thread_default (), 1000, timeout_finish_message, msg); g_source_unref (timeout); @@ -817,14 +833,14 @@ g_bytes_unref (body); g_object_unref (msg2); - /* We get a new one if we force a new connection */ + /* We get the same one if we force a new connection */ msg2 = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); soup_message_add_flags (msg2, SOUP_MESSAGE_NEW_CONNECTION); g_assert_null (soup_message_get_remote_address (msg2)); body = soup_test_session_async_send (session, msg2, NULL, NULL); g_assert_nonnull (soup_message_get_remote_address (msg2)); g_assert_cmpuint (soup_message_get_connection_id (msg1), !=, soup_message_get_connection_id (msg2)); - g_assert_false (soup_message_get_remote_address (msg1) == soup_message_get_remote_address (msg2)); + g_assert_true (soup_message_get_remote_address (msg1) == soup_message_get_remote_address (msg2)); g_bytes_unref (body); g_object_unref (msg2); @@ -871,6 +887,141 @@ soup_test_session_abort_unref (session); } +typedef struct { + SoupSession *session; + GCancellable *cancellable; + GBytes *body; + guint64 connections2; + gboolean done; +} ConflictTestData; + +static void +conflict_test_send_ready_cb (SoupSession *session, + GAsyncResult *result, + ConflictTestData *data) +{ + GInputStream *stream; + SoupMessage *msg = soup_session_get_async_result_message (session, result); + GError *error = NULL; + + stream = soup_session_send_finish (session, result, &error); + if (stream) { + guint status = soup_message_get_status (msg); + + soup_test_request_read_all (stream, NULL, NULL); + g_object_unref (stream); + + if (status != SOUP_STATUS_CONFLICT) { + g_assert_cmpuint (status, ==, SOUP_STATUS_CREATED); + data->connections1 = soup_message_get_connection_id (msg); + data->done = TRUE; + } + } else { + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_error_free (error); + } +} + +static void +conflict_test_on_conflict_cb (SoupMessage *msg, + ConflictTestData *data) +{ + SoupMessageHeaders *response_headers; + SoupMessageHeaders *request_headers; + const gchar *session_id; + SoupMessage *new_msg; + + g_cancellable_cancel (data->cancellable); + g_clear_object (&data->cancellable); + + data->connections0 = soup_message_get_connection_id (msg); + response_headers = soup_message_get_response_headers (msg); + session_id = soup_message_headers_get_one (response_headers, "X-SoupTest-Session-Id"); + new_msg = soup_message_new_from_uri (SOUP_METHOD_PUT, soup_message_get_uri (msg)); + request_headers = soup_message_get_request_headers (new_msg); + soup_message_headers_replace (request_headers, "X-SoupTest-Session-Id", session_id); + + data->cancellable = g_cancellable_new (); + soup_message_set_request_body_from_bytes (new_msg, "text/plain", data->body); + soup_session_send_async (data->session, new_msg, G_PRIORITY_DEFAULT, data->cancellable, + (GAsyncReadyCallback)conflict_test_send_ready_cb, data); + g_object_unref (new_msg); +} + +static void +conflict_test_on_got_body_cb (SoupMessage *msg, + ConflictTestData *data) +{ + if (soup_message_get_status (msg) == SOUP_STATUS_CONFLICT) + conflict_test_on_conflict_cb (msg, data); +} + +static void +do_new_request_on_conflict_test (void) +{ + GUri *uri; + SoupMessage *msg; + ConflictTestData data; + static const char *body = "conflict test body"; + + data.session = soup_test_session_new (NULL); + data.cancellable = g_cancellable_new (); + data.body = g_bytes_new_static (body, strlen (body)); + data.connections0 = data.connections1 = 0; + data.done = FALSE; + + /* First try with restarting on got-headers */ + uri = g_uri_parse_relative (base_uri, "/session", SOUP_HTTP_URI_FLAGS, NULL); + msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri); + soup_message_set_request_body_from_bytes (msg, "text/plain", data.body); + soup_message_add_status_code_handler (msg, "got-headers", SOUP_STATUS_CONFLICT, + G_CALLBACK (conflict_test_on_conflict_cb), + &data); + soup_session_send_async (data.session, msg, G_PRIORITY_DEFAULT, data.cancellable, + (GAsyncReadyCallback)conflict_test_send_ready_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.connections0, >, 0); + g_assert_cmpuint (data.connections1, >, 0); + g_assert_cmpuint (data.connections0, !=, data.connections1); + + g_object_unref (msg); + g_object_unref (data.cancellable); + + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); + + data.cancellable = g_cancellable_new (); + data.connections0 = data.connections1 = 0; + data.done = FALSE; + + /* Now try with the restarting on got-body */ + msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri); + soup_message_set_request_body_from_bytes (msg, "text/plain", data.body); + g_signal_connect (msg, "got-body", G_CALLBACK (conflict_test_on_got_body_cb), &data); + soup_session_send_async (data.session, msg, G_PRIORITY_DEFAULT, data.cancellable, + (GAsyncReadyCallback)conflict_test_send_ready_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.connections0, >, 0); + g_assert_cmpuint (data.connections1, >, 0); + g_assert_cmpuint (data.connections0, ==, data.connections1); + + g_object_unref (msg); + g_object_unref (data.cancellable); + + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); + + g_uri_unref (uri); + g_bytes_unref (data.body); + soup_test_session_abort_unref (data.session); +} + static void wrote_informational_check_content_length (SoupServerMessage *msg, gpointer user_data) @@ -1006,6 +1157,7 @@ g_test_add_func ("/misc/connection-id", do_connection_id_test); g_test_add_func ("/misc/remote-address", do_remote_address_test); g_test_add_func ("/misc/new-request-on-redirect", do_new_request_on_redirect_test); + g_test_add_func ("/misc/new-request-on-conflict", do_new_request_on_conflict_test); g_test_add_func ("/misc/response/informational/content-length", do_response_informational_content_length_test); g_test_add_func ("/misc/invalid-utf8-headers", do_invalid_utf8_headers_test);
View file
_service:tar_scm:libsoup-3.2.2.tar.xz/tests/multithread-test.c
Added
@@ -0,0 +1,564 @@ +/* + * Copyright 2022 Igalia S.L. + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "test-utils.h" + +#include "soup-connection.h" +#include "soup-message-private.h" + +static GUri *base_uri; + +typedef enum { + BASIC_SYNC = 1 << 0, + BASIC_SSL = 1 << 1, + BASIC_PROXY = 1 << 2, + BASIC_HTTP2 = 1 << 3, + BASIC_MAX_CONNS = 1 << 4, + BASIC_NO_MAIN_THREAD = 1 << 5 +} BasicTestFlags; + +typedef struct { + SoupSession *session; + BasicTestFlags flags; +} Test; + +#define HTTPS_SERVER "https://127.0.0.1:47525" +#define HTTP_PROXY "http://127.0.0.1:47526" + +static void +test_setup (Test *test, gconstpointer data) +{ + test->flags = GPOINTER_TO_UINT (data); + if (test->flags & BASIC_MAX_CONNS) + test->session = soup_test_session_new ("max-conns", 1, NULL); + else + test->session = soup_test_session_new (NULL); +} + +static void +test_teardown (Test *test, gconstpointer data) +{ + soup_test_session_abort_unref (test->session); + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); +} + +static void +msg_signal_check_context (SoupMessage *msg, + GMainContext *context) +{ + g_assert_true (g_object_get_data (G_OBJECT (msg), "thread-context") == context); +} + +static void +connect_message_signals_to_check_context (SoupMessage *msg, + GMainContext *context) +{ + g_object_set_data (G_OBJECT (msg), "thread-context", context); + g_signal_connect (msg, "starting", G_CALLBACK (msg_signal_check_context), context); + g_signal_connect (msg, "wrote-headers", G_CALLBACK (msg_signal_check_context), context); + g_signal_connect (msg, "wrote-body", G_CALLBACK (msg_signal_check_context), context); + g_signal_connect (msg, "got-headers", G_CALLBACK (msg_signal_check_context), context); + g_signal_connect (msg, "got-body", G_CALLBACK (msg_signal_check_context), context); + g_signal_connect (msg, "finished", G_CALLBACK (msg_signal_check_context), context); +} + +static void +msg_signal_check_thread (SoupMessage *msg, + GThread *thread) +{ + g_assert_true (g_object_get_data (G_OBJECT (msg), "thread-id") == thread); +} + +static void +connect_message_signals_to_check_thread (SoupMessage *msg, + GThread *thread) +{ + g_object_set_data (G_OBJECT (msg), "thread-id", thread); + g_signal_connect (msg, "starting", G_CALLBACK (msg_signal_check_thread), thread); + g_signal_connect (msg, "wrote-headers", G_CALLBACK (msg_signal_check_thread), thread); + g_signal_connect (msg, "wrote-body", G_CALLBACK (msg_signal_check_thread), thread); + g_signal_connect (msg, "got-headers", G_CALLBACK (msg_signal_check_thread), thread); + g_signal_connect (msg, "got-body", G_CALLBACK (msg_signal_check_thread), thread); + g_signal_connect (msg, "finished", G_CALLBACK (msg_signal_check_thread), thread); +} + +static void +message_send_and_read_ready_cb (SoupSession *session, + GAsyncResult *result, + GMainLoop *loop) +{ + GBytes *body; + GBytes *index = soup_test_get_index (); + GError *error = NULL; + + if (loop) + g_assert_true (g_main_loop_get_context (loop) == g_main_context_get_thread_default ()); + + body = soup_session_send_and_read_finish (session, result, &error); + g_assert_no_error (error); + g_assert_nonnull (body); + g_assert_cmpmem (g_bytes_get_data (body, NULL), g_bytes_get_size (body), g_bytes_get_data (index, NULL), g_bytes_get_size (index)); + g_bytes_unref (body); + + if (loop) + g_main_loop_quit (loop); +} + +static void +task_async_function (GTask *task, + GObject *source, + Test *test, + GCancellable *cancellable) +{ + GMainContext *context; + GMainLoop *loop; + SoupMessage *msg; + + context = g_main_context_new (); + g_main_context_push_thread_default (context); + + loop = g_main_loop_new (context, FALSE); + + if (test->flags & BASIC_SSL) + msg = soup_message_new ("GET", HTTPS_SERVER); + else + msg = soup_message_new_from_uri ("GET", base_uri); + if (test->flags & BASIC_HTTP2) + soup_message_set_force_http_version (msg, SOUP_HTTP_2_0); + connect_message_signals_to_check_context (msg, context); + soup_session_send_and_read_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)message_send_and_read_ready_cb, + loop); + g_object_unref (msg); + + g_main_loop_run (loop); + g_main_loop_unref (loop); + + g_task_return_boolean (task, TRUE); + + g_main_context_pop_thread_default (context); + g_main_context_unref (context); +} + +static void +task_sync_function (GTask *task, + GObject *source, + Test *test, + GCancellable *cancellable) +{ + SoupMessage *msg; + GBytes *body; + GBytes *index = soup_test_get_index (); + GError *error = NULL; + + if (test->flags & BASIC_SSL) + msg = soup_message_new ("GET", HTTPS_SERVER); + else + msg = soup_message_new_from_uri ("GET", base_uri); + if (test->flags & BASIC_HTTP2) + soup_message_set_force_http_version (msg, SOUP_HTTP_2_0); + connect_message_signals_to_check_thread (msg, g_thread_self ()); + body = soup_session_send_and_read (test->session, msg, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (body); + g_assert_cmpmem (g_bytes_get_data (body, NULL), g_bytes_get_size (body), g_bytes_get_data (index, NULL), g_bytes_get_size (index)); + g_bytes_unref (body); + g_object_unref (msg); + + g_task_return_boolean (task, TRUE); +} + +static void +task_finished_cb (GObject *source, + GAsyncResult *result, + guint *finished_count) +{ + g_assert_true (g_task_propagate_boolean (G_TASK (result), NULL)); + g_atomic_int_inc (finished_count); +} + +static void +message_finished_cb (SoupMessage *msg, + guint *finished_count) +{ + g_atomic_int_inc (finished_count); +} + +static void +do_multithread_basic_test (Test *test, + gconstpointer data) +{ + SoupMessage *msg = NULL; + guint n_msgs = 6; + guint n_main_thread_msgs; + guint i; + guint finished_count = 0; + + if (test->flags & BASIC_PROXY) { + GProxyResolver *resolver; + + resolver = g_simple_proxy_resolver_new (HTTP_PROXY, NULL); + soup_session_set_proxy_resolver (test->session, resolver); + g_object_unref (resolver); + } + + n_main_thread_msgs = test->flags & BASIC_NO_MAIN_THREAD ? 0 : 1; + + if (n_main_thread_msgs) { + if (test->flags & BASIC_SSL) + msg = soup_message_new ("GET", HTTPS_SERVER); + else + msg = soup_message_new_from_uri ("GET", base_uri); + if (test->flags & BASIC_HTTP2) + soup_message_set_force_http_version (msg, SOUP_HTTP_2_0); + if (test->flags & BASIC_SYNC) + connect_message_signals_to_check_thread (msg, g_thread_self ()); + else + connect_message_signals_to_check_context (msg, g_main_context_get_thread_default ()); + g_signal_connect (msg, "finished", + G_CALLBACK (message_finished_cb), + &finished_count); + soup_session_send_and_read_async (test->session, msg, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)message_send_and_read_ready_cb, + NULL); + } + + for (i = 0; i < n_msgs - n_main_thread_msgs; i++) { + GTask *task; + + task = g_task_new (NULL, NULL, (GAsyncReadyCallback)task_finished_cb, &finished_count); + g_task_set_task_data (task, test, NULL); + g_task_run_in_thread (task, (GTaskThreadFunc)(test->flags & BASIC_SYNC ? task_sync_function : task_async_function)); + g_object_unref (task); + } + + while (g_atomic_int_get (&finished_count) != n_msgs) + g_main_context_iteration (NULL, TRUE); + + g_clear_object (&msg); + + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); +} + +static void +do_multithread_basic_proxy_test (Test *test, + gconstpointer data) +{ + SOUP_TEST_SKIP_IF_NO_APACHE; + + do_multithread_basic_test (test, data); +} + +static void +do_multithread_basic_ssl_test (Test *test, + gconstpointer data) +{ + SOUP_TEST_SKIP_IF_NO_TLS; + SOUP_TEST_SKIP_IF_NO_APACHE; + + do_multithread_basic_test (test, data); +} + +static void +connections_test_msg_starting (SoupMessage *msg, + SoupConnection **conn) +{ + *conn = g_object_ref (soup_message_get_connection (msg)); +} + +static void +connections_test_task_async_function (GTask *task, + GObject *source, + Test *test, + GCancellable *cancellable) +{ + GMainContext *context; + SoupMessage *msg; + GBytes *body; + SoupConnection *conn = NULL; + + context = g_main_context_new (); + g_main_context_push_thread_default (context); + + if (test->flags & BASIC_SSL) + msg = soup_message_new ("GET", HTTPS_SERVER); + else + msg = soup_message_new_from_uri ("GET", base_uri); + if (test->flags & BASIC_HTTP2) + soup_message_set_force_http_version (msg, SOUP_HTTP_2_0); + g_signal_connect (msg, "starting", + G_CALLBACK (connections_test_msg_starting), + &conn); + body = soup_test_session_async_send (test->session, msg, NULL, NULL); + g_bytes_unref (body); + g_object_unref (msg); + + g_task_return_pointer (task, conn, g_object_unref); + + g_main_context_pop_thread_default (context); + g_main_context_unref (context); +} + +static void +connections_test_task_sync_function (GTask *task, + GObject *source, + Test *test, + GCancellable *cancellable) +{ + SoupMessage *msg; + GBytes *body; + SoupConnection *conn = NULL; + + if (test->flags & BASIC_SSL) + msg = soup_message_new ("GET", HTTPS_SERVER); + else + msg = soup_message_new_from_uri ("GET", base_uri); + if (test->flags & BASIC_HTTP2) + soup_message_set_force_http_version (msg, SOUP_HTTP_2_0); + g_signal_connect (msg, "starting", + G_CALLBACK (connections_test_msg_starting), + &conn); + body = soup_session_send_and_read (test->session, msg, NULL, NULL); + g_bytes_unref (body); + g_object_unref (msg); + + g_task_return_pointer (task, conn, g_object_unref); +} + +static void +do_multithread_connections_test (Test *test, + gconstpointer data) +{ + SoupMessage *msg; + SoupConnection *conn = NULL; + SoupConnection *thread_conn; + GBytes *body; + GTask *task; + + if (test->flags & BASIC_SSL) + msg = soup_message_new ("GET", HTTPS_SERVER); + else + msg = soup_message_new_from_uri ("GET", base_uri); + if (test->flags & BASIC_HTTP2) + soup_message_set_force_http_version (msg, SOUP_HTTP_2_0); + g_signal_connect (msg, "starting", + G_CALLBACK (connections_test_msg_starting), + &conn); + body = soup_test_session_async_send (test->session, msg, NULL, NULL); + g_bytes_unref (body); + + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); + + g_assert_nonnull (conn); + g_assert_cmpuint (soup_connection_get_state (conn), ==, SOUP_CONNECTION_IDLE); + + /* An idle connection can be reused by another thread */ + task = g_task_new (NULL, NULL, NULL, NULL); + g_task_set_task_data (task, test, NULL); + g_task_run_in_thread_sync (task, (GTaskThreadFunc)(test->flags & BASIC_SYNC ? connections_test_task_sync_function : connections_test_task_async_function)); + thread_conn = g_task_propagate_pointer (task, NULL); + g_object_unref (task); + g_assert_true (conn == thread_conn); + g_object_unref (thread_conn); + + g_object_unref (conn); + g_object_unref (msg); +} + +static void +do_multithread_connections_http2_test (Test *test, + gconstpointer data) +{ + SOUP_TEST_SKIP_IF_NO_TLS; + SOUP_TEST_SKIP_IF_NO_APACHE; + + do_multithread_connections_test (test, data); +} + +static void +do_multithread_no_main_context_test (void) +{ + SoupSession *session; + SoupMessage *msg; + GBytes *body; + GBytes *index = soup_test_get_index (); + guint i; + + SOUP_TEST_SKIP_IF_NO_TLS; + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (NULL); + + for (i = 0; i < 2; i++) { + msg = soup_message_new ("GET", HTTPS_SERVER); + if (i > 0) + soup_message_set_force_http_version (msg, SOUP_HTTP_2_0); + body = soup_session_send_and_read (session, msg, NULL, NULL); + g_assert_nonnull (body); + g_assert_cmpmem (g_bytes_get_data (body, NULL), g_bytes_get_size (body), g_bytes_get_data (index, NULL), g_bytes_get_size (index)); + g_bytes_unref (body); + g_object_unref (msg); + } + + soup_test_session_abort_unref (session); +} + +static void +server_callback (SoupServer *server, + SoupServerMessage *msg, + const char *path, + GHashTable *query, + gpointer data) +{ + GBytes *index = soup_test_get_index (); + + soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); + soup_server_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + g_bytes_get_data (index, NULL), + g_bytes_get_size (index)); +} + +int +main (int argc, char **argv) +{ + int ret; + SoupServer *server; + + test_init (argc, argv, NULL); + apache_init (); + + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); + soup_server_add_handler (server, NULL, server_callback, "http", NULL); + base_uri = soup_test_server_get_uri (server, "http", NULL); + + g_test_add ("/multithread/basic/async", Test, + GUINT_TO_POINTER (0), + test_setup, + do_multithread_basic_test, + test_teardown); + g_test_add ("/multithread/basic/sync", Test, + GUINT_TO_POINTER (BASIC_SYNC), + test_setup, + do_multithread_basic_test, + test_teardown); + g_test_add ("/multithread/basic-ssl/async", Test, + GUINT_TO_POINTER (BASIC_SSL), + test_setup, + do_multithread_basic_ssl_test, + test_teardown); + g_test_add ("/multithread/basic-ssl/sync", Test, + GUINT_TO_POINTER (BASIC_SSL | BASIC_SYNC), + test_setup, + do_multithread_basic_ssl_test, + test_teardown); + g_test_add ("/multithread/basic-proxy/async", Test, + GUINT_TO_POINTER (BASIC_PROXY), + test_setup, + do_multithread_basic_proxy_test, + test_teardown); + g_test_add ("/multithread/basic-proxy/sync", Test, + GUINT_TO_POINTER (BASIC_PROXY | BASIC_SYNC), + test_setup, + do_multithread_basic_proxy_test, + test_teardown); + g_test_add ("/multithread/basic-no-main-thread/async", Test, + GUINT_TO_POINTER (BASIC_NO_MAIN_THREAD), + test_setup, + do_multithread_basic_test, + test_teardown); + g_test_add ("/multithread/basic-no-main-thread/sync", Test, + GUINT_TO_POINTER (BASIC_NO_MAIN_THREAD | BASIC_SYNC), + test_setup, + do_multithread_basic_test, + test_teardown); + g_test_add ("/multithread/basic-ssl-proxy/async", Test, + GUINT_TO_POINTER (BASIC_SSL | BASIC_PROXY), + test_setup, + do_multithread_basic_ssl_test, + test_teardown); + g_test_add ("/multithread/basic-ssl-proxy/sync", Test, + GUINT_TO_POINTER (BASIC_SSL | BASIC_PROXY | BASIC_SYNC), + test_setup, + do_multithread_basic_ssl_test, + test_teardown); + g_test_add ("/multithread/basic-http2/async", Test, + GUINT_TO_POINTER (BASIC_HTTP2 | BASIC_SSL), + test_setup, + do_multithread_basic_ssl_test, + test_teardown); + g_test_add ("/multithread/basic-http2/sync", Test, + GUINT_TO_POINTER (BASIC_HTTP2 | BASIC_SSL | BASIC_SYNC), + test_setup, + do_multithread_basic_ssl_test, + test_teardown); + g_test_add ("/multithread/basic-no-main-thread-http2/async", Test, + GUINT_TO_POINTER (BASIC_NO_MAIN_THREAD | BASIC_HTTP2 | BASIC_SSL), + test_setup, + do_multithread_basic_ssl_test, + test_teardown); + g_test_add ("/multithread/basic-no-main-thread-http2/sync", Test, + GUINT_TO_POINTER (BASIC_NO_MAIN_THREAD | BASIC_HTTP2 | BASIC_SSL | BASIC_SYNC), + test_setup, + do_multithread_basic_ssl_test, + test_teardown); + g_test_add ("/multithread/basic-max-conns/async", Test, + GUINT_TO_POINTER (BASIC_MAX_CONNS), + test_setup, + do_multithread_basic_test, + test_teardown); + g_test_add ("/multithread/basic-max-conns/sync", Test, + GUINT_TO_POINTER (BASIC_MAX_CONNS | BASIC_SYNC), + test_setup, + do_multithread_basic_test, + test_teardown); + g_test_add ("/multithread/connections/async", Test, + GUINT_TO_POINTER (0), + test_setup, + do_multithread_connections_test, + test_teardown); + g_test_add ("/multithread/connections/sync", Test, + GUINT_TO_POINTER (BASIC_SYNC), + test_setup, + do_multithread_connections_test, + test_teardown); + g_test_add ("/multithread/connections-http2/async", Test, + GUINT_TO_POINTER (BASIC_HTTP2 | BASIC_SSL), + test_setup, + do_multithread_connections_http2_test, + test_teardown); + g_test_add ("/multithread/connections-http2/sync", Test, + GUINT_TO_POINTER (BASIC_HTTP2 | BASIC_SSL | BASIC_SYNC), + test_setup, + do_multithread_connections_http2_test, + test_teardown); + g_test_add_func ("/multithread/no-main-context", + do_multithread_no_main_context_test); + + ret = g_test_run (); + + g_uri_unref (base_uri); + soup_test_server_quit_unref (server); + test_cleanup (); + + return ret; +}
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/proxy-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/proxy-test.c
Changed
@@ -340,11 +340,43 @@ g_object_unref (cache); } +static void +do_proxy_connect_error_test (gconstpointer data) +{ + GUri *base_uri = (GUri *)data; + GUri *proxy_uri; + char *proxy_uri_str; + SoupSession *session; + SoupMessage *msg; + GProxyResolver *resolver; + GBytes *body; + GError *error = NULL; + + /* Proxy connection will success, but CONNECT message to https will fail due to TLS errors */ + proxy_uri = soup_uri_copy (base_uri, SOUP_URI_SCHEME, "http", NULL); + proxy_uri_str = g_uri_to_string (proxy_uri); + g_uri_unref (proxy_uri); + + resolver = g_simple_proxy_resolver_new (proxy_uri_str, (char **)ignore_hosts); + g_free (proxy_uri_str); + session = soup_test_session_new ("proxy-resolver", resolver, NULL); + g_object_unref (resolver); + + msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); + body = soup_test_session_async_send (session, msg, NULL, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED); + + g_error_free (error); + g_bytes_unref (body); + g_object_unref (msg); + soup_test_session_abort_unref (session); +} + int main (int argc, char **argv) { SoupServer *server; - GUri *base_uri; + GUri *base_uri, *base_https_uri; char *path; int i, ret; @@ -359,6 +391,7 @@ server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); base_uri = soup_test_server_get_uri (server, "http", NULL); + base_https_uri = soup_test_server_get_uri (server, "https", NULL); for (i = 0; i < ntests; i++) { path = g_strdup_printf ("/proxy/%s", testsi.explanation); @@ -369,10 +402,12 @@ g_test_add_data_func ("/proxy/fragment", base_uri, do_proxy_fragment_test); g_test_add_func ("/proxy/redirect", do_proxy_redirect_test); g_test_add_func ("/proxy/auth-cache", do_proxy_auth_cache_test); + g_test_add_data_func ("/proxy/connect-error", base_https_uri, do_proxy_connect_error_test); ret = g_test_run (); g_uri_unref (base_uri); + g_uri_unref (base_https_uri); soup_test_server_quit_unref (server); for (i = 0; i < 3; i++) g_object_unref (proxy_resolversi);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/server-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/server-test.c
Changed
@@ -6,6 +6,7 @@ #include "test-utils.h" #include "soup-message-private.h" #include "soup-uri-utils-private.h" +#include "soup-misc.h" #include <gio/gnetworking.h> @@ -770,7 +771,6 @@ } typedef struct { - SoupServer *server; SoupServerMessage *smsg; gboolean handler_called; gboolean paused; @@ -781,7 +781,7 @@ { UnhandledServerData *usd = user_data; - soup_server_unpause_message (usd->server, usd->smsg); + soup_server_message_unpause (usd->smsg); return FALSE; } @@ -798,10 +798,10 @@ if (soup_message_headers_get_one (soup_server_message_get_request_headers (msg), "X-Test-Server-Pause")) { usd->paused = TRUE; - usd->server = server; usd->smsg = msg; - soup_server_pause_message (server, msg); - g_idle_add (idle_unpause_message, usd); + soup_server_message_pause (msg); + soup_add_completion (g_main_context_get_thread_default (), + idle_unpause_message, usd); } } @@ -1237,7 +1237,7 @@ SOUP_MEMORY_COPY, error->message, strlen (error->message)); g_error_free (error); - soup_server_unpause_message (tunnel->self, tunnel->msg); + soup_server_message_unpause (tunnel->msg); tunnel_close (tunnel); return; } @@ -1246,7 +1246,7 @@ tunnel->server.ostream = g_io_stream_get_output_stream (tunnel->server.iostream); soup_server_message_set_status (tunnel->msg, SOUP_STATUS_OK, NULL); - soup_server_unpause_message (tunnel->self, tunnel->msg); + soup_server_message_unpause (tunnel->msg); g_signal_connect (tunnel->msg, "wrote-body", G_CALLBACK (start_tunnel), tunnel); } @@ -1267,7 +1267,7 @@ return; } - soup_server_pause_message (server, msg); + soup_server_message_pause (msg); tunnel = g_new0 (Tunnel, 1); tunnel->self = g_object_ref (server);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/ssl-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/ssl-test.c
Changed
@@ -165,10 +165,12 @@ GTlsDatabase *tls_db; GTlsCertificate *certificate; GTlsInteraction *interaction; + GUri *peer_uri; GError *error = NULL; SOUP_TEST_SKIP_IF_NO_TLS; + peer_uri = g_uri_parse_relative (uri, "/check-peer", SOUP_HTTP_URI_FLAGS, NULL); session = soup_test_session_new (NULL); tls_db = soup_session_get_tls_database (session); g_object_set (server, "tls-database", tls_db, "tls-auth-mode", G_TLS_AUTHENTICATION_REQUIRED, NULL); @@ -200,7 +202,7 @@ g_object_unref (interaction); /* With a GTlsInteraction */ - msg = soup_message_new_from_uri ("GET", uri); + msg = soup_message_new_from_uri ("GET", peer_uri); body = soup_test_session_async_send (session, msg, NULL, &error); g_assert_no_error (error); soup_test_assert_message_status (msg, SOUP_STATUS_OK); @@ -213,6 +215,7 @@ soup_test_session_abort_unref (session); g_object_unref (certificate); + g_uri_unref (peer_uri); } static gboolean @@ -700,6 +703,26 @@ soup_server_message_set_response (msg, "text/plain", SOUP_MEMORY_STATIC, "ok\r\n", 4); + + if (!strcmp (path, "/check-peer")) { + GTlsCertificate *certificate; + GTlsCertificateFlags flags; + + certificate = soup_server_message_get_tls_peer_certificate (msg); + g_assert_nonnull (certificate); + + flags = soup_server_message_get_tls_peer_certificate_errors (msg); + g_assert_cmpuint (flags, ==, 0); + + /* Check also the properties are properly working */ + g_object_get (G_OBJECT (msg), + "tls-peer-certificate", &certificate, + "tls-peer-certificate-errors", &flags, + NULL); + g_assert_nonnull (certificate); + g_assert_cmpuint (flags, ==, 0); + g_object_unref (certificate); + } } int
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/test-utils.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/test-utils.c
Changed
@@ -3,6 +3,7 @@ #include "test-utils.h" #include "soup-misc.h" #include "soup-session-private.h" +#include "soup-server-private.h" #include <glib/gprintf.h> #ifdef G_OS_UNIX @@ -114,8 +115,6 @@ apache_cleanup (); #endif - quart_cleanup (); - if (logger) g_object_unref (logger); if (index_buffer) @@ -274,75 +273,6 @@ #endif /* HAVE_APACHE */ -static GSubprocess *quart_proc; - -gboolean -quart_init (void) -{ - if (quart_proc) - return TRUE; - - GSubprocessLauncher *launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE); // | G_SUBPROCESS_FLAGS_STDERR_SILENCE - g_subprocess_launcher_set_cwd (launcher, g_test_get_dir (G_TEST_DIST)); - - GError *error = NULL; - char *script = soup_test_build_filename_abs (G_TEST_DIST, "http2-server.py", NULL); - quart_proc = g_subprocess_launcher_spawn (launcher, &error, script, NULL); - g_free (script); - g_object_unref (launcher); - - if (error) { - g_test_message ("Failed to start quart server: %s", error->message); - g_error_free (error); - return FALSE; - } - - GDataInputStream *in_stream = g_data_input_stream_new (g_subprocess_get_stdout_pipe (quart_proc)); - - // We don't own the stream, don't break the pipe - g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (in_stream), FALSE); - - // Read stdout until the server says it is running - while (TRUE) { - char *line = g_data_input_stream_read_line_utf8 (in_stream, NULL, NULL, &error); - - if (error) { - g_test_message ("Failed to start quart server: %s", error->message); - g_error_free (error); - g_object_unref (in_stream); - return FALSE; - } else if (line == NULL) { - g_test_message ("Failed to start quart server (not installed?)"); - g_object_unref (in_stream); - return FALSE; - } - - if (g_str_has_prefix (line, " * Running")) { - g_test_message ("Started quart server: %s", line + 3); - g_free (line); - g_object_unref (in_stream); - return TRUE; - } - g_free (line); - } -} - -void -quart_cleanup (void) -{ - if (quart_proc) { - GError *error = NULL; - g_subprocess_force_exit (quart_proc); - g_subprocess_wait (quart_proc, NULL, &error); - if (error) { - g_test_message ("Failed to stop quart server: %s", error->message); - g_error_free (error); - } - } - - g_clear_object (&quart_proc); -} - SoupSession * soup_test_session_new (const char *propname, ...) { @@ -567,6 +497,8 @@ NULL); g_clear_object (&cert); + soup_server_set_http2_enabled (server, options & SOUP_TEST_SERVER_HTTP2); + g_object_set_data (G_OBJECT (server), "options", GUINT_TO_POINTER (options)); if (options & SOUP_TEST_SERVER_UNIX_SOCKET) {
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/test-utils.h -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/test-utils.h
Changed
@@ -47,9 +47,6 @@ } G_STMT_END #endif -gboolean quart_init (void); -void quart_cleanup (void); - gboolean have_curl (void); typedef enum { @@ -74,7 +71,8 @@ SOUP_TEST_SERVER_DEFAULT = 0, SOUP_TEST_SERVER_IN_THREAD = (1 << 0), SOUP_TEST_SERVER_NO_DEFAULT_LISTENER = (1 << 1), - SOUP_TEST_SERVER_UNIX_SOCKET = (1 << 2) + SOUP_TEST_SERVER_UNIX_SOCKET = (1 << 2), + SOUP_TEST_SERVER_HTTP2 = (1 << 3) } SoupTestServerOptions; SoupServer *soup_test_server_new (SoupTestServerOptions options);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/timeout-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/timeout-test.c
Changed
@@ -179,9 +179,7 @@ static gboolean timeout_finish_message (gpointer msg) { - SoupServer *server = g_object_get_data (G_OBJECT (msg), "server"); - - soup_server_unpause_message (server, msg); + soup_server_message_unpause (msg); return FALSE; } @@ -199,8 +197,7 @@ if (!strcmp (path, "/slow")) { GSource *timeout; - soup_server_pause_message (server, msg); - g_object_set_data (G_OBJECT (msg), "server", server); + soup_server_message_pause (msg); timeout = soup_add_timeout (g_main_context_get_thread_default (), 4000, timeout_finish_message, msg); g_source_unref (timeout);
View file
_service:tar_scm:libsoup-3.0.6.tar.xz/tests/websocket-test.c -> _service:tar_scm:libsoup-3.2.2.tar.xz/tests/websocket-test.c
Changed
@@ -907,6 +907,11 @@ g_assert_cmpstr (soup_message_headers_get_one (soup_message_get_response_headers (test->msg), "Sec-WebSocket-Protocol"), ==, NULL); } +typedef enum { + CLOSE_TEST_FLAG_SERVER = 1 << 0, + CLOSE_TEST_FLAG_CLIENT = 1 << 1 +} CloseTestFlags; + static const struct { gushort code; const char *reason; @@ -914,11 +919,16 @@ const char *expected_sender_reason; gushort expected_receiver_code; const char *expected_receiver_reason; + CloseTestFlags flags; } close_clean_tests = { - { SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL", SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL", SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL" }, - { SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY", SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY", SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY" }, - { SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL }, - { SOUP_WEBSOCKET_CLOSE_NO_STATUS, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NO_STATUS, NULL }, + { SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL", SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL", SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL", CLOSE_TEST_FLAG_SERVER | CLOSE_TEST_FLAG_CLIENT }, + { SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY", SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY", SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY", CLOSE_TEST_FLAG_SERVER | CLOSE_TEST_FLAG_CLIENT }, + { SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, CLOSE_TEST_FLAG_SERVER | CLOSE_TEST_FLAG_CLIENT }, + { SOUP_WEBSOCKET_CLOSE_NO_STATUS, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NO_STATUS, NULL, CLOSE_TEST_FLAG_SERVER | CLOSE_TEST_FLAG_CLIENT }, + { 2999, NULL, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR, NULL, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR, NULL, CLOSE_TEST_FLAG_CLIENT }, + { 2999, NULL, 0, NULL, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR, NULL, CLOSE_TEST_FLAG_SERVER }, + { 5000, NULL, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR, NULL, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR, NULL, CLOSE_TEST_FLAG_CLIENT }, + { 5000, NULL, 0, NULL, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR, NULL, CLOSE_TEST_FLAG_SERVER }, }; static void @@ -958,6 +968,9 @@ guint i; for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) { + if (!(close_clean_testsi.flags & CLOSE_TEST_FLAG_CLIENT)) + continue; + setup_soup_connection (test, data); do_close_clean_client (test, @@ -979,6 +992,9 @@ guint i; for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) { + if (!(close_clean_testsi.flags & CLOSE_TEST_FLAG_CLIENT)) + continue; + setup_direct_connection (test, data); do_close_clean_client (test, @@ -1030,6 +1046,9 @@ guint i; for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) { + if (!(close_clean_testsi.flags & CLOSE_TEST_FLAG_SERVER)) + continue; + setup_direct_connection (test, data); do_close_clean_server (test, @@ -1051,6 +1070,9 @@ guint i; for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) { + if (!(close_clean_testsi.flags & CLOSE_TEST_FLAG_SERVER)) + continue; + setup_direct_connection (test, data); do_close_clean_server (test, @@ -1549,6 +1571,8 @@ g_assert_cmpstr (remote_ip, ==, str); g_free (str); + g_assert_nonnull (soup_server_message_get_socket (msg)); + test->server = g_object_ref (connection); }
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2