diff options
Diffstat (limited to 'src/network/access')
-rw-r--r-- | src/network/access/http2/http2protocol.cpp | 2 | ||||
-rw-r--r-- | src/network/access/http2/http2protocol_p.h | 14 | ||||
-rw-r--r-- | src/network/access/qhttp2protocolhandler.cpp | 52 | ||||
-rw-r--r-- | src/network/access/qhttp2protocolhandler_p.h | 8 | ||||
-rw-r--r-- | src/network/access/qnetworkaccessmanager.cpp | 11 | ||||
-rw-r--r-- | src/network/access/qnetworkdiskcache.cpp | 4 | ||||
-rw-r--r-- | src/network/access/qnetworkreplyhttpimpl.cpp | 8 |
7 files changed, 67 insertions, 32 deletions
diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp index 54811aeab0..bb3d6bf575 100644 --- a/src/network/access/http2/http2protocol.cpp +++ b/src/network/access/http2/http2protocol.cpp @@ -216,6 +216,8 @@ Frame default_SETTINGS_frame() // MAX frame size (16 kb), disable/enable PUSH_PROMISE builder.append(Settings::MAX_FRAME_SIZE_ID); builder.append(quint32(maxFrameSize)); + builder.append(Settings::INITIAL_WINDOW_SIZE_ID); + builder.append(initialStreamReceiveWindowSize); builder.append(Settings::ENABLE_PUSH_ID); builder.append(quint32(is_PUSH_PROMISE_enabled())); diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h index b26ff0e9f4..c64e960002 100644 --- a/src/network/access/http2/http2protocol_p.h +++ b/src/network/access/http2/http2protocol_p.h @@ -129,10 +129,20 @@ enum Http2PredefinedParameters maxConcurrentStreams = 100 // HTTP/2, 6.5.2 }; -// It's int, it has internal linkage, it's ok to have it in headers - -// no ODR violation is possible. +// These are ints, const, they have internal linkage, it's ok to have them in +// headers - no ODR violation. const quint32 lastValidStreamID((quint32(1) << 31) - 1); // HTTP/2, 5.1.1 +// The default size of 64K is too small and limiting: if we use it, we end up +// sending WINDOW_UPDATE frames on a stream/session all the time, for each +// 2 DATE frames of size 16K (also default) we'll send a WINDOW_UPDATE frame +// for a given stream and have a download speed order of magnitude lower than +// our own HTTP/1.1 protocol handler. We choose a bigger window size (normally, +// HTTP/2 servers are not afraid to immediately set it to possible max anyway) +// and split this window size between our concurrent streams. +const qint32 initialSessionReceiveWindowSize = defaultSessionWindowSize * 10000; +const qint32 initialStreamReceiveWindowSize = initialSessionReceiveWindowSize / maxConcurrentStreams; + extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength]; void prepare_for_protocol_upgrade(QHttpNetworkRequest &request); diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp index 5032f6017f..461f2429b3 100644 --- a/src/network/access/qhttp2protocolhandler.cpp +++ b/src/network/access/qhttp2protocolhandler.cpp @@ -161,8 +161,6 @@ bool sum_will_overflow(qint32 windowSize, qint32 delta) using namespace Http2; const std::deque<quint32>::size_type QHttp2ProtocolHandler::maxRecycledStreams = 10000; -const qint32 QHttp2ProtocolHandler::sessionMaxRecvWindowSize; -const qint32 QHttp2ProtocolHandler::streamInitialRecvWindowSize; const quint32 QHttp2ProtocolHandler::maxAcceptableTableSize; QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel) @@ -374,12 +372,10 @@ bool QHttp2ProtocolHandler::sendClientPreface() if (!frameWriter.write(*m_socket)) return false; - sessionRecvWindowSize = sessionMaxRecvWindowSize; - if (defaultSessionWindowSize < sessionMaxRecvWindowSize) { - const auto delta = sessionMaxRecvWindowSize - defaultSessionWindowSize; - if (!sendWINDOW_UPDATE(connectionStreamID, delta)) - return false; - } + sessionRecvWindowSize = Http2::initialSessionReceiveWindowSize; + const auto delta = Http2::initialSessionReceiveWindowSize - Http2::defaultSessionWindowSize; + if (!sendWINDOW_UPDATE(Http2::connectionStreamID, delta)) + return false; prefaceSent = true; waitingForSettingsACK = true; @@ -549,20 +545,20 @@ void QHttp2ProtocolHandler::handleDATA() if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) { finishStream(stream); deleteActiveStream(stream.streamID); - } else if (stream.recvWindow < streamInitialRecvWindowSize / 2) { + } else if (stream.recvWindow < Http2::initialStreamReceiveWindowSize / 2) { QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection, Q_ARG(quint32, stream.streamID), - Q_ARG(quint32, streamInitialRecvWindowSize - stream.recvWindow)); - stream.recvWindow = streamInitialRecvWindowSize; + Q_ARG(quint32, Http2::initialStreamReceiveWindowSize - stream.recvWindow)); + stream.recvWindow = Http2::initialStreamReceiveWindowSize; } } } - if (sessionRecvWindowSize < sessionMaxRecvWindowSize / 2) { + if (sessionRecvWindowSize < Http2::initialSessionReceiveWindowSize / 2) { QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection, Q_ARG(quint32, connectionStreamID), - Q_ARG(quint32, sessionMaxRecvWindowSize - sessionRecvWindowSize)); - sessionRecvWindowSize = sessionMaxRecvWindowSize; + Q_ARG(quint32, Http2::initialSessionReceiveWindowSize - sessionRecvWindowSize)); + sessionRecvWindowSize = Http2::initialSessionReceiveWindowSize; } } @@ -1042,12 +1038,26 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader } const auto httpReplyPrivate = httpReply->d_func(); + + // For HTTP/1 'location' is handled (and redirect URL set) when a protocol + // handler emits channel->allDone(). Http/2 protocol handler never emits + // allDone, since we have many requests multiplexed in one channel at any + // moment and we are probably not done yet. So we extract url and set it + // here, if needed. + int statusCode = 0; + QUrl redirectUrl; + for (const auto &pair : headers) { const auto &name = pair.name; auto value = pair.value; + // TODO: part of this code copies what SPDY protocol handler does when + // processing headers. Binary nature of HTTP/2 and SPDY saves us a lot + // of parsing and related errors/bugs, but it would be nice to have + // more detailed validation of headers. if (name == ":status") { - httpReply->setStatusCode(value.left(3).toInt()); + statusCode = value.left(3).toInt(); + httpReply->setStatusCode(statusCode); httpReplyPrivate->reasonPhrase = QString::fromLatin1(value.mid(4)); } else if (name == ":version") { httpReplyPrivate->majorVersion = value.at(5) - '0'; @@ -1058,6 +1068,8 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader if (ok) httpReply->setContentLength(length); } else { + if (name == "location") + redirectUrl = QUrl::fromEncoded(value); QByteArray binder(", "); if (name == "set-cookie") binder = "\n"; @@ -1065,6 +1077,9 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader } } + if (QHttpNetworkReply::isHttpRedirect(statusCode) && redirectUrl.isValid()) + httpReply->setRedirectUrl(redirectUrl); + if (connectionType == Qt::DirectConnection) emit httpReply->headerChanged(); else @@ -1184,7 +1199,7 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, b const Stream newStream(message, newStreamID, streamInitialSendWindowSize, - streamInitialRecvWindowSize); + Http2::initialStreamReceiveWindowSize); if (!uploadDone) { if (auto src = newStream.data()) { @@ -1379,7 +1394,8 @@ bool QHttp2ProtocolHandler::tryReserveStream(const Http2::Frame &pushPromiseFram promise.reservedID = reservedID; promise.pushHeader = requestHeader; - activeStreams.insert(reservedID, Stream(urlKey, reservedID, streamInitialRecvWindowSize)); + activeStreams.insert(reservedID, Stream(urlKey, reservedID, + Http2::initialStreamReceiveWindowSize)); return true; } @@ -1413,7 +1429,7 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess // Let's pretent we're sending a request now: Stream closedStream(message, promise.reservedID, streamInitialSendWindowSize, - streamInitialRecvWindowSize); + Http2::initialStreamReceiveWindowSize); closedStream.state = Stream::halfClosedLocal; activeStreams.insert(promise.reservedID, closedStream); promisedStream = &activeStreams[promise.reservedID]; diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h index 82eea21818..b52f8ae10c 100644 --- a/src/network/access/qhttp2protocolhandler_p.h +++ b/src/network/access/qhttp2protocolhandler_p.h @@ -176,13 +176,9 @@ private: quint32 maxConcurrentStreams = Http2::maxConcurrentStreams; // Control flow: - static const qint32 sessionMaxRecvWindowSize = Http2::defaultSessionWindowSize * 10; - // Signed integer, it can become negative (it's still a valid window size): - qint32 sessionRecvWindowSize = sessionMaxRecvWindowSize; - // We do not negotiate this window size - // We have to send WINDOW_UPDATE frames to our peer also. - static const qint32 streamInitialRecvWindowSize = Http2::defaultSessionWindowSize; + // Signed integer, it can become negative (it's still a valid window size): + qint32 sessionRecvWindowSize = Http2::initialSessionReceiveWindowSize; // Updated by SETTINGS and WINDOW_UPDATE. qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize; diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 8bbef0a0d8..edf9dee78e 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -75,6 +75,12 @@ #include <QHostInfo> +#if defined(Q_OS_MACOS) +#include <CoreServices/CoreServices.h> +#include <SystemConfiguration/SystemConfiguration.h> +#include <Security/SecKeychain.h> +#endif + QT_BEGIN_NAMESPACE Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend) @@ -87,11 +93,6 @@ Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend) #endif #if defined(Q_OS_MACX) - -#include <CoreServices/CoreServices.h> -#include <SystemConfiguration/SystemConfiguration.h> -#include <Security/SecKeychain.h> - bool getProxyAuth(const QString& proxyHostname, const QString &scheme, QString& username, QString& password) { OSStatus err; diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp index fca880d9b3..c9d658225e 100644 --- a/src/network/access/qnetworkdiskcache.cpp +++ b/src/network/access/qnetworkdiskcache.cpp @@ -537,7 +537,9 @@ qint64 QNetworkDiskCache::expire() QFileInfo info = it.fileInfo(); QString fileName = info.fileName(); if (fileName.endsWith(CACHE_POSTFIX)) { - cacheItems.insert(info.created(), path); + const QDateTime birthTime = info.fileTime(QFile::FileBirthTime); + cacheItems.insert(birthTime.isValid() ? birthTime + : info.fileTime(QFile::FileMetadataChangeTime), path); totalSize += info.size(); } } diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 55eb7d4f08..684fee610c 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -1175,6 +1175,14 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt redirectRequest = createRedirectRequest(originalRequest, url, maxRedirectsRemaining); operation = getRedirectOperation(operation, httpStatus); + if (const QNetworkCookieJar *const cookieJar = (manager ? manager->cookieJar() : nullptr)) { + auto cookies = cookieJar->cookiesForUrl(url); + if (!cookies.empty()) { + redirectRequest.setHeader(QNetworkRequest::KnownHeaders::CookieHeader, + QVariant::fromValue(cookies)); + } + } + if (httpRequest.redirectPolicy() != QNetworkRequest::UserVerifiedRedirectPolicy) followRedirect(); |