Network: Use QHttpHeaders in QHttpHeaderParser
QHttpHeaderParser::headers() method is changed to return QHttpHeaders. QAuthenticatorPrivate::parseHttpResponse() method is changed to work with QHttpHeaders. QHttpNetworkHeader::header() method is updated to return QHttpHeaders. Tests are updated. Task-number: QTBUG-120133 Change-Id: I20a18b509acd7a8b8d93884cff8349519d64293e Reviewed-by: Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io> Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Øystein Heskestad <oystein.heskestad@qt.io>
This commit is contained in:
parent
c29a235833
commit
15b0bd69ff
@ -20,6 +20,7 @@ qt_internal_add_module(Network
|
|||||||
access/qnetworkcookie.cpp access/qnetworkcookie.h access/qnetworkcookie_p.h
|
access/qnetworkcookie.cpp access/qnetworkcookie.h access/qnetworkcookie_p.h
|
||||||
access/qnetworkcookiejar.cpp access/qnetworkcookiejar.h access/qnetworkcookiejar_p.h
|
access/qnetworkcookiejar.cpp access/qnetworkcookiejar.h access/qnetworkcookiejar_p.h
|
||||||
access/qnetworkfile.cpp access/qnetworkfile_p.h
|
access/qnetworkfile.cpp access/qnetworkfile_p.h
|
||||||
|
access/qhttpheaders.cpp access/qhttpheaders.h
|
||||||
access/qhttpheaderparser.cpp access/qhttpheaderparser_p.h
|
access/qhttpheaderparser.cpp access/qhttpheaderparser_p.h
|
||||||
access/qnetworkreply.cpp access/qnetworkreply.h access/qnetworkreply_p.h
|
access/qnetworkreply.cpp access/qnetworkreply.h access/qnetworkreply_p.h
|
||||||
access/qnetworkreplydataimpl.cpp access/qnetworkreplydataimpl_p.h
|
access/qnetworkreplydataimpl.cpp access/qnetworkreplydataimpl_p.h
|
||||||
@ -124,7 +125,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_http
|
|||||||
access/qhttp2configuration.cpp access/qhttp2configuration.h
|
access/qhttp2configuration.cpp access/qhttp2configuration.h
|
||||||
access/qhttp2connection.cpp access/qhttp2connection_p.h
|
access/qhttp2connection.cpp access/qhttp2connection_p.h
|
||||||
access/qhttp2protocolhandler.cpp access/qhttp2protocolhandler_p.h
|
access/qhttp2protocolhandler.cpp access/qhttp2protocolhandler_p.h
|
||||||
access/qhttpheaders.cpp access/qhttpheaders.h
|
|
||||||
access/qhttpmultipart.cpp access/qhttpmultipart.h access/qhttpmultipart_p.h
|
access/qhttpmultipart.cpp access/qhttpmultipart.h access/qhttpmultipart_p.h
|
||||||
access/qhttpnetworkconnection.cpp access/qhttpnetworkconnection_p.h
|
access/qhttpnetworkconnection.cpp access/qhttpnetworkconnection_p.h
|
||||||
access/qhttpnetworkconnectionchannel.cpp access/qhttpnetworkconnectionchannel_p.h
|
access/qhttpnetworkconnectionchannel.cpp access/qhttpnetworkconnectionchannel_p.h
|
||||||
|
@ -184,14 +184,13 @@ QNetworkReply::NetworkError qt_error(quint32 errorCode)
|
|||||||
|
|
||||||
bool is_protocol_upgraded(const QHttpNetworkReply &reply)
|
bool is_protocol_upgraded(const QHttpNetworkReply &reply)
|
||||||
{
|
{
|
||||||
if (reply.statusCode() == 101) {
|
if (reply.statusCode() != 101)
|
||||||
// Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
|
return false;
|
||||||
const auto &header = reply.header();
|
|
||||||
for (const QPair<QByteArray, QByteArray> &field : header) {
|
// Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
|
||||||
if (field.first.compare("upgrade", Qt::CaseInsensitive) == 0 &&
|
for (const auto &v : reply.header().values(QHttpHeaders::WellKnownHeader::Upgrade)) {
|
||||||
field.second.compare("h2c", Qt::CaseInsensitive) == 0)
|
if (v.compare("h2c", Qt::CaseInsensitive) == 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "qhsts_p.h"
|
#include "qhsts_p.h"
|
||||||
|
|
||||||
|
#include "qhttpheaders.h"
|
||||||
|
|
||||||
#include "QtCore/private/qipaddress_p.h"
|
#include "QtCore/private/qipaddress_p.h"
|
||||||
#include "QtCore/qlist.h"
|
#include "QtCore/qlist.h"
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ static bool is_valid_domain_name(const QString &host)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHstsCache::updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
|
void QHstsCache::updateFromHeaders(const QHttpHeaders &headers,
|
||||||
const QUrl &url)
|
const QUrl &url)
|
||||||
{
|
{
|
||||||
if (!url.isValid())
|
if (!url.isValid())
|
||||||
@ -324,27 +326,25 @@ quoted-pair = "\" CHAR
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool QHstsHeaderParser::parse(const QList<QPair<QByteArray, QByteArray>> &headers)
|
bool QHstsHeaderParser::parse(const QHttpHeaders &headers)
|
||||||
{
|
{
|
||||||
for (const auto &h : headers) {
|
for (const auto &value : headers.values(
|
||||||
// We compare directly because header name was already 'trimmed' for us:
|
QHttpHeaders::WellKnownHeader::StrictTransportSecurity)) {
|
||||||
if (h.first.compare("Strict-Transport-Security", Qt::CaseInsensitive) == 0) {
|
header = value;
|
||||||
header = h.second;
|
// RFC6797, 8.1:
|
||||||
// RFC6797, 8.1:
|
//
|
||||||
//
|
// The UA MUST ignore any STS header fields not conforming to the
|
||||||
// The UA MUST ignore any STS header fields not conforming to the
|
// grammar specified in Section 6.1 ("Strict-Transport-Security HTTP
|
||||||
// grammar specified in Section 6.1 ("Strict-Transport-Security HTTP
|
// Response Header Field").
|
||||||
// Response Header Field").
|
//
|
||||||
//
|
// If a UA receives more than one STS header field in an HTTP
|
||||||
// If a UA receives more than one STS header field in an HTTP
|
// response message over secure transport, then the UA MUST process
|
||||||
// response message over secure transport, then the UA MUST process
|
// only the first such header field.
|
||||||
// only the first such header field.
|
//
|
||||||
//
|
// We read this as: ignore all invalid headers and take the first valid:
|
||||||
// We read this as: ignore all invalid headers and take the first valid:
|
if (parseSTSHeader() && maxAgeFound) {
|
||||||
if (parseSTSHeader() && maxAgeFound) {
|
expiry = QDateTime::currentDateTimeUtc().addSecs(maxAge);
|
||||||
expiry = QDateTime::currentDateTimeUtc().addSecs(maxAge);
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,11 +31,13 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QHttpHeaders;
|
||||||
|
|
||||||
class Q_AUTOTEST_EXPORT QHstsCache
|
class Q_AUTOTEST_EXPORT QHstsCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
|
void updateFromHeaders(const QHttpHeaders &headers,
|
||||||
const QUrl &url);
|
const QUrl &url);
|
||||||
void updateFromPolicies(const QList<QHstsPolicy> &hosts);
|
void updateFromPolicies(const QList<QHstsPolicy> &hosts);
|
||||||
void updateKnownHost(const QUrl &url, const QDateTime &expires,
|
void updateKnownHost(const QUrl &url, const QDateTime &expires,
|
||||||
@ -90,7 +92,7 @@ class Q_AUTOTEST_EXPORT QHstsHeaderParser
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
bool parse(const QList<QPair<QByteArray, QByteArray>> &headers);
|
bool parse(const QHttpHeaders &headers);
|
||||||
|
|
||||||
QDateTime expirationDate() const { return expiry; }
|
QDateTime expirationDate() const { return expiry; }
|
||||||
bool includeSubDomains() const { return subDomainsFound; }
|
bool includeSubDomains() const { return subDomainsFound; }
|
||||||
|
@ -60,7 +60,7 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
|
|||||||
if (size.second > maxHeaderListSize)
|
if (size.second > maxHeaderListSize)
|
||||||
return HttpHeader(); // Bad, we cannot send this request ...
|
return HttpHeader(); // Bad, we cannot send this request ...
|
||||||
|
|
||||||
const auto requestHeader = request.header();
|
const auto requestHeader = request.header().toListOfPairs();
|
||||||
for (const auto &field : requestHeader) {
|
for (const auto &field : requestHeader) {
|
||||||
const HeaderSize delta = entry_size(field.first, field.second);
|
const HeaderSize delta = entry_size(field.first, field.second);
|
||||||
if (!delta.first) // Overflow???
|
if (!delta.first) // Overflow???
|
||||||
|
@ -52,11 +52,11 @@ bool QHttpHeaderParser::parseHeaders(QByteArrayView header)
|
|||||||
if (header.size() - (header.endsWith("\r\n") ? 2 : 1) > maxTotalSize)
|
if (header.size() - (header.endsWith("\r\n") ? 2 : 1) > maxTotalSize)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
QList<QPair<QByteArray, QByteArray>> result;
|
QHttpHeaders result;
|
||||||
while (!header.empty()) {
|
while (!header.empty()) {
|
||||||
const qsizetype colon = header.indexOf(':');
|
const qsizetype colon = header.indexOf(':');
|
||||||
if (colon == -1) // if no colon check if empty headers
|
if (colon == -1) // if no colon check if empty headers
|
||||||
return result.empty() && (header == "\n" || header == "\r\n");
|
return result.isEmpty() && (header == "\n" || header == "\r\n");
|
||||||
if (result.size() >= maxFieldCount)
|
if (result.size() >= maxFieldCount)
|
||||||
return false;
|
return false;
|
||||||
QByteArrayView name = header.first(colon);
|
QByteArrayView name = header.first(colon);
|
||||||
@ -82,7 +82,7 @@ bool QHttpHeaderParser::parseHeaders(QByteArrayView header)
|
|||||||
header = header.sliced(endLine + 1);
|
header = header.sliced(endLine + 1);
|
||||||
} while (hSpaceStart(header));
|
} while (hSpaceStart(header));
|
||||||
Q_ASSERT(name.size() + 1 + value.size() <= maxFieldSize);
|
Q_ASSERT(name.size() + 1 + value.size() <= maxFieldSize);
|
||||||
result.append(qMakePair(name.toByteArray(), value));
|
result.append(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fields = result;
|
fields = result;
|
||||||
@ -128,25 +128,15 @@ bool QHttpHeaderParser::parseStatus(QByteArrayView status)
|
|||||||
return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
|
return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QList<QPair<QByteArray, QByteArray> >& QHttpHeaderParser::headers() const
|
const QHttpHeaders& QHttpHeaderParser::headers() const
|
||||||
{
|
{
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto firstEqualsName(QByteArrayView name)
|
|
||||||
{
|
|
||||||
return [name](const QPair<QByteArray, QByteArray> &header) {
|
|
||||||
return name.compare(header.first, Qt::CaseInsensitive) == 0;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray QHttpHeaderParser::firstHeaderField(QByteArrayView name,
|
QByteArray QHttpHeaderParser::firstHeaderField(QByteArrayView name,
|
||||||
const QByteArray &defaultValue) const
|
const QByteArray &defaultValue) const
|
||||||
{
|
{
|
||||||
const auto it = std::find_if(fields.begin(), fields.end(), firstEqualsName(name));
|
return fields.value(name, defaultValue).toByteArray();
|
||||||
if (it != fields.end())
|
|
||||||
return it->second;
|
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray QHttpHeaderParser::combinedHeaderValue(QByteArrayView name, const QByteArray &defaultValue) const
|
QByteArray QHttpHeaderParser::combinedHeaderValue(QByteArrayView name, const QByteArray &defaultValue) const
|
||||||
@ -159,33 +149,28 @@ QByteArray QHttpHeaderParser::combinedHeaderValue(QByteArrayView name, const QBy
|
|||||||
|
|
||||||
QList<QByteArray> QHttpHeaderParser::headerFieldValues(QByteArrayView name) const
|
QList<QByteArray> QHttpHeaderParser::headerFieldValues(QByteArrayView name) const
|
||||||
{
|
{
|
||||||
QList<QByteArray> result;
|
return fields.values(name);
|
||||||
for (auto it = fields.constBegin(); it != fields.constEnd(); ++it)
|
|
||||||
if (name.compare(it->first, Qt::CaseInsensitive) == 0)
|
|
||||||
result += it->second;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHttpHeaderParser::removeHeaderField(QByteArrayView name)
|
void QHttpHeaderParser::removeHeaderField(QByteArrayView name)
|
||||||
{
|
{
|
||||||
fields.removeIf(firstEqualsName(name));
|
fields.removeAll(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHttpHeaderParser::setHeaderField(const QByteArray &name, const QByteArray &data)
|
void QHttpHeaderParser::setHeaderField(const QByteArray &name, const QByteArray &data)
|
||||||
{
|
{
|
||||||
removeHeaderField(name);
|
removeHeaderField(name);
|
||||||
fields.append(qMakePair(name, data));
|
fields.append(name, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHttpHeaderParser::prependHeaderField(const QByteArray &name, const QByteArray &data)
|
void QHttpHeaderParser::prependHeaderField(const QByteArray &name, const QByteArray &data)
|
||||||
{
|
{
|
||||||
fields.prepend(qMakePair(name, data));
|
fields.insert(0, name, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHttpHeaderParser::appendHeaderField(const QByteArray &name, const QByteArray &data)
|
void QHttpHeaderParser::appendHeaderField(const QByteArray &name, const QByteArray &data)
|
||||||
{
|
{
|
||||||
fields.append(qMakePair(name, data));
|
fields.append(name, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHttpHeaderParser::clearHeaders()
|
void QHttpHeaderParser::clearHeaders()
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
||||||
|
#include <QtNetwork/qhttpheaders.h>
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
@ -52,7 +53,7 @@ public:
|
|||||||
bool parseHeaders(QByteArrayView headers);
|
bool parseHeaders(QByteArrayView headers);
|
||||||
bool parseStatus(QByteArrayView status);
|
bool parseStatus(QByteArrayView status);
|
||||||
|
|
||||||
const QList<QPair<QByteArray, QByteArray> >& headers() const;
|
const QHttpHeaders& headers() const;
|
||||||
void setStatusCode(int code);
|
void setStatusCode(int code);
|
||||||
int getStatusCode() const;
|
int getStatusCode() const;
|
||||||
int getMajorVersion() const;
|
int getMajorVersion() const;
|
||||||
@ -83,7 +84,7 @@ public:
|
|||||||
qsizetype maxHeaderFields() const { return maxFieldCount; }
|
qsizetype maxHeaderFields() const { return maxFieldCount; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<QPair<QByteArray, QByteArray> > fields;
|
QHttpHeaders fields;
|
||||||
QString reasonPhrase;
|
QString reasonPhrase;
|
||||||
int statusCode;
|
int statusCode;
|
||||||
int majorVersion;
|
int majorVersion;
|
||||||
|
@ -411,7 +411,7 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
|
|||||||
|
|
||||||
resend = false;
|
resend = false;
|
||||||
//create the response header to be used with QAuthenticatorPrivate.
|
//create the response header to be used with QAuthenticatorPrivate.
|
||||||
QList<QPair<QByteArray, QByteArray> > fields = reply->header();
|
const auto headers = reply->header();
|
||||||
|
|
||||||
// Check that any of the proposed authenticate methods are supported
|
// Check that any of the proposed authenticate methods are supported
|
||||||
const QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
|
const QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
|
||||||
@ -427,7 +427,7 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
|
|||||||
if (auth->isNull())
|
if (auth->isNull())
|
||||||
auth->detach();
|
auth->detach();
|
||||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
|
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
|
||||||
priv->parseHttpResponse(fields, isProxy, reply->url().host());
|
priv->parseHttpResponse(headers, isProxy, reply->url().host());
|
||||||
// Update method in case it changed
|
// Update method in case it changed
|
||||||
if (priv->method == QAuthenticatorPrivate::None)
|
if (priv->method == QAuthenticatorPrivate::None)
|
||||||
return false;
|
return false;
|
||||||
@ -523,12 +523,9 @@ QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
|
|||||||
return {{}, QNetworkReply::NoError};
|
return {{}, QNetworkReply::NoError};
|
||||||
|
|
||||||
QUrl redirectUrl;
|
QUrl redirectUrl;
|
||||||
const QList<QPair<QByteArray, QByteArray> > fields = reply->header();
|
const QHttpHeaders fields = reply->header();
|
||||||
for (const QNetworkReply::RawHeaderPair &header : fields) {
|
if (const auto h = fields.values(QHttpHeaders::WellKnownHeader::Location); !h.empty()) {
|
||||||
if (header.first.compare("location", Qt::CaseInsensitive) == 0) {
|
redirectUrl = QUrl::fromEncoded(h.first());
|
||||||
redirectUrl = QUrl::fromEncoded(header.second);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the location url is invalid/empty, we return ProtocolUnknownError
|
// If the location url is invalid/empty, we return ProtocolUnknownError
|
||||||
|
@ -53,7 +53,7 @@ void QHttpNetworkHeaderPrivate::prependHeaderField(const QByteArray &name, const
|
|||||||
parser.prependHeaderField(name, data);
|
parser.prependHeaderField(name, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QPair<QByteArray, QByteArray> > QHttpNetworkHeaderPrivate::headers() const
|
QHttpHeaders QHttpNetworkHeaderPrivate::headers() const
|
||||||
{
|
{
|
||||||
return parser.headers();
|
return parser.headers();
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
||||||
#include <QtNetwork/private/qhttpheaderparser_p.h>
|
#include <QtNetwork/private/qhttpheaderparser_p.h>
|
||||||
|
#include <QtNetwork/qhttpheaders.h>
|
||||||
|
|
||||||
#include <qshareddata.h>
|
#include <qshareddata.h>
|
||||||
#include <qurl.h>
|
#include <qurl.h>
|
||||||
@ -40,7 +41,7 @@ public:
|
|||||||
virtual qint64 contentLength() const = 0;
|
virtual qint64 contentLength() const = 0;
|
||||||
virtual void setContentLength(qint64 length) = 0;
|
virtual void setContentLength(qint64 length) = 0;
|
||||||
|
|
||||||
virtual QList<QPair<QByteArray, QByteArray> > header() const = 0;
|
virtual QHttpHeaders header() const = 0;
|
||||||
virtual QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const = 0;
|
virtual QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const = 0;
|
||||||
virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
|
virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
|
||||||
};
|
};
|
||||||
@ -61,7 +62,7 @@ public:
|
|||||||
void setHeaderField(const QByteArray &name, const QByteArray &data);
|
void setHeaderField(const QByteArray &name, const QByteArray &data);
|
||||||
void prependHeaderField(const QByteArray &name, const QByteArray &data);
|
void prependHeaderField(const QByteArray &name, const QByteArray &data);
|
||||||
void clearHeaders();
|
void clearHeaders();
|
||||||
QList<QPair<QByteArray, QByteArray> > headers() const;
|
QHttpHeaders headers() const;
|
||||||
bool operator==(const QHttpNetworkHeaderPrivate &other) const;
|
bool operator==(const QHttpNetworkHeaderPrivate &other) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -67,7 +67,7 @@ void QHttpNetworkReply::setContentLength(qint64 length)
|
|||||||
d->setContentLength(length);
|
d->setContentLength(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
|
QHttpHeaders QHttpNetworkReply::header() const
|
||||||
{
|
{
|
||||||
return d_func()->parser.headers();
|
return d_func()->parser.headers();
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ Q_MOC_INCLUDE(<QtNetwork/QNetworkProxy>)
|
|||||||
Q_MOC_INCLUDE(<QtNetwork/QAuthenticator>)
|
Q_MOC_INCLUDE(<QtNetwork/QAuthenticator>)
|
||||||
|
|
||||||
#include <private/qdecompresshelper_p.h>
|
#include <private/qdecompresshelper_p.h>
|
||||||
|
#include <QtNetwork/qhttpheaders.h>
|
||||||
|
|
||||||
#include <QtCore/qpointer.h>
|
#include <QtCore/qpointer.h>
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ public:
|
|||||||
qint64 contentLength() const override;
|
qint64 contentLength() const override;
|
||||||
void setContentLength(qint64 length) override;
|
void setContentLength(qint64 length) override;
|
||||||
|
|
||||||
QList<QPair<QByteArray, QByteArray> > header() const override;
|
QHttpHeaders header() const override;
|
||||||
QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override;
|
QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override;
|
||||||
void setHeaderField(const QByteArray &name, const QByteArray &data) override;
|
void setHeaderField(const QByteArray &name, const QByteArray &data) override;
|
||||||
void appendHeaderField(const QByteArray &name, const QByteArray &data);
|
void appendHeaderField(const QByteArray &name, const QByteArray &data);
|
||||||
|
@ -112,7 +112,7 @@ QByteArray QHttpNetworkRequest::uri(bool throughProxy) const
|
|||||||
|
|
||||||
QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
|
QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
|
||||||
{
|
{
|
||||||
const QList<QPair<QByteArray, QByteArray> > fields = request.header();
|
const QList<QPair<QByteArray, QByteArray> > fields = request.header().toListOfPairs();
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
ba.reserve(40 + fields.size()*25); // very rough lower bound estimation
|
ba.reserve(40 + fields.size()*25); // very rough lower bound estimation
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ void QHttpNetworkRequest::setContentLength(qint64 length)
|
|||||||
d->setContentLength(length);
|
d->setContentLength(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const
|
QHttpHeaders QHttpNetworkRequest::header() const
|
||||||
{
|
{
|
||||||
return d->parser.headers();
|
return d->parser.headers();
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ public:
|
|||||||
qint64 contentLength() const override;
|
qint64 contentLength() const override;
|
||||||
void setContentLength(qint64 length) override;
|
void setContentLength(qint64 length) override;
|
||||||
|
|
||||||
QList<QPair<QByteArray, QByteArray> > header() const override;
|
QHttpHeaders header() const override;
|
||||||
QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override;
|
QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override;
|
||||||
void setHeaderField(const QByteArray &name, const QByteArray &data) override;
|
void setHeaderField(const QByteArray &name, const QByteArray &data) override;
|
||||||
void prependHeaderField(const QByteArray &name, const QByteArray &data);
|
void prependHeaderField(const QByteArray &name, const QByteArray &data);
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "private/qnoncontiguousbytedevice_p.h"
|
#include "private/qnoncontiguousbytedevice_p.h"
|
||||||
#include "qnetworkaccessauthenticationmanager_p.h"
|
#include "qnetworkaccessauthenticationmanager_p.h"
|
||||||
#include <QtNetwork/private/http2protocol_p.h>
|
#include <QtNetwork/private/http2protocol_p.h>
|
||||||
|
#include <QtNetwork/qhttpheaders.h>
|
||||||
|
|
||||||
QT_REQUIRE_CONFIG(http);
|
QT_REQUIRE_CONFIG(http);
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ public:
|
|||||||
|
|
||||||
// outgoing, Retrieved in the synchronous HTTP case
|
// outgoing, Retrieved in the synchronous HTTP case
|
||||||
QByteArray synchronousDownloadData;
|
QByteArray synchronousDownloadData;
|
||||||
QList<QPair<QByteArray,QByteArray> > incomingHeaders;
|
QHttpHeaders incomingHeaders;
|
||||||
int incomingStatusCode;
|
int incomingStatusCode;
|
||||||
QString incomingReasonPhrase;
|
QString incomingReasonPhrase;
|
||||||
bool isPipeliningUsed;
|
bool isPipeliningUsed;
|
||||||
@ -112,7 +113,7 @@ signals:
|
|||||||
#endif
|
#endif
|
||||||
void socketStartedConnecting();
|
void socketStartedConnecting();
|
||||||
void requestSent();
|
void requestSent();
|
||||||
void downloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &, bool,
|
void downloadMetaData(const QHttpHeaders &, int, const QString &, bool,
|
||||||
QSharedPointer<char>, qint64, qint64, bool, bool);
|
QSharedPointer<char>, qint64, qint64, bool, bool);
|
||||||
void downloadProgress(qint64, qint64);
|
void downloadProgress(qint64, qint64);
|
||||||
void downloadData(const QByteArray &);
|
void downloadData(const QByteArray &);
|
||||||
|
@ -1325,7 +1325,7 @@ void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &hm,
|
void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QHttpHeaders &hm,
|
||||||
int sc, const QString &rp, bool pu,
|
int sc, const QString &rp, bool pu,
|
||||||
QSharedPointer<char> db,
|
QSharedPointer<char> db,
|
||||||
qint64 contentLength,
|
qint64 contentLength,
|
||||||
@ -1364,7 +1364,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
|
|||||||
const bool autoDecompress = request.rawHeader("accept-encoding").isEmpty();
|
const bool autoDecompress = request.rawHeader("accept-encoding").isEmpty();
|
||||||
const bool shouldDecompress = isCompressed && autoDecompress;
|
const bool shouldDecompress = isCompressed && autoDecompress;
|
||||||
// reconstruct the HTTP header
|
// reconstruct the HTTP header
|
||||||
for (const auto &[key, originValue] : hm) {
|
for (const auto &[key, originValue] : hm.toListOfPairs()) {
|
||||||
QByteArray value = q->rawHeader(key);
|
QByteArray value = q->rawHeader(key);
|
||||||
|
|
||||||
// Reset any previous "location" header set in the reply. In case of
|
// Reset any previous "location" header set in the reply. In case of
|
||||||
|
@ -244,7 +244,7 @@ public:
|
|||||||
// From HTTP thread:
|
// From HTTP thread:
|
||||||
void replyDownloadData(QByteArray);
|
void replyDownloadData(QByteArray);
|
||||||
void replyFinished();
|
void replyFinished();
|
||||||
void replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &,
|
void replyDownloadMetaData(const QHttpHeaders &, int, const QString &,
|
||||||
bool, QSharedPointer<char>, qint64, qint64, bool, bool);
|
bool, QSharedPointer<char>, qint64, qint64, bool, bool);
|
||||||
void replyDownloadProgressSlot(qint64,qint64);
|
void replyDownloadProgressSlot(qint64,qint64);
|
||||||
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
|
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <qstring.h>
|
#include <qstring.h>
|
||||||
#include <qdatetime.h>
|
#include <qdatetime.h>
|
||||||
#include <qrandom.h>
|
#include <qrandom.h>
|
||||||
|
#include <QtNetwork/qhttpheaders.h>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <qmutex.h>
|
#include <qmutex.h>
|
||||||
@ -444,14 +445,14 @@ static bool verifyDigestMD5(QByteArrayView value)
|
|||||||
return true; // assume it's ok if algorithm is not specified
|
return true; // assume it's ok if algorithm is not specified
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray>> &values,
|
void QAuthenticatorPrivate::parseHttpResponse(const QHttpHeaders &headers,
|
||||||
bool isProxy, QStringView host)
|
bool isProxy, QStringView host)
|
||||||
{
|
{
|
||||||
#if !QT_CONFIG(gssapi)
|
#if !QT_CONFIG(gssapi)
|
||||||
Q_UNUSED(host);
|
Q_UNUSED(host);
|
||||||
#endif
|
#endif
|
||||||
const auto search = isProxy ?
|
const auto search = isProxy ? QHttpHeaders::WellKnownHeader::ProxyAuthenticate
|
||||||
QByteArrayView("proxy-authenticate") : QByteArrayView("www-authenticate");
|
: QHttpHeaders::WellKnownHeader::WWWAuthenticate;
|
||||||
|
|
||||||
method = None;
|
method = None;
|
||||||
/*
|
/*
|
||||||
@ -465,23 +466,21 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
QByteArrayView headerVal;
|
QByteArrayView headerVal;
|
||||||
for (const auto ¤t : values) {
|
for (const auto ¤t : headers.values(search)) {
|
||||||
if (current.first.compare(search, Qt::CaseInsensitive) != 0)
|
const QLatin1StringView str(current);
|
||||||
continue;
|
|
||||||
const QLatin1StringView str(current.second);
|
|
||||||
if (method < Basic && str.startsWith("basic"_L1, Qt::CaseInsensitive)) {
|
if (method < Basic && str.startsWith("basic"_L1, Qt::CaseInsensitive)) {
|
||||||
method = Basic;
|
method = Basic;
|
||||||
headerVal = QByteArrayView(current.second).mid(6);
|
headerVal = QByteArrayView(current).mid(6);
|
||||||
} else if (method < Ntlm && str.startsWith("ntlm"_L1, Qt::CaseInsensitive)) {
|
} else if (method < Ntlm && str.startsWith("ntlm"_L1, Qt::CaseInsensitive)) {
|
||||||
method = Ntlm;
|
method = Ntlm;
|
||||||
headerVal = QByteArrayView(current.second).mid(5);
|
headerVal = QByteArrayView(current).mid(5);
|
||||||
} else if (method < DigestMd5 && str.startsWith("digest"_L1, Qt::CaseInsensitive)) {
|
} else if (method < DigestMd5 && str.startsWith("digest"_L1, Qt::CaseInsensitive)) {
|
||||||
// Make sure the algorithm is actually MD5 before committing to it:
|
// Make sure the algorithm is actually MD5 before committing to it:
|
||||||
if (!verifyDigestMD5(QByteArrayView(current.second).sliced(7)))
|
if (!verifyDigestMD5(QByteArrayView(current).sliced(7)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
method = DigestMd5;
|
method = DigestMd5;
|
||||||
headerVal = QByteArrayView(current.second).mid(7);
|
headerVal = QByteArrayView(current).mid(7);
|
||||||
} else if (method < Negotiate && str.startsWith("negotiate"_L1, Qt::CaseInsensitive)) {
|
} else if (method < Negotiate && str.startsWith("negotiate"_L1, Qt::CaseInsensitive)) {
|
||||||
#if QT_CONFIG(sspi) || QT_CONFIG(gssapi) // if it's not supported then we shouldn't try to use it
|
#if QT_CONFIG(sspi) || QT_CONFIG(gssapi) // if it's not supported then we shouldn't try to use it
|
||||||
#if QT_CONFIG(gssapi)
|
#if QT_CONFIG(gssapi)
|
||||||
@ -492,7 +491,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
|
|||||||
continue;
|
continue;
|
||||||
#endif
|
#endif
|
||||||
method = Negotiate;
|
method = Negotiate;
|
||||||
headerVal = QByteArrayView(current.second).mid(10);
|
headerVal = QByteArrayView(current).mid(10);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
class QHttpResponseHeader;
|
class QHttpResponseHeader;
|
||||||
|
class QHttpHeaders;
|
||||||
#if QT_CONFIG(sspi) // SSPI
|
#if QT_CONFIG(sspi) // SSPI
|
||||||
class QSSPIWindowsHandles;
|
class QSSPIWindowsHandles;
|
||||||
#elif QT_CONFIG(gssapi) // GSSAPI
|
#elif QT_CONFIG(gssapi) // GSSAPI
|
||||||
@ -80,8 +81,7 @@ public:
|
|||||||
static QHash<QByteArray, QByteArray>
|
static QHash<QByteArray, QByteArray>
|
||||||
parseDigestAuthenticationChallenge(QByteArrayView challenge);
|
parseDigestAuthenticationChallenge(QByteArrayView challenge);
|
||||||
|
|
||||||
void parseHttpResponse(const QList<QPair<QByteArray, QByteArray>> &, bool isProxy,
|
void parseHttpResponse(const QHttpHeaders &headers, bool isProxy, QStringView host);
|
||||||
QStringView host);
|
|
||||||
void updateCredentials();
|
void updateCredentials();
|
||||||
|
|
||||||
static bool isMethodSupported(QByteArrayView method);
|
static bool isMethodSupported(QByteArrayView method);
|
||||||
|
@ -553,7 +553,8 @@ void QHttpSocketEngine::slotSocketReadNotification()
|
|||||||
d->authenticator.detach();
|
d->authenticator.detach();
|
||||||
priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
|
priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
|
||||||
|
|
||||||
priv->parseHttpResponse(d->reply->header(), true, d->proxy.hostName());
|
const auto headers = d->reply->header();
|
||||||
|
priv->parseHttpResponse(headers, true, d->proxy.hostName());
|
||||||
|
|
||||||
if (priv->phase == QAuthenticatorPrivate::Invalid) {
|
if (priv->phase == QAuthenticatorPrivate::Invalid) {
|
||||||
// problem parsing the reply
|
// problem parsing the reply
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <QtCore/qpair.h>
|
#include <QtCore/qpair.h>
|
||||||
#include <QtCore/qurl.h>
|
#include <QtCore/qurl.h>
|
||||||
|
|
||||||
|
#include <QtNetwork/qhttpheaders.h>
|
||||||
#include <QtNetwork/private/qhstsstore_p.h>
|
#include <QtNetwork/private/qhstsstore_p.h>
|
||||||
#include <QtNetwork/private/qhsts_p.h>
|
#include <QtNetwork/private/qhsts_p.h>
|
||||||
|
|
||||||
@ -189,110 +190,108 @@ void tst_QHsts::testPolicyExpiration()
|
|||||||
void tst_QHsts::testSTSHeaderParser()
|
void tst_QHsts::testSTSHeaderParser()
|
||||||
{
|
{
|
||||||
QHstsHeaderParser parser;
|
QHstsHeaderParser parser;
|
||||||
using Header = QPair<QByteArray, QByteArray>;
|
|
||||||
using Headers = QList<Header>;
|
|
||||||
|
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(!parser.expirationDate().isValid());
|
QVERIFY(!parser.expirationDate().isValid());
|
||||||
Headers list;
|
QHttpHeaders headers;
|
||||||
QVERIFY(!parser.parse(list));
|
QVERIFY(!parser.parse(headers));
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(!parser.expirationDate().isValid());
|
QVERIFY(!parser.expirationDate().isValid());
|
||||||
|
|
||||||
list << Header("Strict-Transport-security", "200");
|
headers.append("Strict-Transport-security", "200");
|
||||||
QVERIFY(!parser.parse(list));
|
QVERIFY(!parser.parse(headers));
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(!parser.expirationDate().isValid());
|
QVERIFY(!parser.expirationDate().isValid());
|
||||||
|
|
||||||
// This header is missing REQUIRED max-age directive, so we'll ignore it:
|
// This header is missing REQUIRED max-age directive, so we'll ignore it:
|
||||||
list << Header("Strict-Transport-Security", "includeSubDomains");
|
headers.append("Strict-Transport-Security", "includeSubDomains");
|
||||||
QVERIFY(!parser.parse(list));
|
QVERIFY(!parser.parse(headers));
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(!parser.expirationDate().isValid());
|
QVERIFY(!parser.expirationDate().isValid());
|
||||||
|
|
||||||
list.pop_back();
|
headers.removeAt(headers.size() - 1);
|
||||||
list << Header("Strict-Transport-Security", "includeSubDomains;max-age=1000");
|
headers.append("Strict-Transport-Security", "includeSubDomains;max-age=1000");
|
||||||
QVERIFY(parser.parse(list));
|
QVERIFY(parser.parse(headers));
|
||||||
QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
|
QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
|
||||||
QVERIFY(parser.includeSubDomains());
|
QVERIFY(parser.includeSubDomains());
|
||||||
|
|
||||||
list.pop_back();
|
headers.removeAt(headers.size() - 1);
|
||||||
list << Header("strict-transport-security", "includeSubDomains;max-age=1000");
|
headers.append("strict-transport-security", "includeSubDomains;max-age=1000");
|
||||||
QVERIFY(parser.parse(list));
|
QVERIFY(parser.parse(headers));
|
||||||
QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
|
QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
|
||||||
QVERIFY(parser.includeSubDomains());
|
QVERIFY(parser.includeSubDomains());
|
||||||
|
|
||||||
list.pop_back();
|
headers.removeAt(headers.size() - 1);
|
||||||
// Invalid (includeSubDomains twice):
|
// Invalid (includeSubDomains twice):
|
||||||
list << Header("Strict-Transport-Security", "max-age = 1000 ; includeSubDomains;includeSubDomains");
|
headers.append("Strict-Transport-Security", "max-age = 1000 ; includeSubDomains;includeSubDomains");
|
||||||
QVERIFY(!parser.parse(list));
|
QVERIFY(!parser.parse(headers));
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(!parser.expirationDate().isValid());
|
QVERIFY(!parser.expirationDate().isValid());
|
||||||
|
|
||||||
list.pop_back();
|
headers.removeAt(headers.size() - 1);
|
||||||
// Invalid (weird number of seconds):
|
// Invalid (weird number of seconds):
|
||||||
list << Header("Strict-Transport-Security", "max-age=-1000 ; includeSubDomains");
|
headers.append("Strict-Transport-Security", "max-age=-1000 ; includeSubDomains");
|
||||||
QVERIFY(!parser.parse(list));
|
QVERIFY(!parser.parse(headers));
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(!parser.expirationDate().isValid());
|
QVERIFY(!parser.expirationDate().isValid());
|
||||||
|
|
||||||
list.pop_back();
|
headers.removeAt(headers.size() - 1);
|
||||||
// Note, directives are case-insensitive + we should ignore unknown directive.
|
// Note, directives are case-insensitive + we should ignore unknown directive.
|
||||||
list << Header("Strict-Transport-Security", ";max-age=1000 ;includesubdomains;;"
|
headers.append("Strict-Transport-Security", ";max-age=1000 ;includesubdomains;;"
|
||||||
"nowsomeunknownheader=\"somevaluewithescapes\\;\"");
|
"nowsomeunknownheader=\"somevaluewithescapes\\;\"");
|
||||||
QVERIFY(parser.parse(list));
|
QVERIFY(parser.parse(headers));
|
||||||
QVERIFY(parser.includeSubDomains());
|
QVERIFY(parser.includeSubDomains());
|
||||||
QVERIFY(parser.expirationDate().isValid());
|
QVERIFY(parser.expirationDate().isValid());
|
||||||
|
|
||||||
list.pop_back();
|
headers.removeAt(headers.size() - 1);
|
||||||
// Check that we know how to unescape max-age:
|
// Check that we know how to unescape max-age:
|
||||||
list << Header("Strict-Transport-Security", "max-age=\"1000\"");
|
headers.append("Strict-Transport-Security", "max-age=\"1000\"");
|
||||||
QVERIFY(parser.parse(list));
|
QVERIFY(parser.parse(headers));
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(parser.expirationDate().isValid());
|
QVERIFY(parser.expirationDate().isValid());
|
||||||
|
|
||||||
list.pop_back();
|
headers.removeAt(headers.size() - 1);
|
||||||
// The only STS header, with invalid syntax though, to be ignored:
|
// The only STS header, with invalid syntax though, to be ignored:
|
||||||
list << Header("Strict-Transport-Security", "max-age; max-age=15768000");
|
headers.append("Strict-Transport-Security", "max-age; max-age=15768000");
|
||||||
QVERIFY(!parser.parse(list));
|
QVERIFY(!parser.parse(headers));
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(!parser.expirationDate().isValid());
|
QVERIFY(!parser.expirationDate().isValid());
|
||||||
|
|
||||||
// Now we check that our parse chosses the first valid STS header and ignores
|
// Now we check that our parse chosses the first valid STS header and ignores
|
||||||
// others:
|
// others:
|
||||||
list.clear();
|
headers.clear();
|
||||||
list << Header("Strict-Transport-Security", "includeSubdomains; max-age=\"hehehe\";");
|
headers.append("Strict-Transport-Security", "includeSubdomains; max-age=\"hehehe\";");
|
||||||
list << Header("Strict-Transport-Security", "max-age=10101");
|
headers.append("Strict-Transport-Security", "max-age=10101");
|
||||||
QVERIFY(parser.parse(list));
|
QVERIFY(parser.parse(headers));
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(parser.expirationDate().isValid());
|
QVERIFY(parser.expirationDate().isValid());
|
||||||
|
|
||||||
|
|
||||||
list.clear();
|
headers.clear();
|
||||||
list << Header("Strict-Transport-Security", "max-age=0");
|
headers.append("Strict-Transport-Security", "max-age=0");
|
||||||
QVERIFY(parser.parse(list));
|
QVERIFY(parser.parse(headers));
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(parser.expirationDate() <= QDateTime::currentDateTimeUtc());
|
QVERIFY(parser.expirationDate() <= QDateTime::currentDateTimeUtc());
|
||||||
|
|
||||||
// Parsing is case-insensitive:
|
// Parsing is case-insensitive:
|
||||||
list.pop_back();
|
headers.removeAt(headers.size() - 1);
|
||||||
list << Header("Strict-Transport-Security", "Max-aGE=1000; InclUdesUbdomains");
|
headers.append("Strict-Transport-Security", "Max-aGE=1000; InclUdesUbdomains");
|
||||||
QVERIFY(parser.parse(list));
|
QVERIFY(parser.parse(headers));
|
||||||
QVERIFY(parser.includeSubDomains());
|
QVERIFY(parser.includeSubDomains());
|
||||||
QVERIFY(parser.expirationDate().isValid());
|
QVERIFY(parser.expirationDate().isValid());
|
||||||
|
|
||||||
// Grammar of STS header is quite permissive, let's check we can parse
|
// Grammar of STS header is quite permissive, let's check we can parse
|
||||||
// some weird but valid header:
|
// some weird but valid header:
|
||||||
list.pop_back();
|
headers.removeAt(headers.size() - 1);
|
||||||
list << Header("Strict-Transport-Security", ";;; max-age = 17; ; ; ; ;;; ;;"
|
headers.append("Strict-Transport-Security", ";;; max-age = 17; ; ; ; ;;; ;;"
|
||||||
";;; ; includeSubdomains ;;thisIsUnknownDirective;;;;");
|
";;; ; includeSubdomains ;;thisIsUnknownDirective;;;;");
|
||||||
QVERIFY(parser.parse(list));
|
QVERIFY(parser.parse(headers));
|
||||||
QVERIFY(parser.includeSubDomains());
|
QVERIFY(parser.includeSubDomains());
|
||||||
QVERIFY(parser.expirationDate().isValid());
|
QVERIFY(parser.expirationDate().isValid());
|
||||||
|
|
||||||
list.pop_back();
|
headers.removeAt(headers.size() - 1);
|
||||||
list << Header("Strict-Transport-Security", "max-age=1000; includeSubDomains bogon");
|
headers.append("Strict-Transport-Security", "max-age=1000; includeSubDomains bogon");
|
||||||
QVERIFY(!parser.parse(list));
|
QVERIFY(!parser.parse(headers));
|
||||||
QVERIFY(!parser.includeSubDomains());
|
QVERIFY(!parser.includeSubDomains());
|
||||||
QVERIFY(!parser.expirationDate().isValid());
|
QVERIFY(!parser.expirationDate().isValid());
|
||||||
}
|
}
|
||||||
|
@ -377,16 +377,12 @@ bool Http2Server::verifyProtocolUpgradeRequest()
|
|||||||
bool settingsOk = false;
|
bool settingsOk = false;
|
||||||
|
|
||||||
QHttpNetworkReplyPrivate *firstRequestReader = protocolUpgradeHandler->d_func();
|
QHttpNetworkReplyPrivate *firstRequestReader = protocolUpgradeHandler->d_func();
|
||||||
|
const auto headers = firstRequestReader->headers();
|
||||||
|
|
||||||
// That's how we append them, that's what I expect to find:
|
// That's how we append them, that's what I expect to find:
|
||||||
for (const auto &header : firstRequestReader->headers()) {
|
connectionOk = headers.combinedValue(QHttpHeaders::WellKnownHeader::Connection).contains("Upgrade, HTTP2-Settings");
|
||||||
if (header.first == "Connection")
|
upgradeOk = headers.combinedValue(QHttpHeaders::WellKnownHeader::Upgrade).contains("h2c");
|
||||||
connectionOk = header.second.contains("Upgrade, HTTP2-Settings");
|
settingsOk = headers.contains("HTTP2-Settings");
|
||||||
else if (header.first == "Upgrade")
|
|
||||||
upgradeOk = header.second.contains("h2c");
|
|
||||||
else if (header.first == "HTTP2-Settings")
|
|
||||||
settingsOk = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return connectionOk && upgradeOk && settingsOk;
|
return connectionOk && upgradeOk && settingsOk;
|
||||||
}
|
}
|
||||||
|
@ -731,11 +731,11 @@ private:
|
|||||||
|
|
||||||
void parseContentLength()
|
void parseContentLength()
|
||||||
{
|
{
|
||||||
int index = receivedData.indexOf("Content-Length:");
|
int index = receivedData.indexOf("content-length:");
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
index += sizeof("Content-Length:") - 1;
|
index += sizeof("content-length:") - 1;
|
||||||
const auto end = std::find(receivedData.cbegin() + index, receivedData.cend(), '\r');
|
const auto end = std::find(receivedData.cbegin() + index, receivedData.cend(), '\r');
|
||||||
auto num = receivedData.mid(index, std::distance(receivedData.cbegin() + index, end));
|
auto num = receivedData.mid(index, std::distance(receivedData.cbegin() + index, end));
|
||||||
bool ok;
|
bool ok;
|
||||||
@ -3451,7 +3451,7 @@ void tst_QNetworkReply::connectToIPv6Address()
|
|||||||
if (!QtNetworkSettings::hasIPv6())
|
if (!QtNetworkSettings::hasIPv6())
|
||||||
QSKIP("system doesn't support ipv6!");
|
QSKIP("system doesn't support ipv6!");
|
||||||
|
|
||||||
QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
|
QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\ncontent-length: ");
|
||||||
httpResponse += QByteArray::number(dataToSend.size());
|
httpResponse += QByteArray::number(dataToSend.size());
|
||||||
httpResponse += "\r\n\r\n";
|
httpResponse += "\r\n\r\n";
|
||||||
httpResponse += dataToSend;
|
httpResponse += dataToSend;
|
||||||
@ -3466,7 +3466,7 @@ void tst_QNetworkReply::connectToIPv6Address()
|
|||||||
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
|
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
|
||||||
QByteArray content = reply->readAll();
|
QByteArray content = reply->readAll();
|
||||||
//qDebug() << server.receivedData;
|
//qDebug() << server.receivedData;
|
||||||
QByteArray hostinfo = "\r\nHost: " + hostfield + ':' + QByteArray::number(server.serverPort()) + "\r\n";
|
QByteArray hostinfo = "\r\nhost: " + hostfield + ':' + QByteArray::number(server.serverPort()) + "\r\n";
|
||||||
QVERIFY(server.receivedData.contains(hostinfo));
|
QVERIFY(server.receivedData.contains(hostinfo));
|
||||||
QCOMPARE(content, dataToSend);
|
QCOMPARE(content, dataToSend);
|
||||||
QCOMPARE(reply->url(), request.url());
|
QCOMPARE(reply->url(), request.url());
|
||||||
@ -6275,7 +6275,7 @@ void tst_QNetworkReply::httpProxyCommands()
|
|||||||
|
|
||||||
manager.setProxy(proxy);
|
manager.setProxy(proxy);
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
request.setRawHeader("User-Agent", "QNetworkReplyAutoTest/1.0");
|
request.setRawHeader("user-agent", "QNetworkReplyAutoTest/1.0");
|
||||||
QNetworkReplyPtr reply(manager.get(request));
|
QNetworkReplyPtr reply(manager.get(request));
|
||||||
|
|
||||||
// wait for the finished signal
|
// wait for the finished signal
|
||||||
@ -6292,10 +6292,11 @@ void tst_QNetworkReply::httpProxyCommands()
|
|||||||
QCOMPARE(receivedHeader, expectedCommand);
|
QCOMPARE(receivedHeader, expectedCommand);
|
||||||
|
|
||||||
//QTBUG-17223 - make sure the user agent from the request is sent to proxy server even for CONNECT
|
//QTBUG-17223 - make sure the user agent from the request is sent to proxy server even for CONNECT
|
||||||
int uapos = proxyServer.receivedData.indexOf("User-Agent");
|
const QByteArray cUserAgent = "user-agent: ";
|
||||||
|
int uapos = proxyServer.receivedData.toLower().indexOf(cUserAgent) + cUserAgent.size();
|
||||||
int uaend = proxyServer.receivedData.indexOf("\r\n", uapos);
|
int uaend = proxyServer.receivedData.indexOf("\r\n", uapos);
|
||||||
QByteArray uaheader = proxyServer.receivedData.mid(uapos, uaend - uapos);
|
QByteArray uaheader = proxyServer.receivedData.mid(uapos, uaend - uapos);
|
||||||
QCOMPARE(uaheader, QByteArray("User-Agent: QNetworkReplyAutoTest/1.0"));
|
QCOMPARE(uaheader, QByteArray("QNetworkReplyAutoTest/1.0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProxyChangeHelper : public QObject
|
class ProxyChangeHelper : public QObject
|
||||||
@ -8513,7 +8514,7 @@ void tst_QNetworkReply::httpUserAgent()
|
|||||||
|
|
||||||
QVERIFY(reply->isFinished());
|
QVERIFY(reply->isFinished());
|
||||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||||
QVERIFY(server.receivedData.contains("\r\nUser-Agent: abcDEFghi\r\n"));
|
QVERIFY(server.receivedData.contains("\r\nuser-agent: abcDEFghi\r\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QNetworkReply::synchronousAuthenticationCache()
|
void tst_QNetworkReply::synchronousAuthenticationCache()
|
||||||
@ -8533,7 +8534,7 @@ void tst_QNetworkReply::synchronousAuthenticationCache()
|
|||||||
"Content-Type: text/plain\r\n"
|
"Content-Type: text/plain\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"auth";
|
"auth";
|
||||||
QRegularExpression rx("Authorization: Basic ([^\r\n]*)\r\n");
|
QRegularExpression rx("authorization: Basic ([^\r\n]*)\r\n");
|
||||||
QRegularExpressionMatch match = rx.match(receivedData);
|
QRegularExpressionMatch match = rx.match(receivedData);
|
||||||
if (match.hasMatch()) {
|
if (match.hasMatch()) {
|
||||||
if (QByteArray::fromBase64(match.captured(1).toLatin1()) == "login:password") {
|
if (QByteArray::fromBase64(match.captured(1).toLatin1()) == "login:password") {
|
||||||
@ -9190,7 +9191,7 @@ void tst_QNetworkReply::ioHttpCookiesDuringRedirect()
|
|||||||
manager.setRedirectPolicy(oldRedirectPolicy);
|
manager.setRedirectPolicy(oldRedirectPolicy);
|
||||||
|
|
||||||
QVERIFY(waitForFinish(reply) == Success);
|
QVERIFY(waitForFinish(reply) == Success);
|
||||||
QVERIFY(target.receivedData.contains("\r\nCookie: hello=world\r\n"));
|
QVERIFY(target.receivedData.contains("\r\ncookie: hello=world\r\n"));
|
||||||
QVERIFY(validateRedirectedResponseHeaders(reply));
|
QVERIFY(validateRedirectedResponseHeaders(reply));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10081,7 +10082,7 @@ void tst_QNetworkReply::contentEncoding()
|
|||||||
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
|
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
|
||||||
if (!decompress) {
|
if (!decompress) {
|
||||||
// This disables decompression of the received content:
|
// This disables decompression of the received content:
|
||||||
request.setRawHeader("Accept-Encoding", QLatin1String("%1").arg(encoding).toLatin1());
|
request.setRawHeader("accept-encoding", QLatin1String("%1").arg(encoding).toLatin1());
|
||||||
// This disables the zerocopy optimization
|
// This disables the zerocopy optimization
|
||||||
request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 0);
|
request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 0);
|
||||||
}
|
}
|
||||||
@ -10093,7 +10094,7 @@ void tst_QNetworkReply::contentEncoding()
|
|||||||
{
|
{
|
||||||
// Check that we included the content encoding method in our Accept-Encoding header
|
// Check that we included the content encoding method in our Accept-Encoding header
|
||||||
const QByteArray &receivedData = server.receivedData;
|
const QByteArray &receivedData = server.receivedData;
|
||||||
int start = receivedData.indexOf("Accept-Encoding");
|
int start = receivedData.indexOf("accept-encoding");
|
||||||
QVERIFY(start != -1);
|
QVERIFY(start != -1);
|
||||||
int end = receivedData.indexOf("\r\n", start);
|
int end = receivedData.indexOf("\r\n", start);
|
||||||
QVERIFY(end != -1);
|
QVERIFY(end != -1);
|
||||||
|
@ -67,8 +67,9 @@ void HttpTestServer::handleDataAvailable()
|
|||||||
Q_ASSERT(m_handler);
|
Q_ASSERT(m_handler);
|
||||||
Q_ASSERT(state == State::AllDone);
|
Q_ASSERT(state == State::AllDone);
|
||||||
|
|
||||||
if (m_request.headers.contains("Host")) {
|
if (auto values = m_request.headers.values(
|
||||||
const auto parts = m_request.headers["Host"].split(':');
|
QHttpHeaders::WellKnownHeader::Host); !values.empty()) {
|
||||||
|
const auto parts = values.first().split(':');
|
||||||
m_request.url.setHost(parts.at(0));
|
m_request.url.setHost(parts.at(0));
|
||||||
if (parts.size() == 2)
|
if (parts.size() == 2)
|
||||||
m_request.url.setPort(parts.at(1).toUInt());
|
m_request.url.setPort(parts.at(1).toUInt());
|
||||||
@ -83,8 +84,9 @@ void HttpTestServer::handleDataAvailable()
|
|||||||
responseMessage += QByteArray::number(response.status);
|
responseMessage += QByteArray::number(response.status);
|
||||||
responseMessage += CRLF;
|
responseMessage += CRLF;
|
||||||
// Insert headers if any
|
// Insert headers if any
|
||||||
for (const auto &[name,value] : response.headers.asKeyValueRange()) {
|
for (const auto &[name,value] : response.headers.toListOfPairs()) {
|
||||||
responseMessage += name;
|
responseMessage += name;
|
||||||
|
responseMessage += ": ";
|
||||||
responseMessage += value;
|
responseMessage += value;
|
||||||
responseMessage += CRLF;
|
responseMessage += CRLF;
|
||||||
}
|
}
|
||||||
@ -236,7 +238,7 @@ bool HttpTestServer::readHeaders(QTcpSocket *socket)
|
|||||||
|
|
||||||
QByteArray key = fragment.sliced(0, index).trimmed();
|
QByteArray key = fragment.sliced(0, index).trimmed();
|
||||||
QByteArray value = fragment.sliced(index + 1).trimmed();
|
QByteArray value = fragment.sliced(index + 1).trimmed();
|
||||||
m_request.headers.insert(std::move(key), std::move(value));
|
m_request.headers.append(key, value);
|
||||||
fragment.clear();
|
fragment.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,9 +249,10 @@ bool HttpTestServer::readHeaders(QTcpSocket *socket)
|
|||||||
bool HttpTestServer::readBody(QTcpSocket *socket)
|
bool HttpTestServer::readBody(QTcpSocket *socket)
|
||||||
{
|
{
|
||||||
qint64 bytesLeft = 0;
|
qint64 bytesLeft = 0;
|
||||||
if (m_request.headers.contains("Content-Length")) {
|
if (auto values = m_request.headers.values(
|
||||||
|
QHttpHeaders::WellKnownHeader::ContentLength); !values.empty()) {
|
||||||
bool conversionResult;
|
bool conversionResult;
|
||||||
bytesLeft = m_request.headers["Content-Length"].toInt(&conversionResult);
|
bytesLeft = values.first().toInt(&conversionResult);
|
||||||
if (!conversionResult)
|
if (!conversionResult)
|
||||||
return false;
|
return false;
|
||||||
fragment.resize(bytesLeft);
|
fragment.resize(bytesLeft);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#define QRESTACCESSSMANAGER_HTTPTESTSERVER_P_H
|
#define QRESTACCESSSMANAGER_HTTPTESTSERVER_P_H
|
||||||
|
|
||||||
#include <QtNetwork/qtcpserver.h>
|
#include <QtNetwork/qtcpserver.h>
|
||||||
|
#include <QtNetwork/qhttpheaders.h>
|
||||||
|
|
||||||
#include <QtCore/qmap.h>
|
#include <QtCore/qmap.h>
|
||||||
#include <QtCore/qurl.h>
|
#include <QtCore/qurl.h>
|
||||||
@ -18,7 +19,7 @@ struct HttpData {
|
|||||||
QByteArray method;
|
QByteArray method;
|
||||||
quint16 port = 0;
|
quint16 port = 0;
|
||||||
QPair<quint8, quint8> version;
|
QPair<quint8, quint8> version;
|
||||||
QMap<QByteArray, QByteArray> headers;
|
QHttpHeaders headers;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResponseControl
|
struct ResponseControl
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
using Header = QHttpHeaders::WellKnownHeader;
|
||||||
|
|
||||||
class tst_QRestAccessManager : public QObject
|
class tst_QRestAccessManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -533,9 +535,9 @@ void tst_QRestAccessManager::authentication()
|
|||||||
|
|
||||||
HttpData serverSideRequest;
|
HttpData serverSideRequest;
|
||||||
server.setHandler([&](HttpData request, HttpData &response, ResponseControl&) {
|
server.setHandler([&](HttpData request, HttpData &response, ResponseControl&) {
|
||||||
if (!request.headers.contains("Authorization"_ba)) {
|
if (!request.headers.contains(Header::Authorization)) {
|
||||||
response.status = 401;
|
response.status = 401;
|
||||||
response.headers.insert("WWW-Authenticate: "_ba, "Basic realm=\"secret_place\""_ba);
|
response.headers.append(Header::WWWAuthenticate, "Basic realm=\"secret_place\""_ba);
|
||||||
} else {
|
} else {
|
||||||
response.status = 200;
|
response.status = 200;
|
||||||
}
|
}
|
||||||
@ -557,7 +559,9 @@ void tst_QRestAccessManager::authentication()
|
|||||||
QTRY_VERIFY(replyFromServer);
|
QTRY_VERIFY(replyFromServer);
|
||||||
// Server and QRestAM/QNAM exchange req/res twice, but finished() should be emitted just once
|
// Server and QRestAM/QNAM exchange req/res twice, but finished() should be emitted just once
|
||||||
QCOMPARE(finishedCount, 1);
|
QCOMPARE(finishedCount, 1);
|
||||||
QCOMPARE(serverSideRequest.headers["Authorization"_ba], "Basic YV91c2VyOmFfcGFzc3dvcmQ="_ba);
|
const auto resultHeaders = serverSideRequest.headers.values(Header::Authorization);
|
||||||
|
QVERIFY(!resultHeaders.empty());
|
||||||
|
QCOMPARE(resultHeaders.first(), "Basic YV91c2VyOmFfcGFzc3dvcmQ="_ba);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QRestAccessManager::userInfo()
|
void tst_QRestAccessManager::userInfo()
|
||||||
@ -576,9 +580,9 @@ void tst_QRestAccessManager::userInfo()
|
|||||||
|
|
||||||
HttpData serverSideRequest;
|
HttpData serverSideRequest;
|
||||||
server.setHandler([&](HttpData request, HttpData& response, ResponseControl&) {
|
server.setHandler([&](HttpData request, HttpData& response, ResponseControl&) {
|
||||||
if (!request.headers.contains("Authorization"_ba)) {
|
if (!request.headers.contains(Header::Authorization)) {
|
||||||
response.status = 401;
|
response.status = 401;
|
||||||
response.headers.insert("WWW-Authenticate: "_ba, "Basic realm=\"secret_place\""_ba);
|
response.headers.append(Header::WWWAuthenticate,"Basic realm=\"secret_place\""_ba);
|
||||||
} else {
|
} else {
|
||||||
response.status = 200;
|
response.status = 200;
|
||||||
}
|
}
|
||||||
@ -589,7 +593,9 @@ void tst_QRestAccessManager::userInfo()
|
|||||||
QTRY_VERIFY(reply.get()->isFinished());
|
QTRY_VERIFY(reply.get()->isFinished());
|
||||||
QVERIFY(reply.get()->isSuccess());
|
QVERIFY(reply.get()->isSuccess());
|
||||||
QCOMPARE(reply.get()->httpStatus(), 200);
|
QCOMPARE(reply.get()->httpStatus(), 200);
|
||||||
QCOMPARE(serverSideRequest.headers["Authorization"_ba], "Basic YV91c2VyOmFfcGFzc3dvcmQ="_ba);
|
const auto resultHeaders = serverSideRequest.headers.values(Header::Authorization);
|
||||||
|
QVERIFY(!resultHeaders.empty());
|
||||||
|
QCOMPARE(resultHeaders.first(), "Basic YV91c2VyOmFfcGFzc3dvcmQ="_ba);
|
||||||
|
|
||||||
// Verify that debug output does not contain password
|
// Verify that debug output does not contain password
|
||||||
QString debugOutput;
|
QString debugOutput;
|
||||||
@ -864,33 +870,38 @@ void tst_QRestAccessManager::text()
|
|||||||
// QString from text() should match with the original (UTF-16) QString.
|
// QString from text() should match with the original (UTF-16) QString.
|
||||||
|
|
||||||
// Successful UTF-8
|
// Successful UTF-8
|
||||||
serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-8"_ba);
|
serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-8"_ba);
|
||||||
serverSideResponse.body = encUTF8(sourceString);
|
serverSideResponse.body = encUTF8(sourceString);
|
||||||
VERIFY_TEXT_REPLY_OK;
|
VERIFY_TEXT_REPLY_OK;
|
||||||
|
|
||||||
// Successful UTF-16
|
// Successful UTF-16
|
||||||
serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-16"_ba);
|
serverSideResponse.headers.removeAll(Header::ContentType);
|
||||||
|
serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-16"_ba);
|
||||||
serverSideResponse.body = encUTF16(sourceString);
|
serverSideResponse.body = encUTF16(sourceString);
|
||||||
VERIFY_TEXT_REPLY_OK;
|
VERIFY_TEXT_REPLY_OK;
|
||||||
|
|
||||||
// Successful UTF-16, parameter case insensitivity
|
// Successful UTF-16, parameter case insensitivity
|
||||||
serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; chARset=uTf-16"_ba);
|
serverSideResponse.headers.removeAll(Header::ContentType);
|
||||||
|
serverSideResponse.headers.append(Header::ContentType, "text/plain; chARset=uTf-16"_ba);
|
||||||
serverSideResponse.body = encUTF16(sourceString);
|
serverSideResponse.body = encUTF16(sourceString);
|
||||||
VERIFY_TEXT_REPLY_OK;
|
VERIFY_TEXT_REPLY_OK;
|
||||||
|
|
||||||
// Successful UTF-32
|
// Successful UTF-32
|
||||||
serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-32"_ba);
|
serverSideResponse.headers.removeAll(Header::ContentType);
|
||||||
|
serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-32"_ba);
|
||||||
serverSideResponse.body = encUTF32(sourceString);
|
serverSideResponse.body = encUTF32(sourceString);
|
||||||
VERIFY_TEXT_REPLY_OK;
|
VERIFY_TEXT_REPLY_OK;
|
||||||
|
|
||||||
// Successful UTF-32 with spec-wise allowed extra content in the Content-Type header value
|
// Successful UTF-32 with spec-wise allowed extra content in the Content-Type header value
|
||||||
serverSideResponse.headers.insert("Content-Type:"_ba,
|
serverSideResponse.headers.removeAll(Header::ContentType);
|
||||||
|
serverSideResponse.headers.append(Header::ContentType,
|
||||||
"text/plain; charset = \"UTF-32\";extraparameter=bar"_ba);
|
"text/plain; charset = \"UTF-32\";extraparameter=bar"_ba);
|
||||||
serverSideResponse.body = encUTF32(sourceString);
|
serverSideResponse.body = encUTF32(sourceString);
|
||||||
VERIFY_TEXT_REPLY_OK;
|
VERIFY_TEXT_REPLY_OK;
|
||||||
|
|
||||||
// Unsuccessful UTF-32, wrong encoding indicated (indicated charset UTF-32 but data is UTF-8)
|
// Unsuccessful UTF-32, wrong encoding indicated (indicated charset UTF-32 but data is UTF-8)
|
||||||
serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-32"_ba);
|
serverSideResponse.headers.removeAll(Header::ContentType);
|
||||||
|
serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-32"_ba);
|
||||||
serverSideResponse.body = encUTF8(sourceString);
|
serverSideResponse.body = encUTF8(sourceString);
|
||||||
manager.get(request, this, [&](QRestReply *reply) { replyFromServer = reply; });
|
manager.get(request, this, [&](QRestReply *reply) { replyFromServer = reply; });
|
||||||
QTRY_VERIFY(replyFromServer);
|
QTRY_VERIFY(replyFromServer);
|
||||||
@ -900,12 +911,14 @@ void tst_QRestAccessManager::text()
|
|||||||
replyFromServer = nullptr;
|
replyFromServer = nullptr;
|
||||||
|
|
||||||
// Unsupported encoding
|
// Unsupported encoding
|
||||||
serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=foo"_ba);
|
serverSideResponse.headers.removeAll(Header::ContentType);
|
||||||
|
serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=foo"_ba);
|
||||||
serverSideResponse.body = encUTF8(sourceString);
|
serverSideResponse.body = encUTF8(sourceString);
|
||||||
VERIFY_TEXT_REPLY_ERROR("text(): Charset \"foo\" is not supported")
|
VERIFY_TEXT_REPLY_ERROR("text(): Charset \"foo\" is not supported")
|
||||||
|
|
||||||
// Broken UTF-8
|
// Broken UTF-8
|
||||||
serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-8"_ba);
|
serverSideResponse.headers.removeAll(Header::ContentType);
|
||||||
|
serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-8"_ba);
|
||||||
serverSideResponse.body = "\xF0\x28\x8C\x28\xA0\xB0\xC0\xD0"; // invalid characters
|
serverSideResponse.body = "\xF0\x28\x8C\x28\xA0\xB0\xC0\xD0"; // invalid characters
|
||||||
VERIFY_TEXT_REPLY_ERROR("text() Decoding error occurred");
|
VERIFY_TEXT_REPLY_ERROR("text() Decoding error occurred");
|
||||||
}
|
}
|
||||||
@ -925,7 +938,8 @@ void tst_QRestAccessManager::textStreaming()
|
|||||||
ResponseControl *responseControl = nullptr;
|
ResponseControl *responseControl = nullptr;
|
||||||
|
|
||||||
HttpData serverSideResponse; // The response data the server responds with
|
HttpData serverSideResponse; // The response data the server responds with
|
||||||
serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-8"_ba);
|
serverSideResponse.headers.removeAll(Header::ContentType);
|
||||||
|
serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-8"_ba);
|
||||||
serverSideResponse.body = encUTF8(expectedData);
|
serverSideResponse.body = encUTF8(expectedData);
|
||||||
serverSideResponse.status = 200;
|
serverSideResponse.status = 200;
|
||||||
|
|
||||||
@ -983,7 +997,7 @@ void tst_QRestAccessManager::download()
|
|||||||
ResponseControl *responseControl = nullptr;
|
ResponseControl *responseControl = nullptr;
|
||||||
serverSideResponse.status = 200;
|
serverSideResponse.status = 200;
|
||||||
// Set content-length header so that underlying QNAM is able to report bytesTotal correctly
|
// Set content-length header so that underlying QNAM is able to report bytesTotal correctly
|
||||||
serverSideResponse.headers.insert("Content-Length: ",
|
serverSideResponse.headers.append(Header::ContentType,
|
||||||
QString::number(expectedData.size()).toLatin1());
|
QString::number(expectedData.size()).toLatin1());
|
||||||
server.setHandler([&](HttpData, HttpData &response, ResponseControl &control) {
|
server.setHandler([&](HttpData, HttpData &response, ResponseControl &control) {
|
||||||
response = serverSideResponse;
|
response = serverSideResponse;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <QTest>
|
#include <QTest>
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QtNetwork/QAuthenticator>
|
#include <QtNetwork/QAuthenticator>
|
||||||
|
#include <QtNetwork/QHttpHeaders>
|
||||||
|
|
||||||
#include <private/qauthenticator_p.h>
|
#include <private/qauthenticator_p.h>
|
||||||
|
|
||||||
@ -60,8 +61,8 @@ void tst_QAuthenticator::basicAuth()
|
|||||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth);
|
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth);
|
||||||
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
||||||
|
|
||||||
QList<QPair<QByteArray, QByteArray> > headers;
|
QHttpHeaders headers;
|
||||||
headers << qMakePair(QByteArray("WWW-Authenticate"), "Basic " + data.toUtf8());
|
headers.append(QByteArray("WWW-Authenticate"), "Basic " + data.toUtf8());
|
||||||
priv->parseHttpResponse(headers, /*isProxy = */ false, {});
|
priv->parseHttpResponse(headers, /*isProxy = */ false, {});
|
||||||
|
|
||||||
QCOMPARE(auth.realm(), realm);
|
QCOMPARE(auth.realm(), realm);
|
||||||
@ -103,13 +104,13 @@ void tst_QAuthenticator::ntlmAuth()
|
|||||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth);
|
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth);
|
||||||
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
||||||
|
|
||||||
QList<QPair<QByteArray, QByteArray> > headers;
|
QHttpHeaders headers;
|
||||||
|
|
||||||
// NTLM phase 1: negotiate
|
// NTLM phase 1: negotiate
|
||||||
// This phase of NTLM contains no information, other than what we're willing to negotiate
|
// This phase of NTLM contains no information, other than what we're willing to negotiate
|
||||||
// Current implementation uses flags:
|
// Current implementation uses flags:
|
||||||
// NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET
|
// NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET
|
||||||
headers << qMakePair(QByteArrayLiteral("WWW-Authenticate"), QByteArrayLiteral("NTLM"));
|
headers.append(QByteArrayLiteral("WWW-Authenticate"), QByteArrayLiteral("NTLM"));
|
||||||
priv->parseHttpResponse(headers, /*isProxy = */ false, {});
|
priv->parseHttpResponse(headers, /*isProxy = */ false, {});
|
||||||
if (sso)
|
if (sso)
|
||||||
QVERIFY(priv->calculateResponse("GET", "/", u"").startsWith("NTLM "));
|
QVERIFY(priv->calculateResponse("GET", "/", u"").startsWith("NTLM "));
|
||||||
@ -118,7 +119,7 @@ void tst_QAuthenticator::ntlmAuth()
|
|||||||
|
|
||||||
// NTLM phase 2: challenge
|
// NTLM phase 2: challenge
|
||||||
headers.clear();
|
headers.clear();
|
||||||
headers << qMakePair(QByteArray("WWW-Authenticate"), "NTLM " + data.toUtf8());
|
headers.append(QByteArray("WWW-Authenticate"), "NTLM " + data.toUtf8());
|
||||||
priv->parseHttpResponse(headers, /*isProxy = */ false, {});
|
priv->parseHttpResponse(headers, /*isProxy = */ false, {});
|
||||||
|
|
||||||
QEXPECT_FAIL("with-realm", "NTLM authentication code doesn't extract the realm", Continue);
|
QEXPECT_FAIL("with-realm", "NTLM authentication code doesn't extract the realm", Continue);
|
||||||
@ -143,10 +144,10 @@ void tst_QAuthenticator::sha256AndMd5Digest()
|
|||||||
QVERIFY(priv->isMethodSupported("digest")); // sanity check
|
QVERIFY(priv->isMethodSupported("digest")); // sanity check
|
||||||
|
|
||||||
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
||||||
QList<QPair<QByteArray, QByteArray>> headers;
|
QHttpHeaders headers;
|
||||||
// Put sha256 first, so that its parsed first...
|
// Put sha256 first, so that its parsed first...
|
||||||
headers.emplace_back("WWW-Authenticate", sha256);
|
headers.append("WWW-Authenticate", sha256);
|
||||||
headers.emplace_back("WWW-Authenticate", md5);
|
headers.append("WWW-Authenticate", md5);
|
||||||
priv->parseHttpResponse(headers, false, QString());
|
priv->parseHttpResponse(headers, false, QString());
|
||||||
|
|
||||||
QByteArray response = priv->calculateResponse("GET", "/index", {});
|
QByteArray response = priv->calculateResponse("GET", "/index", {});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user