#include "amqp_network_p.h" #include #include #include namespace QAMQP { class NetworkPrivate { public: NetworkPrivate(Network *qq); void initSocket(bool ssl = false); static int s_frameMethodMetaType; QPointer socket; QByteArray buffer; QString lastHost; int lastPort; bool autoReconnect; int timeOut; bool connect; Frame::MethodHandler *connectionMethodHandler; QHash > methodHandlersByChannel; QHash > contentHandlerByChannel; QHash > bodyHandlersByChannel; Q_DECLARE_PUBLIC(Network) Network * const q_ptr; }; int NetworkPrivate::s_frameMethodMetaType = qRegisterMetaType("QAMQP::Frame::Method"); NetworkPrivate::NetworkPrivate(Network *qq) : lastPort(0), autoReconnect(false), timeOut(1000), connect(false), q_ptr(qq) { buffer.reserve(Frame::HEADER_SIZE); } void NetworkPrivate::initSocket(bool ssl) { Q_Q(Network); if (socket) { socket->deleteLater(); socket = 0; } if (ssl) { #ifndef QT_NO_SSL socket = new QSslSocket(q); QSslSocket *sslSocket = static_cast(socket.data()); sslSocket->setProtocol(QSsl::AnyProtocol); QObject::connect(socket, SIGNAL(sslErrors(const QList &)), q, SLOT(sslErrors())); QObject::connect(socket, SIGNAL(connected()), q, SLOT(conectionReady())); #else qWarning("AMQP: You library has builded with QT_NO_SSL option."); #endif } else { socket = new QTcpSocket(q); QObject::connect(socket, SIGNAL(connected()), q, SLOT(conectionReady())); } if (socket) { QObject::connect(socket, SIGNAL(disconnected()), q, SIGNAL(disconnected())); QObject::connect(socket, SIGNAL(readyRead()), q, SLOT(readyRead())); QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), q, SLOT(error(QAbstractSocket::SocketError))); } } } // namespace QAMQP ////////////////////////////////////////////////////////////////////////// using namespace QAMQP; Network::Network(QObject *parent) : QObject(parent), d_ptr(new NetworkPrivate(this)) { Q_D(Network); d->initSocket(false); } Network::~Network() { disconnect(); } void Network::connectTo(const QString &host, quint16 port) { Q_D(Network); if (!d->socket) { qWarning("AMQP: Socket didn't create."); return; } QString h(host); int p(port); d->connect = true; if (host.isEmpty()) h = d->lastHost; if (port == 0) p = d->lastPort; if (isSsl()) { #ifndef QT_NO_SSL static_cast(d->socket.data())->connectToHostEncrypted(h, p); #else qWarning("AMQP: You library has builded with QT_NO_SSL option."); #endif } else { d->socket->connectToHost(h, p); } d->lastHost = h; d->lastPort = p; } void Network::disconnect() { Q_D(Network); d->connect = false; if (d->socket) d->socket->close(); } void Network::error(QAbstractSocket::SocketError socketError) { Q_D(Network); if (d->timeOut == 0) { d->timeOut = 1000; } else { if (d->timeOut < 120000) d->timeOut *= 5; } switch (socketError) { case QAbstractSocket::ConnectionRefusedError: case QAbstractSocket::RemoteHostClosedError: case QAbstractSocket::SocketTimeoutError: case QAbstractSocket::NetworkError: case QAbstractSocket::ProxyConnectionClosedError: case QAbstractSocket::ProxyConnectionRefusedError: case QAbstractSocket::ProxyConnectionTimeoutError: default: qWarning() << "AMQP: Socket Error: " << d->socket->errorString(); break; } if (d->autoReconnect && d->connect) QTimer::singleShot(d->timeOut, this, SLOT(connectTo())); } void Network::readyRead() { Q_D(Network); while (d->socket->bytesAvailable() >= Frame::HEADER_SIZE) { char *headerData = d->buffer.data(); d->socket->peek(headerData, Frame::HEADER_SIZE); const quint32 payloadSize = qFromBigEndian(*(quint32*)&headerData[3]); const qint64 readSize = Frame::HEADER_SIZE + payloadSize + Frame::FRAME_END_SIZE; if (d->socket->bytesAvailable() >= readSize) { d->buffer.resize(readSize); d->socket->read(d->buffer.data(), readSize); const char *bufferData = d->buffer.constData(); const quint8 type = *(quint8*)&bufferData[0]; const quint8 magic = *(quint8*)&bufferData[Frame::HEADER_SIZE + payloadSize]; if (magic != Frame::FRAME_END) qWarning() << "Wrong end frame"; QDataStream streamB(&d->buffer, QIODevice::ReadOnly); switch(Frame::Type(type)) { case Frame::ftMethod: { Frame::Method frame(streamB); if (frame.methodClass() == Frame::fcConnection) { d->connectionMethodHandler->_q_method(frame); } else { foreach (Frame::MethodHandler *methodHandler, d->methodHandlersByChannel[frame.channel()]) methodHandler->_q_method(frame); } } break; case Frame::ftHeader: { Frame::Content frame(streamB); foreach (Frame::ContentHandler *methodHandler, d->contentHandlerByChannel[frame.channel()]) methodHandler->_q_content(frame); } break; case Frame::ftBody: { Frame::ContentBody frame(streamB); foreach (Frame::ContentBodyHandler *methodHandler, d->bodyHandlersByChannel[frame.channel()]) methodHandler->_q_body(frame); } break; case Frame::ftHeartbeat: qDebug("AMQP: Heartbeat"); break; default: qWarning() << "AMQP: Unknown frame type: " << type; } } else { break; } } } void Network::sendFrame(const Frame::Base &frame) { Q_D(Network); if (d->socket->state() != QAbstractSocket::ConnectedState) { qDebug() << Q_FUNC_INFO << "socket not connected: " << d->socket->state(); return; } QDataStream stream(d->socket); frame.toStream(stream); } bool Network::isSsl() const { Q_D(const Network); if (d->socket) return QString(d->socket->metaObject()->className()).compare("QSslSocket", Qt::CaseInsensitive) == 0; return false; } void Network::setSsl(bool value) { Q_D(Network); d->initSocket(value); } void Network::sslErrors() { #ifndef QT_NO_SSL Q_D(Network); static_cast(d->socket.data())->ignoreSslErrors(); #endif } void Network::conectionReady() { Q_D(Network); d->timeOut = 0; char header[8] = {'A', 'M', 'Q', 'P', 0, 0, 9, 1}; d->socket->write(header, 8); Q_EMIT connected(); } bool Network::autoReconnect() const { Q_D(const Network); return d->autoReconnect; } void Network::setAutoReconnect(bool value) { Q_D(Network); d->autoReconnect = value; } QAbstractSocket::SocketState Network::state() const { Q_D(const Network); if (d->socket) return d->socket->state(); return QAbstractSocket::UnconnectedState; } void Network::setMethodHandlerConnection(Frame::MethodHandler *connectionMethodHandler) { Q_D(Network); d->connectionMethodHandler = connectionMethodHandler; } void Network::addMethodHandlerForChannel(Channel channel, Frame::MethodHandler *methodHandler) { Q_D(Network); d->methodHandlersByChannel[channel].append(methodHandler); } void Network::addContentHandlerForChannel(Channel channel, Frame::ContentHandler *methodHandler) { Q_D(Network); d->contentHandlerByChannel[channel].append(methodHandler); } void Network::addContentBodyHandlerForChannel(Channel channel, Frame::ContentBodyHandler *methodHandler) { Q_D(Network); d->bodyHandlersByChannel[channel].append(methodHandler); }