summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@qt.io>2017-09-02 10:27:08 +0200
committerFrederik Gladhorn <frederik.gladhorn@qt.io>2017-09-02 10:27:09 +0200
commit9831118378c5d489f76089011277a1b7234a8d68 (patch)
treea67e82fde179fa727172fff9098758d60912189d /src/network
parentb5d471d0c23128528a0aa33ed5172bb1bab05bb1 (diff)
parent4fa90c1757c15425d5e0df1a4f5dbc7e77c265f8 (diff)
Merge dev into 5.10
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/http2/http2frames.cpp6
-rw-r--r--src/network/access/http2/http2frames_p.h2
-rw-r--r--src/network/access/http2/http2protocol.cpp75
-rw-r--r--src/network/access/http2/http2protocol_p.h6
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp38
-rw-r--r--src/network/access/qhttp2protocolhandler_p.h4
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp35
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h2
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp77
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h1
-rw-r--r--src/network/access/qhttpnetworkheader_p.h2
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp14
-rw-r--r--src/network/configure.json3
-rw-r--r--src/network/kernel/kernel.pri2
-rw-r--r--src/network/kernel/qhostinfo_unix.cpp3
-rw-r--r--src/network/kernel/qnetworkinterface_unix.cpp5
-rw-r--r--src/network/kernel/qnetworkproxy_libproxy.cpp143
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp17
-rw-r--r--src/network/ssl/qsslkey_openssl.cpp3
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp7
20 files changed, 367 insertions, 78 deletions
diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp
index 5a684c2f41..e695b4dd9e 100644
--- a/src/network/access/http2/http2frames.cpp
+++ b/src/network/access/http2/http2frames.cpp
@@ -361,6 +361,12 @@ FrameWriter::FrameWriter(FrameType type, FrameFlags flags, quint32 streamID)
start(type, flags, streamID);
}
+void FrameWriter::setOutboundFrame(Frame &&newFrame)
+{
+ frame = std::move(newFrame);
+ updatePayloadSize();
+}
+
void FrameWriter::start(FrameType type, FrameFlags flags, quint32 streamID)
{
auto &buffer = frame.buffer;
diff --git a/src/network/access/http2/http2frames_p.h b/src/network/access/http2/http2frames_p.h
index e5f6d46c67..4bdc775806 100644
--- a/src/network/access/http2/http2frames_p.h
+++ b/src/network/access/http2/http2frames_p.h
@@ -129,6 +129,8 @@ public:
return frame;
}
+ void setOutboundFrame(Frame &&newFrame);
+
// Frame 'builders':
void start(FrameType type, FrameFlags flags, quint32 streamID);
void setPayloadSize(quint32 size);
diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp
index 7f788a6f42..54811aeab0 100644
--- a/src/network/access/http2/http2protocol.cpp
+++ b/src/network/access/http2/http2protocol.cpp
@@ -37,9 +37,14 @@
**
****************************************************************************/
-#include <QtCore/qstring.h>
-
#include "http2protocol_p.h"
+#include "http2frames_p.h"
+
+#include "private/qhttpnetworkrequest_p.h"
+#include "private/qhttpnetworkreply_p.h"
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qstring.h>
QT_BEGIN_NAMESPACE
@@ -57,6 +62,38 @@ const char Http2clientPreface[clientPrefaceLength] =
0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a,
0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a};
+QByteArray default_SETTINGS_to_Base64()
+{
+ Frame frame(default_SETTINGS_frame());
+ // SETTINGS frame's payload consists of pairs:
+ // 2-byte-identifier | 4-byte-value == multiple of 6.
+ Q_ASSERT(frame.payloadSize() && !(frame.payloadSize() % 6));
+ const char *src = reinterpret_cast<const char *>(frame.dataBegin());
+ const QByteArray wrapper(QByteArray::fromRawData(src, int(frame.dataSize())));
+ // 3.2.1
+ // The content of the HTTP2-Settings header field is the payload
+ // of a SETTINGS frame (Section 6.5), encoded as a base64url string
+ // (that is, the URL- and filename-safe Base64 encoding described in
+ // Section 5 of [RFC4648], with any trailing '=' characters omitted).
+ return wrapper.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
+}
+
+void prepare_for_protocol_upgrade(QHttpNetworkRequest &request)
+{
+ // RFC 2616, 14.10
+ // RFC 7540, 3.2
+ QByteArray value(request.headerField("Connection"));
+ // We _append_ 'Upgrade':
+ if (value.size())
+ value += ", ";
+
+ value += "Upgrade, HTTP2-Settings";
+ request.setHeaderField("Connection", value);
+ // This we just (re)write.
+ request.setHeaderField("Upgrade", "h2c");
+ // This we just (re)write.
+ request.setHeaderField("HTTP2-Settings", default_SETTINGS_to_Base64());
+}
void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
QString &errorMessage)
@@ -151,6 +188,40 @@ QNetworkReply::NetworkError qt_error(quint32 errorCode)
return error;
}
+bool is_PUSH_PROMISE_enabled()
+{
+ bool ok = false;
+ const int env = qEnvironmentVariableIntValue("QT_HTTP2_ENABLE_PUSH_PROMISE", &ok);
+ return ok && env;
+}
+
+bool is_protocol_upgraded(const QHttpNetworkReply &reply)
+{
+ if (reply.statusCode() == 101) {
+ // Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
+ const auto &header = reply.header();
+ for (const QPair<QByteArray, QByteArray> &field : header) {
+ if (field.first.toLower() == "upgrade" && field.second.toLower() == "h2c")
+ return true;
+ }
+ }
+
+ return false;
}
+Frame default_SETTINGS_frame()
+{
+ // 6.5 SETTINGS
+ FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID);
+ // MAX frame size (16 kb), disable/enable PUSH_PROMISE
+ builder.append(Settings::MAX_FRAME_SIZE_ID);
+ builder.append(quint32(maxFrameSize));
+ builder.append(Settings::ENABLE_PUSH_ID);
+ builder.append(quint32(is_PUSH_PROMISE_enabled()));
+
+ return builder.outboundFrame();
+}
+
+} // namespace Http2
+
QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h
index 5d730404bb..b26ff0e9f4 100644
--- a/src/network/access/http2/http2protocol_p.h
+++ b/src/network/access/http2/http2protocol_p.h
@@ -59,6 +59,8 @@
QT_BEGIN_NAMESPACE
+class QHttpNetworkRequest;
+class QHttpNetworkReply;
class QString;
namespace Http2
@@ -132,6 +134,7 @@ enum Http2PredefinedParameters
const quint32 lastValidStreamID((quint32(1) << 31) - 1); // HTTP/2, 5.1.1
extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength];
+void prepare_for_protocol_upgrade(QHttpNetworkRequest &request);
enum class FrameStatus
{
@@ -169,6 +172,9 @@ enum Http2Error
void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &errorString);
QString qt_error_string(quint32 errorCode);
QNetworkReply::NetworkError qt_error(quint32 errorCode);
+bool is_PUSH_PROMISE_enabled();
+bool is_protocol_upgraded(const QHttpNetworkReply &reply);
+struct Frame default_SETTINGS_frame();
}
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index 44ab637da8..5032f6017f 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -170,10 +170,22 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan
decoder(HPack::FieldLookupTable::DefaultSize),
encoder(HPack::FieldLookupTable::DefaultSize, true)
{
+ Q_ASSERT(channel);
continuedFrames.reserve(20);
- bool ok = false;
- const int env = qEnvironmentVariableIntValue("QT_HTTP2_ENABLE_PUSH_PROMISE", &ok);
- pushPromiseEnabled = ok && env;
+ pushPromiseEnabled = is_PUSH_PROMISE_enabled();
+
+ if (!channel->ssl) {
+ // We upgraded from HTTP/1.1 to HTTP/2. channel->request was already sent
+ // as HTTP/1.1 request. The response with status code 101 triggered
+ // protocol switch and now we are waiting for the real response, sent
+ // as HTTP/2 frames.
+ Q_ASSERT(channel->reply);
+ const quint32 initialStreamID = createNewStream(HttpMessagePair(channel->request, channel->reply),
+ true /* uploaded by HTTP/1.1 */);
+ Q_ASSERT(initialStreamID == 1);
+ Stream &stream = activeStreams[initialStreamID];
+ stream.state = Stream::halfClosedLocal;
+ }
}
void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
@@ -356,12 +368,8 @@ bool QHttp2ProtocolHandler::sendClientPreface()
return false;
// 6.5 SETTINGS
- frameWriter.start(FrameType::SETTINGS, FrameFlag::EMPTY, Http2::connectionStreamID);
- // MAX frame size (16 kb), enable/disable PUSH
- frameWriter.append(Settings::MAX_FRAME_SIZE_ID);
- frameWriter.append(quint32(Http2::maxFrameSize));
- frameWriter.append(Settings::ENABLE_PUSH_ID);
- frameWriter.append(quint32(pushPromiseEnabled));
+ frameWriter.setOutboundFrame(default_SETTINGS_frame());
+ Q_ASSERT(frameWriter.outboundFrame().payloadSize());
if (!frameWriter.write(*m_socket))
return false;
@@ -1157,7 +1165,7 @@ void QHttp2ProtocolHandler::finishStreamWithError(Stream &stream, QNetworkReply:
<< "finished with error:" << message;
}
-quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message)
+quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, bool uploadDone)
{
const qint32 newStreamID = allocateStreamID();
if (!newStreamID)
@@ -1178,10 +1186,12 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message)
streamInitialSendWindowSize,
streamInitialRecvWindowSize);
- if (auto src = newStream.data()) {
- connect(src, SIGNAL(readyRead()), this,
- SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
- src->setProperty("HTTP2StreamID", newStreamID);
+ if (!uploadDone) {
+ if (auto src = newStream.data()) {
+ connect(src, SIGNAL(readyRead()), this,
+ SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
+ src->setProperty("HTTP2StreamID", newStreamID);
+ }
}
activeStreams.insert(newStreamID, newStream);
diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h
index df0cf6a288..82eea21818 100644
--- a/src/network/access/qhttp2protocolhandler_p.h
+++ b/src/network/access/qhttp2protocolhandler_p.h
@@ -98,7 +98,7 @@ private:
using Stream = Http2::Stream;
void _q_readyRead() override;
- void _q_receiveReply() override;
+ Q_INVOKABLE void _q_receiveReply() override;
Q_INVOKABLE bool sendRequest() override;
bool sendClientPreface();
@@ -136,7 +136,7 @@ private:
const QString &message);
// Stream's lifecycle management:
- quint32 createNewStream(const HttpMessagePair &message);
+ quint32 createNewStream(const HttpMessagePair &message, bool uploadDone = false);
void addToSuspended(Stream &stream);
void markAsReset(quint32 streamID);
quint32 popStreamToResume();
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index ae30d3a8cf..0b474ba116 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -627,7 +627,8 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
if (request.isPreConnect())
preConnectRequests++;
- if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP) {
+ if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP
+ || (!encrypt && connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 && !channels[0].switchedToHttp2)) {
switch (request.priority()) {
case QHttpNetworkRequest::HighPriority:
highPriorityQueue.prepend(pair);
@@ -638,7 +639,7 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
break;
}
}
- else { // SPDY, HTTP/2
+ else { // SPDY, HTTP/2 ('h2' mode)
if (!pair.second->d_func()->requestIsPrepared)
prepareRequest(pair);
channels[0].spdyRequestsToSend.insertMulti(request.priority(), pair);
@@ -672,6 +673,25 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
return reply;
}
+void QHttpNetworkConnectionPrivate::fillHttp2Queue()
+{
+ for (auto &pair : highPriorityQueue) {
+ if (!pair.second->d_func()->requestIsPrepared)
+ prepareRequest(pair);
+ channels[0].spdyRequestsToSend.insertMulti(QHttpNetworkRequest::HighPriority, pair);
+ }
+
+ highPriorityQueue.clear();
+
+ for (auto &pair : lowPriorityQueue) {
+ if (!pair.second->d_func()->requestIsPrepared)
+ prepareRequest(pair);
+ channels[0].spdyRequestsToSend.insertMulti(pair.first.priority(), pair);
+ }
+
+ lowPriorityQueue.clear();
+}
+
void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
{
Q_Q(QHttpNetworkConnection);
@@ -1047,8 +1067,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
}
case QHttpNetworkConnection::ConnectionTypeHTTP2:
case QHttpNetworkConnection::ConnectionTypeSPDY: {
-
- if (channels[0].spdyRequestsToSend.isEmpty())
+ if (channels[0].spdyRequestsToSend.isEmpty() && channels[0].switchedToHttp2)
return;
if (networkLayerState == IPv4)
@@ -1057,7 +1076,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
channels[0].networkLayerPreference = QAbstractSocket::IPv6Protocol;
channels[0].ensureConnection();
if (channels[0].socket && channels[0].socket->state() == QAbstractSocket::ConnectedState
- && !channels[0].pendingEncrypt)
+ && !channels[0].pendingEncrypt && channels[0].spdyRequestsToSend.size())
channels[0].sendRequest();
break;
}
@@ -1355,6 +1374,12 @@ QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest
return d->queueRequest(request);
}
+void QHttpNetworkConnection::fillHttp2Queue()
+{
+ Q_D(QHttpNetworkConnection);
+ d->fillHttp2Queue();
+}
+
bool QHttpNetworkConnection::isSsl() const
{
Q_D(const QHttpNetworkConnection);
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 3dd9bde9bd..f01a2318a5 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -122,6 +122,7 @@ public:
//add a new HTTP request through this connection
QHttpNetworkReply* sendRequest(const QHttpNetworkRequest &request);
+ void fillHttp2Queue();
#ifndef QT_NO_NETWORKPROXY
//set the proxy for this connection
@@ -208,6 +209,7 @@ public:
QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke
+ void fillHttp2Queue();
bool dequeueRequest(QAbstractSocket *socket);
void prepareRequest(HttpMessagePair &request);
void updateChannel(int i, const HttpMessagePair &messagePair);
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 6b2018ef86..b1ae29427e 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -63,6 +63,20 @@
QT_BEGIN_NAMESPACE
+namespace
+{
+
+class ProtocolHandlerDeleter : public QObject
+{
+public:
+ explicit ProtocolHandlerDeleter(QAbstractProtocolHandler *h) : handler(h) {}
+ ~ProtocolHandlerDeleter() { delete handler; }
+private:
+ QAbstractProtocolHandler *handler = nullptr;
+};
+
+}
+
// TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
// Because in-flight when sending a request, the server might close our connection (because the persistent HTTP
@@ -424,6 +438,40 @@ void QHttpNetworkConnectionChannel::allDone()
return;
}
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
+ && !ssl && !switchedToHttp2) {
+ if (Http2::is_protocol_upgraded(*reply)) {
+ switchedToHttp2 = true;
+ protocolHandler->setReply(nullptr);
+
+ // As allDone() gets called from the protocol handler, it's not yet
+ // safe to delete it. There is no 'deleteLater', since
+ // QAbstractProtocolHandler is not a QObject. Instead we do this
+ // trick with ProtocolHandlerDeleter, a QObject-derived class.
+ // These dances below just make it somewhat exception-safe.
+ // 1. Create a new owner:
+ QAbstractProtocolHandler *oldHandler = protocolHandler.data();
+ QScopedPointer<ProtocolHandlerDeleter> deleter(new ProtocolHandlerDeleter(oldHandler));
+ // 2. Retire the old one:
+ protocolHandler.take();
+ // 3. Call 'deleteLater':
+ deleter->deleteLater();
+ // 3. Give up the ownerthip:
+ deleter.take();
+
+ connection->fillHttp2Queue();
+ protocolHandler.reset(new QHttp2ProtocolHandler(this));
+ QHttp2ProtocolHandler *h2c = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data());
+ QMetaObject::invokeMethod(h2c, "_q_receiveReply", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
+ } else {
+ // Ok, whatever happened, we do not try HTTP/2 anymore ...
+ connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP);
+ connection->d_func()->activeChannelCount = connection->d_func()->channelCount;
+ }
+ }
+
// while handling 401 & 407, we might reset the status code, so save this.
bool emitFinished = reply->d_func()->shouldEmitSignals();
bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
@@ -838,19 +886,23 @@ void QHttpNetworkConnectionChannel::_q_connected()
#endif
} else {
state = QHttpNetworkConnectionChannel::IdleState;
- if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
- // We have to reset QHttp2ProtocolHandler's state machine, it's a new
- // connection and the handler's state is unique per connection.
- protocolHandler.reset(new QHttp2ProtocolHandler(this));
- if (spdyRequestsToSend.count() > 0) {
- // wait for data from the server first (e.g. initial window, max concurrent requests)
- QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ const bool tryProtocolUpgrade = connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2;
+ if (tryProtocolUpgrade) {
+ // For HTTP/1.1 it's already created and never reset.
+ protocolHandler.reset(new QHttpProtocolHandler(this));
+ }
+ switchedToHttp2 = false;
+
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+
+ if (reply) {
+ if (tryProtocolUpgrade) {
+ // Let's augment our request with some magic headers and try to
+ // switch to HTTP/2.
+ Http2::prepare_for_protocol_upgrade(request);
}
- } else {
- if (!reply)
- connection->d_func()->dequeueRequest(socket);
- if (reply)
- sendRequest();
+ sendRequest();
}
}
}
@@ -1078,6 +1130,7 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
// has gone to the SPDY queue already
break;
} else if (nextProtocol == QSslConfiguration::ALPNProtocolHTTP2) {
+ switchedToHttp2 = true;
protocolHandler.reset(new QHttp2ProtocolHandler(this));
connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP2);
break;
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index 584d52ddb7..844a7d5d15 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -127,6 +127,7 @@ public:
// HTTP/2 can be cleartext also, that's why it's
// outside of QT_NO_SSL section. Sorted by priority:
QMultiMap<int, HttpMessagePair> spdyRequestsToSend;
+ bool switchedToHttp2 = false;
#ifndef QT_NO_SSL
bool ignoreAllSslErrors;
QList<QSslError> ignoreSslErrorsList;
diff --git a/src/network/access/qhttpnetworkheader_p.h b/src/network/access/qhttpnetworkheader_p.h
index 89169b9331..46aec1dd8c 100644
--- a/src/network/access/qhttpnetworkheader_p.h
+++ b/src/network/access/qhttpnetworkheader_p.h
@@ -78,7 +78,7 @@ public:
virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
};
-class QHttpNetworkHeaderPrivate : public QSharedData
+class Q_AUTOTEST_EXPORT QHttpNetworkHeaderPrivate : public QSharedData
{
public:
QUrl url;
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index eeee82a87c..15a0359391 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -71,6 +71,8 @@
#include "qthread.h"
+#include <QHostInfo>
+
QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
@@ -689,7 +691,7 @@ void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar)
if (d->cookieJar && d->cookieJar->parent() == this)
delete d->cookieJar;
d->cookieJar = cookieJar;
- if (thread() == cookieJar->thread())
+ if (cookieJar && thread() == cookieJar->thread())
d->cookieJar->setParent(this);
}
}
@@ -1366,10 +1368,16 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
}
#ifndef QT_NO_BEARERMANAGEMENT
+
// Return a disabled network reply if network access is disabled.
- // Except if the scheme is empty or file://.
+ // Except if the scheme is empty or file:// or if the host resolves to a loopback address.
if (d->networkAccessible == NotAccessible && !isLocalFile) {
- return new QDisabledNetworkReply(this, req, op);
+ QHostAddress dest;
+ QString host = req.url().host().toLower();
+ if (!(dest.setAddress(host) && dest.isLoopback()) && host != QLatin1String("localhost")
+ && host != QHostInfo::localHostName().toLower()) {
+ return new QDisabledNetworkReply(this, req, op);
+ }
}
if (!d->networkSessionStrongRef && (d->initializeSession || !d->networkConfiguration.identifier().isEmpty())) {
diff --git a/src/network/configure.json b/src/network/configure.json
index de40872e16..b1c943de6f 100644
--- a/src/network/configure.json
+++ b/src/network/configure.json
@@ -135,7 +135,8 @@
"main": [
"char buf[IFNAMSIZ];",
"if_nametoindex(\"eth0\");",
- "if_indextoname(1, buf);"
+ "if_indextoname(1, buf);",
+ "if_freenameindex(if_nameindex());"
]
},
"use": "network"
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
index c3fcf25233..ac6bebbfae 100644
--- a/src/network/kernel/kernel.pri
+++ b/src/network/kernel/kernel.pri
@@ -63,6 +63,6 @@ osx:SOURCES += kernel/qnetworkproxy_mac.cpp
else:win32:!winrt: SOURCES += kernel/qnetworkproxy_win.cpp
else: qtConfig(libproxy) {
SOURCES += kernel/qnetworkproxy_libproxy.cpp
- QMAKE_USE_PRIVATE += libproxy
+ QMAKE_USE_PRIVATE += libproxy libdl
}
else:SOURCES += kernel/qnetworkproxy_generic.cpp
diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp
index cf08a15f96..9a24938284 100644
--- a/src/network/kernel/qhostinfo_unix.cpp
+++ b/src/network/kernel/qhostinfo_unix.cpp
@@ -150,8 +150,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
QHostAddress address;
if (address.setAddress(hostName)) {
// Reverse lookup
-// Reverse lookups using getnameinfo are broken on darwin, use gethostbyaddr instead.
-#if !defined (QT_NO_GETADDRINFO) && !defined (Q_OS_DARWIN)
+#if !defined (QT_NO_GETADDRINFO)
sockaddr_in sa4;
sockaddr_in6 sa6;
sockaddr *sa = 0;
diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp
index 4f4615d4d0..afa6b4296e 100644
--- a/src/network/kernel/qnetworkinterface_unix.cpp
+++ b/src/network/kernel/qnetworkinterface_unix.cpp
@@ -60,11 +60,6 @@
# define QT_NO_GETIFADDRS
#endif
-#ifdef Q_OS_ANDROID
-// android lacks if_nameindex
-# define QT_NO_IPV6IFNAME
-#endif
-
#ifdef Q_OS_HAIKU
# include <sys/sockio.h>
# define IFF_RUNNING 0x0001
diff --git a/src/network/kernel/qnetworkproxy_libproxy.cpp b/src/network/kernel/qnetworkproxy_libproxy.cpp
index 184dc6469d..29d2a0bd3b 100644
--- a/src/network/kernel/qnetworkproxy_libproxy.cpp
+++ b/src/network/kernel/qnetworkproxy_libproxy.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -42,57 +43,149 @@
#ifndef QT_NO_NETWORKPROXY
#include <QtCore/QByteArray>
+#include <QtCore/QMutex>
+#include <QtCore/QSemaphore>
#include <QtCore/QUrl>
+#include <QtCore/private/qeventdispatcher_unix_p.h>
+#include <QtCore/private/qthread_p.h>
#include <proxy.h>
+#include <dlfcn.h>
QT_BEGIN_NAMESPACE
-class QLibProxyWrapper
+static bool isThreadingNeeded()
{
-public:
- QLibProxyWrapper()
- : factory(px_proxy_factory_new())
- {
- if (!factory)
- qWarning("libproxy initialization failed.");
- }
+ // Try to guess if the libproxy we linked to is from the libproxy project
+ // or if it is from pacrunner. Neither library is thread-safe, but the one
+ // from libproxy is worse, since it may launch JS engines that don't take
+ // kindly to being executed from multiple threads (even if at different
+ // times). The pacrunner implementation doesn't suffer from this because
+ // the JS execution is out of process, in the pacrunner daemon.
- ~QLibProxyWrapper()
- {
- px_proxy_factory_free(factory);
- }
+ void *sym;
+
+#ifdef Q_CC_GNU
+ // Search for the mangled name of the virtual table of the pacrunner
+ // extension. Even if libproxy begins using -fvisibility=hidden, this
+ // symbol can't be hidden.
+ sym = dlsym(RTLD_DEFAULT, "_ZTVN8libproxy19pacrunner_extensionE");
+#else
+ // The default libproxy one uses libmodman for its module management and
+ // leaks symbols because it doesn't use -fvisibility=hidden (as of
+ // v0.4.15).
+ sym = dlsym(RTLD_DEFAULT, "mm_info_ignore_hostname");
+#endif
+
+ return sym != nullptr;
+}
+
+class QLibProxyWrapper : public QDaemonThread
+{
+ Q_OBJECT
+public:
+ QLibProxyWrapper();
+ ~QLibProxyWrapper();
QList<QUrl> getProxies(const QUrl &url);
private:
- pxProxyFactory *factory;
+ struct Data {
+ // we leave the conversion to/from QUrl to the calling thread
+ const char *url;
+ char **proxies;
+ QSemaphore replyReady;
+ };
+
+ void run() override;
+
+ pxProxyFactory *factory; // not subject to the mutex
+
+ QMutex mutex;
+ QSemaphore requestReady;
+ Data *request;
};
Q_GLOBAL_STATIC(QLibProxyWrapper, libProxyWrapper);
+QLibProxyWrapper::QLibProxyWrapper()
+{
+ if (isThreadingNeeded()) {
+ setEventDispatcher(new QEventDispatcherUNIX); // don't allow the Glib one
+ start();
+ } else {
+ factory = px_proxy_factory_new();
+ Q_CHECK_PTR(factory);
+ }
+}
+
+QLibProxyWrapper::~QLibProxyWrapper()
+{
+ if (isRunning()) {
+ requestInterruption();
+ requestReady.release();
+ wait();
+ } else {
+ px_proxy_factory_free(factory);
+ }
+}
+
/*
- Gets the list of proxies from libproxy, converted to QUrl list.
- Thread safe, according to libproxy documentation.
+ Gets the list of proxies from libproxy, converted to QUrl list. Apply
+ thread-safety, though its documentation says otherwise, libproxy isn't
+ thread-safe.
*/
QList<QUrl> QLibProxyWrapper::getProxies(const QUrl &url)
{
- QList<QUrl> ret;
+ QByteArray encodedUrl = url.toEncoded();
+ Data data;
+ data.url = encodedUrl.constData();
+
+ {
+ QMutexLocker locker(&mutex);
+ if (isRunning()) {
+ // threaded mode
+ // it's safe to write to request because we hold the mutex:
+ // our aux thread is blocked waiting for work and no other thread
+ // could have got here
+ request = &data;
+ requestReady.release();
- if (factory) {
- char **proxies = px_proxy_factory_get_proxies(factory, url.toEncoded());
- if (proxies) {
- for (int i = 0; proxies[i]; i++) {
- ret.append(QUrl::fromEncoded(proxies[i]));
- free(proxies[i]);
- }
- free(proxies);
+ // wait for the reply
+ data.replyReady.acquire();
+ } else {
+ // non-threaded mode
+ data.proxies = px_proxy_factory_get_proxies(factory, data.url);
}
}
+ QList<QUrl> ret;
+ if (data.proxies) {
+ for (int i = 0; data.proxies[i]; i++) {
+ ret.append(QUrl::fromEncoded(data.proxies[i]));
+ free(data.proxies[i]);
+ }
+ free(data.proxies);
+ }
return ret;
}
+void QLibProxyWrapper::run()
+{
+ factory = px_proxy_factory_new();
+ Q_CHECK_PTR(factory);
+
+ forever {
+ requestReady.acquire();
+ if (isInterruptionRequested())
+ break;
+ request->proxies = px_proxy_factory_get_proxies(factory, request->url);
+ request->replyReady.release();
+ }
+
+ px_proxy_factory_free(factory);
+}
+
QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
{
QList<QNetworkProxy> proxyList;
@@ -161,4 +254,6 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
QT_END_NAMESPACE
+#include "qnetworkproxy_libproxy.moc"
+
#endif
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index 13ceb4c612..a8f756dc31 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -834,7 +834,7 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
QT_SOCKLEN_T storageSize = sizeof(storage);
memset(&storage, 0, storageSize);
- // Peek 0 bytes into the next message. The size of the message may
+ // Peek 1 bytes into the next message. The size of the message may
// well be 0, so we can't check recvfrom's return value.
ssize_t readBytes;
do {
@@ -855,8 +855,20 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
{
- QVarLengthArray<char, 8192> udpMessagePeekBuffer(8192);
ssize_t recvResult = -1;
+#ifdef Q_OS_LINUX
+ // Linux can return the actual datagram size if we use MSG_TRUNC
+ char c;
+ EINTR_LOOP(recvResult, ::recv(socketDescriptor, &c, 1, MSG_PEEK | MSG_TRUNC));
+#elif defined(SO_NREAD)
+ // macOS can return the actual datagram size if we use SO_NREAD
+ int value;
+ socklen_t valuelen = sizeof(value);
+ recvResult = getsockopt(socketDescriptor, SOL_SOCKET, SO_NREAD, &value, &valuelen);
+ if (recvResult != -1)
+ recvResult = value;
+#else
+ QVarLengthArray<char, 8192> udpMessagePeekBuffer(8192);
for (;;) {
// the data written to udpMessagePeekBuffer is discarded, so
@@ -872,6 +884,7 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
udpMessagePeekBuffer.resize(udpMessagePeekBuffer.size() * 2);
}
+#endif
#if defined (QNATIVESOCKETENGINE_DEBUG)
qDebug("QNativeSocketEnginePrivate::nativePendingDatagramSize() == %zd", recvResult);
diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp
index be7033c8aa..aa81b735b9 100644
--- a/src/network/ssl/qsslkey_openssl.cpp
+++ b/src/network/ssl/qsslkey_openssl.cpp
@@ -85,6 +85,9 @@ void QSslKeyPrivate::clear(bool deep)
bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey)
{
+ if (pkey == nullptr)
+ return false;
+
#if QT_CONFIG(opensslv11)
const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
#else
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 650d37fdbb..23a63ed063 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -704,10 +704,9 @@ void QSslSocketBackendPrivate::transmit()
// Write encrypted data from the buffer into the read BIO.
int writtenToBio = q_BIO_write(readBio, data.constData(), encryptedBytesRead);
- // do the actual read() here and throw away the results.
+ // Throw away the results.
if (writtenToBio > 0) {
- // ### TODO: make this cheaper by not making it memcpy. E.g. make it work with data=0x0 or make it work with seek
- plainSocket->read(data.data(), writtenToBio);
+ plainSocket->skip(writtenToBio);
} else {
// ### Better error handling.
setErrorAndEmit(QAbstractSocket::SslInternalError,
@@ -1500,7 +1499,7 @@ bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
}
// Extract the data
- EVP_PKEY *pkey;
+ EVP_PKEY *pkey = nullptr;
X509 *x509;
STACK_OF(X509) *ca = 0;