diff options
| author | Lincoln Ramsay <lincoln.ramsay@nokia.com> | 2010-04-06 15:20:11 +1000 |
|---|---|---|
| committer | Lincoln Ramsay <lincoln.ramsay@nokia.com> | 2010-04-14 14:15:43 +1000 |
| commit | ccd0a2b9bf5adfcf8ff2a8f29e63e5d462e0bc46 (patch) | |
| tree | 713c1e5a81f0383cbf38bc2dd98d3142b4f32efb | |
| parent | 52a1595de5914073e69cae0086dfbc68b1dc9d61 (diff) | |
Limit memory use while sending messages.2010W15
Message data is streamed to a temporary file and then queued
in blocks so that the sending buffers do not become large.
This seems to result in a roughly 1.5MB fixed size when
sending, no matter the size of the message being sent.
Fixes: QTSOL-76
| -rw-r--r-- | src/plugins/messageservices/smtp/smtpclient.cpp | 89 | ||||
| -rw-r--r-- | src/plugins/messageservices/smtp/smtpclient.h | 8 |
2 files changed, 86 insertions, 11 deletions
diff --git a/src/plugins/messageservices/smtp/smtpclient.cpp b/src/plugins/messageservices/smtp/smtpclient.cpp index 08502f85..b996facb 100644 --- a/src/plugins/messageservices/smtp/smtpclient.cpp +++ b/src/plugins/messageservices/smtp/smtpclient.cpp @@ -46,11 +46,18 @@ #include <QHostAddress> #include <QTextCodec> +#include <QTemporaryFile> +#include <QCoreApplication> +#include <QDir> #include <qmaillog.h> #include <qmailaddress.h> #include <qmailstore.h> #include <qmailtransport.h> +#include <qmailnamespace.h> +// The size of the buffer used when sending messages. +// Only this many bytes is queued to be sent at a time. +#define SENDING_BUFFER_SIZE 5000 static bool initialiseRng() { @@ -80,14 +87,18 @@ static QByteArray messageId(const QByteArray& domainName, quint32 addressCompone SmtpClient::SmtpClient(QObject* parent) : QObject(parent) + , sending(false) + , transport(0) + , temporaryFile(0) + , waitingForBytes(0) { - sending = false; - transport = 0; } SmtpClient::~SmtpClient() { delete transport; + if (temporaryFile) + delete temporaryFile; } QMailMessage::MessageType SmtpClient::messageType() const @@ -577,15 +588,37 @@ void SmtpClient::nextAction(const QString &response) // Set the message's message ID mailItr->mail.setHeaderField("Message-ID", messageId(domainName, addressComponent)); - transport->mark(); - mailItr->mail.toRfc2822(transport->stream(), QMailMessage::TransmissionFormat); - messageLength = transport->bytesSinceMark(); - - transport->stream().writeRawData("\r\n.\r\n", 5); - - qMailLog(SMTP) << "Body: sent:" << messageLength << "bytes"; - - status = Sent; + Q_ASSERT(temporaryFile == 0); + + // Buffer the message to a temporary file. + QString tempPath = QMail::tempPath(); + QDir dir; + if (!dir.exists(tempPath)) + dir.mkpath(tempPath); + temporaryFile = new QTemporaryFile(tempPath + QLatin1String("/qtopiamail.XXXXXX")); + bool ok = temporaryFile->open(); + Q_ASSERT(ok); + { + // Note that there is no progress update while the message is streamed to the file. + // This isn't optimal but it's how the original code worked and fixing it requires + // putting this call onto a separate thread because it blocks until done. + QDataStream dataStream(temporaryFile); + mailItr->mail.toRfc2822(dataStream, QMailMessage::TransmissionFormat); + } + messageLength = temporaryFile->size(); + //qMailLog(SMTP) << "Body: queued" << messageLength << "bytes to" << temporaryFile->fileName(); + + // Now write the message to the transport in blocks, waiting for the bytes to be + // written each time so there is no need to allocate large buffers to hold everything. + temporaryFile->seek(0); + waitingForBytes = 0; + if (transport->isEncrypted()) + connect(&(transport->socket()), SIGNAL(encryptedBytesWritten(qint64)), this, SLOT(sendMoreData(qint64))); + else + connect(transport, SIGNAL(bytesWritten(qint64)), this, SLOT(sendMoreData(qint64))); + + // trigger the sending of the first block of data + sendMoreData(0); } else { operationFailed(QMailServiceAction::Status::ErrUnknownResponse, response); } @@ -754,3 +787,37 @@ void SmtpClient::operationFailed(QMailServiceAction::Status::ErrorCode code, con emit errorOccurred(code, msg); } +void SmtpClient::sendMoreData(qint64 bytesWritten) +{ + Q_ASSERT(status == Body && temporaryFile); + + waitingForBytes -= bytesWritten; + + // If anyone else writes bytes we end up with a negative value... just reset to 0 when that happens. + if (waitingForBytes < 0) waitingForBytes = 0; + + // Don't send more data until all bytes have been written. + if (waitingForBytes) return; + + // No more data to send + if (temporaryFile->atEnd()) { + if (transport->isEncrypted()) + disconnect(&(transport->socket()), SIGNAL(encryptedBytesWritten(qint64)), this, SLOT(sendMoreData(qint64))); + else + disconnect(transport, SIGNAL(bytesWritten(qint64)), this, SLOT(sendMoreData(qint64))); + delete temporaryFile; + temporaryFile = 0; + transport->stream().writeRawData("\r\n.\r\n", 5); + qMailLog(SMTP) << "Body: sent:" << messageLength << "bytes"; + status = Sent; + return; + } + + // Queue up to SENDING_BUFFER_SIZE bytes for transmission + char buffer[SENDING_BUFFER_SIZE]; + qint64 bytes = temporaryFile->read(buffer, SENDING_BUFFER_SIZE); + waitingForBytes += bytes; + transport->stream().writeRawData(buffer, bytes); + //qMailLog(SMTP) << "Body: sent a" << bytes << "byte block"; +} + diff --git a/src/plugins/messageservices/smtp/smtpclient.h b/src/plugins/messageservices/smtp/smtpclient.h index 27be1a37..0d92dec7 100644 --- a/src/plugins/messageservices/smtp/smtpclient.h +++ b/src/plugins/messageservices/smtp/smtpclient.h @@ -51,6 +51,8 @@ #include <qmailmessageserver.h> #include <qmailtransport.h> +class QTemporaryFile; + struct RawEmail { QString from; @@ -91,6 +93,9 @@ protected slots: void readyRead(); void sent(qint64); +private slots: + void sendMoreData(qint64); + private: void sendCommand(const char *data, int len = -1); void sendCommand(const QString &cmd); @@ -132,6 +137,9 @@ private: QStringList capabilities; quint32 addressComponent; QByteArray domainName; + + QTemporaryFile *temporaryFile; + qint64 waitingForBytes; }; #endif |
