QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice() { QNonContiguousByteDevice* device = 0; if (reply->outgoingDataBuffer) device = QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer); else if (reply->outgoingData) { device = QNonContiguousByteDeviceFactory::create(reply->outgoingData); } else { return 0; } bool bufferDisallowed = reply->request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute, QVariant(false)) == QVariant(true); if (bufferDisallowed) device->disableReset(); // make sure we delete this later device->setParent(this); connect(device, SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64))); return device; }
bool QHttpNetworkConnectionChannel::resetUploadData() { QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); if (!uploadByteDevice) return true; if (uploadByteDevice->reset()) { written = 0; return true; } else { connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError); return false; } }
void QHttpNetworkConnectionChannel::handleStatus() { Q_ASSERT(socket); Q_ASSERT(reply); int statusCode = reply->statusCode(); bool resend = false; switch (statusCode) { case 401: // auth required case 407: // proxy auth required if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) { if (resend) { QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); if (uploadByteDevice) { if (uploadByteDevice->reset()) { written = 0; } else { connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError); break; } } reply->d_func()->eraseData(); if (alreadyPipelinedRequests.isEmpty()) { // this does a re-send without closing the connection resendCurrent = true; QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } else { // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest closeAndResendCurrentRequest(); QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } } } else { emit reply->headerChanged(); emit reply->readyRead(); QNetworkReply::NetworkError errorCode = (statusCode == 407) ? QNetworkReply::ProxyAuthenticationRequiredError : QNetworkReply::AuthenticationRequiredError; reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket); emit connection->error(errorCode, reply->d_func()->errorString); emit reply->finished(); } break; default: QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } }
bool QHttpNetworkConnectionChannel::resetUploadData() { if (!reply) { //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending return false; } QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); if (!uploadByteDevice) return true; if (uploadByteDevice->reset()) { written = 0; return true; } else { connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError); return false; } }
bool QHttpNetworkConnectionChannel::sendRequest() { if (!reply) { // heh, how should that happen! qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply"; state = QHttpNetworkConnectionChannel::IdleState; return false; } switch (state) { case QHttpNetworkConnectionChannel::IdleState: { // write the header if (!ensureConnection()) { // wait for the connection (and encryption) to be done // sendRequest will be called again from either // _q_connected or _q_encrypted return false; } written = 0; // excluding the header bytesTotal = 0; 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 if (!request.url().userInfo().isEmpty() && request.withCredentials()) { QUrl url = request.url(); QAuthenticator &auth = authenticator; if (url.userName() != auth.user() || (!url.password().isEmpty() && url.password() != auth.password())) { auth.setUser(url.userName()); auth.setPassword(url.password()); connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false); } // clear the userinfo, since we use the same request for resending // userinfo in url can conflict with the one in the authenticator url.setUserInfo(QString()); request.setUrl(url); } // Will only be false if QtWebKit is performing a cross-origin XMLHttpRequest // and withCredentials has not been set to true. if (request.withCredentials()) connection->d_func()->createAuthorization(socket, request); #ifndef QT_NO_NETWORKPROXY QByteArray header = QHttpNetworkRequestPrivate::header(request, (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); #else QByteArray header = QHttpNetworkRequestPrivate::header(request, false); #endif socket->write(header); // flushing is dangerous (QSslSocket calls transmit which might read or error) // socket->flush(); QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); if (uploadByteDevice) { // connect the signals so this function gets called again QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead())); bytesTotal = request.contentLength(); state = QHttpNetworkConnectionChannel::WritingState; // start writing data sendRequest(); //recurse } else { state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response sendRequest(); //recurse } break; } case QHttpNetworkConnectionChannel::WritingState: { // write the data QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); if (!uploadByteDevice || bytesTotal == written) { if (uploadByteDevice) emit reply->dataSendProgress(written, bytesTotal); state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response sendRequest(); // recurse break; } // only feed the QTcpSocket buffer when there is less than 32 kB in it const qint64 socketBufferFill = 32*1024; const qint64 socketWriteMaxSize = 16*1024; #ifndef QT_NO_OPENSSL QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); // if it is really an ssl socket, check more than just bytesToWrite() while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) <= socketBufferFill && bytesTotal != written) #else while (socket->bytesToWrite() <= socketBufferFill && bytesTotal != written) #endif { // get pointer to upload data qint64 currentReadSize = 0; qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written); const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize); if (currentReadSize == -1) { // premature eof happened connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError); return false; break; } else if (readPointer == 0 || currentReadSize == 0) { // nothing to read currently, break the loop break; } else { qint64 currentWriteSize = socket->write(readPointer, currentReadSize); if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { // socket broke down connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError); return false; } else { written += currentWriteSize; uploadByteDevice->advanceReadPointer(currentWriteSize); emit reply->dataSendProgress(written, bytesTotal); if (written == bytesTotal) { // make sure this function is called once again state = QHttpNetworkConnectionChannel::WaitingState; sendRequest(); break; } } } } break; } case QHttpNetworkConnectionChannel::WaitingState: { QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); if (uploadByteDevice) { QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead())); } // HTTP pipelining //connection->d_func()->fillPipeline(socket); //socket->flush(); // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called // this is needed if the sends an reply before we have finished sending the request. In that // case receiveReply had been called before but ignored the server reply if (socket->bytesAvailable()) QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection); break; } case QHttpNetworkConnectionChannel::ReadingState: // ignore _q_bytesWritten in these states // fall through default: break; } return true; }
void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) { QHttpNetworkRequest &request = messagePair.first; QHttpNetworkReply *reply = messagePair.second; // add missing fields for the request QByteArray value; // check if Content-Length is provided QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); if (uploadByteDevice) { if (request.contentLength() != -1 && uploadByteDevice->size() != -1) { // both values known, take the smaller one. request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength())); } else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) { // content length not supplied by user, but the upload device knows it request.setContentLength(uploadByteDevice->size()); } else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) { // everything OK, the user supplied us the contentLength } else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) { qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given"); } } // set the Connection/Proxy-Connection: Keep-Alive headers #ifndef QT_NO_NETWORKPROXY if (networkProxy.type() == QNetworkProxy::HttpCachingProxy) { value = request.headerField("proxy-connection"); if (value.isEmpty()) request.setHeaderField("Proxy-Connection", "Keep-Alive"); } else { #endif value = request.headerField("connection"); if (value.isEmpty()) request.setHeaderField("Connection", "Keep-Alive"); #ifndef QT_NO_NETWORKPROXY } #endif // If the request had a accept-encoding set, we better not mess // with it. If it was not set, we announce that we understand gzip // and remember this fact in request.d->autoDecompress so that // we can later decompress the HTTP reply if it has such an // encoding. value = request.headerField("accept-encoding"); if (value.isEmpty()) { #ifndef QT_NO_COMPRESS request.setHeaderField("Accept-Encoding", "gzip"); request.d->autoDecompress = true; #else // if zlib is not available set this to false always request.d->autoDecompress = false; #endif } // some websites mandate an accept-language header and fail // if it is not sent. This is a problem with the website and // not with us, but we work around this by setting // one always. value = request.headerField("accept-language"); if (value.isEmpty()) { QString systemLocale = QLocale::system().name().replace(QChar::fromAscii('_'),QChar::fromAscii('-')); QString acceptLanguage; if (systemLocale == QLatin1String("C")) acceptLanguage = QString::fromAscii("en,*"); else if (systemLocale.startsWith(QLatin1String("en-"))) acceptLanguage = QString::fromAscii("%1,*").arg(systemLocale); else acceptLanguage = QString::fromAscii("%1,en,*").arg(systemLocale); request.setHeaderField("Accept-Language", acceptLanguage.toAscii()); } // set the User Agent value = request.headerField("user-agent"); if (value.isEmpty()) request.setHeaderField("User-Agent", "Mozilla/5.0"); // set the host value = request.headerField("host"); if (value.isEmpty()) { QHostAddress add; QByteArray host; if(add.setAddress(hostName)) { if(add.protocol() == QAbstractSocket::IPv6Protocol) { host = "[" + hostName.toAscii() + "]";//format the ipv6 in the standard way } else { host = QUrl::toAce(hostName); } } else { host = QUrl::toAce(hostName); } int port = request.url().port(); if (port != -1) { host += ':'; host += QByteArray::number(port); } request.setHeaderField("Host", host); } reply->d_func()->requestIsPrepared = true; }