summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>2016-08-22 11:30:00 +0200
committerOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>2016-08-22 11:30:01 +0200
commitd314819fc02139e05e16c56657898c704f7fb48f (patch)
treea61ba968233634948401c8339f9613844de1c2b5 /src/network
parent9f888d2fde9c5413e5519e0914e9b13638760985 (diff)
parente0e9e196a72ffe5457034894eaaadc90ed0d34ef (diff)
Merge dev into 5.8
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/http2/http2frames.cpp444
-rw-r--r--src/network/access/http2/http2frames_p.h101
-rw-r--r--src/network/access/http2/http2streams.cpp4
-rw-r--r--src/network/access/http2/http2streams_p.h3
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp263
-rw-r--r--src/network/access/qhttp2protocolhandler_p.h13
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp13
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp68
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h7
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp5
-rw-r--r--src/network/access/qnetworkrequest.cpp8
-rw-r--r--src/network/kernel/kernel.pri7
-rw-r--r--src/network/kernel/qnetworkinterface.cpp2
-rw-r--r--src/network/kernel/qnetworkproxy.cpp4
-rw-r--r--src/network/socket/qabstractsocket.cpp43
-rw-r--r--src/network/socket/qlocalserver.cpp2
-rw-r--r--src/network/socket/qlocalsocket_tcp.cpp2
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp2
-rw-r--r--src/network/socket/socket.pri4
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp2
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.cpp66
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.h13
-rw-r--r--src/network/ssl/qsslsocket.cpp2
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp4
-rw-r--r--src/network/ssl/qsslsocket_mac_p.h4
-rw-r--r--src/network/ssl/ssl.pri6
26 files changed, 551 insertions, 541 deletions
diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp
index 55e9f93b19..978bee09b1 100644
--- a/src/network/access/http2/http2frames.cpp
+++ b/src/network/access/http2/http2frames.cpp
@@ -51,46 +51,125 @@ namespace Http2
// HTTP/2 frames are defined by RFC7540, clauses 4 and 6.
-FrameStatus validate_frame_header(FrameType type, FrameFlags flags, quint32 payloadSize)
+Frame::Frame()
+ : buffer(frameHeaderSize)
{
+}
+
+FrameType Frame::type() const
+{
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+
+ if (int(buffer[3]) >= int(FrameType::LAST_FRAME_TYPE))
+ return FrameType::LAST_FRAME_TYPE;
+
+ return FrameType(buffer[3]);
+}
+
+quint32 Frame::streamID() const
+{
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+ return qFromBigEndian<quint32>(&buffer[5]);
+}
+
+FrameFlags Frame::flags() const
+{
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+ return FrameFlags(buffer[4]);
+}
+
+quint32 Frame::payloadSize() const
+{
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+ return buffer[0] << 16 | buffer[1] << 8 | buffer[2];
+}
+
+uchar Frame::padding() const
+{
+ Q_ASSERT(validateHeader() == FrameStatus::goodFrame);
+
+ if (!flags().testFlag(FrameFlag::PADDED))
+ return 0;
+
+ switch (type()) {
+ case FrameType::DATA:
+ case FrameType::PUSH_PROMISE:
+ case FrameType::HEADERS:
+ Q_ASSERT(buffer.size() > frameHeaderSize);
+ return buffer[frameHeaderSize];
+ default:
+ return 0;
+ }
+}
+
+bool Frame::priority(quint32 *streamID, uchar *weight) const
+{
+ Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
+
+ if (buffer.size() <= frameHeaderSize)
+ return false;
+
+ const uchar *src = &buffer[0] + frameHeaderSize;
+ if (type() == FrameType::HEADERS && flags().testFlag(FrameFlag::PADDED))
+ ++src;
+
+ if ((type() == FrameType::HEADERS && flags().testFlag(FrameFlag::PRIORITY))
+ || type() == FrameType::PRIORITY) {
+ if (streamID)
+ *streamID = qFromBigEndian<quint32>(src);
+ if (weight)
+ *weight = src[4];
+ return true;
+ }
+
+ return false;
+}
+
+FrameStatus Frame::validateHeader() const
+{
+ // Should be called only on a frame with
+ // a complete header.
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+
+ const auto framePayloadSize = payloadSize();
// 4.2 Frame Size
- if (payloadSize > maxPayloadSize)
+ if (framePayloadSize > maxPayloadSize)
return FrameStatus::sizeError;
- switch (type) {
+ switch (type()) {
case FrameType::SETTINGS:
// SETTINGS ACK can not have any payload.
// The payload of a SETTINGS frame consists of zero
// or more parameters, each consisting of an unsigned
// 16-bit setting identifier and an unsigned 32-bit value.
// Thus the payload size must be a multiple of 6.
- if (flags.testFlag(FrameFlag::ACK) ? payloadSize : payloadSize % 6)
+ if (flags().testFlag(FrameFlag::ACK) ? framePayloadSize : framePayloadSize % 6)
return FrameStatus::sizeError;
break;
case FrameType::PRIORITY:
// 6.3 PRIORITY
- if (payloadSize != 5)
+ if (framePayloadSize != 5)
return FrameStatus::sizeError;
break;
case FrameType::PING:
// 6.7 PING
- if (payloadSize != 8)
+ if (framePayloadSize != 8)
return FrameStatus::sizeError;
break;
case FrameType::GOAWAY:
// 6.8 GOAWAY
- if (payloadSize < 8)
+ if (framePayloadSize < 8)
return FrameStatus::sizeError;
break;
case FrameType::RST_STREAM:
case FrameType::WINDOW_UPDATE:
// 6.4 RST_STREAM, 6.9 WINDOW_UPDATE
- if (payloadSize != 4)
+ if (framePayloadSize != 4)
return FrameStatus::sizeError;
break;
case FrameType::PUSH_PROMISE:
// 6.6 PUSH_PROMISE
- if (payloadSize < 4)
+ if (framePayloadSize < 4)
return FrameStatus::sizeError;
default:
// DATA/HEADERS/CONTINUATION will be verified
@@ -102,31 +181,37 @@ FrameStatus validate_frame_header(FrameType type, FrameFlags flags, quint32 payl
return FrameStatus::goodFrame;
}
-FrameStatus validate_frame_payload(FrameType type, FrameFlags flags,
- quint32 size, const uchar *src)
+FrameStatus Frame::validatePayload() const
{
- Q_ASSERT(!size || src);
+ // Should be called only on a complete frame with a valid header.
+ Q_ASSERT(validateHeader() == FrameStatus::goodFrame);
// Ignored, 5.1
- if (type == FrameType::LAST_FRAME_TYPE)
+ if (type() == FrameType::LAST_FRAME_TYPE)
return FrameStatus::goodFrame;
+ auto size = payloadSize();
+ Q_ASSERT(buffer.size() >= frameHeaderSize && size == buffer.size() - frameHeaderSize);
+
+ const uchar *src = size ? &buffer[0] + frameHeaderSize : nullptr;
+ const auto frameFlags = flags();
+ switch (type()) {
// 6.1 DATA, 6.2 HEADERS
- if (type == FrameType::DATA || type == FrameType::HEADERS) {
- if (flags.testFlag(FrameFlag::PADDED)) {
+ case FrameType::DATA:
+ case FrameType::HEADERS:
+ if (frameFlags.testFlag(FrameFlag::PADDED)) {
if (!size || size < src[0])
return FrameStatus::sizeError;
size -= src[0];
}
- if (type == FrameType::HEADERS && flags.testFlag(FrameFlag::PRIORITY)) {
+ if (type() == FrameType::HEADERS && frameFlags.testFlag(FrameFlag::PRIORITY)) {
if (size < 5)
return FrameStatus::sizeError;
}
- }
-
+ break;
// 6.6 PUSH_PROMISE
- if (type == FrameType::PUSH_PROMISE) {
- if (flags.testFlag(FrameFlag::PADDED)) {
+ case FrameType::PUSH_PROMISE:
+ if (frameFlags.testFlag(FrameFlag::PADDED)) {
if (!size || size < src[0])
return FrameStatus::sizeError;
size -= src[0];
@@ -134,284 +219,158 @@ FrameStatus validate_frame_payload(FrameType type, FrameFlags flags,
if (size < 4)
return FrameStatus::sizeError;
+ break;
+ default:
+ break;
}
return FrameStatus::goodFrame;
}
-FrameStatus validate_frame_payload(FrameType type, FrameFlags flags,
- const std::vector<uchar> &payload)
-{
- const uchar *src = payload.size() ? &payload[0] : nullptr;
- return validate_frame_payload(type, flags, quint32(payload.size()), src);
-}
-
-FrameReader::FrameReader(FrameReader &&rhs)
- : framePayload(std::move(rhs.framePayload))
+quint32 Frame::dataSize() const
{
- type = rhs.type;
- rhs.type = FrameType::LAST_FRAME_TYPE;
-
- flags = rhs.flags;
- rhs.flags = FrameFlag::EMPTY;
-
- streamID = rhs.streamID;
- rhs.streamID = 0;
+ Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
- payloadSize = rhs.payloadSize;
- rhs.payloadSize = 0;
+ quint32 size = payloadSize();
+ if (const uchar pad = padding()) {
+ // + 1 one for a byte with padding number itself:
+ size -= pad + 1;
+ }
- incompleteRead = rhs.incompleteRead;
- rhs.incompleteRead = false;
+ if (priority())
+ size -= 5;
- offset = rhs.offset;
- rhs.offset = 0;
+ return size;
}
-FrameReader &FrameReader::operator = (FrameReader &&rhs)
+const uchar *Frame::dataBegin() const
{
- framePayload = std::move(rhs.framePayload);
-
- type = rhs.type;
- rhs.type = FrameType::LAST_FRAME_TYPE;
-
- flags = rhs.flags;
- rhs.flags = FrameFlag::EMPTY;
-
- streamID = rhs.streamID;
- rhs.streamID = 0;
-
- payloadSize = rhs.payloadSize;
- rhs.payloadSize = 0;
+ Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
+ if (buffer.size() <= frameHeaderSize)
+ return nullptr;
- incompleteRead = rhs.incompleteRead;
- rhs.incompleteRead = false;
+ const uchar *src = &buffer[0] + frameHeaderSize;
+ if (padding())
+ ++src;
- offset = rhs.offset;
- rhs.offset = 0;
+ if (priority())
+ src += 5;
- return *this;
+ return src;
}
FrameStatus FrameReader::read(QAbstractSocket &socket)
{
- if (!incompleteRead) {
+ if (offset < frameHeaderSize) {
if (!readHeader(socket))
return FrameStatus::incompleteFrame;
- const auto status = validate_frame_header(type, flags, payloadSize);
+ const auto status = frame.validateHeader();
if (status != FrameStatus::goodFrame) {
// No need to read any payload.
return status;
}
- if (Http2PredefinedParameters::maxFrameSize < payloadSize)
+ if (Http2PredefinedParameters::maxFrameSize < frame.payloadSize())
return FrameStatus::sizeError;
- framePayload.resize(payloadSize);
- offset = 0;
- }
-
- if (framePayload.size()) {
- if (!readPayload(socket))
- return FrameStatus::incompleteFrame;
- }
-
- return validate_frame_payload(type, flags, framePayload);
-}
-
-bool FrameReader::padded(uchar *pad) const
-{
- Q_ASSERT(pad);
-
- if (!flags.testFlag(FrameFlag::PADDED))
- return false;
-
- if (type == FrameType::DATA
- || type == FrameType::PUSH_PROMISE
- || type == FrameType::HEADERS) {
- Q_ASSERT(framePayload.size() >= 1);
- *pad = framePayload[0];
- return true;
- }
-
- return false;
-}
-
-bool FrameReader::priority(quint32 *streamID, uchar *weight) const
-{
- Q_ASSERT(streamID);
- Q_ASSERT(weight);
-
- if (!framePayload.size())
- return false;
-
- const uchar *src = &framePayload[0];
- if (type == FrameType::HEADERS && flags.testFlag(FrameFlag::PADDED))
- ++src;
-
- if ((type == FrameType::HEADERS && flags.testFlag(FrameFlag::PRIORITY))
- || type == FrameType::PRIORITY) {
- *streamID = qFromBigEndian<quint32>(src);
- *weight = src[4];
- return true;
+ frame.buffer.resize(frame.payloadSize() + frameHeaderSize);
}
- return false;
-}
+ if (offset < frame.buffer.size() && !readPayload(socket))
+ return FrameStatus::incompleteFrame;
-quint32 FrameReader::dataSize() const
-{
- quint32 size = quint32(framePayload.size());
- uchar pad = 0;
- if (padded(&pad)) {
- // + 1 one for a byte with padding number itself:
- size -= pad + 1;
- }
-
- quint32 dummyID = 0;
- uchar dummyW = 0;
- if (priority(&dummyID, &dummyW))
- size -= 5;
+ // Reset the offset, our frame can be re-used
+ // now (re-read):
+ offset = 0;
- return size;
-}
-
-const uchar *FrameReader::dataBegin() const
-{
- if (!framePayload.size())
- return nullptr;
-
- const uchar *src = &framePayload[0];
- uchar dummyPad = 0;
- if (padded(&dummyPad))
- ++src;
-
- quint32 dummyID = 0;
- uchar dummyW = 0;
- if (priority(&dummyID, &dummyW))
- src += 5;
-
- return src;
+ return frame.validatePayload();
}
bool FrameReader::readHeader(QAbstractSocket &socket)
{
- if (socket.bytesAvailable() < frameHeaderSize)
- return false;
-
- uchar src[frameHeaderSize] = {};
- socket.read(reinterpret_cast<char*>(src), frameHeaderSize);
-
- payloadSize = src[0] << 16 | src[1] << 8 | src[2];
+ Q_ASSERT(offset < frameHeaderSize);
- type = FrameType(src[3]);
- if (int(type) >= int(FrameType::LAST_FRAME_TYPE))
- type = FrameType::LAST_FRAME_TYPE; // To be ignored, 5.1
+ auto &buffer = frame.buffer;
+ if (buffer.size() < frameHeaderSize)
+ buffer.resize(frameHeaderSize);
- flags = FrameFlags(src[4]);
- streamID = qFromBigEndian<quint32>(src + 5);
+ const auto chunkSize = socket.read(reinterpret_cast<char *>(&buffer[offset]),
+ frameHeaderSize - offset);
+ if (chunkSize > 0)
+ offset += chunkSize;
- return true;
+ return offset == frameHeaderSize;
}
bool FrameReader::readPayload(QAbstractSocket &socket)
{
- Q_ASSERT(offset <= framePayload.size());
+ Q_ASSERT(offset < frame.buffer.size());
+ Q_ASSERT(frame.buffer.size() > frameHeaderSize);
+ auto &buffer = frame.buffer;
// Casts and ugliness - to deal with MSVC. Values are guaranteed to fit into quint32.
- if (const auto residue = std::min(qint64(framePayload.size() - offset), socket.bytesAvailable())) {
- socket.read(reinterpret_cast<char *>(&framePayload[offset]), residue);
- offset += quint32(residue);
- }
+ const auto chunkSize = socket.read(reinterpret_cast<char *>(&buffer[offset]),
+ qint64(buffer.size() - offset));
+ if (chunkSize > 0)
+ offset += quint32(chunkSize);
- incompleteRead = offset < framePayload.size();
- return !incompleteRead;
+ return offset == buffer.size();
}
-
FrameWriter::FrameWriter()
{
- frameBuffer.reserve(Http2PredefinedParameters::maxFrameSize +
- Http2PredefinedParameters::frameHeaderSize);
}
FrameWriter::FrameWriter(FrameType type, FrameFlags flags, quint32 streamID)
{
- frameBuffer.reserve(Http2PredefinedParameters::maxFrameSize +
- Http2PredefinedParameters::frameHeaderSize);
start(type, flags, streamID);
}
void FrameWriter::start(FrameType type, FrameFlags flags, quint32 streamID)
{
- frameBuffer.resize(frameHeaderSize);
+ auto &buffer = frame.buffer;
+
+ buffer.resize(frameHeaderSize);
// The first three bytes - payload size, which is 0 for now.
- frameBuffer[0] = 0;
- frameBuffer[1] = 0;
- frameBuffer[2] = 0;
+ buffer[0] = 0;
+ buffer[1] = 0;
+ buffer[2] = 0;
- frameBuffer[3] = uchar(type);
- frameBuffer[4] = uchar(flags);
+ buffer[3] = uchar(type);
+ buffer[4] = uchar(flags);
- qToBigEndian(streamID, &frameBuffer[5]);
+ qToBigEndian(streamID, &buffer[5]);
}
void FrameWriter::setPayloadSize(quint32 size)
{
- Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+ auto &buffer = frame.buffer;
+
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
Q_ASSERT(size < maxPayloadSize);
- frameBuffer[0] = size >> 16;
- frameBuffer[1] = size >> 8;
- frameBuffer[2] = size;
-}
-
-quint32 FrameWriter::payloadSize() const
-{
- Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
- return frameBuffer[0] << 16 | frameBuffer[1] << 8 | frameBuffer[2];
+ buffer[0] = size >> 16;
+ buffer[1] = size >> 8;
+ buffer[2] = size;
}
void FrameWriter::setType(FrameType type)
{
- Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
- frameBuffer[3] = uchar(type);
-}
-
-FrameType FrameWriter::type() const
-{
- Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
- return FrameType(frameBuffer[3]);
+ Q_ASSERT(frame.buffer.size() >= frameHeaderSize);
+ frame.buffer[3] = uchar(type);
}
void FrameWriter::setFlags(FrameFlags flags)
{
- Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
- frameBuffer[4] = uchar(flags);
+ Q_ASSERT(frame.buffer.size() >= frameHeaderSize);
+ frame.buffer[4] = uchar(flags);
}
void FrameWriter::addFlag(FrameFlag flag)
{
- setFlags(flags() | flag);
-}
-
-FrameFlags FrameWriter::flags() const
-{
- Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
- return FrameFlags(frameBuffer[4]);
-}
-
-quint32 FrameWriter::streamID() const
-{
- return qFromBigEndian<quint32>(&frameBuffer[5]);
-}
-
-void FrameWriter::append(uchar val)
-{
- frameBuffer.push_back(val);
- updatePayloadSize();
+ setFlags(frame.flags() | flag);
}
void FrameWriter::append(const uchar *begin, const uchar *end)
@@ -419,64 +378,71 @@ void FrameWriter::append(const uchar *begin, const uchar *end)
Q_ASSERT(begin && end);
Q_ASSERT(begin < end);
- frameBuffer.insert(frameBuffer.end(), begin, end);
+ frame.buffer.insert(frame.buffer.end(), begin, end);
updatePayloadSize();
}
void FrameWriter::updatePayloadSize()
{
- // First, compute size:
- const quint32 payloadSize = quint32(frameBuffer.size() - frameHeaderSize);
- Q_ASSERT(payloadSize <= maxPayloadSize);
- setPayloadSize(payloadSize);
+ const quint32 size = quint32(frame.buffer.size() - frameHeaderSize);
+ Q_ASSERT(size <= maxPayloadSize);
+ setPayloadSize(size);
}
bool FrameWriter::write(QAbstractSocket &socket) const
{
- Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+ auto &buffer = frame.buffer;
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
// Do some sanity check first:
- Q_ASSERT(int(type()) < int(FrameType::LAST_FRAME_TYPE));
- Q_ASSERT(validate_frame_header(type(), flags(), payloadSize()) == FrameStatus::goodFrame);
- const auto nWritten = socket.write(reinterpret_cast<const char *>(&frameBuffer[0]),
- frameBuffer.size());
- return nWritten != -1 && size_type(nWritten) == frameBuffer.size();
+ Q_ASSERT(int(frame.type()) < int(FrameType::LAST_FRAME_TYPE));
+ Q_ASSERT(frame.validateHeader() == FrameStatus::goodFrame);
+
+ const auto nWritten = socket.write(reinterpret_cast<const char *>(&buffer[0]),
+ buffer.size());
+ return nWritten != -1 && size_type(nWritten) == buffer.size();
}
bool FrameWriter::writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
{
- Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+ auto &buffer = frame.buffer;
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
if (sizeLimit > quint32(maxPayloadSize))
sizeLimit = quint32(maxPayloadSize);
- if (quint32(frameBuffer.size() - frameHeaderSize) <= sizeLimit) {
+ if (quint32(buffer.size() - frameHeaderSize) <= sizeLimit) {
+ addFlag(FrameFlag::END_HEADERS);
updatePayloadSize();
return write(socket);
}
+ // Our HPACK block does not fit into the size limit, remove
+ // END_HEADERS bit from the first frame, we'll later set
+ // it on the last CONTINUATION frame:
+ setFlags(frame.flags() & ~FrameFlags(FrameFlag::END_HEADERS));
// Write a frame's header (not controlled by sizeLimit) and
// as many bytes of payload as we can within sizeLimit,
// then send CONTINUATION frames, as needed.
setPayloadSize(sizeLimit);
const quint32 firstChunkSize = frameHeaderSize + sizeLimit;
- qint64 written = socket.write(reinterpret_cast<const char *>(&frameBuffer[0]),
+ qint64 written = socket.write(reinterpret_cast<const char *>(&buffer[0]),
firstChunkSize);
if (written != qint64(firstChunkSize))
return false;
- FrameWriter continuationFrame(FrameType::CONTINUATION, FrameFlag::EMPTY, streamID());
+ FrameWriter continuationWriter(FrameType::CONTINUATION, FrameFlag::EMPTY, frame.streamID());
quint32 offset = firstChunkSize;
- while (offset != frameBuffer.size()) {
- const auto chunkSize = std::min(sizeLimit, quint32(frameBuffer.size() - offset));
- if (chunkSize + offset == frameBuffer.size())
- continuationFrame.addFlag(FrameFlag::END_HEADERS);
- continuationFrame.setPayloadSize(chunkSize);
- if (!continuationFrame.write(socket))
+ while (offset != buffer.size()) {
+ const auto chunkSize = std::min(sizeLimit, quint32(buffer.size() - offset));
+ if (chunkSize + offset == buffer.size())
+ continuationWriter.addFlag(FrameFlag::END_HEADERS);
+ continuationWriter.setPayloadSize(chunkSize);
+ if (!continuationWriter.write(socket))
return false;
- written = socket.write(reinterpret_cast<const char *>(&frameBuffer[offset]),
+ written = socket.write(reinterpret_cast<const char *>(&buffer[offset]),
chunkSize);
if (written != qint64(chunkSize))
return false;
@@ -524,6 +490,6 @@ bool FrameWriter::writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
return true;
}
-}
+} // Namespace Http2
QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2frames_p.h b/src/network/access/http2/http2frames_p.h
index 6abed315ca..84ba9c3662 100644
--- a/src/network/access/http2/http2frames_p.h
+++ b/src/network/access/http2/http2frames_p.h
@@ -68,52 +68,53 @@ class QAbstractSocket;
namespace Http2
{
-class Q_AUTOTEST_EXPORT FrameReader
+struct Q_AUTOTEST_EXPORT Frame
{
- friend class QT_PREPEND_NAMESPACE(QHttp2ProtocolHandler);
-
-public:
- FrameReader() = default;
-
- FrameReader(const FrameReader &) = default;
- FrameReader(FrameReader &&rhs);
-
- FrameReader &operator = (const FrameReader &) = default;
- FrameReader &operator = (FrameReader &&rhs);
-
- FrameStatus read(QAbstractSocket &socket);
+ Frame();
+ // Reading these values without first forming a valid frame
+ // (either reading it from a socket or building it) will result
+ // in undefined behavior:
+ FrameType type() const;
+ quint32 streamID() const;
+ FrameFlags flags() const;
+ quint32 payloadSize() const;
+ uchar padding() const;
+ // In HTTP/2 a stream's priority is specified by its weight
+ // and a stream (id) it depends on:
+ bool priority(quint32 *streamID = nullptr,
+ uchar *weight = nullptr) const;
- bool padded(uchar *pad) const;
- bool priority(quint32 *streamID, uchar *weight) const;
+ FrameStatus validateHeader() const;
+ FrameStatus validatePayload() const;
- // N of bytes without padding and/or priority
+ // Number of payload bytes without padding and/or priority
quint32 dataSize() const;
// Beginning of payload without priority/padding
// bytes.
const uchar *dataBegin() const;
- FrameType type = FrameType::LAST_FRAME_TYPE;
- FrameFlags flags = FrameFlag::EMPTY;
- quint32 streamID = 0;
- quint32 payloadSize = 0;
+ std::vector<uchar> buffer;
+};
+class Q_AUTOTEST_EXPORT FrameReader
+{
+public:
+ FrameStatus read(QAbstractSocket &socket);
+
+ Frame &inboundFrame()
+ {
+ return frame;
+ }
private:
bool readHeader(QAbstractSocket &socket);
bool readPayload(QAbstractSocket &socket);
- // As soon as we got a header, we
- // know payload size, offset is
- // needed if we do not have enough
- // data and will read the next chunk.
- bool incompleteRead = false;
quint32 offset = 0;
- std::vector<uchar> framePayload;
+ Frame frame;
};
class Q_AUTOTEST_EXPORT FrameWriter
{
- friend class QT_PREPEND_NAMESPACE(QHttp2ProtocolHandler);
-
public:
using payload_type = std::vector<uchar>;
using size_type = payload_type::size_type;
@@ -121,19 +122,17 @@ public:
FrameWriter();
FrameWriter(FrameType type, FrameFlags flags, quint32 streamID);
- void start(FrameType type, FrameFlags flags, quint32 streamID);
+ Frame &outboundFrame()
+ {
+ return frame;
+ }
+ // Frame 'builders':
+ void start(FrameType type, FrameFlags flags, quint32 streamID);
void setPayloadSize(quint32 size);
- quint32 payloadSize() const;
-
void setType(FrameType type);
- FrameType type() const;
-
void setFlags(FrameFlags flags);
void addFlag(FrameFlag flag);
- FrameFlags flags() const;
-
- quint32 streamID() const;
// All append functions also update frame's payload
// length.
@@ -144,7 +143,11 @@ public:
qToBigEndian(val, wired);
append(wired, wired + sizeof val);
}
- void append(uchar val);
+ void append(uchar val)
+ {
+ frame.buffer.push_back(val);
+ updatePayloadSize();
+ }
void append(Settings identifier)
{
append(quint16(identifier));
@@ -156,25 +159,23 @@ public:
void append(const uchar *begin, const uchar *end);
- // Write 'frameBuffer' as a single frame:
+ // Write as a single frame:
bool write(QAbstractSocket &socket) const;
- // Write as a single frame if we can, or write headers and
- // CONTINUATION(s) frame(s).
+ // Two types of frames we are sending are affected by
+ // frame size limits: HEADERS and DATA. HEADERS' payload
+ // (hpacked HTTP headers, following a frame header)
+ // is always in our 'buffer', we send the initial HEADERS
+ // frame first and then CONTINUTATION frame(s) if needed:
bool writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit);
- // Write either a single DATA frame or several DATA frames
- // depending on 'sizeLimit'. Data itself is 'external' to
- // FrameWriter, since it's a 'readPointer' from QNonContiguousData.
+ // With DATA frames the actual payload is never in our 'buffer',
+ // it's a 'readPointer' from QNonContiguousData. We split
+ // this payload as needed into DATA frames with correct
+ // payload size fitting into frame size limit:
bool writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
const uchar *src, quint32 size);
-
- std::vector<uchar> &rawFrameBuffer()
- {
- return frameBuffer;
- }
-
private:
void updatePayloadSize();
- std::vector<uchar> frameBuffer;
+ Frame frame;
};
}
diff --git a/src/network/access/http2/http2streams.cpp b/src/network/access/http2/http2streams.cpp
index f57f8d8367..660100f5e4 100644
--- a/src/network/access/http2/http2streams.cpp
+++ b/src/network/access/http2/http2streams.cpp
@@ -49,6 +49,10 @@ QT_BEGIN_NAMESPACE
namespace Http2
{
+Stream::Stream()
+{
+}
+
Stream::Stream(const HttpMessagePair &message, quint32 id, qint32 sendSize, qint32 recvSize)
: httpPair(message),
streamID(id),
diff --git a/src/network/access/http2/http2streams_p.h b/src/network/access/http2/http2streams_p.h
index 5d9a6ab512..8a825a5457 100644
--- a/src/network/access/http2/http2streams_p.h
+++ b/src/network/access/http2/http2streams_p.h
@@ -73,11 +73,10 @@ struct Q_AUTOTEST_EXPORT Stream
closed
};
- Stream() = default;
+ Stream();
Stream(const HttpMessagePair &message, quint32 streamID, qint32 sendSize,
qint32 recvSize);
- // TODO: check includes!!!
QHttpNetworkReply *reply() const;
const QHttpNetworkRequest &request() const;
QHttpNetworkRequest &request();
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index 937686920c..68a00c6837 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -40,7 +40,7 @@
#include "qhttpnetworkconnection_p.h"
#include "qhttp2protocolhandler_p.h"
-#if !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
+#if !defined(QT_NO_HTTP)
#include "http2/bitstreams_p.h"
@@ -170,64 +170,63 @@ void QHttp2ProtocolHandler::_q_receiveReply()
Q_ASSERT(m_socket);
Q_ASSERT(m_channel);
- const auto result = inboundFrame.read(*m_socket);
- switch (result) {
- case FrameStatus::incompleteFrame:
- return;
- case FrameStatus::protocolError:
- return connectionError(PROTOCOL_ERROR, "invalid frame");
- case FrameStatus::sizeError:
- return connectionError(FRAME_SIZE_ERROR, "invalid frame size");
- default:
- break;
- }
+ do {
+ const auto result = frameReader.read(*m_socket);
+ switch (result) {
+ case FrameStatus::incompleteFrame:
+ return;
+ case FrameStatus::protocolError:
+ return connectionError(PROTOCOL_ERROR, "invalid frame");
+ case FrameStatus::sizeError:
+ return connectionError(FRAME_SIZE_ERROR, "invalid frame size");
+ default:
+ break;
+ }
- Q_ASSERT(result == FrameStatus::goodFrame);
+ Q_ASSERT(result == FrameStatus::goodFrame);
- if (continuationExpected && inboundFrame.type != FrameType::CONTINUATION)
- return connectionError(PROTOCOL_ERROR, "CONTINUATION expected");
+ inboundFrame = std::move(frameReader.inboundFrame());
- switch (inboundFrame.type) {
- case FrameType::DATA:
- handleDATA();
- break;
- case FrameType::HEADERS:
- handleHEADERS();
- break;
- case FrameType::PRIORITY:
- handlePRIORITY();
- break;
- case FrameType::RST_STREAM:
- handleRST_STREAM();
- break;
- case FrameType::SETTINGS:
- handleSETTINGS();
- break;
- case FrameType::PUSH_PROMISE:
- handlePUSH_PROMISE();
- break;
- case FrameType::PING:
- handlePING();
- break;
- case FrameType::GOAWAY:
- handleGOAWAY();
- break;
- case FrameType::WINDOW_UPDATE:
- handleWINDOW_UPDATE();
- break;
- case FrameType::CONTINUATION:
- handleCONTINUATION();
- break;
- case FrameType::LAST_FRAME_TYPE:
- // 5.1 - ignore unknown frames.
- break;
- }
-
- if (goingAway && !activeStreams.size())
- return;
+ const auto frameType = inboundFrame.type();
+ if (continuationExpected && frameType != FrameType::CONTINUATION)
+ return connectionError(PROTOCOL_ERROR, "CONTINUATION expected");
- if (m_socket->bytesAvailable())
- QMetaObject::invokeMethod(m_channel, "_q_receiveReply", Qt::QueuedConnection);
+ switch (frameType) {
+ case FrameType::DATA:
+ handleDATA();
+ break;
+ case FrameType::HEADERS:
+ handleHEADERS();
+ break;
+ case FrameType::PRIORITY:
+ handlePRIORITY();
+ break;
+ case FrameType::RST_STREAM:
+ handleRST_STREAM();
+ break;
+ case FrameType::SETTINGS:
+ handleSETTINGS();
+ break;
+ case FrameType::PUSH_PROMISE:
+ handlePUSH_PROMISE();
+ break;
+ case FrameType::PING:
+ handlePING();
+ break;
+ case FrameType::GOAWAY:
+ handleGOAWAY();
+ break;
+ case FrameType::WINDOW_UPDATE:
+ handleWINDOW_UPDATE();
+ break;
+ case FrameType::CONTINUATION:
+ handleCONTINUATION();
+ break;
+ case FrameType::LAST_FRAME_TYPE:
+ // 5.1 - ignore unknown frames.
+ break;
+ }
+ } while (!goingAway || activeStreams.size());
}
bool QHttp2ProtocolHandler::sendRequest()
@@ -293,14 +292,14 @@ bool QHttp2ProtocolHandler::sendClientPreface()
return false;
// 6.5 SETTINGS
- outboundFrame.start(FrameType::SETTINGS, FrameFlag::EMPTY, Http2::connectionStreamID);
+ frameWriter.start(FrameType::SETTINGS, FrameFlag::EMPTY, Http2::connectionStreamID);
// MAX frame size (16 kb), disable PUSH
- outboundFrame.append(Settings::MAX_FRAME_SIZE_ID);
- outboundFrame.append(quint32(Http2::maxFrameSize));
- outboundFrame.append(Settings::ENABLE_PUSH_ID);
- outboundFrame.append(quint32(0));
+ frameWriter.append(Settings::MAX_FRAME_SIZE_ID);
+ frameWriter.append(quint32(Http2::maxFrameSize));
+ frameWriter.append(Settings::ENABLE_PUSH_ID);
+ frameWriter.append(quint32(0));
- if (!outboundFrame.write(*m_socket))
+ if (!frameWriter.write(*m_socket))
return false;
sessionRecvWindowSize = sessionMaxRecvWindowSize;
@@ -323,38 +322,38 @@ bool QHttp2ProtocolHandler::sendSETTINGS_ACK()
if (!prefaceSent && !sendClientPreface())
return false;
- outboundFrame.start(FrameType::SETTINGS, FrameFlag::ACK, Http2::connectionStreamID);
+ frameWriter.start(FrameType::SETTINGS, FrameFlag::ACK, Http2::connectionStreamID);
- return outboundFrame.write(*m_socket);
+ return frameWriter.write(*m_socket);
}
bool QHttp2ProtocolHandler::sendHEADERS(Stream &stream)
{
using namespace HPack;
- outboundFrame.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS,
- stream.streamID);
+ frameWriter.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS,
+ stream.streamID);
if (!stream.data()) {
- outboundFrame.addFlag(FrameFlag::END_STREAM);
+ frameWriter.addFlag(FrameFlag::END_STREAM);
stream.state = Stream::halfClosedLocal;
} else {
stream.state = Stream::open;
}
- outboundFrame.append(quint32()); // No stream dependency in Qt.
- outboundFrame.append(stream.weight());
+ frameWriter.append(quint32()); // No stream dependency in Qt.
+ frameWriter.append(stream.weight());
const auto headers = build_headers(stream.request(), maxHeaderListSize);
if (!headers.size()) // nothing fits into maxHeaderListSize
return false;
// Compress in-place:
- BitOStream outputStream(outboundFrame.frameBuffer);
+ BitOStream outputStream(frameWriter.outboundFrame().buffer);
if (!encoder.encodeRequest(outputStream, headers))
return false;
- return outboundFrame.writeHEADERS(*m_socket, maxFrameSize);
+ return frameWriter.writeHEADERS(*m_socket, maxFrameSize);
}
bool QHttp2ProtocolHandler::sendDATA(Stream &stream)
@@ -384,10 +383,10 @@ bool QHttp2ProtocolHandler::sendDATA(Stream &stream)
return true;
}
- outboundFrame.start(FrameType::DATA, FrameFlag::EMPTY, stream.streamID);
+ frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, stream.streamID);
const qint32 bytesWritten = std::min<qint32>(slot, chunkSize);
- if (!outboundFrame.writeDATA(*m_socket, maxFrameSize, src, bytesWritten))
+ if (!frameWriter.writeDATA(*m_socket, maxFrameSize, src, bytesWritten))
return false;
stream.data()->advanceReadPointer(bytesWritten);
@@ -400,9 +399,9 @@ bool QHttp2ProtocolHandler::sendDATA(Stream &stream)
}
if (replyPrivate->totallyUploadedData == request.contentLength()) {
- outboundFrame.start(FrameType::DATA, FrameFlag::END_STREAM, stream.streamID);
- outboundFrame.setPayloadSize(0);
- outboundFrame.write(*m_socket);
+ frameWriter.start(FrameType::DATA, FrameFlag::END_STREAM, stream.streamID);
+ frameWriter.setPayloadSize(0);
+ frameWriter.write(*m_socket);
stream.state = Stream::halfClosedLocal;
stream.data()->disconnect(this);
removeFromSuspended(stream.streamID);
@@ -417,61 +416,61 @@ bool QHttp2ProtocolHandler::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
{
Q_ASSERT(m_socket);
- outboundFrame.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
- outboundFrame.append(delta);
- return outboundFrame.write(*m_socket);
+ frameWriter.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
+ frameWriter.append(delta);
+ return frameWriter.write(*m_socket);
}
bool QHttp2ProtocolHandler::sendRST_STREAM(quint32 streamID, quint32 errorCode)
{
Q_ASSERT(m_socket);
- outboundFrame.start(FrameType::RST_STREAM, FrameFlag::EMPTY, streamID);
- outboundFrame.append(errorCode);
- return outboundFrame.write(*m_socket);
+ frameWriter.start(FrameType::RST_STREAM, FrameFlag::EMPTY, streamID);
+ frameWriter.append(errorCode);
+ return frameWriter.write(*m_socket);
}
bool QHttp2ProtocolHandler::sendGOAWAY(quint32 errorCode)
{
Q_ASSERT(m_socket);
- outboundFrame.start(FrameType::GOAWAY, FrameFlag::EMPTY, connectionStreamID);
- outboundFrame.append(quint32(connectionStreamID));
- outboundFrame.append(errorCode);
- return outboundFrame.write(*m_socket);
+ frameWriter.start(FrameType::GOAWAY, FrameFlag::EMPTY, connectionStreamID);
+ frameWriter.append(quint32(connectionStreamID));
+ frameWriter.append(errorCode);
+ return frameWriter.write(*m_socket);
}
void QHttp2ProtocolHandler::handleDATA()
{
- Q_ASSERT(inboundFrame.type == FrameType::DATA);
+ Q_ASSERT(inboundFrame.type() == FrameType::DATA);
- const auto streamID = inboundFrame.streamID;
+ const auto streamID = inboundFrame.streamID();
if (streamID == connectionStreamID)
return connectionError(PROTOCOL_ERROR, "DATA on stream 0x0");
if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
return connectionError(ENHANCE_YOUR_CALM, "DATA on invalid stream");
- if (qint32(inboundFrame.payloadSize) > sessionRecvWindowSize)
+ if (qint32(inboundFrame.payloadSize()) > sessionRecvWindowSize)
return connectionError(FLOW_CONTROL_ERROR, "Flow control error");
- sessionRecvWindowSize -= inboundFrame.payloadSize;
+ sessionRecvWindowSize -= inboundFrame.payloadSize();
if (activeStreams.contains(streamID)) {
auto &stream = activeStreams[streamID];
- if (qint32(inboundFrame.payloadSize) > stream.recvWindow) {
+ if (qint32(inboundFrame.payloadSize()) > stream.recvWindow) {
finishStreamWithError(stream, QNetworkReply::ProtocolInvalidOperationError,
QLatin1String("flow control error"));
sendRST_STREAM(streamID, FLOW_CONTROL_ERROR);
markAsReset(streamID);
deleteActiveStream(streamID);
} else {
- stream.recvWindow -= inboundFrame.payloadSize;
+ stream.recvWindow -= inboundFrame.payloadSize();
// Uncompress data if needed and append it ...
updateStream(stream, inboundFrame);
- if (inboundFrame.flags.testFlag(FrameFlag::END_STREAM)) {
+ if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) {
finishStream(stream);
deleteActiveStream(stream.streamID);
} else if (stream.recvWindow < streamInitialRecvWindowSize / 2) {
@@ -493,22 +492,23 @@ void QHttp2ProtocolHandler::handleDATA()
void QHttp2ProtocolHandler::handleHEADERS()
{
- Q_ASSERT(inboundFrame.type == FrameType::HEADERS);
+ Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
- const auto streamID = inboundFrame.streamID;
+ const auto streamID = inboundFrame.streamID();
if (streamID == connectionStreamID)
return connectionError(PROTOCOL_ERROR, "HEADERS on 0x0 stream");
if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
return connectionError(ENHANCE_YOUR_CALM, "HEADERS on invalid stream");
- if (inboundFrame.flags.testFlag(FrameFlag::PRIORITY)) {
+ const auto flags = inboundFrame.flags();
+ if (flags.testFlag(FrameFlag::PRIORITY)) {
handlePRIORITY();
if (goingAway)
return;
}
- const bool endHeaders = inboundFrame.flags.testFlag(FrameFlag::END_HEADERS);
+ const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
continuedFrames.clear();
continuedFrames.push_back(std::move(inboundFrame));
if (!endHeaders) {
@@ -521,10 +521,10 @@ void QHttp2ProtocolHandler::handleHEADERS()
void QHttp2ProtocolHandler::handlePRIORITY()
{
- Q_ASSERT(inboundFrame.type == FrameType::PRIORITY ||
- inboundFrame.type == FrameType::HEADERS);
+ Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY ||
+ inboundFrame.type() == FrameType::HEADERS);
- const auto streamID = inboundFrame.streamID;
+ const auto streamID = inboundFrame.streamID();
if (streamID == connectionStreamID)
return connectionError(PROTOCOL_ERROR, "PIRORITY on 0x0 stream");
@@ -548,37 +548,38 @@ void QHttp2ProtocolHandler::handlePRIORITY()
void QHttp2ProtocolHandler::handleRST_STREAM()
{
- Q_ASSERT(inboundFrame.type == FrameType::RST_STREAM);
+ Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
// "RST_STREAM frames MUST be associated with a stream.
// If a RST_STREAM frame is received with a stream identifier of 0x0,
// the recipient MUST treat this as a connection error (Section 5.4.1)
// of type PROTOCOL_ERROR.
- if (inboundFrame.streamID == connectionStreamID)
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
return connectionError(PROTOCOL_ERROR, "RST_STREAM on 0x0");
- if (!(inboundFrame.streamID & 0x1)) {
+ if (!(streamID & 0x1)) {
// RST_STREAM on a promised stream:
// since we do not keep track of such streams,
// just ignore.
return;
}
- if (inboundFrame.streamID >= nextID) {
+ if (streamID >= nextID) {
// "RST_STREAM frames MUST NOT be sent for a stream
// in the "idle" state. .. the recipient MUST treat this
// as a connection error (Section 5.4.1) of type PROTOCOL_ERROR."
return connectionError(PROTOCOL_ERROR, "RST_STREAM on idle stream");
}
- if (!activeStreams.contains(inboundFrame.streamID)) {
+ if (!activeStreams.contains(streamID)) {
// 'closed' stream, ignore.
return;
}
Q_ASSERT(inboundFrame.dataSize() == 4);
- Stream &stream = activeStreams[inboundFrame.streamID];
+ Stream &stream = activeStreams[streamID];
finishStreamWithError(stream, qFromBigEndian<quint32>(inboundFrame.dataBegin()));
markAsReset(stream.streamID);
deleteActiveStream(stream.streamID);
@@ -587,12 +588,12 @@ void QHttp2ProtocolHandler::handleRST_STREAM()
void QHttp2ProtocolHandler::handleSETTINGS()
{
// 6.5 SETTINGS.
- Q_ASSERT(inboundFrame.type == FrameType::SETTINGS);
+ Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
- if (inboundFrame.streamID != connectionStreamID)
+ if (inboundFrame.streamID() != connectionStreamID)
return connectionError(PROTOCOL_ERROR, "SETTINGS on invalid stream");
- if (inboundFrame.flags.testFlag(FrameFlag::ACK)) {
+ if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
if (!waitingForSettingsACK)
return connectionError(PROTOCOL_ERROR, "unexpected SETTINGS ACK");
waitingForSettingsACK = false;
@@ -618,7 +619,7 @@ void QHttp2ProtocolHandler::handleSETTINGS()
void QHttp2ProtocolHandler::handlePUSH_PROMISE()
{
// 6.6 PUSH_PROMISE.
- Q_ASSERT(inboundFrame.type == FrameType::PUSH_PROMISE);
+ Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
if (prefaceSent && !waitingForSettingsACK) {
// This means, server ACKed our 'NO PUSH',
@@ -626,7 +627,7 @@ void QHttp2ProtocolHandler::handlePUSH_PROMISE()
return connectionError(PROTOCOL_ERROR, "unexpected PUSH_PROMISE frame");
}
- const auto streamID = inboundFrame.streamID;
+ const auto streamID = inboundFrame.streamID();
if (streamID == connectionStreamID) {
return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with invalid associated stream (0x0)");
@@ -648,7 +649,7 @@ void QHttp2ProtocolHandler::handlePUSH_PROMISE()
sendRST_STREAM(reservedID, REFUSE_STREAM);
markAsReset(reservedID);
- const bool endHeaders = inboundFrame.flags.testFlag(FrameFlag::END_HEADERS);
+ const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
continuedFrames.clear();
continuedFrames.push_back(std::move(inboundFrame));
@@ -664,30 +665,30 @@ void QHttp2ProtocolHandler::handlePING()
{
// Since we're implementing a client and not
// a server, we only reply to a PING, ACKing it.
- Q_ASSERT(inboundFrame.type == FrameType::PING);
+ Q_ASSERT(inboundFrame.type() == FrameType::PING);
Q_ASSERT(m_socket);
- if (inboundFrame.streamID != connectionStreamID)
+ if (inboundFrame.streamID() != connectionStreamID)
return connectionError(PROTOCOL_ERROR, "PING on invalid stream");
- if (inboundFrame.flags & FrameFlag::ACK)
+ if (inboundFrame.flags() & FrameFlag::ACK)
return connectionError(PROTOCOL_ERROR, "unexpected PING ACK");
Q_ASSERT(inboundFrame.dataSize() == 8);
- outboundFrame.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
- outboundFrame.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
- outboundFrame.write(*m_socket);
+ frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
+ frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
+ frameWriter.write(*m_socket);
}
void QHttp2ProtocolHandler::handleGOAWAY()
{
// 6.8 GOAWAY
- Q_ASSERT(inboundFrame.type == FrameType::GOAWAY);
+ Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
// "An endpoint MUST treat a GOAWAY frame with a stream identifier
// other than 0x0 as a connection error (Section 5.4.1) of type PROTOCOL_ERROR."
- if (inboundFrame.streamID != connectionStreamID)
+ if (inboundFrame.streamID() != connectionStreamID)
return connectionError(PROTOCOL_ERROR, "GOAWAY on invalid stream");
const auto src = inboundFrame.dataBegin();
@@ -740,12 +741,12 @@ void QHttp2ProtocolHandler::handleGOAWAY()
void QHttp2ProtocolHandler::handleWINDOW_UPDATE()
{
- Q_ASSERT(inboundFrame.type == FrameType::WINDOW_UPDATE);
+ Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
- const auto streamID = inboundFrame.streamID;
+ const auto streamID = inboundFrame.streamID();
if (streamID == Http2::connectionStreamID) {
if (!valid || sum_will_overflow(sessionSendWindowSize, delta))
@@ -776,13 +777,13 @@ void QHttp2ProtocolHandler::handleWINDOW_UPDATE()
void QHttp2ProtocolHandler::handleCONTINUATION()
{
- Q_ASSERT(inboundFrame.type == FrameType::CONTINUATION);
+ Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
Q_ASSERT(continuedFrames.size()); // HEADERS frame must be already in.
- if (inboundFrame.streamID != continuedFrames.front().streamID)
+ if (inboundFrame.streamID() != continuedFrames.front().streamID())
return connectionError(PROTOCOL_ERROR, "CONTINUATION on invalid stream");
- const bool endHeaders = inboundFrame.flags.testFlag(FrameFlag::END_HEADERS);
+ const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
continuedFrames.push_back(std::move(inboundFrame));
if (!endHeaders)
@@ -796,9 +797,9 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
{
Q_ASSERT(continuedFrames.size());
- const auto streamID = continuedFrames[0].streamID;
+ const auto streamID = continuedFrames[0].streamID();
- if (continuedFrames[0].type == FrameType::HEADERS) {
+ if (continuedFrames[0].type() == FrameType::HEADERS) {
if (activeStreams.contains(streamID)) {
Stream &stream = activeStreams[streamID];
if (stream.state != Stream::halfClosedLocal) {
@@ -841,11 +842,11 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
if (!decoder.decodeHeaderFields(inputStream))
return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
- if (continuedFrames[0].type == FrameType::HEADERS) {
+ if (continuedFrames[0].type() == FrameType::HEADERS) {
if (activeStreams.contains(streamID)) {
Stream &stream = activeStreams[streamID];
updateStream(stream, decoder.decodedHeader());
- if (continuedFrames[0].flags & FrameFlag::END_STREAM) {
+ if (continuedFrames[0].flags() & FrameFlag::END_STREAM) {
finishStream(stream);
deleteActiveStream(stream.streamID);
}
@@ -953,9 +954,9 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
emit httpReply->headerChanged();
}
-void QHttp2ProtocolHandler::updateStream(Stream &stream, const Http2::FrameReader &frame)
+void QHttp2ProtocolHandler::updateStream(Stream &stream, const Frame &frame)
{
- Q_ASSERT(frame.type == FrameType::DATA);
+ Q_ASSERT(frame.type() == FrameType::DATA);
if (const auto length = frame.dataSize()) {
const char *data = reinterpret_cast<const char *>(frame.dataBegin());
@@ -1214,4 +1215,4 @@ void QHttp2ProtocolHandler::closeSession()
QT_END_NAMESPACE
-#endif // !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
+#endif // !defined(QT_NO_HTTP)
diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h
index b146e37dd3..92c6851078 100644
--- a/src/network/access/qhttp2protocolhandler_p.h
+++ b/src/network/access/qhttp2protocolhandler_p.h
@@ -55,7 +55,7 @@
#include <private/qabstractprotocolhandler_p.h>
#include <private/qhttpnetworkrequest_p.h>
-#if !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
+#if !defined(QT_NO_HTTP)
#include "http2/http2protocol_p.h"
#include "http2/http2streams_p.h"
@@ -124,7 +124,7 @@ private:
bool acceptSetting(Http2::Settings identifier, quint32 newValue);
void updateStream(Stream &stream, const HPack::HttpHeader &headers);
- void updateStream(Stream &stream, const Http2::FrameReader &dataFrame);
+ void updateStream(Stream &stream, const Http2::Frame &dataFrame);
void finishStream(Stream &stream);
// Error code send by a peer (GOAWAY/RST_STREAM):
void finishStreamWithError(Stream &stream, quint32 errorCode);
@@ -161,12 +161,13 @@ private:
// Peer's max frame size.
quint32 maxFrameSize = Http2::maxFrameSize;
- Http2::FrameReader inboundFrame;
- Http2::FrameWriter outboundFrame;
+ Http2::FrameReader frameReader;
+ Http2::Frame inboundFrame;
+ Http2::FrameWriter frameWriter;
// Temporary storage to assemble HEADERS' block
// from several CONTINUATION frames ...
bool continuationExpected = false;
- std::vector<Http2::FrameReader> continuedFrames;
+ std::vector<Http2::Frame> continuedFrames;
// Peer's max number of streams ...
quint32 maxConcurrentStreams = Http2::maxConcurrentStreams;
@@ -202,6 +203,6 @@ private:
QT_END_NAMESPACE
-#endif // !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
+#endif // !defined(QT_NO_HTTP)
#endif
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 09cea8e769..74fc23957c 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -86,7 +86,7 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host
, channelCount((type == QHttpNetworkConnection::ConnectionTypeSPDY || type == QHttpNetworkConnection::ConnectionTypeHTTP2)
? 1 : defaultHttpChannelCount)
#else
-, channelCount(defaultHttpChannelCount)
+, channelCount(type == QHttpNetworkConnection::ConnectionTypeHTTP2 ? 1 : defaultHttpChannelCount)
#endif // QT_NO_SSL
#ifndef QT_NO_NETWORKPROXY
, networkProxy(QNetworkProxy::NoProxy)
@@ -619,13 +619,11 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
break;
}
}
-#ifndef QT_NO_SSL
- else { // SPDY
+ else { // SPDY, HTTP/2
if (!pair.second->d_func()->requestIsPrepared)
prepareRequest(pair);
channels[0].spdyRequestsToSend.insertMulti(request.priority(), pair);
}
-#endif // QT_NO_SSL
// For Happy Eyeballs the networkLayerState is set to Unknown
// untill we have started the first connection attempt. So no
@@ -1013,9 +1011,9 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
}
break;
}
- case QHttpNetworkConnection::ConnectionTypeSPDY:
- case QHttpNetworkConnection::ConnectionTypeHTTP2: {
-#ifndef QT_NO_SSL
+ case QHttpNetworkConnection::ConnectionTypeHTTP2:
+ case QHttpNetworkConnection::ConnectionTypeSPDY: {
+
if (channels[0].spdyRequestsToSend.isEmpty())
return;
@@ -1027,7 +1025,6 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
if (channels[0].socket && channels[0].socket->state() == QAbstractSocket::ConnectedState
&& !channels[0].pendingEncrypt)
channels[0].sendRequest();
-#endif // QT_NO_SSL
break;
}
}
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 87f88aad5f..b6a9b80511 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -179,8 +179,11 @@ void QHttpNetworkConnectionChannel::init()
if (!sslConfiguration.isNull())
sslSocket->setSslConfiguration(sslConfiguration);
} else {
-#endif // QT_NO_SSL
- protocolHandler.reset(new QHttpProtocolHandler(this));
+#endif // !QT_NO_SSL
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2)
+ protocolHandler.reset(new QHttp2ProtocolHandler(this));
+ else
+ protocolHandler.reset(new QHttpProtocolHandler(this));
#ifndef QT_NO_SSL
}
#endif
@@ -835,10 +838,17 @@ void QHttpNetworkConnectionChannel::_q_connected()
#endif
} else {
state = QHttpNetworkConnectionChannel::IdleState;
- if (!reply)
- connection->d_func()->dequeueRequest(socket);
- if (reply)
- sendRequest();
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
+ 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);
+ }
+ } else {
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ sendRequest();
+ }
}
}
@@ -972,9 +982,12 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
}
} while (!connection->d_func()->highPriorityQueue.isEmpty()
|| !connection->d_func()->lowPriorityQueue.isEmpty());
+
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
#ifndef QT_NO_SSL
- if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
- connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
+ || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY
+#endif
+ ) {
QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
for (int a = 0; a < spdyPairs.count(); ++a) {
// emit error for all replies
@@ -983,7 +996,6 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
emit currentReply->finishedWithError(errorCode, errorString);
}
}
-#endif // QT_NO_SSL
// send the next request
QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
@@ -1005,20 +1017,19 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
#ifndef QT_NO_NETWORKPROXY
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
{
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
#ifndef QT_NO_SSL
- if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
- connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
+ || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY
+#endif
+ ) {
connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
} else { // HTTP
-#endif // QT_NO_SSL
// Need to dequeue the request before we can emit the error.
if (!reply)
connection->d_func()->dequeueRequest(socket);
if (reply)
connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
-#ifndef QT_NO_SSL
}
-#endif // QT_NO_SSL
}
#endif
@@ -1028,6 +1039,19 @@ void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
sendRequest();
}
+void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::NetworkError error,
+ const char *message)
+{
+ if (reply)
+ emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
+ QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
+ for (int a = 0; a < spdyPairs.count(); ++a) {
+ QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ Q_ASSERT(currentReply);
+ emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
+ }
+}
+
#ifndef QT_NO_SSL
void QHttpNetworkConnectionChannel::_q_encrypted()
{
@@ -1078,9 +1102,10 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
// we call setSpdyWasUsed(true) on the replies in the SPDY handler when the request is sent
- if (spdyRequestsToSend.count() > 0)
+ 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);
+ }
} else { // HTTP
if (!reply)
connection->d_func()->dequeueRequest(socket);
@@ -1102,19 +1127,6 @@ void QHttpNetworkConnectionChannel::requeueSpdyRequests()
spdyRequestsToSend.clear();
}
-void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::NetworkError error,
- const char *message)
-{
- if (reply)
- emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
- QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
- for (int a = 0; a < spdyPairs.count(); ++a) {
- QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
- Q_ASSERT(currentReply);
- emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
- }
-}
-
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
{
if (!socket)
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index d7d5d86a7a..61aea9d35d 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -121,18 +121,21 @@ public:
bool authenticationCredentialsSent;
bool proxyCredentialsSent;
QScopedPointer<QAbstractProtocolHandler> protocolHandler;
+ // SPDY or HTTP/2 requests; SPDY is TLS-only, but
+ // HTTP/2 can be cleartext also, that's why it's
+ // outside of QT_NO_SSL section. Sorted by priority:
+ QMultiMap<int, HttpMessagePair> spdyRequestsToSend;
#ifndef QT_NO_SSL
bool ignoreAllSslErrors;
QList<QSslError> ignoreSslErrorsList;
QSslConfiguration sslConfiguration;
- QMultiMap<int, HttpMessagePair> spdyRequestsToSend; // sorted by priority
void ignoreSslErrors();
void ignoreSslErrors(const QList<QSslError> &errors);
void setSslConfiguration(const QSslConfiguration &config);
void requeueSpdyRequests(); // when we wanted SPDY but got HTTP
+#endif
// to emit the signal for all in-flight replies:
void emitFinishedWithError(QNetworkReply::NetworkError error, const char *message);
-#endif
#ifndef QT_NO_BEARERMANAGEMENT
QSharedPointer<QNetworkSession> networkSession;
#endif
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index e16519c2f2..1dca7f02fb 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -285,10 +285,11 @@ void QHttpThreadDelegate::startRequest()
urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
QHttpNetworkConnection::ConnectionType connectionType
- = QHttpNetworkConnection::ConnectionTypeHTTP;
+ = httpRequest.isHTTP2Allowed() ? QHttpNetworkConnection::ConnectionTypeHTTP2
+ : QHttpNetworkConnection::ConnectionTypeHTTP;
+
#ifndef QT_NO_SSL
if (httpRequest.isHTTP2Allowed() && ssl) {
- connectionType = QHttpNetworkConnection::ConnectionTypeHTTP2;
QList<QByteArray> protocols;
protocols << QSslConfiguration::ALPNProtocolHTTP2
<< QSslConfiguration::NextProtocolHttp1_1;
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 63332d4fd1..29362b81e2 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -260,6 +260,14 @@ QT_BEGIN_NAMESPACE
Indicates whether SPDY was used for receiving
this reply.
+ \value HTTP2AllowedAttribute
+ Requests only, type: QMetaType::Bool (default: false)
+ Indicates whether the QNetworkAccessManager code is
+ allowed to use HTTP/2 with this request. This applies
+ to SSL requests or 'cleartext' HTTP/2.
+
+ \omitvalue HTTP2WasUsedAttribute
+
\value EmitAllUploadProgressSignalsAttribute
Requests only, type: QMetaType::Bool (default: false)
Indicates whether all upload signals should be emitted.
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
index 34d2f164e8..8f37e28669 100644
--- a/src/network/kernel/kernel.pri
+++ b/src/network/kernel/kernel.pri
@@ -56,14 +56,15 @@ win32: {
}
mac {
- LIBS_PRIVATE += -framework SystemConfiguration -framework CoreFoundation
+ LIBS_PRIVATE += -framework CoreFoundation
!uikit: LIBS_PRIVATE += -framework CoreServices
+ !if(watchos:CONFIG(device, simulator|device)): LIBS_PRIVATE += -framework SystemConfiguration
}
osx:SOURCES += kernel/qnetworkproxy_mac.cpp
else:win32:SOURCES += kernel/qnetworkproxy_win.cpp
-else:contains(QT_CONFIG, libproxy) {
+else: qtConfig(libproxy) {
SOURCES += kernel/qnetworkproxy_libproxy.cpp
- LIBS_PRIVATE += -lproxy
+ QMAKE_USE_PRIVATE += libproxy
}
else:SOURCES += kernel/qnetworkproxy_generic.cpp
diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp
index 22d2e6bb70..c5d1adbef0 100644
--- a/src/network/kernel/qnetworkinterface.cpp
+++ b/src/network/kernel/qnetworkinterface.cpp
@@ -365,7 +365,7 @@ void QNetworkAddressEntry::setBroadcast(const QHostAddress &newBroadcast)
Not all operating systems support reporting all features. Only the
IPv4 addresses are guaranteed to be listed by this class in all
platforms. In particular, IPv6 address listing is only supported
- on Windows, Linux, OS X and the BSDs.
+ on Windows, Linux, \macos and the BSDs.
\sa QNetworkAddressEntry
*/
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index 2f7e9d537d..bb1a7d0d8b 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -1557,7 +1557,7 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact
those settings are not found, this function will attempt to obtain
Internet Explorer's settings and use them.
- On MacOS X, this function will obtain the proxy settings using the
+ On \macos, this function will obtain the proxy settings using the
SystemConfiguration framework from Apple. It will apply the FTP,
HTTP and HTTPS proxy configurations for queries that contain the
protocol tag "ftp", "http" and "https", respectively. If the SOCKS
@@ -1576,7 +1576,7 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact
listed here.
\list
- \li On MacOS X, this function will ignore the Proxy Auto Configuration
+ \li On \macos, this function will ignore the Proxy Auto Configuration
settings, since it cannot execute the associated ECMAScript code.
\li On Windows platforms, this function may take several seconds to
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index be60d187ad..87ef31fb22 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -428,7 +428,7 @@
allowed to rebind, even if they pass ReuseAddressHint. This option
provides more security than ShareAddress, but on certain operating
systems, it requires you to run the server with administrator privileges.
- On Unix and OS X, not sharing is the default behavior for binding
+ On Unix and \macos, not sharing is the default behavior for binding
an address and port, so this option is ignored. On Windows, this
option uses the SO_EXCLUSIVEADDRUSE socket option.
@@ -438,7 +438,7 @@
socket option.
\value DefaultForPlatform The default option for the current platform.
- On Unix and OS X, this is equivalent to (DontShareAddress
+ On Unix and \macos, this is equivalent to (DontShareAddress
+ ReuseAddressHint), and on Windows, its equivalent to ShareAddress.
*/
@@ -693,19 +693,12 @@ bool QAbstractSocketPrivate::canReadNotification()
qDebug("QAbstractSocketPrivate::canReadNotification()");
#endif
- if (!isBuffered) {
- if (hasPendingData) {
- socketEngine->setReadNotificationEnabled(false);
- return true;
- }
- hasPendingData = true;
- }
-
// If buffered, read data from the socket into the read buffer
- qint64 newBytes = 0;
if (isBuffered) {
+ const qint64 oldBufferSize = buffer.size();
+
// Return if there is no space in the buffer
- if (readBufferMaxSize && buffer.size() >= readBufferMaxSize) {
+ if (readBufferMaxSize && oldBufferSize >= readBufferMaxSize) {
socketEngine->setReadNotificationEnabled(false);
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::canReadNotification() buffer is full");
@@ -715,7 +708,6 @@ bool QAbstractSocketPrivate::canReadNotification()
// If reading from the socket fails after getting a read
// notification, close the socket.
- newBytes = buffer.size();
if (!readFromSocket()) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::canReadNotification() disconnecting socket");
@@ -723,21 +715,28 @@ bool QAbstractSocketPrivate::canReadNotification()
q->disconnectFromHost();
return false;
}
- newBytes = buffer.size() - newBytes;
+
+ // Return if there is no new data available.
+ if (buffer.size() == oldBufferSize) {
+ // If the socket is opened only for writing, return true
+ // to indicate that the data was discarded.
+ return !q->isReadable();
+ }
+ } else {
+ if (hasPendingData) {
+ socketEngine->setReadNotificationEnabled(false);
+ return true;
+ }
+ hasPendingData = true;
}
- // Only emit readyRead() if there is data available.
- if (newBytes > 0 || !isBuffered)
- emitReadyRead();
+ emitReadyRead();
- // If we were closed as a result of the readyRead() signal,
- // return.
- if (state == QAbstractSocket::UnconnectedState || state == QAbstractSocket::ClosingState) {
#if defined (QABSTRACTSOCKET_DEBUG)
+ // If we were closed as a result of the readyRead() signal.
+ if (state == QAbstractSocket::UnconnectedState || state == QAbstractSocket::ClosingState)
qDebug("QAbstractSocketPrivate::canReadNotification() socket is closing - returning");
#endif
- return true;
- }
return true;
}
diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp
index a1085f726a..94143c2dc0 100644
--- a/src/network/socket/qlocalserver.cpp
+++ b/src/network/socket/qlocalserver.cpp
@@ -147,7 +147,7 @@ QLocalServer::~QLocalServer()
and are created based on the umask. Setting the access flags will
overide this and will restrict or permit access as specified.
- Other Unix-based operating systems, such as OS X, do not
+ Other Unix-based operating systems, such as \macos, do not
honor file permissions for Unix domain sockets and by default
have WorldAccess and these permission flags will have no effect.
diff --git a/src/network/socket/qlocalsocket_tcp.cpp b/src/network/socket/qlocalsocket_tcp.cpp
index 4348b819d9..379cdd096d 100644
--- a/src/network/socket/qlocalsocket_tcp.cpp
+++ b/src/network/socket/qlocalsocket_tcp.cpp
@@ -245,8 +245,8 @@ void QLocalSocket::connectToServer(OpenMode openMode)
QLatin1String("QLocalSocket::connectToServer"));
return;
}
- d->tcpSocket->connectToHost(QHostAddress::LocalHost, port, openMode);
QIODevice::open(openMode);
+ d->tcpSocket->connectToHost(QHostAddress::LocalHost, port, openMode);
}
bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index d7d7dad8e7..d1efc21e09 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -317,7 +317,7 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
case QNativeSocketEngine::BindExclusively:
case QNativeSocketEngine::NonBlockingSocketOption:
case QNativeSocketEngine::BroadcastSocketOption:
- return true;
+ return -1;
case QNativeSocketEngine::MaxStreamsSocketOption: {
#ifndef QT_NO_SCTP
sctp_initmsg sctpInitMsg;
diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri
index b1c0b6bd6e..793ea60b07 100644
--- a/src/network/socket/socket.pri
+++ b/src/network/socket/socket.pri
@@ -27,7 +27,7 @@ SOURCES += socket/qabstractsocketengine.cpp \
# SCTP support.
-contains(QT_CONFIG, sctp) {
+qtConfig(sctp) {
HEADERS += socket/qsctpserver.h \
socket/qsctpserver_p.h \
socket/qsctpsocket.h \
@@ -80,6 +80,6 @@ integrity: {
DEFINES += QT_LOCALSOCKET_TCP
}
-contains(QT_CONFIG, system-proxies) {
+qtConfig(system-proxies) {
DEFINES += QT_USE_SYSTEM_PROXIES
}
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp
index dec8e12abb..c92d8fc3f8 100644
--- a/src/network/ssl/qsslcontext_openssl.cpp
+++ b/src/network/ssl/qsslcontext_openssl.cpp
@@ -320,7 +320,7 @@ init_context:
}
if (!dhparams.isEmpty()) {
- const QByteArray &params = dhparams.d.data()->derData;
+ const QByteArray &params = dhparams.d->derData;
const char *ptr = params.constData();
DH *dh = q_d2i_DHparams(NULL, reinterpret_cast<const unsigned char **>(&ptr), params.length());
if (dh == NULL)
diff --git a/src/network/ssl/qssldiffiehellmanparameters.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp
index e75ffe4da5..d0fcb3189a 100644
--- a/src/network/ssl/qssldiffiehellmanparameters.cpp
+++ b/src/network/ssl/qssldiffiehellmanparameters.cpp
@@ -77,7 +77,7 @@ QT_BEGIN_NAMESPACE
QSslDiffieHellmanParameters QSslDiffieHellmanParameters::defaultParameters()
{
// The 1024-bit MODP group from RFC 2459 (Second Oakley Group)
- return QSslDiffieHellmanParameters(
+ return fromEncoded(
QByteArray::fromBase64(QByteArrayLiteral(
"MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR"
"Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL"
@@ -100,73 +100,82 @@ QSslDiffieHellmanParameters QSslDiffieHellmanParameters::defaultParameters()
QSslDiffieHellmanParameters::QSslDiffieHellmanParameters()
: d(new QSslDiffieHellmanParametersPrivate)
{
+ d->ref.ref();
}
/*!
Constructs a QSslDiffieHellmanParameters object using
the byte array \a encoded in either PEM or DER form as specified by \a encoding.
- After construction, the isValid() method should be used to
+ Use the isValid() method on the returned object to
check whether the Diffie-Hellman parameters were valid and
loaded correctly.
\sa isValid()
\sa QSslConfiguration
*/
-QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(const QByteArray &encoded, QSsl::EncodingFormat encoding)
- : d(new QSslDiffieHellmanParametersPrivate)
+QSslDiffieHellmanParameters QSslDiffieHellmanParameters::fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat encoding)
{
+ QSslDiffieHellmanParameters result;
switch (encoding) {
case QSsl::Der:
- d->decodeDer(encoded);
+ result.d->decodeDer(encoded);
break;
case QSsl::Pem:
- d->decodePem(encoded);
+ result.d->decodePem(encoded);
break;
}
+ return result;
}
/*!
Constructs a QSslDiffieHellmanParameters object by
reading from \a device in either PEM or DER form as specified by \a encoding.
- After construction, the isValid() method should be used
+ Use the isValid() method on the returned object
to check whether the Diffie-Hellman parameters were valid
and loaded correctly.
+ In particular, if \a device is \c nullptr or not open for reading, an invalid
+ object will be returned.
+
\sa isValid()
\sa QSslConfiguration
*/
-QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(QIODevice *device, QSsl::EncodingFormat encoding)
- : d(new QSslDiffieHellmanParametersPrivate)
+QSslDiffieHellmanParameters QSslDiffieHellmanParameters::fromEncoded(QIODevice *device, QSsl::EncodingFormat encoding)
{
- if (!device)
- return;
-
- const QByteArray encoded = device->readAll();
-
- switch (encoding) {
- case QSsl::Der:
- d->decodeDer(encoded);
- break;
- case QSsl::Pem:
- d->decodePem(encoded);
- break;
- }
+ if (device)
+ return fromEncoded(device->readAll(), encoding);
+ else
+ return QSslDiffieHellmanParameters();
}
/*!
Constructs an identical copy of \a other.
*/
-QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other) : d(other.d)
+QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other)
+ : d(other.d)
{
+ if (d)
+ d->ref.ref();
}
/*!
+ \fn QSslDiffieHellmanParameters(QSslDiffieHellmanParameters &&other)
+
+ Move-constructs from \a other.
+
+ \note The moved-from object \a other is placed in a partially-formed state, in which
+ the only valid operations are destruction and assignment of a new value.
+*/
+
+/*!
Destroys the QSslDiffieHellmanParameters object.
*/
QSslDiffieHellmanParameters::~QSslDiffieHellmanParameters()
{
+ if (d && !d->ref.deref())
+ delete d;
}
/*!
@@ -177,7 +186,8 @@ QSslDiffieHellmanParameters::~QSslDiffieHellmanParameters()
*/
QSslDiffieHellmanParameters &QSslDiffieHellmanParameters::operator=(const QSslDiffieHellmanParameters &other)
{
- d = other.d;
+ QSslDiffieHellmanParameters copy(other);
+ swap(copy);
return *this;
}
@@ -185,6 +195,9 @@ QSslDiffieHellmanParameters &QSslDiffieHellmanParameters::operator=(const QSslDi
\fn QSslDiffieHellmanParameters &QSslDiffieHellmanParameters::operator=(QSslDiffieHellmanParameters &&other)
Move-assigns \a other to this QSslDiffieHellmanParameters instance.
+
+ \note The moved-from object \a other is placed in a partially-formed state, in which
+ the only valid operations are destruction and assignment of a new value.
*/
/*!
@@ -265,6 +278,7 @@ QString QSslDiffieHellmanParameters::errorString() const Q_DECL_NOTHROW
}
/*!
+ \since 5.8
\relates QSslDiffieHellmanParameters
Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false.
@@ -276,6 +290,7 @@ bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanP
#ifndef QT_NO_DEBUG_STREAM
/*!
+ \since 5.8
\relates QSslDiffieHellmanParameters
Writes the set of Diffie-Hellman parameters in \a dhparam into the debug object \a debug for
@@ -295,7 +310,8 @@ QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam)
#endif
/*!
- \relates QHash
+ \since 5.8
+ \relates QSslDiffieHellmanParameters
Returns an hash value for \a dhparam, using \a seed to seed
the calculation.
diff --git a/src/network/ssl/qssldiffiehellmanparameters.h b/src/network/ssl/qssldiffiehellmanparameters.h
index aa40be83a6..4533ea4ed2 100644
--- a/src/network/ssl/qssldiffiehellmanparameters.h
+++ b/src/network/ssl/qssldiffiehellmanparameters.h
@@ -82,24 +82,25 @@ public:
Q_NETWORK_EXPORT static QSslDiffieHellmanParameters defaultParameters();
Q_NETWORK_EXPORT QSslDiffieHellmanParameters();
- Q_NETWORK_EXPORT explicit QSslDiffieHellmanParameters(const QByteArray &encoded, QSsl::EncodingFormat format = QSsl::Pem);
- Q_NETWORK_EXPORT explicit QSslDiffieHellmanParameters(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
Q_NETWORK_EXPORT QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other);
+ QSslDiffieHellmanParameters(QSslDiffieHellmanParameters &&other) Q_DECL_NOTHROW : d(other.d) { other.d = nullptr; }
Q_NETWORK_EXPORT ~QSslDiffieHellmanParameters();
+
Q_NETWORK_EXPORT QSslDiffieHellmanParameters &operator=(const QSslDiffieHellmanParameters &other);
-#ifdef Q_COMPILER_RVALUE_REFS
QSslDiffieHellmanParameters &operator=(QSslDiffieHellmanParameters &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
void swap(QSslDiffieHellmanParameters &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ Q_NETWORK_EXPORT static QSslDiffieHellmanParameters fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat format = QSsl::Pem);
+ Q_NETWORK_EXPORT static QSslDiffieHellmanParameters fromEncoded(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
+
Q_NETWORK_EXPORT bool isEmpty() const Q_DECL_NOTHROW;
Q_NETWORK_EXPORT bool isValid() const Q_DECL_NOTHROW;
- Q_NETWORK_EXPORT QSslDiffieHellmanParameters::Error error() const Q_DECL_NOTHROW;
+ Q_NETWORK_EXPORT Error error() const Q_DECL_NOTHROW;
Q_NETWORK_EXPORT QString errorString() const Q_DECL_NOTHROW;
private:
- QExplicitlySharedDataPointer<QSslDiffieHellmanParametersPrivate> d;
+ QSslDiffieHellmanParametersPrivate *d;
friend class QSslContext;
friend Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW;
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 2371dd7212..29e1f32815 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -142,7 +142,7 @@
addDefaultCaCertificates(), and QSslConfiguration::defaultConfiguration().setCaCertificates().
\endlist
- \note If available, root certificates on Unix (excluding OS X) will be
+ \note If available, root certificates on Unix (excluding \macos) will be
loaded on demand from the standard certificate directories. If you do not
want to load root certificates on demand, you need to call either
QSslConfiguration::defaultConfiguration().setCaCertificates() before the first
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
index 438ea9a38e..4c06404415 100644
--- a/src/network/ssl/qsslsocket_mac.cpp
+++ b/src/network/ssl/qsslsocket_mac.cpp
@@ -204,7 +204,7 @@ bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
-#if !defined(QT_PLATFORM_UIKIT) // dhparam is not used on iOS or tvOS. (see the SSLSetDiffieHellmanParams call below)
+#if !defined(QT_PLATFORM_UIKIT) // dhparam is only used on macOS. (see the SSLSetDiffieHellmanParams call below)
static const uint8_t dhparam[] =
"\x30\x82\x01\x08\x02\x82\x01\x01\x00\x97\xea\xd0\x46\xf7\xae\xa7\x76\x80"
"\x9c\x74\x56\x98\xd8\x56\x97\x2b\x20\x6c\x77\xe2\x82\xbb\xc8\x84\xbe\xe7"
@@ -223,7 +223,7 @@ static const uint8_t dhparam[] =
"\x90\x0b\x35\x64\xff\xd9\xe3\xac\xf2\xf2\xeb\x3a\x63\x02\x01\x02";
#endif
-// No ioErr on iOS/tvOS. (defined in MacErrors.h on OS X)
+// No ioErr on iOS/tvOS/watchOS. (defined in MacErrors.h on macOS)
#if defined(QT_PLATFORM_UIKIT)
# define ioErr -36
#endif
diff --git a/src/network/ssl/qsslsocket_mac_p.h b/src/network/ssl/qsslsocket_mac_p.h
index 5210fb7f30..9e1d18981e 100644
--- a/src/network/ssl/qsslsocket_mac_p.h
+++ b/src/network/ssl/qsslsocket_mac_p.h
@@ -75,7 +75,7 @@ public:
private:
SSLContextRef context;
- Q_DISABLE_COPY(QSecureTransportContext);
+ Q_DISABLE_COPY(QSecureTransportContext)
};
class QSslSocketBackendPrivate : public QSslSocketPrivate
@@ -122,7 +122,7 @@ private:
QSecureTransportContext context;
- Q_DISABLE_COPY(QSslSocketBackendPrivate);
+ Q_DISABLE_COPY(QSslSocketBackendPrivate)
};
QT_END_NAMESPACE
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
index edbbeadf51..8139af50af 100644
--- a/src/network/ssl/ssl.pri
+++ b/src/network/ssl/ssl.pri
@@ -1,5 +1,5 @@
# OpenSSL support; compile in QSslSocket.
-contains(QT_CONFIG, ssl) {
+qtConfig(ssl) {
HEADERS += ssl/qasn1element_p.h \
ssl/qssl.h \
ssl/qssl_p.h \
@@ -45,7 +45,7 @@ contains(QT_CONFIG, ssl) {
ssl/qsslellipticcurve_dummy.cpp
}
- contains(QT_CONFIG, securetransport) {
+ qtConfig(securetransport) {
HEADERS += ssl/qsslsocket_mac_p.h
SOURCES += ssl/qsslcertificate_qt.cpp \
ssl/qssldiffiehellmanparameters_dummy.cpp \
@@ -56,7 +56,7 @@ contains(QT_CONFIG, ssl) {
ssl/qsslellipticcurve_dummy.cpp
}
- contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
+ qtConfig(openssl)|qtConfig(openssl-linked) {
HEADERS += ssl/qsslcontext_openssl_p.h \
ssl/qsslsocket_openssl_p.h \
ssl/qsslsocket_openssl_symbols_p.h