finished implementing amqps:// support

This commit is contained in:
Emiel Bruijntjes 2018-03-09 13:55:49 +01:00
parent 67056f6fd9
commit 28b6c903e1
6 changed files with 112 additions and 43 deletions

View File

@ -75,6 +75,7 @@ int main()
// make a connection // make a connection
AMQP::Address address("amqp://guest:guest@localhost/"); AMQP::Address address("amqp://guest:guest@localhost/");
// AMQP::Address address("amqps://guest:guest@localhost/");
AMQP::TcpConnection connection(&handler, address); AMQP::TcpConnection connection(&handler, address);
// we need a channel too // we need a channel too

View File

@ -3,7 +3,7 @@
* *
* Implementation of an AMQP connection * Implementation of an AMQP connection
* *
* @copyright 2014 - 2017 Copernica BV * @copyright 2014 - 2018 Copernica BV
*/ */
#include "includes.h" #include "includes.h"
#include "protocolheaderframe.h" #include "protocolheaderframe.h"

View File

@ -110,6 +110,21 @@ int SSL_set_fd(SSL *ssl, int fd)
return func(ssl, fd); return func(ssl, fd);
} }
/**
* The number of bytes availabe in the ssl struct that have been read
* from the socket, but that have not been returned the SSL_read()
* @param ssl SSL object
* @return int number of bytes
*/
int SSL_pending(const SSL *ssl)
{
// create a function
static Function<decltype(::SSL_pending)> func("SSL_pending");
// call the openssl function
return func(ssl);
}
/** /**
* Free an allocated ssl context * Free an allocated ssl context
* @param ctx * @param ctx

View File

@ -40,6 +40,7 @@ int SSL_do_handshake(SSL *ssl);
int SSL_read(SSL *ssl, void *buf, int num); int SSL_read(SSL *ssl, void *buf, int num);
int SSL_write(SSL *ssl, const void *buf, int num); int SSL_write(SSL *ssl, const void *buf, int num);
int SSL_shutdown(SSL *ssl); int SSL_shutdown(SSL *ssl);
int SSL_pending(const SSL *ssl);
int SSL_set_fd(SSL *ssl, int fd); int SSL_set_fd(SSL *ssl, int fd);
int SSL_get_shutdown(const SSL *ssl); int SSL_get_shutdown(const SSL *ssl);
int SSL_get_error(const SSL *ssl, int ret); int SSL_get_error(const SSL *ssl, int ret);

View File

@ -59,7 +59,7 @@ private:
* Are we now busy with sending or receiving? * Are we now busy with sending or receiving?
* @var enum * @var enum
*/ */
enum { enum State {
state_idle, state_idle,
state_sending, state_sending,
state_receiving state_receiving
@ -131,9 +131,6 @@ private:
// if we still have an outgoing buffer we want to send out data // if we still have an outgoing buffer we want to send out data
if (_out) if (_out)
{ {
// we still have a buffer with outgoing data
_state = state_sending;
// let's wait until the socket becomes writable // let's wait until the socket becomes writable
_handler->monitor(_connection, _socket, readable | writable); _handler->monitor(_connection, _socket, readable | writable);
} }
@ -150,9 +147,6 @@ private:
} }
else else
{ {
// outgoing buffer is empty, we're idle again waiting for further input
_state = state_idle;
// let's wait until the socket becomes readable // let's wait until the socket becomes readable
_handler->monitor(_connection, _socket, readable); _handler->monitor(_connection, _socket, readable);
} }
@ -164,14 +158,18 @@ private:
/** /**
* Method to repeat the previous call\ * Method to repeat the previous call\
* @param monitor monitor to check if connection object still exists * @param monitor monitor to check if connection object still exists
* @param state the state that we were in
* @param result result of an earlier SSL_get_error call * @param result result of an earlier SSL_get_error call
* @return TcpState* * @return TcpState*
*/ */
TcpState *repeat(const Monitor &monitor, int error) TcpState *repeat(const Monitor &monitor, enum State state, int error)
{ {
// check the error // check the error
switch (error) { switch (error) {
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
// remember state
_state = state;
// the operation must be repeated when readable // the operation must be repeated when readable
_handler->monitor(_connection, _socket, readable); _handler->monitor(_connection, _socket, readable);
@ -179,6 +177,9 @@ private:
return monitor.valid() ? this : nullptr; return monitor.valid() ? this : nullptr;
case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_WRITE:
// remember state
_state = state;
// wait until socket becomes writable again // wait until socket becomes writable again
_handler->monitor(_connection, _socket, readable | writable); _handler->monitor(_connection, _socket, readable | writable);
@ -235,7 +236,7 @@ private:
_in = std::move(buffer); _in = std::move(buffer);
// do we have to reallocate? // do we have to reallocate?
if (!_reallocate) return proceed(); if (!_reallocate) return this;
// reallocate the buffer // reallocate the buffer
_in.reallocate(_reallocate); _in.reallocate(_reallocate);
@ -244,9 +245,61 @@ private:
_reallocate = 0; _reallocate = 0;
// done // done
return this;
}
/**
* Perform a write operation
* @param monitor
* @return TcpState*
*/
TcpState *write(const Monitor &monitor)
{
// assume default state
_state = state_idle;
// try to send more data from the outgoing buffer
auto result = _out.sendto(_ssl);
// if this is a success, we can proceed with the event loop
if (result > 0) return proceed();
// the operation failed, we may have to repeat our call
return repeat(monitor, state_sending, OpenSSL::SSL_get_error(_ssl, result));
}
/**
* Perform a receive operation
* @param monitor
* @return TcpState
*/
TcpState *receive(const Monitor &monitor)
{
// start a loop
do
{
// assume default state
_state = state_idle;
// read data from ssl into the buffer
auto result = _in.receivefrom(_ssl, _connection->expected());
// if this is a failure, we are going to repeat the operation
if (result <= 0) return repeat(monitor, state_receiving, OpenSSL::SSL_get_error(_ssl, result));
// go process the received data
auto *nextstate = parse(monitor, result);
// leap out if we moved to a different state
if (nextstate != this) return nextstate;
}
while (OpenSSL::SSL_pending(_ssl) > 0);
// go to the next state
return proceed(); return proceed();
} }
public: public:
/** /**
* Constructor * Constructor
@ -295,29 +348,22 @@ public:
// the socket must be the one this connection writes to // the socket must be the one this connection writes to
if (fd != _socket) return this; if (fd != _socket) return this;
// are we busy with sending or receiving data? // if we were busy with a write operation, we have to repeat that
if (_state == state_sending) if (_state == state_sending) return write(monitor);
{
// try to send more data from the outgoing buffer
auto result = _out.sendto(_ssl);
// if this is a success, we can proceed with the event loop // same is true for read operations, they should also be repeated
if (result > 0) return proceed(); if (_state == state_receiving) return receive(monitor);
// the operation failed, we may have to repeat our call // if the socket is readable, we are going to receive data
else return repeat(monitor, OpenSSL::SSL_get_error(_ssl, result)); if (flags & readable) return receive(monitor);
}
else
{
// read data from ssl into the buffer
auto result = _in.receivefrom(_ssl, _connection->expected());
// if this is a success, we may have to update the monitor // socket is not readable (so it must be writable), do we have data to write?
if (result > 0) return parse(monitor, result); if (_out) return write(monitor);
// the operation failed, we may have to repeat our call // the only scenario in which we can end up here is the socket should be
else return repeat(monitor, OpenSSL::SSL_get_error(_ssl, result)); // closed, but instead of moving to the shutdown-state right, we call proceed()
} // because that function is a little more careful
return proceed();
} }
/** /**
@ -336,14 +382,17 @@ public:
// keep looping while we have an outgoing buffer // keep looping while we have an outgoing buffer
while (_out) while (_out)
{ {
// move to the idle-state
_state = state_idle;
// try to send more data from the outgoing buffer // try to send more data from the outgoing buffer
auto result = _out.sendto(_ssl); auto result = _out.sendto(_ssl);
// was this a success? // was this a success?
if (result > 0) if (result > 0)
{ {
// parse the buffer // proceed to the next state
auto *nextstate = parse(monitor, result); auto *nextstate = proceed();
// leap out if we move to a different state // leap out if we move to a different state
if (nextstate != this) return nextstate; if (nextstate != this) return nextstate;
@ -353,8 +402,8 @@ public:
// error was returned, so we must investigate what is going on // error was returned, so we must investigate what is going on
auto error = OpenSSL::SSL_get_error(_ssl, result); auto error = OpenSSL::SSL_get_error(_ssl, result);
// get the next state given this error // get the next state given the error
auto *nextstate = repeat(monitor, error); auto *nextstate = repeat(monitor, state_sending, error);
// leap out if we move to a different state // leap out if we move to a different state
if (nextstate != this) return nextstate; if (nextstate != this) return nextstate;
@ -385,9 +434,6 @@ public:
// for that operation to complete before we can move on // for that operation to complete before we can move on
if (_state != state_idle) return; if (_state != state_idle) return;
// object is now busy sending
_state = state_sending;
// let's wait until the socket becomes writable // let's wait until the socket becomes writable
_handler->monitor(_connection, _socket, readable | writable); _handler->monitor(_connection, _socket, readable | writable);
} }

View File

@ -190,6 +190,9 @@ public:
// close the connection // close the connection
auto result = OpenSSL::SSL_shutdown(_ssl); auto result = OpenSSL::SSL_shutdown(_ssl);
// on result==0 we need an additional call
while (result == 0) result = OpenSSL::SSL_shutdown(_ssl);
// if this is a success, we can proceed with the event loop // if this is a success, we can proceed with the event loop
if (result > 0) return proceed(monitor); if (result > 0) return proceed(monitor);
@ -213,6 +216,9 @@ public:
// close the connection // close the connection
auto result = OpenSSL::SSL_shutdown(_ssl); auto result = OpenSSL::SSL_shutdown(_ssl);
// on result==0 we need an additional call
while (result == 0) result = OpenSSL::SSL_shutdown(_ssl);
// if this is a success, we can proceed with the event loop // if this is a success, we can proceed with the event loop
if (result > 0) return proceed(monitor); if (result > 0) return proceed(monitor);