diff options
Diffstat (limited to 'src/network')
60 files changed, 1769 insertions, 718 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri index ce79b06eec..f8dfda6ff3 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -8,12 +8,10 @@ HEADERS += \ access/qhttpnetworkreply_p.h \ access/qhttpnetworkconnection_p.h \ access/qhttpnetworkconnectionchannel_p.h \ - access/qfilenetworkreply_p.h \ access/qnetworkaccessmanager.h \ access/qnetworkaccessmanager_p.h \ access/qnetworkaccesscache_p.h \ access/qnetworkaccessbackend_p.h \ - access/qnetworkaccessdatabackend_p.h \ access/qnetworkaccessdebugpipebackend_p.h \ access/qnetworkaccesshttpbackend_p.h \ access/qnetworkaccessfilebackend_p.h \ @@ -29,6 +27,8 @@ HEADERS += \ access/qnetworkreply.h \ access/qnetworkreply_p.h \ access/qnetworkreplyimpl_p.h \ + access/qnetworkreplydataimpl_p.h \ + access/qnetworkreplyfileimpl_p.h \ access/qabstractnetworkcache_p.h \ access/qabstractnetworkcache.h \ access/qnetworkdiskcache_p.h \ @@ -42,11 +42,9 @@ SOURCES += \ access/qhttpnetworkreply.cpp \ access/qhttpnetworkconnection.cpp \ access/qhttpnetworkconnectionchannel.cpp \ - access/qfilenetworkreply.cpp \ access/qnetworkaccessmanager.cpp \ access/qnetworkaccesscache.cpp \ access/qnetworkaccessbackend.cpp \ - access/qnetworkaccessdatabackend.cpp \ access/qnetworkaccessdebugpipebackend.cpp \ access/qnetworkaccessfilebackend.cpp \ access/qnetworkaccesscachebackend.cpp \ @@ -57,6 +55,8 @@ SOURCES += \ access/qnetworkrequest.cpp \ access/qnetworkreply.cpp \ access/qnetworkreplyimpl.cpp \ + access/qnetworkreplydataimpl.cpp \ + access/qnetworkreplyfileimpl.cpp \ access/qabstractnetworkcache.cpp \ access/qnetworkdiskcache.cpp diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index c2eca8a809..e94b099c04 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -119,6 +119,7 @@ void QHttpNetworkConnectionPrivate::init() { for (int i = 0; i < channelCount; i++) { channels[i].setConnection(this->q_func()); + channels[i].ssl = encrypt; channels[i].init(); } } @@ -557,33 +558,35 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket) || channels[i].state == QHttpNetworkConnectionChannel::ReadingState)) return; - - //qDebug() << "QHttpNetworkConnectionPrivate::fillPipeline processing highPriorityQueue, size=" << highPriorityQueue.size() << " alreadyPipelined=" << channels[i].alreadyPipelinedRequests.length(); int lengthBefore; while (!highPriorityQueue.isEmpty()) { lengthBefore = channels[i].alreadyPipelinedRequests.length(); fillPipeline(highPriorityQueue, channels[i]); - if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) + if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) { + channels[i].pipelineFlush(); return; + } if (lengthBefore == channels[i].alreadyPipelinedRequests.length()) break; // did not process anything, now do the low prio queue } - //qDebug() << "QHttpNetworkConnectionPrivate::fillPipeline processing lowPriorityQueue, size=" << lowPriorityQueue.size() << " alreadyPipelined=" << channels[i].alreadyPipelinedRequests.length(); while (!lowPriorityQueue.isEmpty()) { lengthBefore = channels[i].alreadyPipelinedRequests.length(); fillPipeline(lowPriorityQueue, channels[i]); - if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) + if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) { + channels[i].pipelineFlush(); return; + } if (lengthBefore == channels[i].alreadyPipelinedRequests.length()) break; // did not process anything } + channels[i].pipelineFlush(); } // returns true when the processing of a queue has been done diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 82b5ce3a89..079f6081b4 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -60,6 +60,7 @@ QT_BEGIN_NAMESPACE QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel() : socket(0) + , ssl(false) , state(IdleState) , reply(0) , written(0) @@ -90,11 +91,8 @@ void QHttpNetworkConnectionChannel::init() #else socket = new QTcpSocket; #endif - - // limit the socket read buffer size. we will read everything into - // the QHttpNetworkReply anyway, so let's grow only that and not - // here and there. - socket->setReadBufferSize(64*1024); + // Set by QNAM anyway, but let's be safe here + socket->setProxy(QNetworkProxy::NoProxy); QObject::connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_bytesWritten(qint64)), @@ -174,11 +172,12 @@ bool QHttpNetworkConnectionChannel::sendRequest() written = 0; // excluding the header bytesTotal = 0; - reply->d_func()->clear(); - reply->d_func()->connection = connection; - reply->d_func()->connectionChannel = this; - reply->d_func()->autoDecompress = request.d->autoDecompress; - reply->d_func()->pipeliningUsed = false; + QHttpNetworkReplyPrivate *replyPrivate = reply->d_func(); + replyPrivate->clear(); + replyPrivate->connection = connection; + replyPrivate->connectionChannel = this; + replyPrivate->autoDecompress = request.d->autoDecompress; + replyPrivate->pipeliningUsed = false; // if the url contains authentication parameters, use the new ones // both channels will use the new authentication parameters @@ -337,7 +336,6 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() return; } - qint64 bytes = 0; QAbstractSocket::SocketState socketState = socket->state(); // connection might be closed to signal the end of data @@ -358,12 +356,14 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() } // read loop for the response - while (socket->bytesAvailable()) { + qint64 bytes = 0; + qint64 lastBytes = bytes; + do { + lastBytes = bytes; + QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state; switch (state) { case QHttpNetworkReplyPrivate::NothingDoneState: { - // only eat whitespace on the first call - eatWhitespace(); state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState; // fallthrough } @@ -387,6 +387,7 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() return; } bytes += headerBytes; + // If headers were parsed successfully now it is the ReadingDataState if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) { if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) { // remove the Content-Length from header @@ -401,6 +402,10 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() } if (replyPrivate->shouldEmitSignals()) emit reply->headerChanged(); + // After headerChanged had been emitted + // we can suddenly have a replyPrivate->userProvidedDownloadBuffer + // this is handled in the ReadingDataState however + if (!replyPrivate->expectContent()) { replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState; allDone(); @@ -422,22 +427,29 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() // to the read buffer maximum size, but we don't care since they should be small. return; } - if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress - && replyPrivate->bodyLength > 0) { - // bulk files like images should fulfill these properties and - // we can therefore save on memory copying - bytes = replyPrivate->readBodyFast(socket, &replyPrivate->responseData); - replyPrivate->totalProgress += bytes; + + if (replyPrivate->userProvidedDownloadBuffer) { + // the user provided a direct buffer where we should put all our data in. + // this only works when we can tell the user the content length and he/she can allocate + // the buffer in that size. + // note that this call will read only from the still buffered data + qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress); + bytes += haveRead; + replyPrivate->totalProgress += haveRead; + + // the user will get notified of it via progress signal + if (haveRead > 0) + emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); + } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress + && replyPrivate->bodyLength > 0) { + // bulk files like images should fulfill these properties and + // we can therefore save on memory copying + qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData); + bytes += haveRead; + replyPrivate->totalProgress += haveRead; if (replyPrivate->shouldEmitSignals()) { - QPointer<QHttpNetworkReply> replyPointer = reply; emit reply->readyRead(); - // make sure that the reply is valid - if (replyPointer.isNull()) - return; emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); - // make sure that the reply is valid - if (replyPointer.isNull()) - return; } } else @@ -445,8 +457,9 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() // use the traditional slower reading (for compressed encoding, chunked encoding, // no content-length etc) QByteDataBuffer byteDatas; - bytes = replyPrivate->readBody(socket, &byteDatas); - if (bytes) { + qint64 haveRead = replyPrivate->readBody(socket, &byteDatas); + if (haveRead) { + bytes += haveRead; if (replyPrivate->autoDecompress) replyPrivate->appendCompressedReplyData(byteDatas); else @@ -455,17 +468,10 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() if (!replyPrivate->autoDecompress) { replyPrivate->totalProgress += bytes; if (replyPrivate->shouldEmitSignals()) { - QPointer<QHttpNetworkReply> replyPointer = reply; // important: At the point of this readyRead(), the byteDatas list must be empty, // else implicit sharing will trigger memcpy when the user is reading data! emit reply->readyRead(); - // make sure that the reply is valid - if (replyPointer.isNull()) - return; emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); - // make sure that the reply is valid - if (replyPointer.isNull()) - return; } } #ifndef QT_NO_COMPRESS @@ -487,7 +493,7 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() default: break; } - } + } while (bytes != lastBytes && reply); } // called when unexpectedly reading a -1 or when data is expected but socket is closed @@ -530,7 +536,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection() if (socketState != QAbstractSocket::ConnectedState) { // connect to the host if not already connected. state = QHttpNetworkConnectionChannel::ConnectingState; - pendingEncrypt = connection->d_func()->encrypt; + pendingEncrypt = ssl; // reset state pipeliningSupported = PipeliningSupportUnknown; @@ -553,23 +559,43 @@ bool QHttpNetworkConnectionChannel::ensureConnection() #ifndef QT_NO_NETWORKPROXY // HTTPS always use transparent proxy. - if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !connection->d_func()->encrypt) { + if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) { connectHost = connection->d_func()->networkProxy.hostName(); connectPort = connection->d_func()->networkProxy.port(); } #endif - if (connection->d_func()->encrypt) { + if (ssl) { #ifndef QT_NO_OPENSSL QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); sslSocket->connectToHostEncrypted(connectHost, connectPort); if (ignoreAllSslErrors) sslSocket->ignoreSslErrors(); sslSocket->ignoreSslErrors(ignoreSslErrorsList); + + // limit the socket read buffer size. we will read everything into + // the QHttpNetworkReply anyway, so let's grow only that and not + // here and there. + socket->setReadBufferSize(64*1024); #else connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError); #endif } else { - socket->connectToHost(connectHost, connectPort); + // In case of no proxy we can use the Unbuffered QTcpSocket + if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy + && connection->cacheProxy().type() == QNetworkProxy::NoProxy + && connection->transparentProxy().type() == QNetworkProxy::NoProxy) { + socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered); + // For an Unbuffered QTcpSocket, the read buffer size has a special meaning. + socket->setReadBufferSize(1*1024); + + } else { + socket->connectToHost(connectHost, connectPort); + + // limit the socket read buffer size. we will read everything into + // the QHttpNetworkReply anyway, so let's grow only that and not + // here and there. + socket->setReadBufferSize(64*1024); + } } return false; } @@ -599,18 +625,10 @@ bool QHttpNetworkConnectionChannel::expand(bool dataComplete) reply->d_func()->totalProgress += inflated.size(); reply->d_func()->appendUncompressedReplyData(inflated); if (reply->d_func()->shouldEmitSignals()) { - QPointer<QHttpNetworkReply> replyPointer = reply; // important: At the point of this readyRead(), inflated must be cleared, // else implicit sharing will trigger memcpy when the user is reading data! emit reply->readyRead(); - // make sure that the reply is valid - if (replyPointer.isNull()) - return true; emit reply->dataReadProgress(reply->d_func()->totalProgress, 0); - // make sure that the reply is valid - if (replyPointer.isNull()) - return true; - } } } else { @@ -688,10 +706,15 @@ void QHttpNetworkConnectionChannel::allDone() // this was wrong, allDone gets called from that function anyway. } } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) { - eatWhitespace(); // this is weird. we had nothing pipelined but still bytes available. better close it. - if (socket->bytesAvailable() > 0) - close(); + //if (socket->bytesAvailable() > 0) + // close(); + // + // FIXME + // We do not close it anymore now, but should introduce this again after having fixed + // the chunked decoder in QHttpNetworkReply to read the whitespace after the last chunk. + // (Currently this is worked around by readStatus in the QHttpNetworkReply ignoring + // leading whitespace. QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } else if (alreadyPipelinedRequests.isEmpty()) { if (qobject_cast<QHttpNetworkConnection*>(connection)) @@ -738,30 +761,6 @@ void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests() QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } -void QHttpNetworkConnectionChannel::eatWhitespace() -{ - char c; - do { - qint64 ret = socket->peek(&c, 1); - - // nothing read, fine. - if (ret == 0) - return; - - // EOF from socket? - if (ret == -1) - return; // FIXME, we need to stop processing. however the next stuff done will also do that. - - // read all whitespace and line endings - if (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31) { - socket->read(&c, 1); - continue; - } else { - break; - } - } while(true); -} - void QHttpNetworkConnectionChannel::handleStatus() { Q_ASSERT(socket); @@ -822,7 +821,7 @@ bool QHttpNetworkConnectionChannel::resetUploadData() } -void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) +void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) { // this is only called for simple GET @@ -835,16 +834,32 @@ void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) reply->d_func()->pipeliningUsed = true; #ifndef QT_NO_NETWORKPROXY - QByteArray header = QHttpNetworkRequestPrivate::header(request, - (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); + pipeline.append(QHttpNetworkRequestPrivate::header(request, + (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy))); #else - QByteArray header = QHttpNetworkRequestPrivate::header(request, false); + pipeline.append(QHttpNetworkRequestPrivate::header(request, false)); #endif - socket->write(header); alreadyPipelinedRequests.append(pair); + + // pipelineFlush() needs to be called at some point afterwards } +void QHttpNetworkConnectionChannel::pipelineFlush() +{ + if (pipeline.isEmpty()) + return; + + // The goal of this is so that we have everything in one TCP packet. + // For the Unbuffered QTcpSocket this is manually needed, the buffered + // QTcpSocket does it automatically. + // Also, sometimes the OS does it for us (Nagle's algorithm) but that + // happens only sometimes. + socket->write(pipeline); + pipeline.clear(); +} + + void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest() { requeueCurrentlyPipelinedRequests(); @@ -877,6 +892,19 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const //private slots void QHttpNetworkConnectionChannel::_q_readyRead() { + // We got a readyRead but no bytes are available.. + // This happens for the Unbuffered QTcpSocket + // Also check if socket is in ConnectedState since + // this function may also be invoked via the event loop. + if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) { + char c; + qint64 ret = socket->peek(&c, 1); + if (ret < 0) { + socket->disconnectFromHost(); + return; + } + } + if (isSocketWaiting() || isSocketReading()) { state = QHttpNetworkConnectionChannel::ReadingState; if (reply) diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index 22a0d6fc9c..8cbc689d2f 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -98,6 +98,7 @@ public: BusyState = (ConnectingState|WritingState|WaitingState|ReadingState) }; QAbstractSocket *socket; + bool ssl; ChannelState state; QHttpNetworkRequest request; // current request QHttpNetworkReply *reply; // current reply for this request @@ -124,7 +125,11 @@ public: }; PipeliningSupport pipeliningSupported; QList<HttpMessagePair> alreadyPipelinedRequests; - + QByteArray pipeline; // temporary buffer that gets sent to socket in pipelineFlush + void pipelineInto(HttpMessagePair &pair); + void pipelineFlush(); + void requeueCurrentlyPipelinedRequests(); + void detectPipeliningSupport(); QHttpNetworkConnectionChannel(); @@ -144,15 +149,9 @@ public: bool resetUploadData(); // return true if resetting worked or there is no upload data - void pipelineInto(HttpMessagePair &pair); - void requeueCurrentlyPipelinedRequests(); - void detectPipeliningSupport(); - void handleUnexpectedEOF(); void closeAndResendCurrentRequest(); - void eatWhitespace(); - bool isSocketBusy() const; bool isSocketWriting() const; bool isSocketWaiting() const; diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index e60800537f..7f6b71f439 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -119,6 +119,7 @@ void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request) { Q_D(QHttpNetworkReply); d->request = request; + d->ssl = request.isSsl(); } int QHttpNetworkReply::statusCode() const @@ -201,6 +202,25 @@ void QHttpNetworkReply::setDownstreamLimited(bool dsl) d->connection->d_func()->readMoreLater(this); } +bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer() +{ + Q_D(QHttpNetworkReply); + return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0); +} + +void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b) +{ + Q_D(QHttpNetworkReply); + if (supportsUserProvidedDownloadBuffer()) + d->userProvidedDownloadBuffer = b; +} + +char* QHttpNetworkReply::userProvidedDownloadBuffer() +{ + Q_D(QHttpNetworkReply); + return d->userProvidedDownloadBuffer; +} + bool QHttpNetworkReply::isFinished() const { return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState; @@ -218,7 +238,10 @@ QHttpNetworkConnection* QHttpNetworkReply::connection() QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl) - : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100), + : QHttpNetworkHeaderPrivate(newUrl) + , state(NothingDoneState) + , ssl(false) + , statusCode(100), majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0), chunkedTransferEncoding(false), connectionCloseEnabled(true), @@ -226,6 +249,7 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl) currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false), autoDecompress(false), responseData(), requestIsPrepared(false) ,pipeliningUsed(false), downstreamLimited(false) + ,userProvidedDownloadBuffer(0) { } @@ -459,6 +483,8 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket) return -1; // unexpected EOF else if (haveRead == 0) break; // read more later + else if (haveRead == 1 && bytes == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31)) + continue; // Ignore all whitespace that was trailing froma previous request on that socket bytes++; @@ -634,12 +660,32 @@ bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled() // note this function can only be used for non-chunked, non-compressed with // known content length +qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b) +{ + // This first read is to flush the buffer inside the socket + qint64 haveRead = 0; + haveRead = socket->read(b, bodyLength - contentRead); + if (haveRead == -1) { + return 0; // ### error checking here; + } + contentRead += haveRead; + + if (contentRead == bodyLength) { + state = AllDoneState; + } + + return haveRead; +} + +// note this function can only be used for non-chunked, non-compressed with +// known content length qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb) { + qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead); QByteArray bd; bd.resize(toBeRead); - qint64 haveRead = socket->read(bd.data(), bd.size()); + qint64 haveRead = socket->read(bd.data(), toBeRead); if (haveRead == -1) { bd.clear(); return 0; // ### error checking here; @@ -661,29 +707,34 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff { qint64 bytes = 0; if (isChunked()) { - bytes += readReplyBodyChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6) - } else if (bodyLength > 0) { // we have a Content-Length + // chunked transfer encoding (rfc 2616, sec 3.6) + bytes += readReplyBodyChunked(socket, out); + } else if (bodyLength > 0) { + // we have a Content-Length bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead); if (contentRead + bytes == bodyLength) state = AllDoneState; } else { + // no content length. just read what's possible bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable()); } contentRead += bytes; return bytes; } -qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size) +qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size) { + // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable() qint64 bytes = 0; - Q_ASSERT(in); + Q_ASSERT(socket); Q_ASSERT(out); - int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable())); + int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable())); + while (toBeRead > 0) { QByteArray byteData; byteData.resize(toBeRead); - qint64 haveRead = in->read(byteData.data(), byteData.size()); + qint64 haveRead = socket->read(byteData.data(), byteData.size()); if (haveRead <= 0) { // ### error checking here byteData.clear(); @@ -695,25 +746,35 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer bytes += haveRead; size -= haveRead; - toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable())); + toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable())); } return bytes; } -qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out) +qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out) { qint64 bytes = 0; - while (in->bytesAvailable()) { // while we can read from input - // if we are done with the current chunk, get the size of the new chunk + while (socket->bytesAvailable()) { if (currentChunkRead >= currentChunkSize) { + // For the first chunk and when we're done with a chunk currentChunkSize = 0; currentChunkRead = 0; if (bytes) { + // After a chunk char crlf[2]; - bytes += in->read(crlf, 2); // read the "\r\n" after the chunk + // read the "\r\n" after the chunk + qint64 haveRead = socket->read(crlf, 2); + // FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not available yet?! + // For nice reasons (the toLong in getChunkSize accepting \n at the beginning + // it right now still works, but we should definitely fix this. + + if (haveRead != 2) + return bytes; // FIXME + bytes += haveRead; } - bytes += getChunkSize(in, ¤tChunkSize); + // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read + bytes += getChunkSize(socket, ¤tChunkSize); if (currentChunkSize == -1) break; } @@ -723,8 +784,8 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBu break; } - // otherwise, try to read what is missing for this chunk - qint64 haveRead = readReplyBodyRaw (in, out, currentChunkSize - currentChunkRead); + // otherwise, try to begin reading this chunk / to read what is missing for this chunk + qint64 haveRead = readReplyBodyRaw (socket, out, currentChunkSize - currentChunkRead); currentChunkRead += haveRead; bytes += haveRead; @@ -734,22 +795,25 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBu return bytes; } -qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize) +qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize) { qint64 bytes = 0; char crlf[2]; *chunkSize = -1; - int bytesAvailable = in->bytesAvailable(); + + int bytesAvailable = socket->bytesAvailable(); + // FIXME rewrite to permanent loop without bytesAvailable while (bytesAvailable > bytes) { - qint64 sniffedBytes = in->peek(crlf, 2); + qint64 sniffedBytes = socket->peek(crlf, 2); int fragmentSize = fragment.size(); + // check the next two bytes for a "\r\n", skip blank lines if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n') ||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n')) { - bytes += in->read(crlf, 1); // read the \r or \n + bytes += socket->read(crlf, 1); // read the \r or \n if (crlf[0] == '\r') - bytes += in->read(crlf, 1); // read the \n + bytes += socket->read(crlf, 1); // read the \n bool ok = false; // ignore the chunk-extension fragment = fragment.mid(0, fragment.indexOf(';')).trimmed(); @@ -759,10 +823,15 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize) } else { // read the fragment to the buffer char c = 0; - bytes += in->read(&c, 1); + qint64 haveRead = socket->read(&c, 1); + if (haveRead < 0) { + return -1; // FIXME + } + bytes += haveRead; fragment.append(c); } } + return bytes; } diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 812de7d0ca..1856d7a42e 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -129,6 +129,10 @@ public: QByteArray readAll(); void setDownstreamLimited(bool t); + bool supportsUserProvidedDownloadBuffer(); + void setUserProvidedDownloadBuffer(char*); + char* userProvidedDownloadBuffer(); + bool isFinished() const; bool isPipeliningUsed() const; @@ -150,6 +154,7 @@ Q_SIGNALS: void finished(); void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail = QString()); void headerChanged(); + // FIXME we need to change this to qint64! void dataReadProgress(int done, int total); void dataSendProgress(qint64 done, qint64 total); void cacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *authenticator); @@ -175,15 +180,16 @@ public: qint64 readHeader(QAbstractSocket *socket); void parseHeader(const QByteArray &header); qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out); + qint64 readBodyVeryFast(QAbstractSocket *socket, char *b); qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb); bool findChallenge(bool forProxy, QByteArray &challenge) const; QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const; void clear(); void clearHttpLayerInformation(); - qint64 readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size); - qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out); - qint64 getChunkSize(QIODevice *in, qint64 *chunkSize); + qint64 readReplyBodyRaw(QAbstractSocket *in, QByteDataBuffer *out, qint64 size); + qint64 readReplyBodyChunked(QAbstractSocket *in, QByteDataBuffer *out); + qint64 getChunkSize(QAbstractSocket *in, qint64 *chunkSize); void appendUncompressedReplyData(QByteArray &qba); void appendUncompressedReplyData(QByteDataBuffer &data); @@ -212,6 +218,7 @@ public: } state; QHttpNetworkRequest request; + bool ssl; int statusCode; int majorVersion; int minorVersion; @@ -241,6 +248,8 @@ public: bool pipeliningUsed; bool downstreamLimited; + + char* userProvidedDownloadBuffer; }; diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp index 5e5222eeb5..8573364555 100644 --- a/src/network/access/qhttpnetworkrequest.cpp +++ b/src/network/access/qhttpnetworkrequest.cpp @@ -63,6 +63,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest pipeliningAllowed = other.pipeliningAllowed; customVerb = other.customVerb; withCredentials = other.withCredentials; + ssl = other.ssl; } QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate() @@ -73,6 +74,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot { return QHttpNetworkHeaderPrivate::operator==(other) && (operation == other.operation) + && (ssl == other.ssl) && (uploadByteDevice == other.uploadByteDevice); } @@ -199,6 +201,15 @@ void QHttpNetworkRequest::setUrl(const QUrl &url) d->url = url; } +bool QHttpNetworkRequest::isSsl() const +{ + return d->ssl; +} +void QHttpNetworkRequest::setSsl(bool s) +{ + d->ssl = s; +} + qint64 QHttpNetworkRequest::contentLength() const { return d->contentLength(); diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h index 0cf88f20fb..c7e9b0f6f5 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -116,6 +116,9 @@ public: bool withCredentials() const; void setWithCredentials(bool b); + bool isSsl() const; + void setSsl(bool); + void setUploadByteDevice(QNonContiguousByteDevice *bd); QNonContiguousByteDevice* uploadByteDevice() const; @@ -146,6 +149,7 @@ public: bool autoDecompress; bool pipeliningAllowed; bool withCredentials; + bool ssl; }; diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index b1d3ae9cd1..fd1fa60d4e 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -134,7 +134,7 @@ QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice() // and the special backends need to access this. void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal) { - if (reply->isFinished()) + if (reply->isFinished) return; reply->emitUploadProgress(bytesSent, bytesTotal); } @@ -241,6 +241,17 @@ void QNetworkAccessBackend::writeDownstreamData(QIODevice *data) reply->appendDownstreamData(data); } +// not actually appending data, it was already written to the user buffer +void QNetworkAccessBackend::writeDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal) +{ + reply->appendDownstreamDataDownloadBuffer(bytesReceived, bytesTotal); +} + +char* QNetworkAccessBackend::getDownloadBuffer(qint64 size) +{ + return reply->getDownloadBuffer(size); +} + QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const { return reply->q_func()->header(header); diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h index 90a259413d..26ee61ae67 100644 --- a/src/network/access/qnetworkaccessbackend_p.h +++ b/src/network/access/qnetworkaccessbackend_p.h @@ -181,6 +181,10 @@ protected: qint64 nextDownstreamBlockSize() const; void writeDownstreamData(QByteDataBuffer &list); + // not actually appending data, it was already written to the user buffer + void writeDownstreamDataDownloadBuffer(qint64, qint64); + char* getDownloadBuffer(qint64); + public slots: // for task 251801, needs to be a slot to be called asynchronously void writeDownstreamData(QIODevice *data); diff --git a/src/network/access/qnetworkaccessdatabackend.cpp b/src/network/access/qnetworkaccessdatabackend.cpp deleted file mode 100644 index beced2f121..0000000000 --- a/src/network/access/qnetworkaccessdatabackend.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qnetworkaccessdatabackend_p.h" -#include "qnetworkrequest.h" -#include "qnetworkreply.h" -#include "qurlinfo.h" -#include "private/qdataurl_p.h" -#include <qcoreapplication.h> - -QT_BEGIN_NAMESPACE - -QNetworkAccessBackend * -QNetworkAccessDataBackendFactory::create(QNetworkAccessManager::Operation, - const QNetworkRequest &request) const -{ - if (request.url().scheme() == QLatin1String("data")) - return new QNetworkAccessDataBackend; - - return 0; -} - -QNetworkAccessDataBackend::QNetworkAccessDataBackend() -{ -} - -QNetworkAccessDataBackend::~QNetworkAccessDataBackend() -{ -} - -void QNetworkAccessDataBackend::open() -{ - QUrl uri = request().url(); - - if (operation() != QNetworkAccessManager::GetOperation && - operation() != QNetworkAccessManager::HeadOperation) { - // data: doesn't support anything but GET - const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend", - "Operation not supported on %1") - .arg(uri.toString()); - error(QNetworkReply::ContentOperationNotPermittedError, msg); - finished(); - return; - } - - QPair<QString, QByteArray> decoded = qDecodeDataUrl(uri); - - if (! decoded.first.isNull()) { - setHeader(QNetworkRequest::ContentTypeHeader, decoded.first); - setHeader(QNetworkRequest::ContentLengthHeader, decoded.second.size()); - emit metaDataChanged(); - - QByteDataBuffer list; - list.append(decoded.second); - decoded.second.clear(); // important because of implicit sharing! - writeDownstreamData(list); - - finished(); - return; - } - - // something wrong with this URI - const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend", - "Invalid URI: %1").arg(uri.toString()); - error(QNetworkReply::ProtocolFailure, msg); - finished(); -} - -void QNetworkAccessDataBackend::closeDownstreamChannel() -{ -} - -void QNetworkAccessDataBackend::closeUpstreamChannel() -{ -} - -bool QNetworkAccessDataBackend::waitForDownstreamReadyRead(int) -{ - return false; -} - -bool QNetworkAccessDataBackend::waitForUpstreamBytesWritten(int) -{ - return false; -} - -bool QNetworkAccessDataBackend::processRequestSynchronously() -{ -#ifndef QT_NO_BEARERMANAGEMENT - start(); -#else - open(); -#endif - return true; -} - -QT_END_NAMESPACE diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp index ae8a51a4dc..6da0722c66 100644 --- a/src/network/access/qnetworkaccessfilebackend.cpp +++ b/src/network/access/qnetworkaccessfilebackend.cpp @@ -65,10 +65,15 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op, } QUrl url = request.url(); - if (url.scheme() == QLatin1String("qrc") || !url.toLocalFile().isEmpty()) + if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 || url.isLocalFile()) { return new QNetworkAccessFileBackend; - else if (!url.isEmpty() && url.authority().isEmpty()) { - // check if QFile could, in theory, open this URL + } else if (!url.scheme().isEmpty() && url.authority().isEmpty()) { + // check if QFile could, in theory, open this URL via the file engines + // it has to be in the format: + // prefix:path/to/file + // or prefix:/path/to/file + // + // this construct here must match the one below in open() QFileInfo fi(url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery)); if (fi.exists() || (op == QNetworkAccessManager::PutOperation && fi.dir().exists())) return new QNetworkAccessFileBackend; diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp index 42d295532c..bad010fd44 100644 --- a/src/network/access/qnetworkaccessftpbackend.cpp +++ b/src/network/access/qnetworkaccessftpbackend.cpp @@ -77,7 +77,7 @@ QNetworkAccessFtpBackendFactory::create(QNetworkAccessManager::Operation op, } QUrl url = request.url(); - if (url.scheme() == QLatin1String("ftp")) + if (url.scheme().compare(QLatin1String("ftp"), Qt::CaseInsensitive) == 0) return new QNetworkAccessFtpBackend; return 0; } diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index 4189743b39..79c43fabda 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -72,7 +72,7 @@ static QByteArray makeCacheKey(QNetworkAccessHttpBackend *backend, QNetworkProxy QUrl copy = backend->url(); bool isEncrypted = copy.scheme().toLower() == QLatin1String("https"); copy.setPort(copy.port(isEncrypted ? DefaultHttpsPort : DefaultHttpPort)); - result = copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | + result = copy.toEncoded(QUrl::RemoveUserInfo | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment); #ifndef QT_NO_NETWORKPROXY @@ -353,10 +353,12 @@ void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest, QNetworkRequest::CacheLoadControl CacheLoadControlAttribute = (QNetworkRequest::CacheLoadControl)request().attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(); if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) { - // forced reload from the network - // tell any caching proxy servers to reload too - httpRequest.setHeaderField("Cache-Control", "no-cache"); - httpRequest.setHeaderField("Pragma", "no-cache"); + // If the request does not already specify preferred cache-control + // force reload from the network and tell any caching proxy servers to reload too + if (!request().rawHeaderList().contains("Cache-Control")) { + httpRequest.setHeaderField("Cache-Control", "no-cache"); + httpRequest.setHeaderField("Pragma", "no-cache"); + } return; } @@ -528,6 +530,9 @@ void QNetworkAccessHttpBackend::postRequest() break; // can't happen } + bool encrypt = (url().scheme().toLower() == QLatin1String("https")); + httpRequest.setSsl(encrypt); + httpRequest.setUrl(url()); QList<QByteArray> headers = request().rawHeaderList(); @@ -583,7 +588,6 @@ void QNetworkAccessHttpBackend::postRequest() SLOT(sslErrors(QList<QSslError>))); #endif - connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead())); connect(httpReply, SIGNAL(finished()), SLOT(replyFinished())); connect(httpReply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)), SLOT(httpError(QNetworkReply::NetworkError,QString))); @@ -849,9 +853,33 @@ void QNetworkAccessHttpBackend::replyHeaderChanged() if (!isCachingEnabled()) setCachingEnabled(true); } + + // Check if a download buffer is supported from the HTTP reply + char *buf = 0; + if (httpReply->supportsUserProvidedDownloadBuffer()) { + // Check if a download buffer is supported by the user + buf = getDownloadBuffer(httpReply->contentLength()); + if (buf) { + httpReply->setUserProvidedDownloadBuffer(buf); + // If there is a download buffer we react on the progress signal + connect(httpReply, SIGNAL(dataReadProgress(int,int)), SLOT(replyDownloadProgressSlot(int,int))); + } + } + + // If there is no buffer, we react on the readyRead signal + if (!buf) { + connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead())); + } + metaDataChanged(); } +void QNetworkAccessHttpBackend::replyDownloadProgressSlot(int received, int total) +{ + // we can be sure here that there is a download buffer + writeDownstreamDataDownloadBuffer(received, total); +} + void QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkRequest &, QAuthenticator *auth) { @@ -1136,6 +1164,11 @@ bool QNetworkAccessHttpBackend::canResume() const return false; } + // If we're using a download buffer then we don't support resuming/migration + // right now. Too much trouble. + if (httpReply->userProvidedDownloadBuffer()) + return false; + return true; } diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h index 85d498303d..7064d4aefa 100644 --- a/src/network/access/qnetworkaccesshttpbackend_p.h +++ b/src/network/access/qnetworkaccesshttpbackend_p.h @@ -105,6 +105,7 @@ private slots: void replyReadyRead(); void replyFinished(); void replyHeaderChanged(); + void replyDownloadProgressSlot(int,int); void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth); void httpCacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *auth); void httpError(QNetworkReply::NetworkError error, const QString &errorString); diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 5ceaed0e18..27b7945fde 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -52,10 +52,10 @@ #include "qnetworkaccesshttpbackend_p.h" #include "qnetworkaccessftpbackend_p.h" #include "qnetworkaccessfilebackend_p.h" -#include "qnetworkaccessdatabackend_p.h" #include "qnetworkaccessdebugpipebackend_p.h" #include "qnetworkaccesscachebackend_p.h" -#include "qfilenetworkreply_p.h" +#include "qnetworkreplydataimpl_p.h" +#include "qnetworkreplyfileimpl_p.h" #include "QtCore/qbuffer.h" #include "QtCore/qurl.h" @@ -70,7 +70,6 @@ QT_BEGIN_NAMESPACE Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend) #endif // QT_NO_HTTP Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend) -Q_GLOBAL_STATIC(QNetworkAccessDataBackendFactory, dataBackend) #ifndef QT_NO_FTP Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend) #endif // QT_NO_FTP @@ -84,7 +83,7 @@ static void ensureInitialized() #ifndef QT_NO_HTTP (void) httpBackend(); #endif // QT_NO_HTTP - (void) dataBackend(); + #ifndef QT_NO_FTP (void) ftpBackend(); #endif @@ -451,6 +450,8 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent) : QObject(*new QNetworkAccessManagerPrivate, parent) { ensureInitialized(); + + qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError"); } /*! @@ -945,30 +946,19 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera { Q_D(QNetworkAccessManager); - // 4.7 only hotfix fast path for data:// URLs - // In 4.8 this is solved with QNetworkReplyDataImpl and will work there - // This hotfix is done for not needing a QNetworkSession for data:// - if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation) - && (req.url().scheme() == QLatin1String("data"))) { - QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); - QNetworkReplyImplPrivate *priv = reply->d_func(); - priv->manager = this; - priv->backend = new QNetworkAccessDataBackend(); - priv->backend->manager = this->d_func(); - priv->backend->setParent(reply); - priv->backend->reply = priv; - priv->setup(op, req, outgoingData); - return reply; - } + bool isLocalFile = req.url().isLocalFile(); + QString scheme = req.url().scheme().toLower(); // fast path for GET on file:// URLs - // Also if the scheme is empty we consider it a file. // The QNetworkAccessFileBackend will right now only be used for PUT if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation) - && (req.url().scheme() == QLatin1String("file") - || req.url().scheme() == QLatin1String("qrc") - || req.url().scheme().isEmpty())) { - return new QFileNetworkReply(this, req, op); + && (isLocalFile || scheme == QLatin1String("qrc"))) { + return new QNetworkReplyFileImpl(this, req, op); + } + + if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation) + && scheme == QLatin1String("data")) { + return new QNetworkReplyDataImpl(this, req, op); } // A request with QNetworkRequest::AlwaysCache does not need any bearer management @@ -994,8 +984,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera #ifndef QT_NO_BEARERMANAGEMENT // Return a disabled network reply if network access is disabled. // Except if the scheme is empty or file://. - if (!d->networkAccessible && !(req.url().scheme() == QLatin1String("file") || - req.url().scheme().isEmpty())) { + if (!d->networkAccessible && !isLocalFile) { return new QDisabledNetworkReply(this, req, op); } @@ -1029,7 +1018,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera if (d->cookieJar) { QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url()); if (!cookies.isEmpty()) - request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies)); + request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies)); } } @@ -1037,7 +1026,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera QUrl url = request.url(); QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); #ifndef QT_NO_BEARERMANAGEMENT - if (req.url().scheme() != QLatin1String("file") && !req.url().scheme().isEmpty()) { + if (!isLocalFile) { connect(this, SIGNAL(networkSessionConnected()), reply, SLOT(_q_networkSessionConnected())); } diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp index ba7109fad7..c176dde669 100644 --- a/src/network/access/qnetworkreply.cpp +++ b/src/network/access/qnetworkreply.cpp @@ -49,6 +49,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() : readBufferMaxSize(0), operation(QNetworkAccessManager::UnknownOperation), errorCode(QNetworkReply::NoError) + , isFinished(false) { // set the default attribute values attributes.insert(QNetworkRequest::ConnectionEncryptedAttribute, false); @@ -462,7 +463,7 @@ QNetworkReply::NetworkError QNetworkReply::error() const */ bool QNetworkReply::isFinished() const { - return d_func()->isFinished(); + return d_func()->isFinished; } /*! @@ -718,6 +719,21 @@ void QNetworkReply::setError(NetworkError errorCode, const QString &errorString) } /*! + \since 4.8 + Sets the reply as \a finished. + + After having this set the replies data must not change. + + \sa isFinished() +*/ +void QNetworkReply::setFinished(bool finished) +{ + Q_D(QNetworkReply); + d->isFinished = finished; +} + + +/*! Sets the URL being processed to be \a url. Normally, the URL matches that of the request that was posted, but for a variety of reasons it can be different (for example, a file path being made diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h index efc28f7fe2..3a511cc7a9 100644 --- a/src/network/access/qnetworkreply.h +++ b/src/network/access/qnetworkreply.h @@ -163,6 +163,7 @@ protected: void setOperation(QNetworkAccessManager::Operation operation); void setRequest(const QNetworkRequest &request); void setError(NetworkError errorCode, const QString &errorString); + void setFinished(bool); void setUrl(const QUrl &url); void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value); void setRawHeader(const QByteArray &headerName, const QByteArray &value); diff --git a/src/network/access/qnetworkreply_p.h b/src/network/access/qnetworkreply_p.h index c40d8faf1f..03cc6bd060 100644 --- a/src/network/access/qnetworkreply_p.h +++ b/src/network/access/qnetworkreply_p.h @@ -71,12 +71,11 @@ public: qint64 readBufferMaxSize; QNetworkAccessManager::Operation operation; QNetworkReply::NetworkError errorCode; + bool isFinished; static inline void setManager(QNetworkReply *reply, QNetworkAccessManager *manager) { reply->d_func()->manager = manager; } - virtual bool isFinished() const { return false; } - Q_DECLARE_PUBLIC(QNetworkReply) }; diff --git a/src/network/access/qnetworkreplydataimpl.cpp b/src/network/access/qnetworkreplydataimpl.cpp new file mode 100644 index 0000000000..52cfe95b07 --- /dev/null +++ b/src/network/access/qnetworkreplydataimpl.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkreplydataimpl_p.h" +#include "private/qdataurl_p.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QMetaObject> + +QT_BEGIN_NAMESPACE + +QNetworkReplyDataImplPrivate::QNetworkReplyDataImplPrivate() + : QNetworkReplyPrivate() +{ +} + +QNetworkReplyDataImplPrivate::~QNetworkReplyDataImplPrivate() +{ +} + +QNetworkReplyDataImpl::~QNetworkReplyDataImpl() +{ +} + +QNetworkReplyDataImpl::QNetworkReplyDataImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op) + : QNetworkReply(*new QNetworkReplyDataImplPrivate(), parent) +{ + Q_D(QNetworkReplyDataImpl); + setRequest(req); + setUrl(req.url()); + setOperation(op); + setFinished(true); + QNetworkReply::open(QIODevice::ReadOnly); + + QUrl url = req.url(); + + // FIXME qDecodeDataUrl should instead be rewritten to have the QByteArray + // and the mime type as an output parameter and return a bool instead + d->decodeDataUrlResult = qDecodeDataUrl(url); + + if (! d->decodeDataUrlResult.first.isNull()) { + QString &mimeType = d->decodeDataUrlResult.first; + qint64 size = d->decodeDataUrlResult.second.size(); + setHeader(QNetworkRequest::ContentTypeHeader, mimeType); + setHeader(QNetworkRequest::ContentLengthHeader, size); + QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection); + + d->decodedData.setBuffer(&d->decodeDataUrlResult.second); + d->decodedData.open(QIODevice::ReadOnly); + + QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection, + Q_ARG(qint64,size), Q_ARG(qint64, size)); + QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + } else { + // something wrong with this URI + const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend", + "Invalid URI: %1").arg(QString::fromLatin1(url.toEncoded())); + setError(QNetworkReply::ProtocolFailure, msg); + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolFailure)); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + } +} + +void QNetworkReplyDataImpl::close() +{ + QNetworkReply::close(); +} + +void QNetworkReplyDataImpl::abort() +{ + QNetworkReply::close(); +} + +qint64 QNetworkReplyDataImpl::bytesAvailable() const +{ + Q_D(const QNetworkReplyDataImpl); + return QNetworkReply::bytesAvailable() + d->decodedData.bytesAvailable(); +} + +bool QNetworkReplyDataImpl::isSequential () const +{ + return true; +} + +qint64 QNetworkReplyDataImpl::size() const +{ + Q_D(const QNetworkReplyDataImpl); + return d->decodedData.size(); +} + +/*! + \internal +*/ +qint64 QNetworkReplyDataImpl::readData(char *data, qint64 maxlen) +{ + Q_D(QNetworkReplyDataImpl); + + // TODO idea: + // Instead of decoding the whole data into new memory, we could decode on demand. + // Note that this might be tricky to do. + + return d->decodedData.read(data, maxlen); +} + + +QT_END_NAMESPACE + +#include "moc_qnetworkreplydataimpl_p.cpp" + diff --git a/src/network/access/qnetworkreplydataimpl_p.h b/src/network/access/qnetworkreplydataimpl_p.h new file mode 100644 index 0000000000..6c62d28243 --- /dev/null +++ b/src/network/access/qnetworkreplydataimpl_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKREPLYDATAIMPL_H +#define QNETWORKREPLYDATAIMPL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qnetworkreply.h" +#include "qnetworkreply_p.h" +#include "qnetworkaccessmanager.h" +#include <QBuffer> + +QT_BEGIN_NAMESPACE + + +class QNetworkReplyDataImplPrivate; +class QNetworkReplyDataImpl: public QNetworkReply +{ + Q_OBJECT +public: + QNetworkReplyDataImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op); + ~QNetworkReplyDataImpl(); + virtual void abort(); + + // reimplemented from QNetworkReply + virtual void close(); + virtual qint64 bytesAvailable() const; + virtual bool isSequential () const; + qint64 size() const; + + virtual qint64 readData(char *data, qint64 maxlen); + + Q_DECLARE_PRIVATE(QNetworkReplyDataImpl) +}; + +class QNetworkReplyDataImplPrivate: public QNetworkReplyPrivate +{ +public: + QNetworkReplyDataImplPrivate(); + ~QNetworkReplyDataImplPrivate(); + + QPair<QString, QByteArray> decodeDataUrlResult; + QBuffer decodedData; + + Q_DECLARE_PUBLIC(QNetworkReplyDataImpl) +}; + +QT_END_NAMESPACE + +#endif // QNETWORKREPLYDATAIMPL_H diff --git a/src/network/access/qfilenetworkreply.cpp b/src/network/access/qnetworkreplyfileimpl.cpp index 004892878d..686eb5f921 100644 --- a/src/network/access/qfilenetworkreply.cpp +++ b/src/network/access/qnetworkreplyfileimpl.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#include "qfilenetworkreply_p.h" +#include "qnetworkreplyfileimpl_p.h" #include "QtCore/qdatetime.h" #include <QtCore/QCoreApplication> @@ -48,31 +48,30 @@ QT_BEGIN_NAMESPACE -QFileNetworkReplyPrivate::QFileNetworkReplyPrivate() +QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate() : QNetworkReplyPrivate(), fileEngine(0), fileSize(0), filePos(0) { } -QFileNetworkReplyPrivate::~QFileNetworkReplyPrivate() +QNetworkReplyFileImplPrivate::~QNetworkReplyFileImplPrivate() { delete fileEngine; } -QFileNetworkReply::~QFileNetworkReply() +QNetworkReplyFileImpl::~QNetworkReplyFileImpl() { } -QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op) - : QNetworkReply(*new QFileNetworkReplyPrivate(), parent) +QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op) + : QNetworkReply(*new QNetworkReplyFileImplPrivate(), parent) { setRequest(req); setUrl(req.url()); setOperation(op); + setFinished(true); QNetworkReply::open(QIODevice::ReadOnly); - qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError"); - - QFileNetworkReplyPrivate *d = (QFileNetworkReplyPrivate*) d_func(); + QNetworkReplyFileImplPrivate *d = (QNetworkReplyFileImplPrivate*) d_func(); QUrl url = req.url(); if (url.host() == QLatin1String("localhost")) @@ -144,54 +143,48 @@ QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); } - -bool QFileNetworkReplyPrivate::isFinished() const -{ - return true; -} - -void QFileNetworkReply::close() +void QNetworkReplyFileImpl::close() { - Q_D(QFileNetworkReply); + Q_D(QNetworkReplyFileImpl); QNetworkReply::close(); if (d->fileEngine) d->fileEngine->close(); } -void QFileNetworkReply::abort() +void QNetworkReplyFileImpl::abort() { - Q_D(QFileNetworkReply); + Q_D(QNetworkReplyFileImpl); QNetworkReply::close(); if (d->fileEngine) d->fileEngine->close(); } -qint64 QFileNetworkReply::bytesAvailable() const +qint64 QNetworkReplyFileImpl::bytesAvailable() const { - Q_D(const QFileNetworkReply); + Q_D(const QNetworkReplyFileImpl); if (!d->fileEngine) return 0; return QNetworkReply::bytesAvailable() + d->fileSize - d->filePos; } -bool QFileNetworkReply::isSequential () const +bool QNetworkReplyFileImpl::isSequential () const { return true; } -qint64 QFileNetworkReply::size() const +qint64 QNetworkReplyFileImpl::size() const { - Q_D(const QFileNetworkReply); + Q_D(const QNetworkReplyFileImpl); return d->fileSize; } /*! \internal */ -qint64 QFileNetworkReply::readData(char *data, qint64 maxlen) +qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen) { - Q_D(QFileNetworkReply); + Q_D(QNetworkReplyFileImpl); if (!d->fileEngine) return -1; @@ -208,5 +201,5 @@ qint64 QFileNetworkReply::readData(char *data, qint64 maxlen) QT_END_NAMESPACE -#include "moc_qfilenetworkreply_p.cpp" +#include "moc_qnetworkreplyfileimpl_p.cpp" diff --git a/src/network/access/qfilenetworkreply_p.h b/src/network/access/qnetworkreplyfileimpl_p.h index ace556e629..627363f822 100644 --- a/src/network/access/qfilenetworkreply_p.h +++ b/src/network/access/qnetworkreplyfileimpl_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QFILENETWORKREPLY_P_H -#define QFILENETWORKREPLY_P_H +#ifndef QNETWORKREPLYFILEIMPL_H +#define QNETWORKREPLYFILEIMPL_H // // W A R N I N G @@ -62,13 +62,13 @@ QT_BEGIN_NAMESPACE -class QFileNetworkReplyPrivate; -class QFileNetworkReply: public QNetworkReply +class QNetworkReplyFileImplPrivate; +class QNetworkReplyFileImpl: public QNetworkReply { Q_OBJECT public: - QFileNetworkReply(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op); - ~QFileNetworkReply(); + QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op); + ~QNetworkReplyFileImpl(); virtual void abort(); // reimplemented from QNetworkReply @@ -79,24 +79,22 @@ public: virtual qint64 readData(char *data, qint64 maxlen); - Q_DECLARE_PRIVATE(QFileNetworkReply) + Q_DECLARE_PRIVATE(QNetworkReplyFileImpl) }; -class QFileNetworkReplyPrivate: public QNetworkReplyPrivate +class QNetworkReplyFileImplPrivate: public QNetworkReplyPrivate { public: - QFileNetworkReplyPrivate(); - ~QFileNetworkReplyPrivate(); + QNetworkReplyFileImplPrivate(); + ~QNetworkReplyFileImplPrivate(); QAbstractFileEngine *fileEngine; qint64 fileSize; qint64 filePos; - virtual bool isFinished() const; - - Q_DECLARE_PUBLIC(QFileNetworkReply) + Q_DECLARE_PUBLIC(QNetworkReplyFileImpl) }; QT_END_NAMESPACE -#endif // QFILENETWORKREPLY_P_H +#endif // QNETWORKREPLYFILEIMPL_H diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 53d3b119dc..50dbcc267b 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -52,6 +52,8 @@ #include <QtCore/QCoreApplication> +Q_DECLARE_METATYPE(QSharedPointer<char>) + QT_BEGIN_NAMESPACE inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() @@ -62,6 +64,10 @@ inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1), httpStatusCode(0), state(Idle) + , downloadBufferReadPosition(0) + , downloadBufferCurrentSize(0) + , downloadBufferMaximumSize(0) + , downloadBuffer(0) { } @@ -111,6 +117,7 @@ void QNetworkReplyImplPrivate::_q_startOperation() if (backend->isSynchronous()) { state = Finished; + q_func()->setFinished(true); } else { if (state != Finished) { if (operation == QNetworkAccessManager::GetOperation) @@ -129,6 +136,10 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead() if (!copyDevice || !q->isOpen()) return; + // FIXME Optimize to use download buffer if it is a QBuffer. + // Needs to be done where sendCacheContents() (?) of HTTP is emitting + // metaDataChanged ? + forever { qint64 bytesToRead = nextDownstreamBlockSize(); if (bytesToRead == 0) @@ -566,8 +577,6 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions() { Q_Q(QNetworkReplyImpl); - QPointer<QNetworkReplyImpl> qq = q; - QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); if (preMigrationDownloaded != Q_INT64_C(-1)) totalSize = totalSize.toLongLong() + preMigrationDownloaded; @@ -578,13 +587,10 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions() // else implicit sharing will trigger memcpy when the user is reading data! emit q->readyRead(); - // hopefully we haven't been deleted here - if (!qq.isNull()) { - resumeNotificationHandling(); - // do we still have room in the buffer? - if (nextDownstreamBlockSize() > 0) - backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite); - } + resumeNotificationHandling(); + // do we still have room in the buffer? + if (nextDownstreamBlockSize() > 0) + backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite); } // this is used when it was fetched from the cache, right? @@ -619,6 +625,60 @@ void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data) qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented"); } +static void downloadBufferDeleter(char *ptr) +{ + delete[] ptr; +} + +char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size) +{ + Q_Q(QNetworkReplyImpl); + + // Check attribute() if allocating a buffer of that size can be allowed + if (!downloadBuffer) { + QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute); + if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) { + downloadBufferCurrentSize = 0; + downloadBufferMaximumSize = size; + downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails + downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter); + + q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer)); + } + } + + return downloadBuffer; +} + +void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal) +{ + Q_Q(QNetworkReplyImpl); + if (!q->isOpen()) + return; + + if (cacheEnabled && !cacheSaveDevice) + initCacheSaveDevice(); + + if (cacheSaveDevice && bytesReceived == bytesTotal) { +// if (lastBytesDownloaded == -1) +// lastBytesDownloaded = 0; +// cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded); + + // Write everything in one go if we use a download buffer. might be more performant. + cacheSaveDevice->write(downloadBuffer, bytesTotal); + } + + bytesDownloaded = bytesReceived; + lastBytesDownloaded = bytesReceived; + + downloadBufferCurrentSize = bytesReceived; + + emit q->downloadProgress(bytesDownloaded, bytesTotal); + // Only emit readyRead when actual data is there + if (bytesDownloaded > 0) + emit q->readyRead(); +} + void QNetworkReplyImplPrivate::finished() { Q_Q(QNetworkReplyImpl); @@ -657,6 +717,8 @@ void QNetworkReplyImplPrivate::finished() resumeNotificationHandling(); state = Finished; + q->setFinished(true); + pendingNotifications.clear(); pauseNotificationHandling(); @@ -727,11 +789,6 @@ void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors) #endif } -bool QNetworkReplyImplPrivate::isFinished() const -{ - return (state == Finished || state == Aborted); -} - QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent) : QNetworkReply(*new QNetworkReplyImplPrivate, parent) { @@ -766,7 +823,7 @@ void QNetworkReplyImpl::abort() QNetworkReply::close(); if (d->state != QNetworkReplyImplPrivate::Finished) { - // emit signals + // call finished which will emit signals d->error(OperationCanceledError, tr("Operation canceled")); d->finished(); } @@ -794,7 +851,7 @@ void QNetworkReplyImpl::close() QNetworkReply::close(); - // emit signals + // call finished which will emit signals d->error(OperationCanceledError, tr("Operation canceled")); d->finished(); } @@ -813,6 +870,13 @@ bool QNetworkReplyImpl::canReadLine () const */ qint64 QNetworkReplyImpl::bytesAvailable() const { + // Special case for the "zero copy" download buffer + Q_D(const QNetworkReplyImpl); + if (d->downloadBuffer) { + qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition; + return QNetworkReply::bytesAvailable() + maxAvail; + } + return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount(); } @@ -867,8 +931,22 @@ void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &er qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen) { Q_D(QNetworkReplyImpl); + + // Special case code if we have the "zero copy" download buffer + if (d->downloadBuffer) { + qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen); + if (maxAvail == 0) + return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0; + // FIXME what about "Aborted" state? + qMemCopy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail); + d->downloadBufferReadPosition += maxAvail; + return maxAvail; + } + + if (d->readBuffer.isEmpty()) return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0; + // FIXME what about "Aborted" state? d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite); if (maxlen == 1) { diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h index f5ea8ca333..238bee5f22 100644 --- a/src/network/access/qnetworkreplyimpl_p.h +++ b/src/network/access/qnetworkreplyimpl_p.h @@ -62,6 +62,7 @@ #include "QtCore/qbuffer.h" #include "private/qringbuffer_p.h" #include "private/qbytedata_p.h" +#include <QSharedPointer> QT_BEGIN_NAMESPACE @@ -163,14 +164,15 @@ public: void appendDownstreamData(QIODevice *data); void appendDownstreamData(const QByteArray &data); + char* getDownloadBuffer(qint64 size); + void appendDownstreamDataDownloadBuffer(qint64, qint64); + void finished(); void error(QNetworkReply::NetworkError code, const QString &errorString); void metaDataChanged(); void redirectionRequested(const QUrl &target); void sslErrors(const QList<QSslError> &errors); - bool isFinished() const; - QNetworkAccessBackend *backend; QIODevice *outgoingData; QRingBuffer *outgoingDataBuffer; @@ -191,6 +193,7 @@ public: QList<QNetworkProxy> proxyList; #endif + // Used for normal downloading. For "zero copy" the downloadBuffer is used QByteDataBuffer readBuffer; qint64 bytesDownloaded; qint64 lastBytesDownloaded; @@ -202,6 +205,14 @@ public: State state; + // only used when the "zero copy" style is used. Else readBuffer is used. + // Please note that the whole "zero copy" download buffer API is private right now. Do not use it. + qint64 downloadBufferReadPosition; + qint64 downloadBufferCurrentSize; + qint64 downloadBufferMaximumSize; + QSharedPointer<char> downloadBufferPointer; + char* downloadBuffer; + Q_DECLARE_PUBLIC(QNetworkReplyImpl) }; diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 89f7d4447a..fc33e8de9b 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -756,7 +756,7 @@ static QVariant parseCookieHeader(const QByteArray &raw) result += parsed; } - return qVariantFromValue(result); + return QVariant::fromValue(result); } static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value) @@ -789,7 +789,7 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy return parseCookieHeader(value); case QNetworkRequest::SetCookieHeader: - return qVariantFromValue(QNetworkCookie::parseCookies(value)); + return QVariant::fromValue(QNetworkCookie::parseCookies(value)); default: Q_ASSERT(0); diff --git a/src/network/bearer/bearer.pri b/src/network/bearer/bearer.pri index 44e97fdef0..bc5b0b595d 100644 --- a/src/network/bearer/bearer.pri +++ b/src/network/bearer/bearer.pri @@ -15,4 +15,3 @@ SOURCES += bearer/qnetworksession.cpp \ bearer/qnetworkconfigmanager_p.cpp \ bearer/qbearerengine.cpp \ bearer/qbearerplugin.cpp - diff --git a/src/network/bearer/qbearerengine.cpp b/src/network/bearer/qbearerengine.cpp index 55a1a6949b..7447051018 100644 --- a/src/network/bearer/qbearerengine.cpp +++ b/src/network/bearer/qbearerengine.cpp @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE QBearerEngine::QBearerEngine(QObject *parent) -: QObject(parent), mutex(QMutex::Recursive) + : QObject(parent), mutex(QMutex::Recursive) { } @@ -54,6 +54,7 @@ QBearerEngine::~QBearerEngine() { QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it; QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end; + for (it = snapConfigurations.begin(), end = snapConfigurations.end(); it != end; ++it) { it.value()->isValid = false; it.value()->id.clear(); @@ -93,19 +94,20 @@ bool QBearerEngine::configurationsInUse() const QMutexLocker locker(&mutex); - for (it = accessPointConfigurations.begin(), - end = accessPointConfigurations.end(); it != end; ++it) { + for (it = accessPointConfigurations.constBegin(), + end = accessPointConfigurations.constEnd(); it != end; ++it) { if (it.value()->ref > 1) return true; } - for (it = snapConfigurations.begin(), end = snapConfigurations.end(); it != end; ++it) { + for (it = snapConfigurations.constBegin(), + end = snapConfigurations.constEnd(); it != end; ++it) { if (it.value()->ref > 1) return true; } - for (it = userChoiceConfigurations.begin(), - end = userChoiceConfigurations.end(); it != end; ++it) { + for (it = userChoiceConfigurations.constBegin(), + end = userChoiceConfigurations.constEnd(); it != end; ++it) { if (it.value()->ref > 1) return true; } diff --git a/src/network/bearer/qbearerengine_p.h b/src/network/bearer/qbearerengine_p.h index 9bce2b6e4b..e246355142 100644 --- a/src/network/bearer/qbearerengine_p.h +++ b/src/network/bearer/qbearerengine_p.h @@ -78,7 +78,7 @@ class Q_NETWORK_EXPORT QBearerEngine : public QObject friend class QNetworkConfigurationManagerPrivate; public: - QBearerEngine(QObject *parent = 0); + explicit QBearerEngine(QObject *parent = 0); virtual ~QBearerEngine(); virtual bool hasIdentifier(const QString &id) = 0; @@ -96,7 +96,6 @@ Q_SIGNALS: void configurationAdded(QNetworkConfigurationPrivatePointer config); void configurationRemoved(QNetworkConfigurationPrivatePointer config); void configurationChanged(QNetworkConfigurationPrivatePointer config); - void updateCompleted(); protected: @@ -114,4 +113,4 @@ QT_END_NAMESPACE #endif // QT_NO_BEARERMANAGEMENT -#endif +#endif // QBEARERENGINE_P_H diff --git a/src/network/bearer/qbearerplugin.cpp b/src/network/bearer/qbearerplugin.cpp index 76659b50b2..c198d674ac 100644 --- a/src/network/bearer/qbearerplugin.cpp +++ b/src/network/bearer/qbearerplugin.cpp @@ -41,14 +41,12 @@ #include "qbearerplugin_p.h" -#include <QtCore/qdebug.h> - #ifndef QT_NO_BEARERMANAGEMENT QT_BEGIN_NAMESPACE QBearerEnginePlugin::QBearerEnginePlugin(QObject *parent) -: QObject(parent) + : QObject(parent) { } diff --git a/src/network/bearer/qbearerplugin_p.h b/src/network/bearer/qbearerplugin_p.h index 4c7c22166a..a800f90fb5 100644 --- a/src/network/bearer/qbearerplugin_p.h +++ b/src/network/bearer/qbearerplugin_p.h @@ -68,7 +68,7 @@ QT_MODULE(Network) struct Q_NETWORK_EXPORT QBearerEngineFactoryInterface : public QFactoryInterface { - virtual QBearerEngine *create(const QString &key = QString()) const = 0; + virtual QBearerEngine *create(const QString &key) const = 0; }; #define QBearerEngineFactoryInterface_iid "com.trolltech.Qt.QBearerEngineFactoryInterface" @@ -84,7 +84,7 @@ public: virtual ~QBearerEnginePlugin(); virtual QStringList keys() const = 0; - virtual QBearerEngine *create(const QString &key = QString()) const = 0; + virtual QBearerEngine *create(const QString &key) const = 0; }; QT_END_NAMESPACE @@ -93,4 +93,4 @@ QT_END_HEADER #endif // QT_NO_BEARERMANAGEMENT -#endif +#endif // QBEARERPLUGIN_P_H diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp index 0e3c519c72..9927f29536 100644 --- a/src/network/bearer/qnetworkconfigmanager.cpp +++ b/src/network/bearer/qnetworkconfigmanager.cpp @@ -124,14 +124,14 @@ QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate() */ /*! - \fn void QNetworkConfigurationManager::configurationAdded(const QNetworkConfiguration& config) + \fn void QNetworkConfigurationManager::configurationAdded(const QNetworkConfiguration &config) This signal is emitted whenever a new network configuration is added to the system. The new configuration is specified by \a config. */ /*! - \fn void QNetworkConfigurationManager::configurationRemoved(const QNetworkConfiguration& configuration) + \fn void QNetworkConfigurationManager::configurationRemoved(const QNetworkConfiguration &config) This signal is emitted when a configuration is about to be removed from the system. The removed \a configuration is invalid but retains name and identifier. @@ -144,7 +144,7 @@ QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate() be initiated via \l updateConfigurations(). */ -/*! \fn void QNetworkConfigurationManager::configurationChanged(const QNetworkConfiguration& config) +/*! \fn void QNetworkConfigurationManager::configurationChanged(const QNetworkConfiguration &config) This signal is emitted when the \l {QNetworkConfiguration::state()}{state} of \a config changes. */ @@ -204,7 +204,7 @@ QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate() /*! Constructs a QNetworkConfigurationManager with the given \a parent. */ -QNetworkConfigurationManager::QNetworkConfigurationManager( QObject* parent ) +QNetworkConfigurationManager::QNetworkConfigurationManager(QObject *parent) : QObject(parent) { QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate(); @@ -213,12 +213,12 @@ QNetworkConfigurationManager::QNetworkConfigurationManager( QObject* parent ) this, SIGNAL(configurationAdded(QNetworkConfiguration))); connect(priv, SIGNAL(configurationRemoved(QNetworkConfiguration)), this, SIGNAL(configurationRemoved(QNetworkConfiguration))); - connect(priv, SIGNAL(configurationUpdateComplete()), - this, SIGNAL(updateCompleted())); - connect(priv, SIGNAL(onlineStateChanged(bool)), - this, SIGNAL(onlineStateChanged(bool))); connect(priv, SIGNAL(configurationChanged(QNetworkConfiguration)), this, SIGNAL(configurationChanged(QNetworkConfiguration))); + connect(priv, SIGNAL(onlineStateChanged(bool)), + this, SIGNAL(onlineStateChanged(bool))); + connect(priv, SIGNAL(configurationUpdateComplete()), + this, SIGNAL(updateCompleted())); priv->enablePolling(); } diff --git a/src/network/bearer/qnetworkconfigmanager.h b/src/network/bearer/qnetworkconfigmanager.h index 5949c6afd1..565156cbc5 100644 --- a/src/network/bearer/qnetworkconfigmanager.h +++ b/src/network/bearer/qnetworkconfigmanager.h @@ -68,7 +68,6 @@ class QNetworkConfigurationManagerExport QNetworkConfigurationManager : public Q Q_OBJECT public: - enum Capability { CanStartAndStopInterfaces = 0x00000001, DirectConnectionRouting = 0x00000002, @@ -81,26 +80,26 @@ public: Q_DECLARE_FLAGS(Capabilities, Capability) - QNetworkConfigurationManager( QObject* parent = 0 ); + explicit QNetworkConfigurationManager(QObject *parent = 0); virtual ~QNetworkConfigurationManager(); - QNetworkConfigurationManager::Capabilities capabilities() const; - QNetworkConfiguration defaultConfiguration() const; + QNetworkConfiguration defaultConfiguration() const; QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags flags = 0) const; - QNetworkConfiguration configurationFromIdentifier(const QString& identifier) const; - void updateConfigurations(); + QNetworkConfiguration configurationFromIdentifier(const QString &identifier) const; bool isOnline() const; +public Q_SLOTS: + void updateConfigurations(); + Q_SIGNALS: - void configurationAdded(const QNetworkConfiguration& config); - void configurationRemoved(const QNetworkConfiguration& config); - void configurationChanged(const QNetworkConfiguration& config); + void configurationAdded(const QNetworkConfiguration &config); + void configurationRemoved(const QNetworkConfiguration &config); + void configurationChanged(const QNetworkConfiguration &config); void onlineStateChanged(bool isOnline); void updateCompleted(); - }; Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkConfigurationManager::Capabilities) @@ -115,5 +114,4 @@ QT_END_HEADER #endif // QT_NO_BEARERMANAGEMENT -#endif //QNETWORKCONFIGURATIONMANAGER_H - +#endif // QNETWORKCONFIGURATIONMANAGER_H diff --git a/src/network/bearer/qnetworkconfigmanager_p.cpp b/src/network/bearer/qnetworkconfigmanager_p.cpp index c32132885b..c108ad34cf 100644 --- a/src/network/bearer/qnetworkconfigmanager_p.cpp +++ b/src/network/bearer/qnetworkconfigmanager_p.cpp @@ -60,7 +60,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, #endif QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate() -: pollTimer(0), mutex(QMutex::Recursive), forcedPolling(0), firstUpdate(true) + : QObject(), mutex(QMutex::Recursive), forcedPolling(0), firstUpdate(true) { qRegisterMetaType<QNetworkConfiguration>("QNetworkConfiguration"); qRegisterMetaType<QNetworkConfigurationPrivatePointer>("QNetworkConfigurationPrivatePointer"); @@ -73,13 +73,12 @@ QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate() qDeleteAll(sessionEngines); } -QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration() +QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration() const { QMutexLocker locker(&mutex); foreach (QBearerEngine *engine, sessionEngines) { QNetworkConfigurationPrivatePointer ptr = engine->defaultConfiguration(); - if (ptr) { QNetworkConfiguration config; config.d = ptr; @@ -98,8 +97,8 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration( QMutexLocker locker(&engine->mutex); - for (it = engine->snapConfigurations.begin(), end = engine->snapConfigurations.end(); - it != end; ++it) { + for (it = engine->snapConfigurations.begin(), + end = engine->snapConfigurations.end(); it != end; ++it) { QNetworkConfigurationPrivatePointer ptr = it.value(); QMutexLocker configLocker(&ptr->mutex); @@ -109,10 +108,8 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration( config.d = ptr; return config; } else if (!defaultConfiguration) { - if ((ptr->state & QNetworkConfiguration::Discovered) == - QNetworkConfiguration::Discovered) { + if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered) defaultConfiguration = ptr; - } } } } @@ -136,8 +133,6 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration( 6. Discovered Other */ - defaultConfiguration.reset(); - foreach (QBearerEngine *engine, sessionEngines) { QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it; QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end; @@ -151,8 +146,7 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration( QMutexLocker configLocker(&ptr->mutex); QNetworkConfiguration::BearerType bearerType = ptr->bearerType; - if ((ptr->state & QNetworkConfiguration::Discovered) == - QNetworkConfiguration::Discovered) { + if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered) { if (!defaultConfiguration) { defaultConfiguration = ptr; } else { @@ -196,7 +190,7 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration( return QNetworkConfiguration(); } -QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurations(QNetworkConfiguration::StateFlags filter) +QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurations(QNetworkConfiguration::StateFlags filter) const { QList<QNetworkConfiguration> result; @@ -240,7 +234,7 @@ QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurati return result; } -QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIdentifier(const QString &identifier) +QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIdentifier(const QString &identifier) const { QNetworkConfiguration item; @@ -250,11 +244,11 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIden QMutexLocker locker(&engine->mutex); if (engine->accessPointConfigurations.contains(identifier)) - item.d = engine->accessPointConfigurations.value(identifier); + item.d = engine->accessPointConfigurations[identifier]; else if (engine->snapConfigurations.contains(identifier)) - item.d = engine->snapConfigurations.value(identifier); + item.d = engine->snapConfigurations[identifier]; else if (engine->userChoiceConfigurations.contains(identifier)) - item.d = engine->userChoiceConfigurations.value(identifier); + item.d = engine->userChoiceConfigurations[identifier]; else continue; @@ -264,14 +258,14 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIden return item; } -bool QNetworkConfigurationManagerPrivate::isOnline() +bool QNetworkConfigurationManagerPrivate::isOnline() const { QMutexLocker locker(&mutex); return !onlineConfigurations.isEmpty(); } -QNetworkConfigurationManager::Capabilities QNetworkConfigurationManagerPrivate::capabilities() +QNetworkConfigurationManager::Capabilities QNetworkConfigurationManagerPrivate::capabilities() const { QMutexLocker locker(&mutex); @@ -353,7 +347,7 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations() QMutexLocker locker(&mutex); if (firstUpdate) { - if (sender()) + if (qobject_cast<QBearerEngine *>(sender())) return; if (thread() != QCoreApplicationPrivate::mainThread()) { @@ -366,10 +360,9 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations() updating = false; #ifndef QT_NO_LIBRARY - QFactoryLoader *l = loader(); - QBearerEngine *generic = 0; + QFactoryLoader *l = loader(); foreach (const QString &key, l->keys()) { QBearerEnginePlugin *plugin = qobject_cast<QBearerEnginePlugin *>(l->instance(key)); if (plugin) { @@ -403,11 +396,8 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations() } QBearerEngine *engine = qobject_cast<QBearerEngine *>(sender()); - if (!updatingEngines.isEmpty() && engine) { - int index = sessionEngines.indexOf(engine); - if (index >= 0) - updatingEngines.remove(index); - } + if (engine && !updatingEngines.isEmpty()) + updatingEngines.remove(engine); if (updating && updatingEngines.isEmpty()) { updating = false; @@ -415,10 +405,7 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations() } if (engine && !pollingEngines.isEmpty()) { - int index = sessionEngines.indexOf(engine); - if (index >= 0) - pollingEngines.remove(index); - + pollingEngines.remove(engine); if (pollingEngines.isEmpty()) startPolling(); } @@ -438,13 +425,13 @@ void QNetworkConfigurationManagerPrivate::performAsyncConfigurationUpdate() updating = true; - for (int i = 0; i < sessionEngines.count(); ++i) { - updatingEngines.insert(i); - QMetaObject::invokeMethod(sessionEngines.at(i), "requestUpdate"); + foreach (QBearerEngine *engine, sessionEngines) { + updatingEngines.insert(engine); + QMetaObject::invokeMethod(engine, "requestUpdate"); } } -QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines() +QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines() const { QMutexLocker locker(&mutex); @@ -455,35 +442,11 @@ void QNetworkConfigurationManagerPrivate::startPolling() { QMutexLocker locker(&mutex); - bool pollingRequired = false; - - if (forcedPolling > 0) { - foreach (QBearerEngine *engine, sessionEngines) { - if (engine->requiresPolling()) { - pollingRequired = true; - break; - } - } - } - - if (!pollingRequired) { - foreach (QBearerEngine *engine, sessionEngines) { - if (engine->configurationsInUse()) { - pollingRequired = true; - break; - } - } - } - - if (pollingRequired) { - if (!pollTimer) { - pollTimer = new QTimer(this); - pollTimer->setInterval(10000); - pollTimer->setSingleShot(true); - connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollEngines())); + foreach (QBearerEngine *engine, sessionEngines) { + if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) { + QTimer::singleShot(10000, this, SLOT(pollEngines())); + break; } - - pollTimer->start(); } } @@ -491,13 +454,10 @@ void QNetworkConfigurationManagerPrivate::pollEngines() { QMutexLocker locker(&mutex); - for (int i = 0; i < sessionEngines.count(); ++i) { - if (!sessionEngines.at(i)->requiresPolling()) - continue; - - if (forcedPolling || sessionEngines.at(i)->configurationsInUse()) { - pollingEngines.insert(i); - QMetaObject::invokeMethod(sessionEngines.at(i), "requestUpdate"); + foreach (QBearerEngine *engine, sessionEngines) { + if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) { + pollingEngines.insert(engine); + QMetaObject::invokeMethod(engine, "requestUpdate"); } } } @@ -509,7 +469,7 @@ void QNetworkConfigurationManagerPrivate::enablePolling() ++forcedPolling; if (forcedPolling == 1) - QMetaObject::invokeMethod(this, "startPolling"); + startPolling(); } void QNetworkConfigurationManagerPrivate::disablePolling() diff --git a/src/network/bearer/qnetworkconfigmanager_p.h b/src/network/bearer/qnetworkconfigmanager_p.h index 0c20853851..81f38c5183 100644 --- a/src/network/bearer/qnetworkconfigmanager_p.h +++ b/src/network/bearer/qnetworkconfigmanager_p.h @@ -64,7 +64,6 @@ QT_BEGIN_NAMESPACE class QBearerEngine; -class QTimer; class Q_NETWORK_EXPORT QNetworkConfigurationManagerPrivate : public QObject { @@ -74,57 +73,54 @@ public: QNetworkConfigurationManagerPrivate(); virtual ~QNetworkConfigurationManagerPrivate(); - QNetworkConfiguration defaultConfiguration(); - QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags filter); - QNetworkConfiguration configurationFromIdentifier(const QString &identifier); + QNetworkConfiguration defaultConfiguration() const; + QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags filter) const; + QNetworkConfiguration configurationFromIdentifier(const QString &identifier) const; - bool isOnline(); + bool isOnline() const; - QNetworkConfigurationManager::Capabilities capabilities(); + QNetworkConfigurationManager::Capabilities capabilities() const; void performAsyncConfigurationUpdate(); - QList<QBearerEngine *> engines(); - - Q_INVOKABLE void startPolling(); + QList<QBearerEngine *> engines() const; void enablePolling(); void disablePolling(); -public slots: +public Q_SLOTS: void updateConfigurations(); Q_SIGNALS: - void configurationAdded(const QNetworkConfiguration& config); - void configurationRemoved(const QNetworkConfiguration& config); + void configurationAdded(const QNetworkConfiguration &config); + void configurationRemoved(const QNetworkConfiguration &config); + void configurationChanged(const QNetworkConfiguration &config); void configurationUpdateComplete(); - void configurationChanged(const QNetworkConfiguration& config); void onlineStateChanged(bool isOnline); - void abort(); +private Q_SLOTS: + void configurationAdded(QNetworkConfigurationPrivatePointer ptr); + void configurationRemoved(QNetworkConfigurationPrivatePointer ptr); + void configurationChanged(QNetworkConfigurationPrivatePointer ptr); + + void pollEngines(); private: - QTimer *pollTimer; + void startPolling(); - QMutex mutex; +private: + mutable QMutex mutex; QList<QBearerEngine *> sessionEngines; QSet<QString> onlineConfigurations; - QSet<int> pollingEngines; - QSet<int> updatingEngines; + QSet<QBearerEngine *> pollingEngines; + QSet<QBearerEngine *> updatingEngines; int forcedPolling; bool updating; bool firstUpdate; - -private Q_SLOTS: - void configurationAdded(QNetworkConfigurationPrivatePointer ptr); - void configurationRemoved(QNetworkConfigurationPrivatePointer ptr); - void configurationChanged(QNetworkConfigurationPrivatePointer ptr); - - void pollEngines(); }; Q_NETWORK_EXPORT QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate(); @@ -133,4 +129,4 @@ QT_END_NAMESPACE #endif // QT_NO_BEARERMANAGEMENT -#endif //QNETWORKCONFIGURATIONMANAGERPRIVATE_H +#endif // QNETWORKCONFIGURATIONMANAGERPRIVATE_H diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp index fbcdc74164..4cc3099b17 100644 --- a/src/network/bearer/qnetworkconfiguration.cpp +++ b/src/network/bearer/qnetworkconfiguration.cpp @@ -212,44 +212,38 @@ QNetworkConfiguration::QNetworkConfiguration() /*! Creates a copy of the QNetworkConfiguration object contained in \a other. */ -QNetworkConfiguration::QNetworkConfiguration(const QNetworkConfiguration& other) +QNetworkConfiguration::QNetworkConfiguration(const QNetworkConfiguration &other) : d(other.d) { } /*! - Copies the content of the QNetworkConfiguration object contained in \a other into this one. + Frees the resources associated with the QNetworkConfiguration object. */ -QNetworkConfiguration& QNetworkConfiguration::operator=(const QNetworkConfiguration& other) +QNetworkConfiguration::~QNetworkConfiguration() { - d = other.d; - return *this; } /*! - Frees the resources associated with the QNetworkConfiguration object. + Copies the content of the QNetworkConfiguration object contained in \a other into this one. */ -QNetworkConfiguration::~QNetworkConfiguration() +QNetworkConfiguration &QNetworkConfiguration::operator=(const QNetworkConfiguration &other) { + d = other.d; + return *this; } /*! Returns true, if this configuration is the same as the \a other configuration given; otherwise returns false. */ -bool QNetworkConfiguration::operator==(const QNetworkConfiguration& other) const +bool QNetworkConfiguration::operator==(const QNetworkConfiguration &other) const { - if (!d) - return !other.d; - - if (!other.d) - return false; - return (d == other.d); } /*! - \fn bool QNetworkConfiguration::operator!=(const QNetworkConfiguration& other) const + \fn bool QNetworkConfiguration::operator!=(const QNetworkConfiguration &other) const Returns true if this configuration is not the same as the \a other configuration given; otherwise returns false. @@ -370,11 +364,14 @@ QList<QNetworkConfiguration> QNetworkConfiguration::children() const { QList<QNetworkConfiguration> results; - if (type() != QNetworkConfiguration::ServiceNetwork || !isValid()) + if (!d) return results; QMutexLocker locker(&d->mutex); + if (d->type != QNetworkConfiguration::ServiceNetwork || !d->isValid) + return results; + QMutableMapIterator<unsigned int, QNetworkConfigurationPrivatePointer> i(d->serviceNetworkMembers); while (i.hasNext()) { i.next(); @@ -510,4 +507,3 @@ QString QNetworkConfiguration::bearerTypeName() const } QT_END_NAMESPACE - diff --git a/src/network/bearer/qnetworkconfiguration.h b/src/network/bearer/qnetworkconfiguration.h index 9370816992..2e8dadda25 100644 --- a/src/network/bearer/qnetworkconfiguration.h +++ b/src/network/bearer/qnetworkconfiguration.h @@ -73,12 +73,12 @@ class QNetworkConfigurationExport QNetworkConfiguration public: QNetworkConfiguration(); QNetworkConfiguration(const QNetworkConfiguration& other); - QNetworkConfiguration &operator=(const QNetworkConfiguration& other); + QNetworkConfiguration &operator=(const QNetworkConfiguration &other); ~QNetworkConfiguration(); - bool operator==(const QNetworkConfiguration& cp) const; - inline bool operator!=(const QNetworkConfiguration& cp) const - { return !operator==(cp); } + bool operator==(const QNetworkConfiguration &other) const; + inline bool operator!=(const QNetworkConfiguration &other) const + { return !operator==(other); } enum Type { InternetAccessPoint = 0, @@ -100,7 +100,6 @@ public: Discovered = 0x0000006, Active = 0x000000e }; - Q_DECLARE_FLAGS(StateFlags, StateFlag) #ifndef QT_MOBILITY_BEARER @@ -155,4 +154,4 @@ QTM_END_NAMESPACE QT_END_HEADER -#endif //QNETWORKCONFIGURATION_H +#endif // QNETWORKCONFIGURATION_H diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h index 3a9259d616..a38bc0b7a1 100644 --- a/src/network/bearer/qnetworkconfiguration_p.h +++ b/src/network/bearer/qnetworkconfiguration_p.h @@ -65,18 +65,17 @@ typedef QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> QNetworkConfi class QNetworkConfigurationPrivate : public QSharedData { public: - QNetworkConfigurationPrivate () - : mutex(QMutex::Recursive), type(QNetworkConfiguration::Invalid), + QNetworkConfigurationPrivate() : + mutex(QMutex::Recursive), + type(QNetworkConfiguration::Invalid), purpose(QNetworkConfiguration::UnknownPurpose), bearerType(QNetworkConfiguration::BearerUnknown), isValid(false), roamingSupported(false) - { - } - + {} virtual ~QNetworkConfigurationPrivate() { //release pointers to member configurations - serviceNetworkMembers.clear(); + serviceNetworkMembers.clear(); } virtual QString bearerTypeName() const @@ -100,11 +99,9 @@ public: bool roamingSupported; private: - // disallow detaching - QNetworkConfigurationPrivate &operator=(const QNetworkConfigurationPrivate &other); - QNetworkConfigurationPrivate(const QNetworkConfigurationPrivate &other); + Q_DISABLE_COPY(QNetworkConfigurationPrivate) }; QT_END_NAMESPACE -#endif //QNETWORKCONFIGURATIONPRIVATE_H +#endif // QNETWORKCONFIGURATIONPRIVATE_H diff --git a/src/network/bearer/qnetworksession.cpp b/src/network/bearer/qnetworksession.cpp index b0b7d3d663..143d9b5dfe 100644 --- a/src/network/bearer/qnetworksession.cpp +++ b/src/network/bearer/qnetworksession.cpp @@ -39,11 +39,12 @@ ** ****************************************************************************/ +#include "qnetworksession.h" +#include "qbearerengine_p.h" + #include <QEventLoop> #include <QTimer> -#include "qnetworksession.h" -#include "qbearerengine_p.h" #include "qnetworkconfigmanager_p.h" #include "qnetworksession_p.h" @@ -165,7 +166,7 @@ QT_BEGIN_NAMESPACE */ /*! - \fn void QNetworkSession::preferredConfigurationChanged(const QNetworkConfiguration& config, bool isSeamless) + \fn void QNetworkSession::preferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless) This signal is emitted when the preferred configuration/access point for the session changes. Only sessions which are based on service network configurations @@ -224,30 +225,29 @@ QT_BEGIN_NAMESPACE \sa QNetworkConfiguration */ -QNetworkSession::QNetworkSession(const QNetworkConfiguration& connectionConfig, QObject* parent) -: QObject(parent), d(0) +QNetworkSession::QNetworkSession(const QNetworkConfiguration &connectionConfig, QObject *parent) + : QObject(parent), d(0) { // invalid configuration - if (connectionConfig.identifier().isNull()) - return; - - foreach (QBearerEngine *engine, qNetworkConfigurationManagerPrivate()->engines()) { - if (engine->hasIdentifier(connectionConfig.identifier())) { - d = engine->createSessionBackend(); - d->q = this; - d->publicConfig = connectionConfig; - d->syncStateWithInterface(); - connect(d, SIGNAL(quitPendingWaitsForOpened()), this, SIGNAL(opened())); - connect(d, SIGNAL(error(QNetworkSession::SessionError)), - this, SIGNAL(error(QNetworkSession::SessionError))); - connect(d, SIGNAL(stateChanged(QNetworkSession::State)), - this, SIGNAL(stateChanged(QNetworkSession::State))); - connect(d, SIGNAL(closed()), this, SIGNAL(closed())); - connect(d, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool)), - this, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))); - connect(d, SIGNAL(newConfigurationActivated()), - this, SIGNAL(newConfigurationActivated())); - break; + if (!connectionConfig.identifier().isEmpty()) { + foreach (QBearerEngine *engine, qNetworkConfigurationManagerPrivate()->engines()) { + if (engine->hasIdentifier(connectionConfig.identifier())) { + d = engine->createSessionBackend(); + d->q = this; + d->publicConfig = connectionConfig; + d->syncStateWithInterface(); + connect(d, SIGNAL(quitPendingWaitsForOpened()), this, SIGNAL(opened())); + connect(d, SIGNAL(error(QNetworkSession::SessionError)), + this, SIGNAL(error(QNetworkSession::SessionError))); + connect(d, SIGNAL(stateChanged(QNetworkSession::State)), + this, SIGNAL(stateChanged(QNetworkSession::State))); + connect(d, SIGNAL(closed()), this, SIGNAL(closed())); + connect(d, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool)), + this, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))); + connect(d, SIGNAL(newConfigurationActivated()), + this, SIGNAL(newConfigurationActivated())); + break; + } } } } @@ -314,19 +314,16 @@ bool QNetworkSession::waitForOpened(int msecs) return false; } - QEventLoop* loop = new QEventLoop(this); - QObject::connect(d, SIGNAL(quitPendingWaitsForOpened()), - loop, SLOT(quit())); - QObject::connect(this, SIGNAL(error(QNetworkSession::SessionError)), - loop, SLOT(quit())); + QEventLoop loop; + QObject::connect(d, SIGNAL(quitPendingWaitsForOpened()), &loop, SLOT(quit())); + QObject::connect(this, SIGNAL(error(QNetworkSession::SessionError)), &loop, SLOT(quit())); //final call - if (msecs>=0) - QTimer::singleShot(msecs, loop, SLOT(quit())); + if (msecs >= 0) + QTimer::singleShot(msecs, &loop, SLOT(quit())); - loop->exec(); - loop->disconnect(); - loop->deleteLater(); + // enter the event loop and wait for opened/error/timeout + loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); return d->isOpen; } @@ -472,7 +469,7 @@ QString QNetworkSession::errorString() const \code QNetworkConfigurationManager mgr; QNetworkConfiguration ap = mgr.defaultConfiguration(); - QNetworkSession* session = new QNetworkSession(ap); + QNetworkSession *session = new QNetworkSession(ap); ... //code activates session QString ident = session->sessionProperty("ActiveConfiguration").toString(); @@ -517,20 +514,13 @@ QString QNetworkSession::errorString() const has no effect for sessions that do not require polling. \endtable */ -QVariant QNetworkSession::sessionProperty(const QString& key) const +QVariant QNetworkSession::sessionProperty(const QString &key) const { - if (!d) - return QVariant(); - - if (!d->publicConfig.isValid()) + if (!d || !d->publicConfig.isValid()) return QVariant(); - if (key == QLatin1String("ActiveConfiguration")) { - if (!d->isOpen) - return QString(); - else - return d->activeConfig.identifier(); - } + if (key == QLatin1String("ActiveConfiguration")) + return d->isOpen ? d->activeConfig.identifier() : QString(); if (key == QLatin1String("UserChoiceConfiguration")) { if (!d->isOpen || d->publicConfig.type() != QNetworkConfiguration::UserChoice) @@ -553,7 +543,7 @@ QVariant QNetworkSession::sessionProperty(const QString& key) const Note that the \e UserChoiceConfiguration and \e ActiveConfiguration properties are read only and cannot be changed using this method. */ -void QNetworkSession::setSessionProperty(const QString& key, const QVariant& value) +void QNetworkSession::setSessionProperty(const QString &key, const QVariant &value) { if (!d) return; @@ -587,7 +577,7 @@ void QNetworkSession::migrate() */ void QNetworkSession::ignore() { - // Needed on at least Symbian platform: the roaming must be explicitly + // Needed on at least Symbian platform: the roaming must be explicitly // ignore()'d or migrate()'d if (d) d->ignore(); @@ -681,32 +671,34 @@ quint64 QNetworkSession::activeTime() const void QNetworkSession::connectNotify(const char *signal) { QObject::connectNotify(signal); - //check for preferredConfigurationChanged() signal connect notification - //This is not required on all platforms + if (!d) return; - if (qstrcmp(signal, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))) == 0) + //check for preferredConfigurationChanged() signal connect notification + //This is not required on all platforms + if (QLatin1String(signal) == SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))) d->setALREnabled(true); } /*! \internal - This function is called when the client disconnects from the preferredConfigurationChanged() - signal. + This function is called when the client disconnects from the + preferredConfigurationChanged() signal. \sa connectNotify() */ void QNetworkSession::disconnectNotify(const char *signal) { QObject::disconnectNotify(signal); - //check for preferredConfigurationChanged() signal disconnect notification - //This is not required on all platforms + if (!d) return; - if (qstrcmp(signal, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))) == 0) + //check for preferredConfigurationChanged() signal disconnect notification + //This is not required on all platforms + if (QLatin1String(signal) == SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))) d->setALREnabled(false); } diff --git a/src/network/bearer/qnetworksession.h b/src/network/bearer/qnetworksession.h index c51cb4f938..a4baec3988 100644 --- a/src/network/bearer/qnetworksession.h +++ b/src/network/bearer/qnetworksession.h @@ -71,6 +71,7 @@ class QNetworkSessionPrivate; class QNetworkSessionExport QNetworkSession : public QObject { Q_OBJECT + public: enum State { Invalid = 0, @@ -89,7 +90,8 @@ public: OperationNotSupportedError, InvalidConfigurationError }; - explicit QNetworkSession(const QNetworkConfiguration& connConfig, QObject* parent =0); + + explicit QNetworkSession(const QNetworkConfiguration &connConfig, QObject *parent = 0); virtual ~QNetworkSession(); bool isOpen() const; @@ -101,8 +103,8 @@ public: State state() const; SessionError error() const; QString errorString() const; - QVariant sessionProperty(const QString& key) const; - void setSessionProperty(const QString& key, const QVariant& value); + QVariant sessionProperty(const QString &key) const; + void setSessionProperty(const QString &key, const QVariant &value); quint64 bytesWritten() const; quint64 bytesReceived() const; @@ -121,13 +123,12 @@ public Q_SLOTS: void accept(); void reject(); - Q_SIGNALS: void stateChanged(QNetworkSession::State); void opened(); void closed(); void error(QNetworkSession::SessionError); - void preferredConfigurationChanged(const QNetworkConfiguration& config, bool isSeamless); + void preferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless); void newConfigurationActivated(); protected: @@ -135,9 +136,9 @@ protected: virtual void disconnectNotify(const char *signal); private: - QNetworkSessionPrivate* d; friend class QNetworkSessionPrivate; - }; + QNetworkSessionPrivate *d; +}; #ifndef QT_MOBILITY_BEARER QT_END_NAMESPACE @@ -149,4 +150,4 @@ QT_END_HEADER #endif // QT_NO_BEARERMANAGEMENT -#endif //QNETWORKSESSION_H +#endif // QNETWORKSESSION_H diff --git a/src/network/bearer/qnetworksession_p.h b/src/network/bearer/qnetworksession_p.h index d24eeb1545..707ad3751e 100644 --- a/src/network/bearer/qnetworksession_p.h +++ b/src/network/bearer/qnetworksession_p.h @@ -67,14 +67,11 @@ class Q_NETWORK_EXPORT QNetworkSessionPrivate : public QObject friend class QNetworkSession; public: - QNetworkSessionPrivate() - : state(QNetworkSession::Invalid), isOpen(false) - { - } - + QNetworkSessionPrivate() : QObject(), + state(QNetworkSession::Invalid), isOpen(false) + {} virtual ~QNetworkSessionPrivate() - { - } + {} //called by QNetworkSession constructor and ensures //that the state is immediately updated (w/o actually opening @@ -85,14 +82,14 @@ public: #ifndef QT_NO_NETWORKINTERFACE virtual QNetworkInterface currentInterface() const = 0; #endif - virtual QVariant sessionProperty(const QString& key) const = 0; - virtual void setSessionProperty(const QString& key, const QVariant& value) = 0; + virtual QVariant sessionProperty(const QString &key) const = 0; + virtual void setSessionProperty(const QString &key, const QVariant &value) = 0; virtual void open() = 0; virtual void close() = 0; virtual void stop() = 0; - virtual void setALREnabled(bool /*enabled*/) { } + virtual void setALREnabled(bool /*enabled*/) {} virtual void migrate() = 0; virtual void accept() = 0; virtual void ignore() = 0; @@ -150,5 +147,4 @@ QT_END_NAMESPACE #endif // QT_NO_BEARERMANAGEMENT -#endif //QNETWORKSESSIONPRIVATE_H - +#endif // QNETWORKSESSIONPRIVATE_H diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index 261313a963..5ec6041503 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -56,9 +56,7 @@ QT_BEGIN_NAMESPACE -#ifndef QT_NO_THREAD Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager) -#endif //#define QHOSTINFO_DEBUG @@ -87,10 +85,8 @@ Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager) \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 0 - The slot is invoked when the results are ready. (If you use - Qt for Embedded Linux and disabled multithreading support by defining - \c QT_NO_THREAD, lookupHost() will block until the lookup has - finished.) The results are stored in a QHostInfo object. Call + The slot is invoked when the results are ready. The results are + stored in a QHostInfo object. Call addresses() to get the list of IP addresses for the host, and hostName() to get the host name that was looked up. @@ -176,14 +172,6 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, return id; } -#ifdef QT_NO_THREAD - QHostInfo hostInfo = QHostInfoAgent::fromName(name); - hostInfo.setLookupId(id); - QScopedPointer<QHostInfoResult> result(new QHostInfoResult); - QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)), - receiver, member, Qt::QueuedConnection); - result.data()->emitResultsReady(hostInfo); -#else QHostInfoLookupManager *manager = theHostInfoLookupManager(); if (manager) { // the application is still alive @@ -204,8 +192,6 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); manager->scheduleLookup(runnable); } -#endif - return id; } @@ -216,12 +202,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, */ void QHostInfo::abortHostLookup(int id) { -#ifndef QT_NO_THREAD theHostInfoLookupManager()->abortLookup(id); -#else - // we cannot abort if it was non threaded.. the result signal has already been posted - Q_UNUSED(id); -#endif } /*! @@ -425,7 +406,6 @@ void QHostInfo::setErrorString(const QString &str) \sa hostName() */ -#ifndef QT_NO_THREAD QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i) { setAutoDelete(true); @@ -753,6 +733,4 @@ void QHostInfoCache::clear() cache.clear(); } -#endif // QT_NO_THREAD - QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h index 331443bda9..b568ec22ea 100644 --- a/src/network/kernel/qhostinfo_p.h +++ b/src/network/kernel/qhostinfo_p.h @@ -60,8 +60,6 @@ #include "QtCore/qwaitcondition.h" #include "QtCore/qobject.h" #include "QtCore/qpointer.h" - -#ifndef QT_NO_THREAD #include "QtCore/qthread.h" #include "QtCore/qthreadpool.h" #include "QtCore/qmutex.h" @@ -70,7 +68,7 @@ #include "QtCore/qqueue.h" #include <QElapsedTimer> #include <QCache> -#endif + QT_BEGIN_NAMESPACE @@ -112,7 +110,6 @@ public: int lookupId; }; -#ifndef QT_NO_THREAD // These functions are outside of the QHostInfo class and strictly internal. // Do NOT use them outside of QAbstractSocket. QHostInfo Q_NETWORK_EXPORT qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id); @@ -192,8 +189,6 @@ private slots: void waitForThreadPoolDone() { threadPool.waitForDone(); } }; -#endif - QT_END_NAMESPACE #endif // QHOSTINFO_P_H diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 111c4c63c1..22f6e0db04 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -132,9 +132,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) // Load res_init on demand. static volatile bool triedResolve = false; if (!triedResolve) { -#ifndef QT_NO_THREAD QMutexLocker locker(QMutexPool::globalInstanceGet(&local_res_init)); -#endif if (!triedResolve) { resolveLibrary(); triedResolve = true; diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp index bddda411ae..58f309b80b 100644 --- a/src/network/kernel/qhostinfo_win.cpp +++ b/src/network/kernel/qhostinfo_win.cpp @@ -39,11 +39,6 @@ ** ****************************************************************************/ -#if defined Q_CC_MSVC && _MSC_VER <=1300 -//VC.net 2002 support for templates doesn't match some PSDK requirements -#define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) -#endif - #include <winsock2.h> #include "qhostinfo_p.h" @@ -115,9 +110,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) // Load res_init on demand. static volatile bool triedResolve = false; if (!triedResolve) { -#ifndef QT_NO_THREAD QMutexLocker locker(QMutexPool::globalInstanceGet(&local_getaddrinfo)); -#endif if (!triedResolve) { resolveLibrary(); triedResolve = true; diff --git a/src/network/network.pro b/src/network/network.pro index 7ed7d3abb9..948922b8c1 100644 --- a/src/network/network.pro +++ b/src/network/network.pro @@ -13,7 +13,7 @@ DEFINES += QT_BUILD_NETWORK_LIB QT_NO_USING_NAMESPACE QT = core win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x64000000 -unix:QMAKE_PKGCONFIG_REQUIRES = QtCore +unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore include(../qbase.pri) include(access/access.pri) diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index f927ae27b6..ead9897448 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -356,6 +356,10 @@ to enable. \value KeepAliveOption Set this to 1 to enable the SO_KEEPALIVE socket option + \value MulticastTtlOption Set this to an integer value to set IP_MULTICAST_TTL (TTL for multicast datagrams) socket option. + + \value MulticastLoopbackOption Set this to 1 to enable the IP_MULTICAST_LOOP (multicast loopback) socket option. + \sa QAbstractSocket::setSocketOption(), QAbstractSocket::socketOption() */ @@ -466,9 +470,6 @@ QAbstractSocketPrivate::QAbstractSocketPrivate() peerPort(0), socketEngine(0), cachedSocketDescriptor(-1), -#ifdef Q_OS_LINUX - addToBytesAvailable(0), -#endif readBufferMaxSize(0), readBuffer(QABSTRACTSOCKET_BUFFERSIZE), writeBuffer(QABSTRACTSOCKET_BUFFERSIZE), @@ -630,8 +631,9 @@ bool QAbstractSocketPrivate::canReadNotification() // only emit readyRead() when not recursing, and only if there is data available bool hasData = newBytes > 0 #ifndef QT_NO_UDPSOCKET - || (!isBuffered && socketEngine && socketEngine->hasPendingDatagrams()) + || (!isBuffered && socketType != QAbstractSocket::TcpSocket && socketEngine && socketEngine->hasPendingDatagrams()) #endif + || (!isBuffered && socketType == QAbstractSocket::TcpSocket && socketEngine) ; if (!emittedReadyRead && hasData) { @@ -1132,10 +1134,6 @@ bool QAbstractSocketPrivate::readFromSocket() Q_Q(QAbstractSocket); // Find how many bytes we can read from the socket layer. qint64 bytesToRead = socketEngine->bytesAvailable(); -#ifdef Q_OS_LINUX - if (bytesToRead > 0) // ### See setSocketDescriptor() - bytesToRead += addToBytesAvailable; -#endif if (bytesToRead == 0) { // Under heavy load, certain conditions can trigger read notifications // for socket notifiers on which there is no activity. If we continue @@ -1370,10 +1368,6 @@ void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint d->localAddress.clear(); d->peerAddress.clear(); d->peerName = hostName; -#ifdef Q_OS_LINUX - // ### See setSocketDescriptor(). - d->addToBytesAvailable = 0; -#endif if (d->hostLookupId != -1) { QHostInfo::abortHostLookup(d->hostLookupId); d->hostLookupId = -1; @@ -1391,8 +1385,11 @@ void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint } #endif - if (!d_func()->isBuffered) - openMode |= QAbstractSocket::Unbuffered; + if (openMode & QIODevice::Unbuffered) + d->isBuffered = false; // Unbuffered QTcpSocket + else if (!d_func()->isBuffered) + openMode |= QAbstractSocket::Unbuffered; // QUdpSocket + QIODevice::open(openMode); d->state = HostLookupState; emit stateChanged(d->state); @@ -1472,10 +1469,12 @@ qint64 QAbstractSocket::bytesAvailable() const { Q_D(const QAbstractSocket); qint64 available = QIODevice::bytesAvailable(); - if (d->isBuffered) - available += (qint64) d->readBuffer.size(); - else if (d->socketEngine && d->socketEngine->isValid()) + + available += (qint64) d->readBuffer.size(); + + if (!d->isBuffered && d->socketEngine && d->socketEngine->isValid()) available += d->socketEngine->bytesAvailable(); + #if defined(QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocket::bytesAvailable() == %llu", available); #endif @@ -1633,17 +1632,6 @@ bool QAbstractSocket::setSocketDescriptor(int socketDescriptor, SocketState sock d->peerAddress = d->socketEngine->peerAddress(); d->cachedSocketDescriptor = socketDescriptor; -#ifdef Q_OS_LINUX - // ### This is a workaround for certain broken Linux kernels, when using - // QTcpSocket with a Unix domain socket. It was introduced around 2.6.9, - // and fixed at some point after that. - // http://archive.linux-usenet.com/index-t-73300.html - // We can provide a better workaround for this: readFromSocket() can loop - // while reading, but this must happen without triggering an implicit - // close because of reading after the socket has closed. - d->addToBytesAvailable = 4096; -#endif - return true; } @@ -1673,6 +1661,14 @@ void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, cons case KeepAliveOption: d_func()->socketEngine->setOption(QAbstractSocketEngine::KeepAliveOption, value.toInt()); break; + + case MulticastTtlOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::MulticastTtlOption, value.toInt()); + break; + + case MulticastLoopbackOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::MulticastLoopbackOption, value.toInt()); + break; } } @@ -1702,6 +1698,13 @@ QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option) case KeepAliveOption: ret = d_func()->socketEngine->option(QAbstractSocketEngine::KeepAliveOption); break; + + case MulticastTtlOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::MulticastTtlOption); + break; + case MulticastLoopbackOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::MulticastLoopbackOption); + break; } if (ret == -1) return QVariant(); @@ -2119,42 +2122,50 @@ bool QAbstractSocket::flush() qint64 QAbstractSocket::readData(char *data, qint64 maxSize) { Q_D(QAbstractSocket); - if (d->socketEngine && !d->socketEngine->isReadNotificationEnabled() && d->socketEngine->isValid()) - d->socketEngine->setReadNotificationEnabled(true); - if (!d->isBuffered) { - if (!d->socketEngine) - return -1; // no socket engine is probably EOF - qint64 readBytes = d->socketEngine->read(data, maxSize); - if (readBytes < 0) { - d->socketError = d->socketEngine->error(); - setErrorString(d->socketEngine->errorString()); - } - if (!d->socketEngine->isReadNotificationEnabled()) - d->socketEngine->setReadNotificationEnabled(true); -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld", - data, qt_prettyDebug(data, 32, readBytes).data(), maxSize, - readBytes); -#endif - return readBytes; - } - - if (d->readBuffer.isEmpty()) + // This is for a buffered QTcpSocket + if (d->isBuffered && d->readBuffer.isEmpty()) // if we're still connected, return 0 indicating there may be more data in the future // if we're not connected, return -1 indicating EOF return d->state == QAbstractSocket::ConnectedState ? qint64(0) : qint64(-1); - // If readFromSocket() read data, copy it to its destination. - if (maxSize == 1) { + // short cut for a char read if we have something in the buffer + if (maxSize == 1 && !d->readBuffer.isEmpty()) { *data = d->readBuffer.getChar(); #if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', 1) == 1", + qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', 1) == 1 [char buffer]", data, isprint(int(uchar(*data))) ? *data : '?', *data); #endif + if (d->readBuffer.isEmpty() && d->socketEngine) + d->socketEngine->setReadNotificationEnabled(true); return 1; } + // Special case for an Unbuffered QTcpSocket + // Re-filling the buffer. + if (d->socketType == TcpSocket + && !d->isBuffered + && d->readBuffer.size() < maxSize + && d->readBufferMaxSize > 0 + && maxSize < d->readBufferMaxSize + && d->socketEngine) { + // Our buffer is empty and a read() was requested for a byte amount that is smaller + // than the readBufferMaxSize. This means that we should fill our buffer since we want + // such small reads come from the buffer and not always go to the costly socket engine read() + qint64 bytesToRead = d->socketEngine->bytesAvailable(); + if (bytesToRead > 0) { + char *ptr = d->readBuffer.reserve(bytesToRead); + qint64 readBytes = d->socketEngine->read(ptr, bytesToRead); + if (readBytes == -2) { + // No bytes currently available for reading. + d->readBuffer.chop(bytesToRead); + } else { + d->readBuffer.chop(int(bytesToRead - (readBytes < 0 ? qint64(0) : readBytes))); + } + } + } + + // First try to satisfy the read from the buffer qint64 bytesToRead = qMin(qint64(d->readBuffer.size()), maxSize); qint64 readSoFar = 0; while (readSoFar < bytesToRead) { @@ -2166,8 +2177,50 @@ qint64 QAbstractSocket::readData(char *data, qint64 maxSize) d->readBuffer.free(bytesToReadFromThisBlock); } + if (d->socketEngine && !d->socketEngine->isReadNotificationEnabled() && d->socketEngine->isValid()) + d->socketEngine->setReadNotificationEnabled(true); + + if (readSoFar > 0) { + // we read some data from buffer. + // Just return, readyRead will be emitted again #if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld", + qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', %lli) == %lli [buffer]", + data, isprint(int(uchar(*data))) ? *data : '?', *data, maxSize, readSoFar); +#endif + + if (d->readBuffer.isEmpty() && d->socketEngine) + d->socketEngine->setReadNotificationEnabled(true); + return readSoFar; + } + + // This code path is for Unbuffered QTcpSocket or for connected UDP + + if (!d->isBuffered) { + if (!d->socketEngine) + return -1; // no socket engine is probably EOF + qint64 readBytes = d->socketEngine->read(data, maxSize); + if (readBytes == -2) { + // -2 from the engine means no bytes available (EAGAIN) so read more later + return 0; + } else if (readBytes < 0) { + d->socketError = d->socketEngine->error(); + setErrorString(d->socketEngine->errorString()); + } else if (!d->socketEngine->isReadNotificationEnabled()) { + // Only do this when there was no error + d->socketEngine->setReadNotificationEnabled(true); + } + +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld [engine]", + data, qt_prettyDebug(data, 32, readBytes).data(), maxSize, + readBytes); +#endif + return readBytes; + } + + +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld [unreachable]", data, qt_prettyDebug(data, qMin<qint64>(32, readSoFar), readSoFar).data(), maxSize, readSoFar); #endif @@ -2192,7 +2245,23 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size) return -1; } - if (!d->isBuffered) { + if (!d->isBuffered && d->socketType == TcpSocket && d->writeBuffer.isEmpty()) { + // This code is for the new Unbuffered QTcpSocket use case + qint64 written = d->socketEngine->write(data, size); + if (written < 0) { + d->socketError = d->socketEngine->error(); + setErrorString(d->socketEngine->errorString()); + return written; + } else if (written < size) { + // Buffer what was not written yet + char *ptr = d->writeBuffer.reserve(size - written); + memcpy(ptr, data + written, size - written); + if (d->socketEngine) + d->socketEngine->setWriteNotificationEnabled(true); + } + return size; // size=actually written + what has been buffered + } else if (!d->isBuffered && d->socketType != TcpSocket) { + // This is for a QUdpSocket that was connect()ed qint64 written = d->socketEngine->write(data, size); if (written < 0) { d->socketError = d->socketEngine->error(); @@ -2211,6 +2280,12 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size) return written; } + // This is the code path for normal buffered QTcpSocket or + // unbuffered QTcpSocket when there was already something in the + // write buffer and therefore we could not do a direct engine write. + // We just write to our write buffer and enable the write notifier + // The write notifier then flush()es the buffer. + char *ptr = d->writeBuffer.reserve(size); if (size == 1) *ptr = *data; diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index 3bc6f97450..24f5478911 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -64,6 +64,7 @@ class QAuthenticator; class Q_NETWORK_EXPORT QAbstractSocket : public QIODevice { Q_OBJECT + Q_ENUMS(SocketType NetworkLayerProtocol SocketError SocketState SocketOption) public: enum SocketType { TcpSocket, @@ -118,7 +119,9 @@ public: }; enum SocketOption { LowDelayOption, // TCP_NODELAY - KeepAliveOption // SO_KEEPALIVE + KeepAliveOption, // SO_KEEPALIVE + MulticastTtlOption, // IP_MULTICAST_TTL + MulticastLoopbackOption // IP_MULTICAST_LOOPBACK }; QAbstractSocket(SocketType socketType, QObject *parent); diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h index e7cc9e332a..7e6343ee40 100644 --- a/src/network/socket/qabstractsocket_p.h +++ b/src/network/socket/qabstractsocket_p.h @@ -138,9 +138,6 @@ public: void setupSocketNotifiers(); bool readFromSocket(); -#ifdef Q_OS_LINUX - qint64 addToBytesAvailable; -#endif qint64 readBufferMaxSize; QRingBuffer readBuffer; QRingBuffer writeBuffer; diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index f83300da96..c00b6d7d4b 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE class QAuthenticator; class QAbstractSocketEnginePrivate; +class QNetworkInterface; class QNetworkProxy; class QAbstractSocketEngineReceiver { @@ -94,7 +95,9 @@ public: BindExclusively, ReceiveOutOfBandData, LowDelayOption, - KeepAliveOption + KeepAliveOption, + MulticastTtlOption, + MulticastLoopbackOption }; virtual bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) = 0; @@ -118,6 +121,13 @@ public: virtual qint64 write(const char *data, qint64 len) = 0; #ifndef QT_NO_UDPSOCKET + virtual bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) = 0; + virtual bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) = 0; + virtual QNetworkInterface multicastInterface() const = 0; + virtual bool setMulticastInterface(const QNetworkInterface &iface) = 0; + virtual qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0) = 0; virtual qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index a338d9757c..4e628f361a 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -45,6 +45,7 @@ #include "qurl.h" #include "qhttp.h" #include "qelapsedtimer.h" +#include "qnetworkinterface.h" #if !defined(QT_NO_NETWORKPROXY) && !defined(QT_NO_HTTP) #include <qdebug.h> @@ -239,6 +240,34 @@ qint64 QHttpSocketEngine::write(const char *data, qint64 len) } #ifndef QT_NO_UDPSOCKET +bool QHttpSocketEngine::joinMulticastGroup(const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + +bool QHttpSocketEngine::leaveMulticastGroup(const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + +QNetworkInterface QHttpSocketEngine::multicastInterface() const +{ + return QNetworkInterface(); +} + +bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + qint64 QHttpSocketEngine::readDatagram(char *, qint64, QHostAddress *, quint16 *) { diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h index 79affd4ae3..2ecd70887c 100644 --- a/src/network/socket/qhttpsocketengine_p.h +++ b/src/network/socket/qhttpsocketengine_p.h @@ -102,6 +102,13 @@ public: qint64 write(const char *data, qint64 len); #ifndef QT_NO_UDPSOCKET + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); + qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0); qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index 5c818bd441..56c171608d 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -98,6 +98,7 @@ #include <qabstracteventdispatcher.h> #include <qsocketnotifier.h> +#include <qnetworkinterface.h> #include "qnativesocketengine_p.h" #include <private/qthread_p.h> @@ -647,6 +648,51 @@ int QNativeSocketEngine::accept() } /*! + \since 4.8 +*/ +bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false); + Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false); + Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false); + return d->nativeJoinMulticastGroup(groupAddress, iface); +} + +/*! + \since 4.8 +*/ +bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::leaveMulticastGroup(), false); + Q_CHECK_STATE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false); + Q_CHECK_TYPE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false); + return d->nativeLeaveMulticastGroup(groupAddress, iface); +} + +/*! \since 4.8 */ +QNetworkInterface QNativeSocketEngine::multicastInterface() const +{ + Q_D(const QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::multicastInterface(), QNetworkInterface()); + Q_CHECK_TYPE(QNativeSocketEngine::multicastInterface(), QAbstractSocket::UdpSocket, QNetworkInterface()); + return d->nativeMulticastInterface(); +} + + +/*! \since 4.8 */ +bool QNativeSocketEngine::setMulticastInterface(const QNetworkInterface &iface) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setMulticastInterface(), false); + Q_CHECK_TYPE(QNativeSocketEngine::setMulticastInterface(), QAbstractSocket::UdpSocket, false); + return d->nativeSetMulticastInterface(iface); +} + +/*! Returns the number of bytes that are currently available for reading. On error, -1 is returned. diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 0abaf24b26..074dd1a33d 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -101,6 +101,7 @@ union qt_sockaddr { }; class QNativeSocketEnginePrivate; +class QNetworkInterface; class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine { @@ -123,6 +124,13 @@ public: int accept(); void close(); + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); + qint64 bytesAvailable() const; qint64 read(char *data, qint64 maxlen); @@ -237,6 +245,12 @@ public: bool nativeBind(const QHostAddress &address, quint16 port); bool nativeListen(int backlog); int nativeAccept(); + bool nativeJoinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + QNetworkInterface nativeMulticastInterface() const; + bool nativeSetMulticastInterface(const QNetworkInterface &iface); qint64 nativeBytesAvailable() const; bool nativeHasPendingDatagrams() const; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index c601bdb19c..c819659915 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -46,6 +46,7 @@ #include "qhostaddress.h" #include "qelapsedtimer.h" #include "qvarlengtharray.h" +#include "qnetworkinterface.h" #include <time.h> #include <errno.h> #include <fcntl.h> @@ -244,6 +245,30 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastTtlOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } + break; + case QNativeSocketEngine::MulticastLoopbackOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } + break; } int v = -1; @@ -327,6 +352,30 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastTtlOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } + break; + case QNativeSocketEngine::MulticastLoopbackOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } + break; } return ::setsockopt(socketDescriptor, level, n, (char *) &v, sizeof(v)) == 0; @@ -566,6 +615,171 @@ int QNativeSocketEnginePrivate::nativeAccept() return acceptedDescriptor; } + +static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, + int how6, + int how4, + const QHostAddress &groupAddress, + const QNetworkInterface &interface) +{ + int level = 0; + int sockOpt = 0; + void *sockArg; + int sockArgSize; + + ip_mreq mreq4; +#ifndef QT_NO_IPV6 + ipv6_mreq mreq6; + + if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + sockOpt = how6; + sockArg = &mreq6; + sockArgSize = sizeof(mreq6); + memset(&mreq6, 0, sizeof(mreq6)); + Q_IPV6ADDR ip6 = groupAddress.toIPv6Address(); + memcpy(&mreq6.ipv6mr_multiaddr, &ip6, sizeof(ip6)); + mreq6.ipv6mr_interface = interface.index(); + } else +#endif + if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; + sockOpt = how4; + sockArg = &mreq4; + sockArgSize = sizeof(mreq4); + memset(&mreq4, 0, sizeof(mreq4)); + mreq4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); + + if (interface.isValid()) { + QList<QNetworkAddressEntry> addressEntries = interface.addressEntries(); + if (!addressEntries.isEmpty()) { + QHostAddress firstIP = addressEntries.first().ip(); + mreq4.imr_interface.s_addr = htonl(firstIP.toIPv4Address()); + } else { + d->setError(QAbstractSocket::NetworkError, + QNativeSocketEnginePrivate::NetworkUnreachableErrorString); + return false; + } + } else { + mreq4.imr_interface.s_addr = INADDR_ANY; + } + } else { + // unreachable + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString); + return false; + } + + int res = setsockopt(d->socketDescriptor, level, sockOpt, sockArg, sockArgSize); + if (res == -1) { + switch (errno) { + case ENOPROTOOPT: + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::OperationUnsupportedErrorString); + break; + case EADDRNOTAVAIL: + d->setError(QAbstractSocket::SocketAddressNotAvailableError, + QNativeSocketEnginePrivate::AddressNotAvailableErrorString); + break; + default: + d->setError(QAbstractSocket::UnknownSocketError, + QNativeSocketEnginePrivate::UnknownSocketErrorString); + break; + } + return false; + } + return true; +} + +bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface) +{ + return multicastMembershipHelper(this, +#ifndef QT_NO_IPV6 + IPV6_JOIN_GROUP, +#else + 0, +#endif + IP_ADD_MEMBERSHIP, + groupAddress, + interface); +} + +bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface) +{ + return multicastMembershipHelper(this, +#ifndef QT_NO_IPV6 + IPV6_LEAVE_GROUP, +#else + 0, +#endif + IP_DROP_MEMBERSHIP, + groupAddress, + interface); +} + +QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, &v, &sizeofv) == -1) + return QNetworkInterface(); + return QNetworkInterface::interfaceFromIndex(v); + } +#endif + + struct in_addr v = { 0 }; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, &sizeofv) == -1) + return QNetworkInterface(); + if (v.s_addr != 0 && sizeofv >= sizeof(v)) { + QHostAddress ipv4(ntohl(v.s_addr)); + QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces(); + for (int i = 0; i < ifaces.count(); ++i) { + const QNetworkInterface &iface = ifaces.at(i); + QList<QNetworkAddressEntry> entries = iface.addressEntries(); + for (int j = 0; j < entries.count(); ++j) { + const QNetworkAddressEntry &entry = entries.at(j); + if (entry.ip() == ipv4) + return iface; + } + } + } + return QNetworkInterface(); +} + +bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInterface &iface) +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v = iface.index(); + return (::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, &v, sizeof(v)) != -1); + } +#endif + + struct in_addr v; + if (iface.isValid()) { + QList<QNetworkAddressEntry> entries = iface.addressEntries(); + for (int i = 0; i < entries.count(); ++i) { + const QNetworkAddressEntry &entry = entries.at(i); + const QHostAddress &ip = entry.ip(); + if (ip.protocol() == QAbstractSocket::IPv4Protocol) { + v.s_addr = htonl(ip.toIPv4Address()); + int r = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, sizeof(v)); + if (r != -1) + return true; + } + } + return false; + } + + v.s_addr = INADDR_ANY; + return (::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, sizeof(v)) != -1); +} + qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const { int nbytes = 0; @@ -877,7 +1091,7 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize) { Q_Q(QNativeSocketEngine); if (!q->isValid()) { - qWarning("QNativeSocketEngine::unbufferedRead: Invalid socket"); + qWarning("QNativeSocketEngine::nativeRead: Invalid socket"); return -1; } diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index c1dc984772..76c915825b 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include <winsock2.h> +#include <ws2tcpip.h> #include "qnativesocketengine_p.h" @@ -47,6 +48,7 @@ #include <qsocketnotifier.h> #include <qdebug.h> #include <qdatetime.h> +#include <qnetworkinterface.h> //#define QNATIVESOCKETENGINE_DEBUG #if defined(QNATIVESOCKETENGINE_DEBUG) @@ -399,6 +401,30 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastTtlOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } + break; + case QNativeSocketEngine::MulticastLoopbackOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } + break; } int v = -1; @@ -459,6 +485,30 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastTtlOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } + break; + case QNativeSocketEngine::MulticastLoopbackOption: +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } + break; } if (::setsockopt(socketDescriptor, level, n, (char*)&v, sizeof(v)) != 0) { @@ -648,8 +698,26 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin } -bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16 port) +bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &a, quint16 port) { + QHostAddress address = a; + switch (address.protocol()) { + case QAbstractSocket::IPv6Protocol: + if (address.toIPv6Address()[0] == 0xff) { + // binding to a multicast address + address = QHostAddress(QHostAddress::AnyIPv6); + } + break; + case QAbstractSocket::IPv4Protocol: + if ((address.toIPv4Address() & 0xffff0000) == 0xefff0000) { + // binding to a multicast address + address = QHostAddress(QHostAddress::Any); + } + break; + default: + break; + } + struct sockaddr_in sockAddrIPv4; qt_sockaddr_in6 sockAddrIPv6; struct sockaddr *sockAddrPtr = 0; @@ -747,6 +815,160 @@ int QNativeSocketEnginePrivate::nativeAccept() return acceptedDescriptor; } +static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, + int how6, + int how4, + const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + int level = 0; + int sockOpt = 0; + char *sockArg; + int sockArgSize; + + struct ip_mreq mreq4; +#ifndef QT_NO_IPV6 + struct ipv6_mreq mreq6; + + if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + sockOpt = how6; + sockArg = reinterpret_cast<char *>(&mreq6); + sockArgSize = sizeof(mreq6); + memset(&mreq6, 0, sizeof(mreq6)); + Q_IPV6ADDR ip6 = groupAddress.toIPv6Address(); + memcpy(&mreq6.ipv6mr_multiaddr, &ip6, sizeof(ip6)); + mreq6.ipv6mr_interface = iface.index(); + } else +#endif + if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; + sockOpt = how4; + sockArg = reinterpret_cast<char *>(&mreq4); + sockArgSize = sizeof(mreq4); + memset(&mreq4, 0, sizeof(mreq4)); + mreq4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); + + if (iface.isValid()) { + QList<QNetworkAddressEntry> addressEntries = iface.addressEntries(); + if (!addressEntries.isEmpty()) { + QHostAddress firstIP = addressEntries.first().ip(); + mreq4.imr_interface.s_addr = htonl(firstIP.toIPv4Address()); + } else { + d->setError(QAbstractSocket::NetworkError, + QNativeSocketEnginePrivate::NetworkUnreachableErrorString); + return false; + } + } else { + mreq4.imr_interface.s_addr = INADDR_ANY; + } + } else { + // unreachable + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString); + return false; + } + + int res = setsockopt(d->socketDescriptor, level, sockOpt, sockArg, sockArgSize); + if (res == -1) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::OperationUnsupportedErrorString); + return false; + } + return true; +} + +bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + return multicastMembershipHelper(this, +#ifndef QT_NO_IPV6 + IPV6_JOIN_GROUP, +#else + 0, +#endif + IP_ADD_MEMBERSHIP, + groupAddress, + iface); +} + +bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + return multicastMembershipHelper(this, +#ifndef QT_NO_IPV6 + IPV6_LEAVE_GROUP, +#else + 0, +#endif + IP_DROP_MEMBERSHIP, + groupAddress, + iface); +} + +QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &v, &sizeofv) == -1) + return QNetworkInterface(); + return QNetworkInterface::interfaceFromIndex(v); + } +#endif + + struct in_addr v; + v.s_addr = 0; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, &sizeofv) == -1) + return QNetworkInterface(); + if (v.s_addr != 0 && sizeofv >= QT_SOCKOPTLEN_T(sizeof(v))) { + QHostAddress ipv4(ntohl(v.s_addr)); + QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces(); + for (int i = 0; i < ifaces.count(); ++i) { + const QNetworkInterface &iface = ifaces.at(i); + if (!(iface.flags() & QNetworkInterface::CanMulticast)) + continue; + QList<QNetworkAddressEntry> entries = iface.addressEntries(); + for (int j = 0; j < entries.count(); ++j) { + const QNetworkAddressEntry &entry = entries.at(j); + if (entry.ip() == ipv4) + return iface; + } + } + } + return QNetworkInterface(); +} + +bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInterface &iface) +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v = iface.isValid() ? iface.index() : 0; + return (::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &v, sizeof(v)) != -1); + } +#endif + + struct in_addr v; + if (iface.isValid()) { + QList<QNetworkAddressEntry> entries = iface.addressEntries(); + for (int i = 0; i < entries.count(); ++i) { + const QNetworkAddressEntry &entry = entries.at(i); + const QHostAddress &ip = entry.ip(); + if (ip.protocol() == QAbstractSocket::IPv4Protocol) { + v.s_addr = htonl(ip.toIPv4Address()); + int r = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, sizeof(v)); + if (r != -1) + return true; + } + } + return false; + } + + v.s_addr = INADDR_ANY; + return (::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, sizeof(v)) != -1); +} qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const { @@ -816,7 +1038,7 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const bool result = false; fd_set readS; FD_ZERO(&readS); - FD_SET(socketDescriptor, &readS); + FD_SET((SOCKET)socketDescriptor, &readS); timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 5000; @@ -1111,7 +1333,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co memset(&fds, 0, sizeof(fd_set)); fds.fd_count = 1; - fds.fd_array[0] = socketDescriptor; + fds.fd_array[0] = (SOCKET)socketDescriptor; struct timeval tv; tv.tv_sec = timeout / 1000; @@ -1125,12 +1347,12 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co // Windows needs this to report errors when connecting a socket ... fd_set fdexception; FD_ZERO(&fdexception); - FD_SET(socketDescriptor, &fdexception); + FD_SET((SOCKET)socketDescriptor, &fdexception); ret = select(0, 0, &fds, &fdexception, timeout < 0 ? 0 : &tv); // ... but if it is actually set, pretend it did not happen - if (ret > 0 && FD_ISSET(socketDescriptor, &fdexception)) + if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception)) ret--; } @@ -1157,16 +1379,16 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, memset(&fdread, 0, sizeof(fd_set)); if (checkRead) { fdread.fd_count = 1; - fdread.fd_array[0] = socketDescriptor; + fdread.fd_array[0] = (SOCKET)socketDescriptor; } memset(&fdwrite, 0, sizeof(fd_set)); FD_ZERO(&fdexception); if (checkWrite) { fdwrite.fd_count = 1; - fdwrite.fd_array[0] = socketDescriptor; + fdwrite.fd_array[0] = (SOCKET)socketDescriptor; // Windows needs this to report errors when connecting a socket - FD_SET(socketDescriptor, &fdexception); + FD_SET((SOCKET)socketDescriptor, &fdexception); } struct timeval tv; @@ -1180,7 +1402,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, #endif //... but if it is actually set, pretend it did not happen - if (ret > 0 && FD_ISSET(socketDescriptor, &fdexception)) + if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception)) ret--; if (readEnabled) @@ -1189,8 +1411,8 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, if (ret <= 0) return ret; - *selectForRead = FD_ISSET(socketDescriptor, &fdread); - *selectForWrite = FD_ISSET(socketDescriptor, &fdwrite); + *selectForRead = FD_ISSET((SOCKET)socketDescriptor, &fdread); + *selectForWrite = FD_ISSET((SOCKET)socketDescriptor, &fdwrite); return ret; } diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 53035d2ee6..ab3d260abf 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -56,6 +56,7 @@ #include "qurl.h" #include "qauthenticator.h" #include <qendian.h> +#include <qnetworkinterface.h> QT_BEGIN_NAMESPACE @@ -1544,6 +1545,35 @@ qint64 QSocks5SocketEngine::write(const char *data, qint64 len) } #ifndef QT_NO_UDPSOCKET +bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + +bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + + +QNetworkInterface QSocks5SocketEngine::multicastInterface() const +{ + return QNetworkInterface(); +} + +bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr, quint16 *port) { diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h index 808628a2c6..b85fd62d2e 100644 --- a/src/network/socket/qsocks5socketengine_p.h +++ b/src/network/socket/qsocks5socketengine_p.h @@ -92,6 +92,13 @@ public: qint64 write(const char *data, qint64 len); #ifndef QT_NO_UDPSOCKET + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &interface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); + qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0); qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index 83f3690846..6a62b12ca2 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -77,14 +77,23 @@ \snippet doc/src/snippets/code/src_network_socket_qudpsocket.cpp 0 + QUdpSocket also supports UDP multicast. Use joinMulticastGroup() and + leaveMulticastGroup() to control group membership, and + QAbstractSocket::MulticastTtlOption and + QAbstractSocket::MulticastLoopbackOption to set the TTL and loopback socket + options. Use setMulticastInterface() to control the outgoing interface for + multicast datagrams, and multicastInterface() to query it. + With QUdpSocket, you can also establish a virtual connection to a UDP server using connectToHost() and then use read() and write() to exchange datagrams without specifying the receiver for each datagram. - The \l{network/broadcastsender}{Broadcast Sender} and - \l{network/broadcastreceiver}{Broadcast Receiver} examples - illustrate how to use QUdpSocket in applications. + The \l{network/broadcastsender}{Broadcast Sender}, + \l{network/broadcastreceiver}{Broadcast Receiver}, + \l{network/multicastsender}{Multicast Sender}, and + \l{network/multicastreceiver}{Multicast Receiver} examples illustrate how + to use QUdpSocket in applications. \section1 Symbian Platform Security Requirements @@ -145,6 +154,7 @@ */ #include "qhostaddress.h" +#include "qnetworkinterface.h" #include "qabstractsocket_p.h" #include "qudpsocket.h" @@ -329,6 +339,112 @@ bool QUdpSocket::bind(quint16 port, BindMode mode) } /*! + \since 4.8 + + Joins the the multicast group specified by \a groupAddress on the default + interface chosen by the operating system. The socket must be in BoundState, + otherwise an error occurs. + + This function returns true if successful; otherwise it returns false + and sets the socket error accordingly. + + \sa leaveMulticastGroup() +*/ +bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress) +{ + return joinMulticastGroup(groupAddress, QNetworkInterface()); +} + +/*! + \since 4.8 + \overload + + Joins the multicast group address \a groupAddress on the interface \a + iface. + + \sa leaveMulticastGroup() +*/ +bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + Q_D(QUdpSocket); + QT_CHECK_BOUND("QUdpSocket::joinMulticastGroup()", false); + return d->socketEngine->joinMulticastGroup(groupAddress, iface); +} + +/*! + \since 4.8 + + Leaves the multicast group specified by \a groupAddress on the default + interface chosen by the operating system. The socket must be in BoundState, + otherwise an error occurs. + + This function returns true if successful; otherwise it returns false and + sets the socket error accordingly. + + \sa joinMulticastGroup() +*/ +bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress) +{ + return leaveMulticastGroup(groupAddress, QNetworkInterface()); +} + +/*! + \since 4.8 + \overload + + Leaves the multicast group specified by \a groupAddress on the interface \a + iface. + + \sa joinMulticastGroup() +*/ +bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + QT_CHECK_BOUND("QUdpSocket::leaveMulticastGroup()", false); + return d_func()->socketEngine->leaveMulticastGroup(groupAddress, iface); +} + +/*! + \since 4.8 + + Returns the interface for the outgoing interface for multicast datagrams. + This corresponds to the IP_MULTICAST_IF socket option for IPv4 sockets and + the IPV6_MULTICAST_IF socket option for IPv6 sockets. If no interface has + been previously set, this function returns an invalid QNetworkInterface. + The socket must be in BoundState, otherwise an invalid QNetworkInterface is + returned. + + \sa setMulticastInterface() +*/ +QNetworkInterface QUdpSocket::multicastInterface() const +{ + Q_D(const QUdpSocket); + QT_CHECK_BOUND("QUdpSocket::multicastInterface()", QNetworkInterface()); + return d->socketEngine->multicastInterface(); +} + +/*! + \since 4.8 + + Sets the outgoing interface for multicast datagrams to the interface \a + iface. This corresponds to the IP_MULTICAST_IF socket option for IPv4 + sockets and the IPV6_MULTICAST_IF socket option for IPv6 sockets. The + socket must be in BoundState, otherwise this function does nothing. + + \sa multicastInterface(), joinMulticastGroup(), leaveMulticastGroup() +*/ +void QUdpSocket::setMulticastInterface(const QNetworkInterface &iface) +{ + Q_D(QUdpSocket); + if (!isValid()) { + qWarning("QUdpSocket::setMulticastInterface() called on a QUdpSocket when not in QUdpSocket::BoundState"); + return; + } + d->socketEngine->setMulticastInterface(iface); +} + +/*! Returns true if at least one datagram is waiting to be read; otherwise returns false. diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h index 1236ee38c0..82266cb995 100644 --- a/src/network/socket/qudpsocket.h +++ b/src/network/socket/qudpsocket.h @@ -53,6 +53,7 @@ QT_MODULE(Network) #ifndef QT_NO_UDPSOCKET +class QNetworkInterface; class QUdpSocketPrivate; class Q_NETWORK_EXPORT QUdpSocket : public QAbstractSocket @@ -76,6 +77,16 @@ public: bool bind(quint16 port, BindMode mode); // ### Qt 5: Merge the bind functions + bool joinMulticastGroup(const QHostAddress &groupAddress); + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + bool leaveMulticastGroup(const QHostAddress &groupAddress); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface); + + QNetworkInterface multicastInterface() const; + void setMulticastInterface(const QNetworkInterface &iface); + bool hasPendingDatagrams() const; qint64 pendingDatagramSize() const; qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host = 0, quint16 *port = 0); |