SMTP Command Pipelining (RFC-2920).

This commit is contained in:
Vincent Richard 2012-11-11 21:55:44 +01:00
parent f4c42cc0cf
commit 6ae75bc971
4 changed files with 149 additions and 35 deletions

View File

@ -41,9 +41,9 @@ namespace net {
namespace smtp {
SMTPResponse::SMTPResponse(ref <socket> sok, ref <timeoutHandler> toh)
SMTPResponse::SMTPResponse(ref <socket> sok, ref <timeoutHandler> toh, const state& st)
: m_socket(sok), m_timeoutHandler(toh),
m_responseContinues(false)
m_responseBuffer(st.responseBuffer), m_responseContinues(false)
{
}
@ -87,9 +87,9 @@ const string SMTPResponse::getText() const
// static
ref <SMTPResponse> SMTPResponse::readResponse
(ref <socket> sok, ref <timeoutHandler> toh)
(ref <socket> sok, ref <timeoutHandler> toh, const state& st)
{
ref <SMTPResponse> resp = vmime::create <SMTPResponse>(sok, toh);
ref <SMTPResponse> resp = vmime::create <SMTPResponse>(sok, toh, st);
resp->readResponse();
@ -218,6 +218,15 @@ const SMTPResponse::responseLine SMTPResponse::getLastLine() const
}
const SMTPResponse::state SMTPResponse::getCurrentState() const
{
state st;
st.responseBuffer = m_responseBuffer;
return st;
}
// SMTPResponse::responseLine

View File

@ -68,7 +68,7 @@ namespace smtp {
SMTPTransport::SMTPTransport(ref <session> sess, ref <security::authenticator> auth, const bool secured)
: transport(sess, getInfosInstance(), auth), m_socket(NULL),
m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL),
m_isSMTPS(secured), m_secured(false)
m_isSMTPS(secured), m_secured(false), m_pipelineStarted(false)
{
}
@ -571,9 +571,58 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
else if (expeditor.isEmpty())
throw exceptions::no_expeditor();
// Emit the "MAIL" command
ref <SMTPResponse> resp;
if (m_extensions.find("PIPELINING") != m_extensions.end())
{
beginCommandPipeline();
// Emit the "MAIL" command
sendRequest("MAIL FROM:<" + expeditor.getEmail() + ">");
// Emit a "RCPT TO" command for each recipient
for (int i = 0 ; i < recipients.getMailboxCount() ; ++i)
{
const mailbox& mbox = *recipients.getMailboxAt(i);
sendRequest("RCPT TO:<" + mbox.getEmail() + ">");
}
// Prepare sending of message data
sendRequest("DATA");
endCommandPipeline();
// Read response for "MAIL" command
if ((resp = readResponse())->getCode() != 250)
{
internalDisconnect();
throw exceptions::command_error("MAIL", resp->getText());
}
// Read responses for "RCPT TO" commands
for (int i = 0 ; i < recipients.getMailboxCount() ; ++i)
{
const mailbox& mbox = *recipients.getMailboxAt(i);
if ((resp = readResponse())->getCode() != 250)
{
internalDisconnect();
throw exceptions::command_error("RCPT TO", resp->getText(), mbox.getEmail());
}
}
// Read response for "DATA" command
if ((resp = readResponse())->getCode() != 354)
{
internalDisconnect();
throw exceptions::command_error("DATA", resp->getText());
}
}
else
{
// Emit the "MAIL" command
sendRequest("MAIL FROM:<" + expeditor.getEmail() + ">");
if ((resp = readResponse())->getCode() != 250)
@ -596,7 +645,7 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
}
}
// Send the message data
// Prepare sending of message data
sendRequest("DATA");
if ((resp = readResponse())->getCode() != 354)
@ -604,7 +653,9 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
internalDisconnect();
throw exceptions::command_error("DATA", resp->getText());
}
}
// Send the message data
// Stream copy with "\n." to "\n.." transformation
utility::outputStreamSocketAdapter sos(*m_socket);
utility::dotFilteredOutputStream fos(sos);
@ -624,18 +675,52 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients
}
void SMTPTransport::beginCommandPipeline()
{
m_pipeline.clear();
m_pipelineStarted = true;
}
void SMTPTransport::endCommandPipeline()
{
if (m_pipelineStarted)
{
m_socket->send(m_pipeline.str());
m_pipeline.clear();
m_pipelineStarted = false;
}
}
void SMTPTransport::sendRequest(const string& buffer, const bool end)
{
if (m_pipelineStarted)
{
m_pipeline << buffer;
if (end)
m_pipeline << "\r\n";
}
else
{
if (end)
m_socket->send(buffer + "\r\n");
else
m_socket->send(buffer);
}
}
ref <SMTPResponse> SMTPTransport::readResponse()
{
return SMTPResponse::readResponse(m_socket, m_timeoutHandler);
ref <SMTPResponse> resp = SMTPResponse::readResponse
(m_socket, m_timeoutHandler, m_responseState);
m_responseState = resp->getCurrentState();
return resp;
}

View File

@ -54,6 +54,12 @@ class SMTPResponse : public object
public:
/** Current state of response parser. */
struct state
{
string responseBuffer;
};
/** An element of a SMTP response. */
class responseLine
{
@ -78,11 +84,12 @@ public:
*
* @param sok socket from which to read
* @param toh time-out handler
* @param st previous state of response parser for the specified socket
* @return SMTP response
* @throws exceptions::operation_timed_out if no data
* has been received within the granted time
*/
static ref <SMTPResponse> readResponse(ref <socket> sok, ref <timeoutHandler> toh);
static ref <SMTPResponse> readResponse(ref <socket> sok, ref <timeoutHandler> toh, const state& st);
/** Return the SMTP response code.
*
@ -116,9 +123,15 @@ public:
*/
const responseLine getLastLine() const;
/** Returns the current state of the response parser.
*
* @return current parser state
*/
const state getCurrentState() const;
private:
SMTPResponse(ref <socket> sok, ref <timeoutHandler> toh);
SMTPResponse(ref <socket> sok, ref <timeoutHandler> toh, const state& st);
SMTPResponse(const SMTPResponse&);
void readResponse();

View File

@ -36,6 +36,7 @@
#include "vmime/net/timeoutHandler.hpp"
#include "vmime/net/smtp/SMTPServiceInfos.hpp"
#include "vmime/net/smtp/SMTPResponse.hpp"
namespace vmime {
@ -43,9 +44,6 @@ namespace net {
namespace smtp {
class SMTPResponse;
/** SMTP transport service.
*/
@ -102,6 +100,15 @@ private:
bool m_secured;
ref <connectionInfos> m_cntInfos;
SMTPResponse::state m_responseState;
// Pipelining
std::ostringstream m_pipeline;
bool m_pipelineStarted;
void beginCommandPipeline();
void endCommandPipeline();
// Service infos
static SMTPServiceInfos sm_infos;