[libsoup/wip/tingping/same-site] Expose support for same-site cookies
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/wip/tingping/same-site] Expose support for same-site cookies
- Date: Wed, 23 Jan 2019 14:12:08 +0000 (UTC)
commit e0435cecc859e7da1172c8fdd4ce291d9e940250
Author: Patrick Griffis <pgriffis igalia com>
Date: Tue Jan 22 10:21:13 2019 -0500
Expose support for same-site cookies
This adds API for web browsers to set extra information to support
same-site cookies.
libsoup/soup-cookie-jar-db.c | 18 ++++--
libsoup/soup-cookie-jar-text.c | 44 +++++++++++--
libsoup/soup-cookie-jar.c | 81 ++++++++++++++++++++---
libsoup/soup-cookie-jar.h | 8 +++
libsoup/soup-cookie.c | 58 +++++++++++++++++
libsoup/soup-cookie.h | 18 ++++++
libsoup/soup-message-private.h | 3 +
libsoup/soup-message.c | 144 +++++++++++++++++++++++++++++++++++++++++
libsoup/soup-message.h | 12 ++++
tests/cookies-test.c | 3 +-
10 files changed, 370 insertions(+), 19 deletions(-)
---
diff --git a/libsoup/soup-cookie-jar-db.c b/libsoup/soup-cookie-jar-db.c
index 0274038d..f525810a 100644
--- a/libsoup/soup-cookie-jar-db.c
+++ b/libsoup/soup-cookie-jar-db.c
@@ -128,9 +128,9 @@ soup_cookie_jar_db_new (const char *filename, gboolean read_only)
NULL);
}
-#define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM
moz_cookies;"
-#define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT,
path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"
-#define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d);"
+#define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly, sameSite
FROM moz_cookies;"
+#define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT,
path TEXT, expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER, sameSite INTEGER)"
+#define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d, %d);"
#define QUERY_DELETE "DELETE FROM moz_cookies WHERE name=%Q AND host=%Q;"
enum {
@@ -143,6 +143,7 @@ enum {
COL_LAST_ACCESS,
COL_SECURE,
COL_HTTP_ONLY,
+ COL_SAME_SITE_POLICY,
N_COL,
};
@@ -157,6 +158,7 @@ callback (void *data, int argc, char **argv, char **colname)
time_t now;
int max_age;
gboolean http_only = FALSE, secure = FALSE;
+ SoupSameSitePolicy same_site_policy;
now = time (NULL);
@@ -172,6 +174,7 @@ callback (void *data, int argc, char **argv, char **colname)
http_only = (g_strcmp0 (argv[COL_HTTP_ONLY], "1") == 0);
secure = (g_strcmp0 (argv[COL_SECURE], "1") == 0);
+ same_site_policy = g_ascii_strtoll (argv[COL_SAME_SITE_POLICY], NULL, 0);
cookie = soup_cookie_new (name, value, host, path, max_age);
@@ -179,6 +182,8 @@ callback (void *data, int argc, char **argv, char **colname)
soup_cookie_set_secure (cookie, TRUE);
if (http_only)
soup_cookie_set_http_only (cookie, TRUE);
+ if (same_site_policy)
+ soup_cookie_set_same_site_policy (cookie, same_site_policy);
soup_cookie_jar_add_cookie (jar, cookie);
@@ -241,6 +246,10 @@ open_db (SoupCookieJar *jar)
sqlite3_free (error);
}
+ /* Migrate old DB to include same-site info. We simply always run this as it
+ will safely handle a column with the same name existing */
+ sqlite3_exec (priv->db, "ALTER TABLE moz_cookies ADD COLUMN sameSite INTEGER DEFAULT 0", NULL, NULL,
NULL);
+
return FALSE;
}
@@ -291,7 +300,8 @@ soup_cookie_jar_db_changed (SoupCookieJar *jar,
new_cookie->path,
expires,
new_cookie->secure,
- new_cookie->http_only);
+ new_cookie->http_only,
+ new_cookie->same_site_policy);
exec_query_with_try_create_table (priv->db, query, NULL, NULL);
sqlite3_free (query);
}
diff --git a/libsoup/soup-cookie-jar-text.c b/libsoup/soup-cookie-jar-text.c
index 46e12e54..d376f6e9 100644
--- a/libsoup/soup-cookie-jar-text.c
+++ b/libsoup/soup-cookie-jar-text.c
@@ -121,6 +121,35 @@ soup_cookie_jar_text_new (const char *filename, gboolean read_only)
NULL);
}
+static SoupSameSitePolicy
+string_to_same_site_policy (const char *string)
+{
+ if (strcmp (string, "Lax") == 0)
+ return SOUP_SAME_SITE_POLICY_LAX;
+ else if (strcmp (string, "Strict") == 0)
+ return SOUP_SAME_SITE_POLICY_STRICT;
+ else if (strcmp (string, "None") == 0)
+ return SOUP_SAME_SITE_POLICY_NONE;
+ else
+ g_return_val_if_reached (SOUP_SAME_SITE_POLICY_NONE);
+}
+
+static const char *
+same_site_policy_to_string (SoupSameSitePolicy policy)
+{
+ switch (policy)
+ {
+ case SOUP_SAME_SITE_POLICY_STRICT:
+ return "Strict";
+ case SOUP_SAME_SITE_POLICY_LAX:
+ return "Lax";
+ case SOUP_SAME_SITE_POLICY_NONE:
+ return "None";
+ }
+
+ g_return_val_if_reached ("None");
+}
+
static SoupCookie*
parse_cookie (char *line, time_t now)
{
@@ -129,7 +158,7 @@ parse_cookie (char *line, time_t now)
gboolean http_only;
gulong expire_time;
int max_age;
- char *host, *path, *secure, *expires, *name, *value;
+ char *host, *path, *secure, *expires, *name, *value, *samesite = NULL;
if (g_str_has_prefix (line, "#HttpOnly_")) {
http_only = TRUE;
@@ -140,7 +169,7 @@ parse_cookie (char *line, time_t now)
http_only = FALSE;
result = g_strsplit (line, "\t", -1);
- if (g_strv_length (result) != 7)
+ if (g_strv_length (result) < 7)
goto out;
/* Check this first */
@@ -164,8 +193,14 @@ parse_cookie (char *line, time_t now)
name = result[5];
value = result[6];
+ if (g_strv_length (result) == 8)
+ samesite = result[7];
+
cookie = soup_cookie_new (name, value, host, path, max_age);
+ if (samesite != NULL)
+ soup_cookie_set_same_site_policy (cookie, string_to_same_site_policy (samesite));
+
if (strcmp (secure, "FALSE") != 0)
soup_cookie_set_secure (cookie, TRUE);
if (http_only)
@@ -219,7 +254,7 @@ write_cookie (FILE *out, SoupCookie *cookie)
{
fseek (out, 0, SEEK_END);
- fprintf (out, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
+ fprintf (out, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\t%s\n",
cookie->http_only ? "#HttpOnly_" : "",
cookie->domain,
*cookie->domain == '.' ? "TRUE" : "FALSE",
@@ -227,7 +262,8 @@ write_cookie (FILE *out, SoupCookie *cookie)
cookie->secure ? "TRUE" : "FALSE",
(gulong)soup_date_to_time_t (cookie->expires),
cookie->name,
- cookie->value);
+ cookie->value,
+ same_site_policy_to_string (cookie->same_site_policy));
}
static void
diff --git a/libsoup/soup-cookie-jar.c b/libsoup/soup-cookie-jar.c
index b2b78909..3dc887c9 100644
--- a/libsoup/soup-cookie-jar.c
+++ b/libsoup/soup-cookie-jar.c
@@ -12,6 +12,7 @@
#include <string.h>
#include "soup-cookie-jar.h"
+#include "soup-message-private.h"
#include "soup-misc-private.h"
#include "soup.h"
@@ -297,8 +298,29 @@ compare_cookies (gconstpointer a, gconstpointer b, gpointer jar)
return aserial - bserial;
}
+static gboolean
+cookie_is_valid_for_same_site_policy (SoupCookie *cookie, const char *method, SoupURI *uri, SoupURI
*top_level, SoupURI *cookie_uri, gboolean is_top_level_navigation, gboolean for_http)
+{
+ SoupSameSitePolicy policy = soup_cookie_get_same_site_policy (cookie);
+
+ if (policy == SOUP_SAME_SITE_POLICY_NONE)
+ return TRUE;
+
+ if (top_level == NULL)
+ return TRUE;
+
+ if (policy == SOUP_SAME_SITE_POLICY_LAX && is_top_level_navigation &&
+ (SOUP_METHOD_IS_SAFE (method) || for_http == FALSE))
+ return TRUE;
+
+ if (is_top_level_navigation && cookie_uri == NULL)
+ return FALSE;
+
+ return soup_host_matches_host (soup_uri_get_host (cookie_uri ? cookie_uri : top_level),
soup_uri_get_host (uri));
+}
+
static GSList *
-get_cookies (SoupCookieJar *jar, SoupURI *uri, gboolean for_http, gboolean copy_cookies)
+get_cookies (SoupCookieJar *jar, SoupURI *uri, SoupURI *top_level, SoupURI *site_for_cookies, const char
*method, gboolean for_http, gboolean is_top_level_navigation, gboolean copy_cookies)
{
SoupCookieJarPrivate *priv;
GSList *cookies, *domain_cookies;
@@ -332,6 +354,7 @@ get_cookies (SoupCookieJar *jar, SoupURI *uri, gboolean for_http, gboolean copy_
g_strdup (cur),
new_head);
} else if (soup_cookie_applies_to_uri (cookie, uri) &&
+ cookie_is_valid_for_same_site_policy (cookie, method, uri, top_level,
site_for_cookies, is_top_level_navigation, for_http) &&
(for_http || !cookie->http_only))
cookies = g_slist_append (cookies, copy_cookies ? soup_cookie_copy (cookie) :
cookie);
@@ -386,7 +409,7 @@ soup_cookie_jar_get_cookies (SoupCookieJar *jar, SoupURI *uri,
g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
g_return_val_if_fail (uri != NULL, NULL);
- cookies = get_cookies (jar, uri, for_http, FALSE);
+ cookies = get_cookies (jar, uri, NULL, NULL, NULL, for_http, FALSE, FALSE);
if (cookies) {
char *result = soup_cookies_to_cookie_header (cookies);
@@ -430,7 +453,39 @@ soup_cookie_jar_get_cookie_list (SoupCookieJar *jar, SoupURI *uri, gboolean for_
g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
g_return_val_if_fail (uri != NULL, NULL);
- return get_cookies (jar, uri, for_http, TRUE);
+ return get_cookies (jar, uri, NULL, NULL, NULL, for_http, FALSE, TRUE);
+}
+
+/**
+ * soup_cookie_jar_get_cookie_list_full:
+ * @jar: a #SoupCookieJar
+ * @uri: a #SoupURI
+ * @top_level: (nullable): a #SoupURI for the top level document
+ * @site_for_cookies: (nullable): a #SoupURI
+ * @method: (nullable): the HTTP method requesting the cookies, this
+ * should only be %NULL when @for_http is %FALSE
+ * @for_http: whether or not the return value is being passed directly
+ * to an HTTP operation
+ * @is_top_level_navigation: whether or not the http request is part of
+ * 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.
+ *
+ * Return value: (transfer full) (element-type Soup.Cookie): a #GSList
+ * with the cookies in the @jar that would be sent with a request to @uri.
+ *
+ * Since: 2.66
+ */
+GSList *
+soup_cookie_jar_get_cookie_list_full (SoupCookieJar *jar, SoupURI *uri, SoupURI *top_level, SoupURI
*site_for_cookies, const char *method, gboolean for_http, gboolean is_top_level_navigation)
+{
+ g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return get_cookies (jar, uri, top_level, site_for_cookies, g_intern_string (method), for_http,
is_top_level_navigation, TRUE);
}
/**
@@ -690,15 +745,21 @@ static void
msg_starting_cb (SoupMessage *msg, gpointer feature)
{
SoupCookieJar *jar = SOUP_COOKIE_JAR (feature);
- char *cookies;
+ GSList *cookies;
- cookies = soup_cookie_jar_get_cookies (jar, soup_message_get_uri (msg), TRUE);
- if (cookies) {
- soup_message_headers_replace (msg->request_headers,
- "Cookie", cookies);
- g_free (cookies);
- } else
+ cookies = soup_cookie_jar_get_cookie_list_full (jar, soup_message_get_uri (msg),
+ soup_message_get_first_party (msg),
+ soup_message_get_site_for_cookies (msg),
+ msg->method,
+ TRUE, soup_message_get_is_toplevel_navigation
(msg));
+ if (cookies != NULL) {
+ char *cookie_header = soup_cookies_to_cookie_header (cookies);
+ soup_message_headers_replace (msg->request_headers, "Cookie", cookie_header);
+ g_free (cookie_header);
+ g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
+ } else {
soup_message_headers_remove (msg->request_headers, "Cookie");
+ }
}
static void
diff --git a/libsoup/soup-cookie-jar.h b/libsoup/soup-cookie-jar.h
index d9c6850e..0c7b99dd 100644
--- a/libsoup/soup-cookie-jar.h
+++ b/libsoup/soup-cookie-jar.h
@@ -59,6 +59,14 @@ SOUP_AVAILABLE_IN_2_40
GSList * soup_cookie_jar_get_cookie_list (SoupCookieJar *jar,
SoupURI *uri,
gboolean for_http);
+SOUP_AVAILABLE_IN_2_66
+GSList * soup_cookie_jar_get_cookie_list_full (SoupCookieJar *jar,
+ SoupURI *uri,
+ SoupURI *top_level,
+ SoupURI
*site_for_cookies,
+ const char *method,
+ gboolean for_http,
+ gboolean
is_top_level_navigation);
SOUP_AVAILABLE_IN_2_24
void soup_cookie_jar_set_cookie (SoupCookieJar *jar,
SoupURI *uri,
diff --git a/libsoup/soup-cookie.c b/libsoup/soup-cookie.c
index 7cea82e5..c31a65d8 100644
--- a/libsoup/soup-cookie.c
+++ b/libsoup/soup-cookie.c
@@ -88,6 +88,7 @@ soup_cookie_copy (SoupCookie *cookie)
copy->expires = soup_date_copy(cookie->expires);
copy->secure = cookie->secure;
copy->http_only = cookie->http_only;
+ copy->same_site_policy = cookie->same_site_policy;
return copy;
}
@@ -238,6 +239,19 @@ parse_one_cookie (const char *header, SoupURI *origin)
cookie->secure = TRUE;
if (has_value)
parse_value (&p, FALSE);
+ } else if (MATCH_NAME ("samesite")) {
+ if (has_value) {
+ char *policy = parse_value (&p, TRUE);
+ if (g_ascii_strcasecmp (policy, "Lax") == 0) {
+ cookie->same_site_policy = SOUP_SAME_SITE_POLICY_LAX;
+ g_free (policy);
+ continue;
+ }
+ g_free (policy);
+ }
+
+ /* "Strict" and any invalid value are treated the same */
+ cookie->same_site_policy = SOUP_SAME_SITE_POLICY_STRICT;
} else {
/* Ignore unknown attributes, but we still have
* to skip over the value.
@@ -743,12 +757,56 @@ serialize_cookie (SoupCookie *cookie, GString *header, gboolean set_cookie)
g_string_append (header, "; domain=");
g_string_append (header, cookie->domain);
}
+ if (cookie->same_site_policy) {
+ g_string_append (header, "; SameSite=");
+ if (cookie->same_site_policy == SOUP_SAME_SITE_POLICY_LAX)
+ g_string_append (header, "Lax");
+ else
+ g_string_append (header, "Strict");
+ }
if (cookie->secure)
g_string_append (header, "; secure");
if (cookie->http_only)
g_string_append (header, "; HttpOnly");
}
+/**
+ * soup_cookie_set_same_site_policy:
+ * @cookie: a #SoupCookie
+ * @policy: a #SoupSameSitePolicy
+ *
+ * Since: 2.66
+ **/
+void
+soup_cookie_set_same_site_policy (SoupCookie *cookie,
+ SoupSameSitePolicy policy)
+{
+ switch (policy)
+ {
+ case SOUP_SAME_SITE_POLICY_NONE:
+ case SOUP_SAME_SITE_POLICY_STRICT:
+ case SOUP_SAME_SITE_POLICY_LAX:
+ cookie->same_site_policy = policy;
+ break;
+ default:
+ g_return_if_reached ();
+ }
+}
+
+/**
+ * soup_cookie_get_same_site_policy:
+ * @cookie: a #SoupCookie
+ *
+ * Returns a #SoupSameSitePolicy
+ *
+ * Since: 2.66
+ **/
+SoupSameSitePolicy
+soup_cookie_get_same_site_policy (SoupCookie *cookie)
+{
+ return cookie->same_site_policy;
+}
+
/**
* soup_cookie_to_set_cookie_header:
* @cookie: a #SoupCookie
diff --git a/libsoup/soup-cookie.h b/libsoup/soup-cookie.h
index 21973a49..53a298ac 100644
--- a/libsoup/soup-cookie.h
+++ b/libsoup/soup-cookie.h
@@ -10,6 +10,17 @@
G_BEGIN_DECLS
+/**
+ * SoupSameSitePolicy:
+ *
+ * Since: 2.66
+ */
+typedef enum {
+ SOUP_SAME_SITE_POLICY_NONE,
+ SOUP_SAME_SITE_POLICY_STRICT,
+ SOUP_SAME_SITE_POLICY_LAX,
+} SoupSameSitePolicy;
+
struct _SoupCookie {
char *name;
char *value;
@@ -18,6 +29,7 @@ struct _SoupCookie {
SoupDate *expires;
gboolean secure;
gboolean http_only;
+ SoupSameSitePolicy same_site_policy;
};
SOUP_AVAILABLE_IN_2_24
@@ -80,6 +92,12 @@ SOUP_AVAILABLE_IN_2_24
void soup_cookie_set_http_only (SoupCookie *cookie,
gboolean http_only);
+SOUP_AVAILABLE_IN_2_66
+void soup_cookie_set_same_site_policy (SoupCookie *cookie,
+ SoupSameSitePolicy policy);
+SOUP_AVAILABLE_IN_2_66
+SoupSameSitePolicy soup_cookie_get_same_site_policy (SoupCookie *cookie);
+
SOUP_AVAILABLE_IN_2_24
char *soup_cookie_to_set_cookie_header (SoupCookie *cookie);
SOUP_AVAILABLE_IN_2_24
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 2f3d58a7..954586e3 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -36,6 +36,7 @@ typedef struct {
GSList *disabled_features;
SoupURI *first_party;
+ SoupURI *site_for_cookies;
GTlsCertificate *tls_certificate;
GTlsCertificateFlags tls_errors;
@@ -43,6 +44,8 @@ typedef struct {
SoupRequest *request;
SoupMessagePriority priority;
+
+ gboolean is_toplevel_navigation;
} SoupMessagePrivate;
#define SOUP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_MESSAGE,
SoupMessagePrivate))
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index e4d78476..a51b617b 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -144,6 +144,8 @@ enum {
PROP_TLS_CERTIFICATE,
PROP_TLS_ERRORS,
PROP_PRIORITY,
+ PROP_SITE_FOR_COOKIES,
+ PROP_IS_TOP_LEVEL_NAVIGATION,
LAST_PROP
};
@@ -174,6 +176,7 @@ soup_message_finalize (GObject *object)
g_clear_pointer (&priv->uri, soup_uri_free);
g_clear_pointer (&priv->first_party, soup_uri_free);
+ g_clear_pointer (&priv->site_for_cookies, soup_uri_free);
g_clear_object (&priv->addr);
g_clear_object (&priv->auth);
@@ -207,6 +210,12 @@ soup_message_set_property (GObject *object, guint prop_id,
case PROP_URI:
soup_message_set_uri (msg, g_value_get_boxed (value));
break;
+ case PROP_SITE_FOR_COOKIES:
+ soup_message_set_site_for_cookies (msg, g_value_get_boxed (value));
+ break;
+ case PROP_IS_TOP_LEVEL_NAVIGATION:
+ soup_message_set_is_toplevel_navigation (msg, g_value_get_boolean (value));
+ break;
case PROP_HTTP_VERSION:
soup_message_set_http_version (msg, g_value_get_enum (value));
break;
@@ -270,6 +279,12 @@ soup_message_get_property (GObject *object, guint prop_id,
case PROP_URI:
g_value_set_boxed (value, priv->uri);
break;
+ case PROP_SITE_FOR_COOKIES:
+ g_value_set_boxed (value, priv->site_for_cookies);
+ break;
+ case PROP_IS_TOP_LEVEL_NAVIGATION:
+ g_value_set_boolean (value, priv->is_toplevel_navigation);
+ break;
case PROP_HTTP_VERSION:
g_value_set_enum (value, priv->http_version);
break;
@@ -805,6 +820,30 @@ soup_message_class_init (SoupMessageClass *message_class)
"The URI loaded in the application when the message was requested.",
SOUP_TYPE_URI,
G_PARAM_READWRITE));
+ /**
+ * SoupMessage:site-for-cookkies:
+ *
+ * Since: 2.66
+ */
+ g_object_class_install_property (
+ object_class, PROP_SITE_FOR_COOKIES,
+ g_param_spec_boxed (SOUP_MESSAGE_SITE_FOR_COOKIES,
+ "Site for cookies",
+ "The URI for the site to compare cookies against",
+ SOUP_TYPE_URI,
+ G_PARAM_READWRITE));
+ /**
+ * SoupMessage:is-toplevel-navigation:
+ *
+ * Since: 2.66
+ */
+ g_object_class_install_property (
+ object_class, PROP_IS_TOP_LEVEL_NAVIGATION,
+ g_param_spec_boolean (SOUP_MESSAGE_IS_TOPLEVEL_NAVIGATION,
+ "Is top-level navigation",
+ "If the current messsage is navigating between top-levels",
+ FALSE,
+ G_PARAM_READWRITE));
/**
* SOUP_MESSAGE_REQUEST_BODY:
*
@@ -1890,6 +1929,111 @@ soup_message_set_first_party (SoupMessage *msg,
g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_FIRST_PARTY);
}
+/**
+ * soup_message_get_site_for_cookies:
+ * @msg: a #SoupMessage
+ *
+ * Gets @msg's site for cookies #SoupURI
+ *
+ * Returns: (transfer none): the @msg's site for cookies #SoupURI
+ *
+ * Since: 2.66
+ **/
+SoupURI *
+soup_message_get_site_for_cookies (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ return priv->site_for_cookies;
+}
+
+/**
+ * soup_message_set_site_for_cookies:
+ * @msg: a #SoupMessage
+ * @site_for_cookies: (nullable): the #SoupURI 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.
+ *
+ * See the [same-site spec](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00)
+ * for more information.
+ *
+ * Since: 2.66
+ **/
+void
+soup_message_set_site_for_cookies (SoupMessage *msg,
+ SoupURI *site_for_cookies)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->site_for_cookies == site_for_cookies)
+ return;
+
+ if (priv->site_for_cookies) {
+ if (site_for_cookies && soup_uri_equal (priv->site_for_cookies, site_for_cookies))
+ return;
+
+ soup_uri_free (priv->site_for_cookies);
+ }
+
+ priv->site_for_cookies = site_for_cookies ? soup_uri_copy (site_for_cookies) : NULL;
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_SITE_FOR_COOKIES);
+}
+
+/**
+ * soup_message_set_is_toplevel_navigation:
+ * @msg: a #SoupMessage
+ * @is_toplevel_navigation: if %TRUE indicate the current request is a toplevel navigation
+ *
+ * See the [same-site spec](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00)
+ * for more information.
+ *
+ * Since: 2.66
+ **/
+void
+soup_message_set_is_toplevel_navigation (SoupMessage *msg,
+ gboolean is_toplevel_navigation)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->is_toplevel_navigation == is_toplevel_navigation)
+ return;
+
+ priv->is_toplevel_navigation = is_toplevel_navigation;
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_IS_TOPLEVEL_NAVIGATION);
+}
+
+/**
+ * soup_message_get_is_toplevel_navigation:
+ * @msg: a #SoupMessage
+ *
+ * Since: 2.66
+ **/
+gboolean
+soup_message_get_is_toplevel_navigation (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ return priv->is_toplevel_navigation;
+}
+
void
soup_message_set_https_status (SoupMessage *msg, SoupConnection *conn)
{
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 93778961..6cc01216 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -69,6 +69,7 @@ GType soup_message_get_type (void);
#define SOUP_MESSAGE_STATUS_CODE "status-code"
#define SOUP_MESSAGE_REASON_PHRASE "reason-phrase"
#define SOUP_MESSAGE_FIRST_PARTY "first-party"
+#define SOUP_MESSAGE_SITE_FOR_COOKIES "site-for-cookies"
#define SOUP_MESSAGE_REQUEST_BODY "request-body"
#define SOUP_MESSAGE_REQUEST_BODY_DATA "request-body-data"
#define SOUP_MESSAGE_REQUEST_HEADERS "request-headers"
@@ -78,6 +79,7 @@ GType soup_message_get_type (void);
#define SOUP_MESSAGE_TLS_CERTIFICATE "tls-certificate"
#define SOUP_MESSAGE_TLS_ERRORS "tls-errors"
#define SOUP_MESSAGE_PRIORITY "priority"
+#define SOUP_MESSAGE_IS_TOPLEVEL_NAVIGATION "is-toplevel-navigation"
SOUP_AVAILABLE_IN_2_4
SoupMessage *soup_message_new (const char *method,
@@ -126,6 +128,16 @@ SoupURI *soup_message_get_first_party (SoupMessage *msg);
SOUP_AVAILABLE_IN_2_30
void soup_message_set_first_party (SoupMessage *msg,
SoupURI *first_party);
+SOUP_AVAILABLE_IN_2_66
+SoupURI *soup_message_get_site_for_cookies (SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_66
+void soup_message_set_site_for_cookies (SoupMessage *msg,
+ SoupURI *site_for_cookies);
+SOUP_AVAILABLE_IN_2_66
+void soup_message_set_is_toplevel_navigation (SoupMessage *msg,
+ gboolean is_toplevel_navigation);
+SOUP_AVAILABLE_IN_2_66
+gboolean soup_message_get_is_toplevel_navigation (SoupMessage *msg);
typedef enum {
SOUP_MESSAGE_NO_REDIRECT = (1 << 1),
diff --git a/tests/cookies-test.c b/tests/cookies-test.c
index e3f79cae..85ec84ed 100644
--- a/tests/cookies-test.c
+++ b/tests/cookies-test.c
@@ -241,7 +241,7 @@ do_cookies_parsing_test (void)
msg = soup_message_new_from_uri ("GET", first_party_uri);
soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
- "three=3; httpONLY=Wednesday; max-age=100");
+ "three=3; httpONLY=Wednesday; max-age=100; SameSite=Lax");
soup_session_send_message (session, msg);
g_object_unref (msg);
@@ -263,6 +263,7 @@ do_cookies_parsing_test (void)
got3 = TRUE;
g_assert_true (soup_cookie_get_http_only (cookie));
g_assert_true (soup_cookie_get_expires (cookie) != NULL);
+ g_assert_cmpint (soup_cookie_get_same_site_policy (cookie), ==,
SOUP_SAME_SITE_POLICY_LAX);
} else {
soup_test_assert (FALSE, "got unexpected cookie '%s'",
soup_cookie_get_name (cookie));
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]