diff options
author | Sergio Ahumada <sahumada@blackberry.com> | 2014-03-13 15:57:03 +0100 |
---|---|---|
committer | Sergio Ahumada <sahumada@blackberry.com> | 2014-03-13 15:57:11 +0100 |
commit | 27016b89ae49da091527548af0ca9022ec4d7902 (patch) | |
tree | de408d49d52dd6a73e75a612ef51342d6ee44638 /src/network/access/qspdyprotocolhandler.cpp | |
parent | 3683bc97d2ebc0de3272fc09656b90a88f5eb326 (diff) | |
parent | fbaa6d3ca6fc2693c5c8a1cd8e565803adc97730 (diff) |
Merge remote-tracking branch 'origin/stable' into dev
Change-Id: Idec54e19963e8d88c711cb179cffc81596323899
Diffstat (limited to 'src/network/access/qspdyprotocolhandler.cpp')
-rw-r--r-- | src/network/access/qspdyprotocolhandler.cpp | 111 |
1 files changed, 78 insertions, 33 deletions
diff --git a/src/network/access/qspdyprotocolhandler.cpp b/src/network/access/qspdyprotocolhandler.cpp index 098b3e9ab0..6d22ebeb35 100644 --- a/src/network/access/qspdyprotocolhandler.cpp +++ b/src/network/access/qspdyprotocolhandler.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2014 BlackBerry Limited. All rights reserved. +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -275,6 +276,7 @@ QSpdyProtocolHandler::QSpdyProtocolHandler(QHttpNetworkConnectionChannel *channe // when sending the headers because of the CRIME attack zlibRet = deflateInit(&m_deflateStream, /* compression level = */ 0); Q_ASSERT(zlibRet == Z_OK); + Q_UNUSED(zlibRet); // silence -Wunused-variable } QSpdyProtocolHandler::~QSpdyProtocolHandler() @@ -307,11 +309,13 @@ bool QSpdyProtocolHandler::sendRequest() currentReply->setSpdyWasUsed(true); qint32 streamID = generateNextStreamID(); + currentReply->setProperty("SPDYStreamID", streamID); currentReply->setRequest(currentRequest); currentReply->d_func()->connection = m_connection; currentReply->d_func()->connectionChannel = m_channel; m_inFlightStreams.insert(streamID, currentPair); + connect(currentReply, SIGNAL(destroyed(QObject*)), this, SLOT(_q_replyDestroyed(QObject*))); sendSYN_STREAM(currentPair, streamID, /* associatedToStreamID = */ 0); int requestsRemoved = m_channel->spdyRequestsToSend.remove( @@ -323,6 +327,13 @@ bool QSpdyProtocolHandler::sendRequest() return true; } +void QSpdyProtocolHandler::_q_replyDestroyed(QObject* reply) +{ + qint32 streamID = reply->property("SPDYStreamID").toInt(); + if (m_inFlightStreams.remove(streamID)) + sendRST_STREAM(streamID, RST_STREAM_CANCEL); +} + void QSpdyProtocolHandler::_q_receiveReply() { Q_ASSERT(m_socket); @@ -618,6 +629,7 @@ void QSpdyProtocolHandler::sendControlFrame(FrameType type, Q_ASSERT(written == 8); written = m_socket->write(data, length); Q_ASSERT(written == length); + Q_UNUSED(written); // silence -Wunused-variable } void QSpdyProtocolHandler::sendSYN_STREAM(HttpMessagePair messagePair, @@ -694,6 +706,10 @@ void QSpdyProtocolHandler::sendPING(quint32 pingID) bool QSpdyProtocolHandler::uploadData(qint32 streamID) { // we only rely on SPDY flow control here and don't care about TCP buffers + if (!m_inFlightStreams.contains(streamID)) { + sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM); + return false; + } HttpMessagePair messagePair = m_inFlightStreams.value(streamID); QHttpNetworkRequest request = messagePair.first; @@ -702,6 +718,11 @@ bool QSpdyProtocolHandler::uploadData(qint32 streamID) QHttpNetworkReplyPrivate *replyPrivate = reply->d_func(); Q_ASSERT(replyPrivate); + if (reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYHalfClosed || reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYClosed) { + qWarning() << Q_FUNC_INFO << "Trying to upload to closed stream"; + return false; + } + qint32 dataLeftInWindow = replyPrivate->windowSizeUpload - replyPrivate->currentlyUploadedDataInWindow; @@ -747,6 +768,8 @@ bool QSpdyProtocolHandler::uploadData(qint32 streamID) Q_ASSERT(writeSize == 0); Q_UNUSED(writeSize); // silence -Wunused-variable replyPrivate->state = QHttpNetworkReplyPrivate::SPDYHalfClosed; + if (reply->request().uploadByteDevice()) + reply->request().uploadByteDevice()->disconnect(this); // ### this will not work if the content length is not known, but // then again many servers will fail in this case anyhow according // to the SPDY RFC @@ -874,6 +897,10 @@ void QSpdyProtocolHandler::handleSYN_REPLY(char flags, quint32 /*length*/, const void QSpdyProtocolHandler::parseHttpHeaders(char flags, const QByteArray &frameData) { qint32 streamID = getStreamID(frameData.constData()); + if (!m_inFlightStreams.contains(streamID)) { + sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM); + return; + } flags &= 0x3f; bool flag_fin = flags & 0x01; @@ -884,6 +911,11 @@ void QSpdyProtocolHandler::parseHttpHeaders(char flags, const QByteArray &frameD QHttpNetworkReply *httpReply = pair.second; Q_ASSERT(httpReply != 0); + if (httpReply->d_func()->state == QHttpNetworkReplyPrivate::SPDYClosed) { + sendRST_STREAM(streamID, RST_STREAM_STREAM_ALREADY_CLOSED); + return; + } + QByteArray uncompressedHeader; if (!uncompressHeader(headerValuePairs, &uncompressedHeader)) { qWarning() << Q_FUNC_INFO << "error reading header from SYN_REPLY message"; @@ -891,16 +923,31 @@ void QSpdyProtocolHandler::parseHttpHeaders(char flags, const QByteArray &frameD } qint32 headerCount = fourBytesToInt(uncompressedHeader.constData()); + if (headerCount * 8 > uncompressedHeader.size()) { + qWarning() << Q_FUNC_INFO << "error parsing header from SYN_REPLY message"; + sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR); + return; + } qint32 readPointer = 4; for (qint32 a = 0; a < headerCount; ++a) { qint32 count = fourBytesToInt(uncompressedHeader.constData() + readPointer); readPointer += 4; QByteArray name = uncompressedHeader.mid(readPointer, count); readPointer += count; + if (readPointer > uncompressedHeader.size()) { + qWarning() << Q_FUNC_INFO << "error parsing header from SYN_REPLY message"; + sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR); + return; + } count = fourBytesToInt(uncompressedHeader.constData() + readPointer); readPointer += 4; QByteArray value = uncompressedHeader.mid(readPointer, count); readPointer += count; + if (readPointer > uncompressedHeader.size()) { + qWarning() << Q_FUNC_INFO << "error parsing header from SYN_REPLY message"; + sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR); + return; + } if (name == ":status") { httpReply->setStatusCode(value.left(3).toInt()); httpReply->d_func()->reasonPhrase = QString::fromLatin1(value.mid(4)); @@ -931,22 +978,9 @@ void QSpdyProtocolHandler::parseHttpHeaders(char flags, const QByteArray &frameD emit httpReply->headerChanged(); if (flag_fin) { - switch (httpReply->d_func()->state) { - case QHttpNetworkReplyPrivate::SPDYSYNSent: - httpReply->d_func()->state = QHttpNetworkReplyPrivate::SPDYHalfClosed; - break; - case QHttpNetworkReplyPrivate::SPDYHalfClosed: - replyFinished(httpReply, streamID); - break; - case QHttpNetworkReplyPrivate::SPDYClosed: { - sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR); - replyFinishedWithError(httpReply, streamID, QNetworkReply::ProtocolFailure, - "server sent SYN_REPLY on an already closed stream"); - break; - } - default: - qWarning() << Q_FUNC_INFO << "got data frame in unknown state"; - } + if (httpReply->d_func()->state != QHttpNetworkReplyPrivate::SPDYHalfClosed) + sendDataFrame(streamID, DataFrame_FLAG_FIN, 0, 0); + replyFinished(httpReply, streamID); } } @@ -1143,11 +1177,20 @@ void QSpdyProtocolHandler::handleWINDOW_UPDATE(char /*flags*/, quint32 /*length* qint32 streamID = getStreamID(frameData.constData()); qint32 deltaWindowSize = fourBytesToInt(frameData.constData() + 4); + if (!m_inFlightStreams.contains(streamID)) { + sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM); + return; + } + QHttpNetworkReply *reply = m_inFlightStreams.value(streamID).second; Q_ASSERT(reply); QHttpNetworkReplyPrivate *replyPrivate = reply->d_func(); Q_ASSERT(replyPrivate); + // Ignore WINDOW_UPDATE if we are already done. + if (replyPrivate->state == QHttpNetworkReplyPrivate::SPDYHalfClosed || replyPrivate->state == QHttpNetworkReplyPrivate::SPDYClosed) + return; + replyPrivate->currentlyUploadedDataInWindow = replyPrivate->windowSizeUpload - deltaWindowSize; uploadData(streamID); // we hopefully can continue to upload } @@ -1158,6 +1201,11 @@ void QSpdyProtocolHandler::handleDataFrame(const QByteArray &frameHeaders) Q_ASSERT(frameHeaders.count() >= 8); qint32 streamID = getStreamID(frameHeaders.constData()); + if (!m_inFlightStreams.contains(streamID)) { + sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM); + return; + } + unsigned char flags = static_cast<unsigned char>(frameHeaders.at(4)); flags &= 0x3f; bool flag_fin = flags & 0x01; @@ -1182,6 +1230,11 @@ void QSpdyProtocolHandler::handleDataFrame(const QByteArray &frameHeaders) QHttpNetworkReplyPrivate *replyPrivate = httpReply->d_func(); + if (replyPrivate->state == QHttpNetworkReplyPrivate::SPDYClosed) { + sendRST_STREAM(streamID, RST_STREAM_STREAM_ALREADY_CLOSED); + return; + } + // check whether we need to send WINDOW_UPDATE (i.e. tell the sender it can send more) replyPrivate->currentlyReceivedDataInWindow += length; qint32 dataLeftInWindow = replyPrivate->windowSizeDownload - replyPrivate->currentlyReceivedDataInWindow; @@ -1234,29 +1287,18 @@ void QSpdyProtocolHandler::handleDataFrame(const QByteArray &frameHeaders) } if (flag_fin) { - switch (httpReply->d_func()->state) { - case QHttpNetworkReplyPrivate::SPDYSYNSent: - httpReply->d_func()->state = QHttpNetworkReplyPrivate::SPDYHalfClosed; - // ### send FIN ourselves? - break; - case QHttpNetworkReplyPrivate::SPDYHalfClosed: - replyFinished(httpReply, streamID); - break; - case QHttpNetworkReplyPrivate::SPDYClosed: { - sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR); - replyFinishedWithError(httpReply, streamID, QNetworkReply::ProtocolFailure, - "server sent data on an already closed stream"); - break; - } - default: - qWarning() << Q_FUNC_INFO << "got data frame in unknown state"; - } + if (httpReply->d_func()->state != QHttpNetworkReplyPrivate::SPDYHalfClosed) + sendDataFrame(streamID, DataFrame_FLAG_FIN, 0, 0); + replyFinished(httpReply, streamID); } } void QSpdyProtocolHandler::replyFinished(QHttpNetworkReply *httpReply, qint32 streamID) { httpReply->d_func()->state = QHttpNetworkReplyPrivate::SPDYClosed; + httpReply->disconnect(this); + if (httpReply->request().uploadByteDevice()) + httpReply->request().uploadByteDevice()->disconnect(this); int streamsRemoved = m_inFlightStreams.remove(streamID); Q_ASSERT(streamsRemoved == 1); Q_UNUSED(streamsRemoved); // silence -Wunused-variable @@ -1268,6 +1310,9 @@ void QSpdyProtocolHandler::replyFinishedWithError(QHttpNetworkReply *httpReply, { Q_ASSERT(httpReply); httpReply->d_func()->state = QHttpNetworkReplyPrivate::SPDYClosed; + httpReply->disconnect(this); + if (httpReply->request().uploadByteDevice()) + httpReply->request().uploadByteDevice()->disconnect(this); int streamsRemoved = m_inFlightStreams.remove(streamID); Q_ASSERT(streamsRemoved == 1); Q_UNUSED(streamsRemoved); // silence -Wunused-variable |