summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShane Kearns <ext-shane.2.kearns@nokia.com>2012-03-27 20:28:44 +0100
committerPasi Pentikäinen <ext-pasi.a.pentikainen@nokia.com>2012-04-03 14:30:23 +0200
commit3eaad41a87a8af6534104a8ff108e1a2c2be66f4 (patch)
tree638a082c8baaf65351c80ea32ea44d1c845da497
parent276fb04edb450eb1bf6736aa8753be6027016291 (diff)
Fix regressions due to partial QSslSocket::peek fix
The fix broke HTTPS transactions with chunked encoding. It also broke use of a QSslSocket in unencrypted mode where peek and read calls are mixed. See change 68b1d5c17aa38d5921bdade2b0e0cb67c6c90513. Change-Id: Ib115b3737b0e4217496f5def10aaaea3c6452ff8 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> (cherry picked from commit 621f18955082fc73471e75d1f8c35c2dcd4befeb) Reviewed-by: Jaakko Helanti <ext-jaakko.helanti@nokia.com> Reviewed-by: Pasi Pentikäinen <ext-pasi.a.pentikainen@nokia.com>
-rw-r--r--src/corelib/io/qiodevice_p.h5
-rw-r--r--src/network/ssl/qsslsocket.cpp38
-rw-r--r--tests/auto/qsslsocket/tst_qsslsocket.cpp132
3 files changed, 163 insertions, 12 deletions
diff --git a/src/corelib/io/qiodevice_p.h b/src/corelib/io/qiodevice_p.h
index 881d85904c..c6b27b1e16 100644
--- a/src/corelib/io/qiodevice_p.h
+++ b/src/corelib/io/qiodevice_p.h
@@ -110,6 +110,11 @@ public:
first += r;
return r;
}
+ int peek(char* target, int size) {
+ int r = qMin(size, len);
+ memcpy(target, first, r);
+ return r;
+ }
char* reserve(int size) {
makeSpace(size + len, freeSpaceAtEnd);
char* writePtr = first + len;
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 7fc36c8523..2b809a70d3 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -2249,15 +2249,24 @@ void QSslSocketPrivate::_q_flushReadBuffer()
qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize)
{
if (mode == QSslSocket::UnencryptedMode && !autoStartHandshake) {
- if (plainSocket)
- return plainSocket->peek(data, maxSize);
- else
+ //unencrypted mode - do not use QIODevice::peek, as it reads ahead data from the plain socket
+ //peek at data already in the QIODevice buffer (from a previous read)
+ qint64 r = buffer.peek(data, maxSize);
+ if (r == maxSize)
+ return r;
+ data += r;
+ //peek at data in the plain socket
+ if (plainSocket) {
+ qint64 r2 = plainSocket->peek(data, maxSize - r);
+ if (r2 < 0)
+ return (r > 0 ? r : r2);
+ return r + r2;
+ } else {
return -1;
+ }
} else {
- QByteArray tmp;
- tmp = readBuffer.peek(maxSize);
- memcpy(data, tmp.data(), tmp.length());
- return tmp.length();
+ //encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer
+ return QTcpSocketPrivate::peek(data, maxSize);
}
}
@@ -2267,14 +2276,21 @@ qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize)
QByteArray QSslSocketPrivate::peek(qint64 maxSize)
{
if (mode == QSslSocket::UnencryptedMode && !autoStartHandshake) {
+ //unencrypted mode - do not use QIODevice::peek, as it reads ahead data from the plain socket
+ //peek at data already in the QIODevice buffer (from a previous read)
+ QByteArray ret;
+ ret.reserve(maxSize);
+ ret.resize(buffer.peek(ret.data(), maxSize));
+ if (ret.length() == maxSize)
+ return ret;
+ //peek at data in the plain socket
if (plainSocket)
- return plainSocket->peek(maxSize);
+ return ret + plainSocket->peek(maxSize - ret.length());
else
return QByteArray();
} else {
- QByteArray tmp;
- tmp = readBuffer.peek(maxSize);
- return tmp;
+ //encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer
+ return QTcpSocketPrivate::peek(maxSize);
}
}
diff --git a/tests/auto/qsslsocket/tst_qsslsocket.cpp b/tests/auto/qsslsocket/tst_qsslsocket.cpp
index cf4d04e07c..1c430955cf 100644
--- a/tests/auto/qsslsocket/tst_qsslsocket.cpp
+++ b/tests/auto/qsslsocket/tst_qsslsocket.cpp
@@ -191,6 +191,7 @@ private slots:
void writeBigChunk();
void blacklistedCertificates();
void qtbug18498_peek();
+ void qtbug18498_peek2();
void setEmptyDefaultConfiguration();
static void exitLoop()
@@ -2119,7 +2120,7 @@ void tst_QSslSocket::qtbug18498_peek()
QVERIFY(server.listen(QHostAddress::LocalHost));
client->connectToHost("127.0.0.1", server.serverPort());
QVERIFY(client->waitForConnected(5000));
- QVERIFY(server.waitForNewConnection(0));
+ QVERIFY(server.waitForNewConnection(1000));
client->setObjectName("client");
client->ignoreSslErrors();
@@ -2159,6 +2160,135 @@ void tst_QSslSocket::qtbug18498_peek()
QCOMPARE(read_data, data);
}
+class SslServer5 : public QTcpServer
+{
+ Q_OBJECT
+public:
+ SslServer5() : socket(0) {}
+ QSslSocket *socket;
+
+protected:
+ void incomingConnection(int socketDescriptor)
+ {
+ socket = new QSslSocket;
+ socket->setSocketDescriptor(socketDescriptor);
+ }
+};
+
+void tst_QSslSocket::qtbug18498_peek2()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ SslServer5 listener;
+ QVERIFY(listener.listen(QHostAddress::Any));
+ QScopedPointer<QSslSocket> client(new QSslSocket);
+ client->connectToHost(QHostAddress::LocalHost, listener.serverPort());
+ QVERIFY(client->waitForConnected(5000));
+ QVERIFY(listener.waitForNewConnection(1000));
+
+ QScopedPointer<QSslSocket> server(listener.socket);
+
+ QVERIFY(server->write("HELLO\r\n", 7));
+ QElapsedTimer stopwatch;
+ stopwatch.start();
+ while (client->bytesAvailable() < 7 && stopwatch.elapsed() < 5000)
+ QTest::qWait(100);
+ char c;
+ QVERIFY(client->peek(&c,1) == 1);
+ QCOMPARE(c, 'H');
+ QVERIFY(client->read(&c,1) == 1);
+ QCOMPARE(c, 'H');
+ QByteArray b = client->peek(2);
+ QCOMPARE(b, QByteArray("EL"));
+ char a[3];
+ QVERIFY(client->peek(a, 2) == 2);
+ QCOMPARE(a[0], 'E');
+ QCOMPARE(a[1], 'L');
+ QCOMPARE(client->readAll(), QByteArray("ELLO\r\n"));
+
+ //check data split between QIODevice and plain socket buffers.
+ QByteArray bigblock;
+ bigblock.fill('#', QIODEVICE_BUFFERSIZE + 1024);
+ QVERIFY(client->write(QByteArray("head")));
+ QVERIFY(client->write(bigblock));
+ while (server->bytesAvailable() < bigblock.length() + 4 && stopwatch.elapsed() < 5000)
+ QTest::qWait(100);
+ QCOMPARE(server->read(4), QByteArray("head"));
+ QCOMPARE(server->peek(bigblock.length()), bigblock);
+ b.reserve(bigblock.length());
+ b.resize(server->peek(b.data(), bigblock.length()));
+ QCOMPARE(b, bigblock);
+
+ //check oversized peek
+ QCOMPARE(server->peek(bigblock.length() * 3), bigblock);
+ b.reserve(bigblock.length() * 3);
+ b.resize(server->peek(b.data(), bigblock.length() * 3));
+ QCOMPARE(b, bigblock);
+
+ QCOMPARE(server->readAll(), bigblock);
+
+ QVERIFY(client->write("STARTTLS\r\n"));
+ stopwatch.start();
+ // ### Qt5 use QTRY_VERIFY
+ while (server->bytesAvailable() < 10 && stopwatch.elapsed() < 5000)
+ QTest::qWait(100);
+ QVERIFY(server->peek(&c,1) == 1);
+ QCOMPARE(c, 'S');
+ b = server->peek(3);
+ QCOMPARE(b, QByteArray("STA"));
+ QCOMPARE(server->read(5), QByteArray("START"));
+ QVERIFY(server->peek(a, 3) == 3);
+ QCOMPARE(a[0], 'T');
+ QCOMPARE(a[1], 'L');
+ QCOMPARE(a[2], 'S');
+ QCOMPARE(server->readAll(), QByteArray("TLS\r\n"));
+
+ QFile file(SRCDIR "certs/fluke.key");
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ QVERIFY(!key.isNull());
+ server->setPrivateKey(key);
+
+ QList<QSslCertificate> localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert");
+ QVERIFY(!localCert.isEmpty());
+ QVERIFY(localCert.first().handle());
+ server->setLocalCertificate(localCert.first());
+
+ server->setProtocol(QSsl::AnyProtocol);
+ server->setPeerVerifyMode(QSslSocket::VerifyNone);
+
+ server->ignoreSslErrors();
+ client->ignoreSslErrors();
+
+ server->startServerEncryption();
+ client->startClientEncryption();
+
+ QVERIFY(server->write("hello\r\n", 7));
+ stopwatch.start();
+ while (client->bytesAvailable() < 7 && stopwatch.elapsed() < 5000)
+ QTest::qWait(100);
+ QVERIFY(server->mode() == QSslSocket::SslServerMode && client->mode() == QSslSocket::SslClientMode);
+ QVERIFY(client->peek(&c,1) == 1);
+ QCOMPARE(c, 'h');
+ QVERIFY(client->read(&c,1) == 1);
+ QCOMPARE(c, 'h');
+ b = client->peek(2);
+ QCOMPARE(b, QByteArray("el"));
+ QCOMPARE(client->readAll(), QByteArray("ello\r\n"));
+
+ QVERIFY(client->write("goodbye\r\n"));
+ stopwatch.start();
+ while (server->bytesAvailable() < 9 && stopwatch.elapsed() < 5000)
+ QTest::qWait(100);
+ QVERIFY(server->peek(&c,1) == 1);
+ QCOMPARE(c, 'g');
+ QCOMPARE(server->readAll(), QByteArray("goodbye\r\n"));
+ client->disconnectFromHost();
+ QVERIFY(client->waitForDisconnected(5000));
+}
+
void tst_QSslSocket::setEmptyDefaultConfiguration()
{
// used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265