diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp index cf1232e37f..cfc3b14c9e 100644 --- a/src/network/access/qnetworkcookie.cpp +++ b/src/network/access/qnetworkcookie.cpp @@ -222,6 +222,29 @@ void QNetworkCookie::setSecure(bool enable) d->secure = enable; } +/*! + Returns the "SameSite" option if specified in the cookie + string, \c SameSite::Default if not present. + + \since 6.1 + \sa setSameSite() +*/ +QNetworkCookie::SameSite QNetworkCookie::sameSite() const +{ + return d->sameSite; +} + +/*! + Sets the "SameSite" option of this cookie to \a sameSite. + + \since 6.1 + \sa sameSite() +*/ +void QNetworkCookie::setSameSite(QNetworkCookie::SameSite sameSite) +{ + d->sameSite = sameSite; +} + /*! \since 4.5 @@ -435,6 +458,49 @@ static QPair nextField(const QByteArray &text, int &posi \sa toRawForm(), parseCookies() */ +/*! + \enum QNetworkCookie::SameSite + \since 6.1 + + \value Default SameSite is not set. Can be interpreted as None or Lax by the browser. + \value None Cookies can be sent in all contexts. This used to be default, but + recent browsers made Lax default, and will now require the cookie to be both secure and to set SameSite=None. + \value Lax Cookies are sent on first party requests and GET requests initiated by third party website. + This is the default in modern browsers (since mid 2020). + \value Strict Cookies will only be sent in a first-party context. + + \sa setSameSite(), sameSite() +*/ + +namespace { +QByteArray sameSiteToRawString(QNetworkCookie::SameSite samesite) +{ + switch (samesite) { + case QNetworkCookie::SameSite::None: + return QByteArrayLiteral("None"); + case QNetworkCookie::SameSite::Lax: + return QByteArrayLiteral("Lax"); + case QNetworkCookie::SameSite::Strict: + return QByteArrayLiteral("Strict"); + case QNetworkCookie::SameSite::Default: + break; + } + return QByteArray(); +} + +QNetworkCookie::SameSite sameSiteFromRawString(QByteArray str) +{ + str = str.toLower(); + if (str == QByteArrayLiteral("none")) + return QNetworkCookie::SameSite::None; + if (str == QByteArrayLiteral("lax")) + return QNetworkCookie::SameSite::Lax; + if (str == QByteArrayLiteral("strict")) + return QNetworkCookie::SameSite::Strict; + return QNetworkCookie::SameSite::Default; +} +} // namespace + /*! Returns the raw form of this QNetworkCookie. The QByteArray returned by this function is suitable for an HTTP header, either @@ -460,9 +526,9 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const result += "; secure"; if (isHttpOnly()) result += "; HttpOnly"; - if (!d->sameSite.isEmpty()) { + if (d->sameSite != SameSite::Default) { result += "; SameSite="; - result += d->sameSite; + result += sameSiteToRawString(d->sameSite); } if (!isSessionCookie()) { result += "; expires="; @@ -999,7 +1065,7 @@ QList QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt } else if (field.first == "httponly") { cookie.setHttpOnly(true); } else if (field.first == "samesite") { - cookie.d->sameSite = field.second; + cookie.setSameSite(sameSiteFromRawString(field.second)); } else { // ignore unknown fields in the cookie (RFC6265 section 5.2, rule 6) } diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h index b712b63849..736a9d7149 100644 --- a/src/network/access/qnetworkcookie.h +++ b/src/network/access/qnetworkcookie.h @@ -57,11 +57,19 @@ class QUrl; class QNetworkCookiePrivate; class Q_NETWORK_EXPORT QNetworkCookie { + Q_GADGET public: enum RawForm { NameAndValueOnly, Full }; + enum class SameSite { + Default, + None, + Lax, + Strict + }; + Q_ENUM(SameSite) explicit QNetworkCookie(const QByteArray &name = QByteArray(), const QByteArray &value = QByteArray()); QNetworkCookie(const QNetworkCookie &other); @@ -79,6 +87,8 @@ public: void setSecure(bool enable); bool isHttpOnly() const; void setHttpOnly(bool enable); + SameSite sameSite() const; + void setSameSite(SameSite sameSite); bool isSessionCookie() const; QDateTime expirationDate() const; diff --git a/src/network/access/qnetworkcookie_p.h b/src/network/access/qnetworkcookie_p.h index e30e611cf5..0cf3442fee 100644 --- a/src/network/access/qnetworkcookie_p.h +++ b/src/network/access/qnetworkcookie_p.h @@ -53,24 +53,25 @@ #include #include "QtCore/qdatetime.h" +#include "QtNetwork/qnetworkcookie.h" QT_BEGIN_NAMESPACE class QNetworkCookiePrivate: public QSharedData { public: - inline QNetworkCookiePrivate() : secure(false), httpOnly(false) { } + QNetworkCookiePrivate() = default; static QList parseSetCookieHeaderLine(const QByteArray &cookieString); QDateTime expirationDate; QString domain; QString path; QString comment; - QByteArray sameSite; QByteArray name; QByteArray value; - bool secure; - bool httpOnly; + QNetworkCookie::SameSite sameSite = QNetworkCookie::SameSite::Default; + bool secure = false; + bool httpOnly = false; }; static inline bool isLWS(char c) diff --git a/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp b/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp index 96c4917473..289ff9e754 100644 --- a/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp +++ b/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp @@ -44,6 +44,8 @@ private slots: void parseMultipleCookies_data(); void parseMultipleCookies(); + + void sameSite(); }; void tst_QNetworkCookie::getterSetter() @@ -683,5 +685,16 @@ void tst_QNetworkCookie::parseMultipleCookies() QCOMPARE(result, expectedCookies); } +void tst_QNetworkCookie::sameSite() +{ + QList result = QNetworkCookie::parseCookies(QByteArrayLiteral("a=b;domain=qt-project.org")); + QCOMPARE(result.first().sameSite(), QNetworkCookie::SameSite::Default); + result = QNetworkCookie::parseCookies(QByteArrayLiteral("a=b;domain=qt-project.org;samesite=strict")); + QCOMPARE(result.first().sameSite(), QNetworkCookie::SameSite::Strict); + result = QNetworkCookie::parseCookies(QByteArrayLiteral("a=b;domain=qt-project.org;samesite=none;secure")); + QCOMPARE(result.first().sameSite(), QNetworkCookie::SameSite::None); + QCOMPARE(result.first().toRawForm(), QByteArrayLiteral("a=b; secure; SameSite=None; domain=qt-project.org")); + +} QTEST_MAIN(tst_QNetworkCookie) #include "tst_qnetworkcookie.moc"