feat:添加事件数据服务模块AlarmEventDataService,完成和RabbitMQ的通信框架
This commit is contained in:
parent
c99d726b72
commit
1907ec8e61
|
|
@ -1,5 +1,6 @@
|
||||||
build/
|
build/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.qtcreator/
|
||||||
|
|
||||||
# ---> CMake
|
# ---> CMake
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Concurrent)
|
||||||
|
|
||||||
set(ADS_VERSION 4.3.1)
|
set(ADS_VERSION 4.3.1)
|
||||||
add_subdirectory(QtADS)
|
add_subdirectory(QtADS)
|
||||||
|
add_subdirectory(qamqp)
|
||||||
|
|
||||||
#默认ui文件要和.h头文件在一个目录,若不在一个目录,需要指定其所在目录
|
#默认ui文件要和.h头文件在一个目录,若不在一个目录,需要指定其所在目录
|
||||||
set(CMAKE_AUTOUIC_SEARCH_PATHS "ui")
|
set(CMAKE_AUTOUIC_SEARCH_PATHS "ui")
|
||||||
|
|
@ -53,6 +54,7 @@ set(H_HEADER_FILES
|
||||||
include/alarmEventDataView.h
|
include/alarmEventDataView.h
|
||||||
include/alarmEventRealTimeDock.h
|
include/alarmEventRealTimeDock.h
|
||||||
include/alarmEventUtils.h
|
include/alarmEventUtils.h
|
||||||
|
include/alarmEventDataService.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(CPP_SOURCE_FILES
|
set(CPP_SOURCE_FILES
|
||||||
|
|
@ -87,6 +89,7 @@ set(CPP_SOURCE_FILES
|
||||||
source/alarmEventDataView.cpp
|
source/alarmEventDataView.cpp
|
||||||
source/alarmEventRealTimeDock.cpp
|
source/alarmEventRealTimeDock.cpp
|
||||||
source/alarmEventUtils.cpp
|
source/alarmEventUtils.cpp
|
||||||
|
source/alarmEventDataService.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(UI_FILES
|
set(UI_FILES
|
||||||
|
|
@ -177,13 +180,15 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(PowerMaster PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
target_include_directories(PowerMaster PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
|
target_include_directories(PowerMaster PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/qamqp/src")
|
||||||
target_link_libraries(PowerMaster PRIVATE Qt${QT_VERSION_MAJOR}::PrintSupport)
|
target_link_libraries(PowerMaster PRIVATE Qt${QT_VERSION_MAJOR}::PrintSupport)
|
||||||
target_link_libraries(PowerMaster PRIVATE Qt${QT_VERSION_MAJOR}::Network)
|
target_link_libraries(PowerMaster PRIVATE Qt${QT_VERSION_MAJOR}::Network)
|
||||||
target_link_libraries(PowerMaster PRIVATE Qt${QT_VERSION_MAJOR}::Concurrent)
|
target_link_libraries(PowerMaster PRIVATE Qt${QT_VERSION_MAJOR}::Concurrent)
|
||||||
target_link_libraries(PowerMaster PRIVATE qt${QT_VERSION_MAJOR}advanceddocking)
|
target_link_libraries(PowerMaster PRIVATE qt${QT_VERSION_MAJOR}advanceddocking)
|
||||||
target_link_libraries(PowerMaster PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
target_link_libraries(PowerMaster PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||||
Qt${QT_VERSION_MAJOR}::Gui
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
Qt${QT_VERSION_MAJOR}::Widgets)
|
Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
|
qamqp)
|
||||||
|
|
||||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||||
# If you are developing for iOS or macOS you should consider setting an
|
# If you are developing for iOS or macOS you should consider setting an
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef ALARMEVENTDATASERVICE_H
|
||||||
|
#define ALARMEVENTDATASERVICE_H
|
||||||
|
|
||||||
|
#include "alarmEventGlobal.h"
|
||||||
|
#include "alarmEventUtils.h"
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class QAmqpClient;
|
||||||
|
class QAmqpQueue;
|
||||||
|
class QAmqpExchange;
|
||||||
|
|
||||||
|
class AlarmEventDataService : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static AlarmEventDataService* instance();
|
||||||
|
bool initialize(const ServiceConfig& config);
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void realTimeEventReceived(const EventData& event);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onRabbitMQConnected();
|
||||||
|
void onRabbitMQDisconnected();
|
||||||
|
void onRabbitMQError(QAMQP::Error error);
|
||||||
|
void onMessageReceived();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit AlarmEventDataService();
|
||||||
|
~AlarmEventDataService();
|
||||||
|
|
||||||
|
//连接管理
|
||||||
|
void startRealTimeDataService();
|
||||||
|
void cleanupRabbitMQConnection();
|
||||||
|
//实时信息处理
|
||||||
|
MessageHandleResult processMessage(const QAmqpMessage& message);
|
||||||
|
EventData parseEventFromMessage(const QByteArray& data, QString& errorString);
|
||||||
|
bool validateEvent(const EventData& event);
|
||||||
|
|
||||||
|
ServiceConfig m_config;
|
||||||
|
ServiceStatus m_serviceStatus;
|
||||||
|
//ConnectionStatus m_realTimeConnectionStatus;
|
||||||
|
//ConnectionStatus m_historicalConnectionStatus;
|
||||||
|
|
||||||
|
//RabbitMQ通信相关组件
|
||||||
|
QAmqpClient* m_amqpClient;
|
||||||
|
QAmqpQueue* m_amqpQueue;
|
||||||
|
QAmqpExchange* m_amqpExchange;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -26,6 +26,15 @@ struct EventData
|
||||||
QString description;
|
QString description;
|
||||||
QVariantMap condition; //事件发生时的简单场景描述,如{'up_limitaion': 40, 'low_limitation': 10, value: 45}
|
QVariantMap condition; //事件发生时的简单场景描述,如{'up_limitaion': 40, 'low_limitation': 10, value: 45}
|
||||||
QVariantMap alarmInfo; //{"driver_name":"ssu_driver_name","device_no":"ssu000","alarm_code":1,"alarm_time":2516666461000,"alarm_status":0}
|
QVariantMap alarmInfo; //{"driver_name":"ssu_driver_name","device_no":"ssu000","alarm_code":1,"alarm_time":2516666461000,"alarm_status":0}
|
||||||
|
|
||||||
|
static EventData fromJson(const QJsonObject& json)
|
||||||
|
{
|
||||||
|
EventData event;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,212 @@
|
||||||
#ifndef ALARMEVENTUTILS_H
|
#ifndef ALARMEVENTUTILS_H
|
||||||
#define ALARMEVENTUTILS_H
|
#define ALARMEVENTUTILS_H
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
#include "alarmEventGlobal.h"
|
#include "alarmEventGlobal.h"
|
||||||
|
|
||||||
|
///////------structs-----
|
||||||
|
//RabbitMQ链接配置
|
||||||
|
struct RabbitMQConfig
|
||||||
|
{
|
||||||
|
QString host;
|
||||||
|
int port;
|
||||||
|
QString username;
|
||||||
|
QString password;
|
||||||
|
QString virtualHost;
|
||||||
|
QString exchangeName;
|
||||||
|
QString queueName;
|
||||||
|
QString routingKey;
|
||||||
|
int reconnectInterval; //重连间隔(毫秒)
|
||||||
|
int heartbeat;
|
||||||
|
bool autoAck;
|
||||||
|
|
||||||
|
RabbitMQConfig()
|
||||||
|
: port(5672)
|
||||||
|
, reconnectInterval(5000)
|
||||||
|
, heartbeat(60)
|
||||||
|
, autoAck(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
if(host.isEmpty())
|
||||||
|
{
|
||||||
|
qWarning() << "RabbitMQ host is empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(port <= 0 || port > 65535)
|
||||||
|
{
|
||||||
|
qWarning() << "RabbitMQ port is invalid: " << port;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(username.isEmpty())
|
||||||
|
{
|
||||||
|
qWarning() << "RabbitMQ username is empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(exchangeName.isEmpty())
|
||||||
|
{
|
||||||
|
qWarning() << "RabbitMQ exchange name is empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(queueName.isEmpty())
|
||||||
|
{
|
||||||
|
qWarning() << "RabbitMQ queue name is empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(routingKey.isEmpty())
|
||||||
|
{
|
||||||
|
qWarning() << "RabbitMQ routingKey is empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (reconnectInterval <= 0)
|
||||||
|
{
|
||||||
|
qWarning() << "RabbitMQ reconnect interval is invalid:" << reconnectInterval;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//历史数据服务配置
|
||||||
|
struct HistoricalConfig
|
||||||
|
{
|
||||||
|
QUrl baseUrl;
|
||||||
|
int timeout; //请求超时(毫秒)
|
||||||
|
QVariantMap requestHeaders;
|
||||||
|
int maxRetries; //最大重试次数
|
||||||
|
int retryInterval; //重试间隔
|
||||||
|
|
||||||
|
HistoricalConfig()
|
||||||
|
: timeout(3000)
|
||||||
|
, maxRetries(3)
|
||||||
|
, retryInterval(1000)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
if(!baseUrl.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "Historical service base URL is invalid:" << baseUrl.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(timeout <= 0)
|
||||||
|
{
|
||||||
|
qWarning() << "Historical service timeout is invalid:" << timeout;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(maxRetries < 0)
|
||||||
|
{
|
||||||
|
qWarning() << "Historical service max retries is invalid:" << maxRetries;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(retryInterval <= 0)
|
||||||
|
{
|
||||||
|
qWarning() << "Historical service retry interval is invalid:" << retryInterval;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//缓存配置
|
||||||
|
struct CacheConfig
|
||||||
|
{
|
||||||
|
int maxSize; //缓存事件最大数量
|
||||||
|
int cleanupInterval; //清理间隔(毫秒)
|
||||||
|
bool enableStatistics; //是否统计
|
||||||
|
|
||||||
|
CacheConfig()
|
||||||
|
: maxSize(10000)
|
||||||
|
, cleanupInterval(60000) //1分钟
|
||||||
|
, enableStatistics(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
if(maxSize <= 0)
|
||||||
|
{
|
||||||
|
qWarning() << "Cache max size is invalid:" << maxSize;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(cleanupInterval < 0 )
|
||||||
|
{
|
||||||
|
qWarning() << "Cache cleanup interval is invalid:" << cleanupInterval;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//统一服务配置
|
||||||
|
struct ServiceConfig
|
||||||
|
{
|
||||||
|
RabbitMQConfig rabbitMQConfig;
|
||||||
|
HistoricalConfig historicalConfig;
|
||||||
|
CacheConfig cacheConfig;
|
||||||
|
|
||||||
|
ServiceConfig() {}
|
||||||
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
return rabbitMQConfig.isValid() && historicalConfig.isValid() && cacheConfig.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
//从JSON对象加载配置
|
||||||
|
static ServiceConfig fromJson(const QJsonObject& json);
|
||||||
|
//转化为JSON对象
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
///////------enums-----
|
||||||
|
//服务状态
|
||||||
|
enum class ServiceStatus
|
||||||
|
{
|
||||||
|
Uninitialized,//未初始化
|
||||||
|
Initialized, //已初始化
|
||||||
|
Disconnected, //未连接
|
||||||
|
Connecting, //连接中
|
||||||
|
Connected, //已连接
|
||||||
|
Error, //错误状态
|
||||||
|
Reconnecting //重连中
|
||||||
|
};
|
||||||
|
|
||||||
|
//连接状态
|
||||||
|
enum class ConnectionStatus
|
||||||
|
{
|
||||||
|
Disconnected,
|
||||||
|
Connecting,
|
||||||
|
Connected
|
||||||
|
};
|
||||||
|
|
||||||
|
//错误严重程度
|
||||||
|
enum class ErrorSeverity
|
||||||
|
{
|
||||||
|
Info, //信息
|
||||||
|
Warning, //警告
|
||||||
|
Error, //错误
|
||||||
|
Critical //严重错误
|
||||||
|
};
|
||||||
|
|
||||||
|
//消息处理结果
|
||||||
|
enum class MessageHandleResult
|
||||||
|
{
|
||||||
|
Success,
|
||||||
|
ParseError, //解析错误
|
||||||
|
ValidationError, //验证错误
|
||||||
|
Duplicate //重复消息
|
||||||
|
};
|
||||||
|
|
||||||
|
//注册为元类型,用于信号槽的chuandi
|
||||||
|
Q_DECLARE_METATYPE(ServiceStatus)
|
||||||
|
Q_DECLARE_METATYPE(ConnectionStatus)
|
||||||
|
Q_DECLARE_METATYPE(ErrorSeverity)
|
||||||
|
Q_DECLARE_METATYPE(MessageHandleResult)
|
||||||
|
|
||||||
|
///////------classes-----
|
||||||
class AlarmEventDataFilter
|
class AlarmEventDataFilter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -33,6 +237,17 @@ private:
|
||||||
int m_status;
|
int m_status;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 数据缓存
|
||||||
|
*
|
||||||
|
* 数据缓存的主要作用:
|
||||||
|
* 批量存储:当数据需要做本地存储时,可以先存入缓存,达到一定数量后批量进行存储,从而减少数据库的频繁操作
|
||||||
|
* 快速访问:按条件查询数据时,先从缓存中查找,减少数据库或网络操作
|
||||||
|
* 数据去重:避免存储重复数据
|
||||||
|
* 清理策略:例如采用LRU,自动清理最久未使用的数据,从而控制内存内使用
|
||||||
|
*
|
||||||
|
* 若有以上场景,可以按需扩展该类,目前暂无场景
|
||||||
|
*/
|
||||||
class AlarmEventCache
|
class AlarmEventCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#ifndef MAINWINDOW_H
|
#ifndef MAINWINDOW_H
|
||||||
#define MAINWINDOW_H
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
#include "alarmEventRealTimeDock.h"
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(qamqp VERSION 0.0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
find_package(Git QUIET)
|
||||||
|
|
||||||
|
if(GIT_FOUND)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} describe --long --dirty --tags
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_VERSION
|
||||||
|
ERROR_QUIET
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} describe --abbrev=0
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_TAG
|
||||||
|
ERROR_QUIET
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
if(GIT_TAG)
|
||||||
|
string(REGEX REPLACE "^v" "" VERSION ${GIT_TAG})
|
||||||
|
else()
|
||||||
|
set(VERSION ${PROJECT_VERSION})
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(GIT_VERSION "unknown")
|
||||||
|
set(VERSION ${PROJECT_VERSION})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_compile_definitions(GIT_VERSION="${GIT_VERSION}")
|
||||||
|
|
||||||
|
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
|
||||||
|
|
||||||
|
find_package(Qt6 COMPONENTS Core Network REQUIRED)
|
||||||
|
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
export(TARGETS qamqp
|
||||||
|
NAMESPACE qamqp::
|
||||||
|
FILE "${CMAKE_CURRENT_BINARY_DIR}/qamqpTargets.cmake"
|
||||||
|
)
|
||||||
|
|
||||||
|
option(BUILD_TESTS "Build tests" OFF)
|
||||||
|
if(BUILD_TESTS)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(BUILD_TUTORIALS "Build tutorials" OFF)
|
||||||
|
if(BUILD_TUTORIALS)
|
||||||
|
add_subdirectory(tutorials)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
install(EXPORT qamqpTargets
|
||||||
|
FILE qamqpTargets.cmake
|
||||||
|
NAMESPACE qamqp::
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qamqp
|
||||||
|
)
|
||||||
|
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
write_basic_package_version_file(
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/qamqpConfigVersion.cmake"
|
||||||
|
VERSION ${VERSION}
|
||||||
|
COMPATIBILITY SameMajorVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/qamqpConfig.cmake.in
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/qamqpConfig.cmake"
|
||||||
|
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qamqp
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/qamqpConfig.cmake"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/qamqpConfigVersion.cmake"
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qamqp
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include(CMakeFindDependencyMacro)
|
||||||
|
find_dependency(Qt6 COMPONENTS Core Network)
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/qamqpTargets.cmake")
|
||||||
|
|
||||||
|
check_required_components(qamqp)
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
set(QAMQP_HEADERS
|
||||||
|
qamqpauthenticator.h
|
||||||
|
qamqpchannel.h
|
||||||
|
qamqpchannel_p.h
|
||||||
|
qamqpchannelhash_p.h
|
||||||
|
qamqpclient.h
|
||||||
|
qamqpclient_p.h
|
||||||
|
qamqpexchange.h
|
||||||
|
qamqpexchange_p.h
|
||||||
|
qamqpglobal.h
|
||||||
|
qamqpmessage.h
|
||||||
|
qamqpmessage_p.h
|
||||||
|
qamqpqueue.h
|
||||||
|
qamqpqueue_p.h
|
||||||
|
qamqptable.h
|
||||||
|
qamqpframe_p.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(QAMQP_SOURCES
|
||||||
|
qamqpauthenticator.cpp
|
||||||
|
qamqpchannel.cpp
|
||||||
|
qamqpchannelhash.cpp
|
||||||
|
qamqpclient.cpp
|
||||||
|
qamqpexchange.cpp
|
||||||
|
qamqpframe.cpp
|
||||||
|
qamqpmessage.cpp
|
||||||
|
qamqpqueue.cpp
|
||||||
|
qamqptable.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build static library for MSVC" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_SHARED_LIBS)
|
||||||
|
add_library(qamqp SHARED ${QAMQP_HEADERS} ${QAMQP_SOURCES})
|
||||||
|
else()
|
||||||
|
add_library(qamqp STATIC ${QAMQP_HEADERS} ${QAMQP_SOURCES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(qamqp::qamqp ALIAS qamqp)
|
||||||
|
|
||||||
|
set_target_properties(qamqp PROPERTIES
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||||
|
EXPORT_NAME qamqp
|
||||||
|
OUTPUT_NAME qamqp
|
||||||
|
DEBUG_POSTFIX d
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(qamqp
|
||||||
|
PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||||
|
$<INSTALL_INTERFACE:include/qamqp>
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(qamqp
|
||||||
|
PUBLIC
|
||||||
|
Qt6::Core
|
||||||
|
Qt6::Network
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(qamqp
|
||||||
|
PRIVATE
|
||||||
|
QAMQP_BUILD
|
||||||
|
PUBLIC
|
||||||
|
$<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:QAMQP_STATIC>
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(qamqp PROPERTIES
|
||||||
|
AUTOMOC ON
|
||||||
|
AUTORCC ON
|
||||||
|
AUTOUIC ON
|
||||||
|
CXX_STANDARD 14
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
CXX_EXTENSIONS OFF
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||||
|
EXPORT_NAME qamqp
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS qamqp
|
||||||
|
EXPORT qamqpTargets
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qamqp
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES ${QAMQP_HEADERS}
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qamqp
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
#include "qamqptable.h"
|
||||||
|
#include "qamqpframe_p.h"
|
||||||
|
#include "qamqpauthenticator.h"
|
||||||
|
|
||||||
|
QAmqpPlainAuthenticator::QAmqpPlainAuthenticator(const QString &l, const QString &p)
|
||||||
|
: login_(l),
|
||||||
|
password_(p)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpPlainAuthenticator::~QAmqpPlainAuthenticator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpPlainAuthenticator::login() const
|
||||||
|
{
|
||||||
|
return login_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpPlainAuthenticator::password() const
|
||||||
|
{
|
||||||
|
return password_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpPlainAuthenticator::type() const
|
||||||
|
{
|
||||||
|
return "AMQPLAIN";
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpPlainAuthenticator::setLogin(const QString &l)
|
||||||
|
{
|
||||||
|
login_ = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpPlainAuthenticator::setPassword(const QString &p)
|
||||||
|
{
|
||||||
|
password_ = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpPlainAuthenticator::write(QDataStream &out)
|
||||||
|
{
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, type());
|
||||||
|
QAmqpTable response;
|
||||||
|
response["LOGIN"] = login_;
|
||||||
|
response["PASSWORD"] = password_;
|
||||||
|
out << response;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Alexey Shcherbakov
|
||||||
|
* Copyright (C) 2014-2015 Matt Broadstone
|
||||||
|
* Contact: https://github.com/mbroadst/qamqp
|
||||||
|
*
|
||||||
|
* This file is part of the QAMQP Library.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
#ifndef QAMQPAUTHENTICATOR_H
|
||||||
|
#define QAMQPAUTHENTICATOR_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
|
||||||
|
class QAMQP_EXPORT QAmqpAuthenticator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~QAmqpAuthenticator() {}
|
||||||
|
virtual QString type() const = 0;
|
||||||
|
virtual void write(QDataStream &out) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QAMQP_EXPORT QAmqpPlainAuthenticator : public QAmqpAuthenticator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QAmqpPlainAuthenticator(const QString &login = QString(), const QString &password = QString());
|
||||||
|
virtual ~QAmqpPlainAuthenticator();
|
||||||
|
|
||||||
|
QString login() const;
|
||||||
|
void setLogin(const QString &l);
|
||||||
|
|
||||||
|
QString password() const;
|
||||||
|
void setPassword(const QString &p);
|
||||||
|
|
||||||
|
virtual QString type() const;
|
||||||
|
virtual void write(QDataStream &out);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString login_;
|
||||||
|
QString password_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPAUTHENTICATOR_H
|
||||||
|
|
@ -0,0 +1,368 @@
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "qamqpchannel.h"
|
||||||
|
#include "qamqpchannel_p.h"
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpclient_p.h"
|
||||||
|
|
||||||
|
quint16 QAmqpChannelPrivate::nextChannelNumber = 0;
|
||||||
|
QAmqpChannelPrivate::QAmqpChannelPrivate(QAmqpChannel *q)
|
||||||
|
: channelNumber(0),
|
||||||
|
opened(false),
|
||||||
|
needOpen(true),
|
||||||
|
prefetchSize(0),
|
||||||
|
requestedPrefetchSize(0),
|
||||||
|
prefetchCount(0),
|
||||||
|
requestedPrefetchCount(0),
|
||||||
|
error(QAMQP::NoError),
|
||||||
|
q_ptr(q)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpChannelPrivate::~QAmqpChannelPrivate()
|
||||||
|
{
|
||||||
|
if (!client.isNull()) {
|
||||||
|
QAmqpClientPrivate *priv = client->d_func();
|
||||||
|
priv->methodHandlersByChannel[channelNumber].removeAll(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::init(int channel, QAmqpClient *c)
|
||||||
|
{
|
||||||
|
client = c;
|
||||||
|
needOpen = (channel <= nextChannelNumber && channel != -1) ? false : true;
|
||||||
|
channelNumber = channel == -1 ? ++nextChannelNumber : channel;
|
||||||
|
nextChannelNumber = qMax(channelNumber, nextChannelNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpChannelPrivate::_q_method(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_ASSERT(frame.channel() == channelNumber);
|
||||||
|
if (frame.channel() != channelNumber)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (frame.methodClass() == QAmqpFrame::Basic) {
|
||||||
|
if (frame.id() == bmQosOk) {
|
||||||
|
qosOk(frame);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame.methodClass() != QAmqpFrame::Channel)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (frame.id()) {
|
||||||
|
case miOpenOk:
|
||||||
|
openOk(frame);
|
||||||
|
break;
|
||||||
|
case miFlow:
|
||||||
|
flow(frame);
|
||||||
|
break;
|
||||||
|
case miFlowOk:
|
||||||
|
flowOk(frame);
|
||||||
|
break;
|
||||||
|
case miClose:
|
||||||
|
close(frame);
|
||||||
|
break;
|
||||||
|
case miCloseOk:
|
||||||
|
closeOk(frame);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::_q_open()
|
||||||
|
{
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::sendFrame(const QAmqpFrame &frame)
|
||||||
|
{
|
||||||
|
if (!client) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "invalid client";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->d_func()->sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::resetInternalState()
|
||||||
|
{
|
||||||
|
if (!opened) return;
|
||||||
|
opened = false;
|
||||||
|
needOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::open()
|
||||||
|
{
|
||||||
|
if (!needOpen || opened)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!client->isConnected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
qAmqpDebug("<- channel#open( channel=%d, name=%s )", channelNumber, qPrintable(name));
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Channel, miOpen);
|
||||||
|
frame.setChannel(channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
arguments.resize(1);
|
||||||
|
arguments[0] = 0;
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::flow(bool active)
|
||||||
|
{
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream stream(&arguments, QIODevice::WriteOnly);
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortShortUint, (active ? 1 : 0));
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Channel, miFlow);
|
||||||
|
frame.setChannel(channelNumber);
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: not implemented until I can figure out a good way to force the server
|
||||||
|
// to pause the channel in a test. It seems like RabbitMQ just doesn't
|
||||||
|
// care about flow control, preferring rather to use basic.qos
|
||||||
|
void QAmqpChannelPrivate::flow(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_UNUSED(frame);
|
||||||
|
qAmqpDebug("-> channel#flow( channel=%d, name=%s )", channelNumber, qPrintable(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::flowOk()
|
||||||
|
{
|
||||||
|
qAmqpDebug("<- channel#flowOk( channel=%d, name=%s )", channelNumber, qPrintable(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::flowOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpChannel);
|
||||||
|
qAmqpDebug("-> channel#flowOk( channel=%d, name=%s )", channelNumber, qPrintable(name));
|
||||||
|
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
bool active = QAmqpFrame::readAmqpField(stream, QAmqpMetaType::Boolean).toBool();
|
||||||
|
if (active)
|
||||||
|
Q_EMIT q->resumed();
|
||||||
|
else
|
||||||
|
Q_EMIT q->paused();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::close(int code, const QString &text, int classId, int methodId)
|
||||||
|
{
|
||||||
|
qAmqpDebug("<- channel#close( channel=%d, name=%s, reply-code=%d, text=%s class-id=%d, method-id:%d, )",
|
||||||
|
channelNumber, qPrintable(name), code, qPrintable(text), classId, methodId);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream stream(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
if (!code) code = 200;
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortUint, code);
|
||||||
|
if (!text.isEmpty()) {
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortString, text);
|
||||||
|
} else {
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortString, QLatin1String("OK"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortUint, classId);
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortUint, methodId);
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Channel, miClose);
|
||||||
|
frame.setChannel(channelNumber);
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::close(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpChannel);
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
qint16 code = 0, classId, methodId;
|
||||||
|
stream >> code;
|
||||||
|
QString text =
|
||||||
|
QAmqpFrame::readAmqpField(stream, QAmqpMetaType::ShortString).toString();
|
||||||
|
|
||||||
|
stream >> classId;
|
||||||
|
stream >> methodId;
|
||||||
|
|
||||||
|
QAMQP::Error checkError = static_cast<QAMQP::Error>(code);
|
||||||
|
if (checkError != QAMQP::NoError) {
|
||||||
|
error = checkError;
|
||||||
|
errorString = qPrintable(text);
|
||||||
|
Q_EMIT q->error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
qAmqpDebug("-> channel#close( channel=%d, name=%s, reply-code=%d, reply-text=%s, class-id=%d, method-id=%d, )",
|
||||||
|
channelNumber, qPrintable(name), code, qPrintable(text), classId, methodId);
|
||||||
|
|
||||||
|
// complete handshake
|
||||||
|
QAmqpMethodFrame closeOkFrame(QAmqpFrame::Channel, miCloseOk);
|
||||||
|
closeOkFrame.setChannel(channelNumber);
|
||||||
|
sendFrame(closeOkFrame);
|
||||||
|
|
||||||
|
// notify everyone that the channel was closed on us.
|
||||||
|
notifyClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::closeOk(const QAmqpMethodFrame &)
|
||||||
|
{
|
||||||
|
qAmqpDebug("-> channel#closeOk( channel=%d, name=%s )", channelNumber, qPrintable(name));
|
||||||
|
notifyClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::notifyClosed()
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpChannel);
|
||||||
|
Q_EMIT q->closed();
|
||||||
|
q->channelClosed();
|
||||||
|
opened = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::openOk(const QAmqpMethodFrame &)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpChannel);
|
||||||
|
qAmqpDebug("-> channel#openOk( channel=%d, name=%s )", channelNumber, qPrintable(name));
|
||||||
|
opened = true;
|
||||||
|
Q_EMIT q->opened();
|
||||||
|
q->channelOpened();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::_q_disconnected()
|
||||||
|
{
|
||||||
|
nextChannelNumber = 0;
|
||||||
|
opened = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannelPrivate::qosOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpChannel);
|
||||||
|
Q_UNUSED(frame)
|
||||||
|
qAmqpDebug("-> basic#qosOk( channel=%d, name=%s )", channelNumber, qPrintable(name));
|
||||||
|
|
||||||
|
prefetchCount = requestedPrefetchCount;
|
||||||
|
prefetchSize = requestedPrefetchSize;
|
||||||
|
Q_EMIT q->qosDefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QAmqpChannel::QAmqpChannel(QAmqpChannelPrivate *dd, QAmqpClient *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
d_ptr(dd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpChannel::~QAmqpChannel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannel::close()
|
||||||
|
{
|
||||||
|
Q_D(QAmqpChannel);
|
||||||
|
d->needOpen = true;
|
||||||
|
if (d->opened)
|
||||||
|
d->close(0, QString(), 0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannel::reopen()
|
||||||
|
{
|
||||||
|
Q_D(QAmqpChannel);
|
||||||
|
if (d->opened)
|
||||||
|
close();
|
||||||
|
d->open();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpChannel::name() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpChannel);
|
||||||
|
return d->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QAmqpChannel::channelNumber() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpChannel);
|
||||||
|
return d->channelNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannel::setName(const QString &name)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpChannel);
|
||||||
|
d->name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpChannel::isOpen() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpChannel);
|
||||||
|
return d->opened;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannel::qos(qint16 prefetchCount, qint32 prefetchSize)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpChannel);
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Basic, QAmqpChannelPrivate::bmQos);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream stream(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
d->requestedPrefetchSize = prefetchSize;
|
||||||
|
d->requestedPrefetchCount = prefetchCount;
|
||||||
|
|
||||||
|
stream << qint32(prefetchSize);
|
||||||
|
stream << qint16(prefetchCount);
|
||||||
|
stream << qint8(0x0); // global
|
||||||
|
|
||||||
|
qAmqpDebug("<- basic#qos( channel=%d, name=%s, prefetch-size=%d, prefetch-count=%d, global=%d )",
|
||||||
|
d->channelNumber, qPrintable(d->name), prefetchSize, prefetchCount, 0);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 QAmqpChannel::prefetchSize() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpChannel);
|
||||||
|
return d->prefetchSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint16 QAmqpChannel::prefetchCount() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpChannel);
|
||||||
|
return d->prefetchCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannel::reset()
|
||||||
|
{
|
||||||
|
Q_D(QAmqpChannel);
|
||||||
|
d->resetInternalState();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAMQP::Error QAmqpChannel::error() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpChannel);
|
||||||
|
return d->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpChannel::errorString() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpChannel);
|
||||||
|
return d->errorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpChannel::resume()
|
||||||
|
{
|
||||||
|
Q_D(QAmqpChannel);
|
||||||
|
d->flow(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_qamqpchannel.cpp"
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Alexey Shcherbakov
|
||||||
|
* Copyright (C) 2014-2015 Matt Broadstone
|
||||||
|
* Contact: https://github.com/mbroadst/qamqp
|
||||||
|
*
|
||||||
|
* This file is part of the QAMQP Library.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
#ifndef QAMQPCHANNEL_H
|
||||||
|
#define QAMQPCHANNEL_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
|
||||||
|
class QAmqpClient;
|
||||||
|
class QAmqpChannelPrivate;
|
||||||
|
class QAMQP_EXPORT QAmqpChannel : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(int number READ channelNumber CONSTANT)
|
||||||
|
Q_PROPERTY(bool open READ isOpen CONSTANT)
|
||||||
|
Q_PROPERTY(QString name READ name WRITE setName)
|
||||||
|
public:
|
||||||
|
virtual ~QAmqpChannel();
|
||||||
|
|
||||||
|
int channelNumber() const;
|
||||||
|
bool isOpen() const;
|
||||||
|
|
||||||
|
QString name() const;
|
||||||
|
void setName(const QString &name);
|
||||||
|
|
||||||
|
QAMQP::Error error() const;
|
||||||
|
QString errorString() const;
|
||||||
|
|
||||||
|
qint32 prefetchSize() const;
|
||||||
|
qint16 prefetchCount() const;
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// AMQP Basic
|
||||||
|
void qos(qint16 prefetchCount, qint32 prefetchSize = 0);
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void close();
|
||||||
|
void reopen();
|
||||||
|
void resume();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void opened();
|
||||||
|
void closed();
|
||||||
|
void resumed();
|
||||||
|
void paused();
|
||||||
|
void error(QAMQP::Error error);
|
||||||
|
void qosDefined();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void channelOpened() = 0;
|
||||||
|
virtual void channelClosed() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit QAmqpChannel(QAmqpChannelPrivate *dd, QAmqpClient *client);
|
||||||
|
|
||||||
|
Q_DISABLE_COPY(QAmqpChannel)
|
||||||
|
Q_DECLARE_PRIVATE(QAmqpChannel)
|
||||||
|
QScopedPointer<QAmqpChannelPrivate> d_ptr;
|
||||||
|
|
||||||
|
Q_PRIVATE_SLOT(d_func(), void _q_open())
|
||||||
|
Q_PRIVATE_SLOT(d_func(), void _q_disconnected())
|
||||||
|
|
||||||
|
friend class QAmqpClientPrivate;
|
||||||
|
friend class QAmqpExchangePrivate;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPCHANNEL_H
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
#ifndef QAMQPCHANNEL_P_H
|
||||||
|
#define QAMQPCHANNEL_P_H
|
||||||
|
|
||||||
|
#include <QPointer>
|
||||||
|
#include "qamqpframe_p.h"
|
||||||
|
#include "qamqptable.h"
|
||||||
|
|
||||||
|
#define METHOD_ID_ENUM(name, id) name = id, name ## Ok
|
||||||
|
|
||||||
|
class QAmqpChannel;
|
||||||
|
class QAmqpClient;
|
||||||
|
class QAmqpClientPrivate;
|
||||||
|
class QAmqpChannelPrivate : public QAmqpMethodFrameHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum MethodId {
|
||||||
|
METHOD_ID_ENUM(miOpen, 10),
|
||||||
|
METHOD_ID_ENUM(miFlow, 20),
|
||||||
|
METHOD_ID_ENUM(miClose, 40)
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BasicMethod {
|
||||||
|
METHOD_ID_ENUM(bmQos, 10),
|
||||||
|
METHOD_ID_ENUM(bmConsume, 20),
|
||||||
|
METHOD_ID_ENUM(bmCancel, 30),
|
||||||
|
bmPublish = 40,
|
||||||
|
bmReturn = 50,
|
||||||
|
bmDeliver = 60,
|
||||||
|
METHOD_ID_ENUM(bmGet, 70),
|
||||||
|
bmGetEmpty = 72,
|
||||||
|
bmAck = 80,
|
||||||
|
bmReject = 90,
|
||||||
|
bmRecoverAsync = 100,
|
||||||
|
METHOD_ID_ENUM(bmRecover, 110),
|
||||||
|
bmNack = 120
|
||||||
|
};
|
||||||
|
|
||||||
|
QAmqpChannelPrivate(QAmqpChannel *q);
|
||||||
|
virtual ~QAmqpChannelPrivate();
|
||||||
|
|
||||||
|
void init(int channel, QAmqpClient *client);
|
||||||
|
void sendFrame(const QAmqpFrame &frame);
|
||||||
|
virtual void resetInternalState();
|
||||||
|
|
||||||
|
void open();
|
||||||
|
void flow(bool active);
|
||||||
|
void flowOk();
|
||||||
|
void close(int code, const QString &text, int classId, int methodId);
|
||||||
|
void notifyClosed();
|
||||||
|
|
||||||
|
// reimp MethodHandler
|
||||||
|
virtual bool _q_method(const QAmqpMethodFrame &frame);
|
||||||
|
void openOk(const QAmqpMethodFrame &frame);
|
||||||
|
void flow(const QAmqpMethodFrame &frame);
|
||||||
|
void flowOk(const QAmqpMethodFrame &frame);
|
||||||
|
void close(const QAmqpMethodFrame &frame);
|
||||||
|
void closeOk(const QAmqpMethodFrame &frame);
|
||||||
|
void qosOk(const QAmqpMethodFrame &frame);
|
||||||
|
|
||||||
|
// private slots
|
||||||
|
virtual void _q_disconnected();
|
||||||
|
void _q_open();
|
||||||
|
|
||||||
|
QPointer<QAmqpClient> client;
|
||||||
|
QString name;
|
||||||
|
quint16 channelNumber;
|
||||||
|
static quint16 nextChannelNumber;
|
||||||
|
bool opened;
|
||||||
|
bool needOpen;
|
||||||
|
|
||||||
|
qint32 prefetchSize;
|
||||||
|
qint32 requestedPrefetchSize;
|
||||||
|
qint16 prefetchCount;
|
||||||
|
qint16 requestedPrefetchCount;
|
||||||
|
|
||||||
|
QAMQP::Error error;
|
||||||
|
QString errorString;
|
||||||
|
|
||||||
|
Q_DECLARE_PUBLIC(QAmqpChannel)
|
||||||
|
QAmqpChannel * const q_ptr;
|
||||||
|
QAmqpTable arguments;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPCHANNEL_P_H
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Alexey Shcherbakov
|
||||||
|
* Copyright (C) 2014-2015 Matt Broadstone
|
||||||
|
* Contact: https://github.com/mbroadst/qamqp
|
||||||
|
*
|
||||||
|
* This file is part of the QAMQP Library.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpchannelhash_p.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Retrieve a pointer to the named channel.
|
||||||
|
*
|
||||||
|
* A NULL string is assumed to be equivalent to "" for the purpose
|
||||||
|
* of retrieving the nameless (default) exchange.
|
||||||
|
*
|
||||||
|
* \param[in] name The name of the channel to retrieve.
|
||||||
|
* \retval NULL Channel does not exist.
|
||||||
|
*/
|
||||||
|
QAmqpChannel* QAmqpChannelHash::get(const QString& name) const
|
||||||
|
{
|
||||||
|
if (name.isEmpty())
|
||||||
|
return m_channels.value(QString());
|
||||||
|
return m_channels.value(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList QAmqpChannelHash::channels() const
|
||||||
|
{
|
||||||
|
return m_channels.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Return true if the named channel exists.
|
||||||
|
*/
|
||||||
|
bool QAmqpChannelHash::contains(const QString& name) const
|
||||||
|
{
|
||||||
|
if (name.isEmpty())
|
||||||
|
return m_channels.contains(QString());
|
||||||
|
return m_channels.contains(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Store an exchange in the hash. The nameless exchange is stored under
|
||||||
|
* the name "".
|
||||||
|
*/
|
||||||
|
void QAmqpChannelHash::put(QAmqpExchange* exchange)
|
||||||
|
{
|
||||||
|
if (exchange->name().isEmpty())
|
||||||
|
put(QString(), exchange);
|
||||||
|
else
|
||||||
|
put(exchange->name(), exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Store a queue in the hash. If the queue is nameless, we hook its
|
||||||
|
* declared signal and store it when the queue receives a name from the
|
||||||
|
* broker, otherwise we store it under the name given.
|
||||||
|
*/
|
||||||
|
void QAmqpChannelHash::put(QAmqpQueue* queue)
|
||||||
|
{
|
||||||
|
if (queue->name().isEmpty())
|
||||||
|
connect(queue, SIGNAL(declared()), this, SLOT(queueDeclared()));
|
||||||
|
else
|
||||||
|
put(queue->name(), queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Handle destruction of a channel. Do a full garbage collection run.
|
||||||
|
*/
|
||||||
|
void QAmqpChannelHash::channelDestroyed(QObject* object)
|
||||||
|
{
|
||||||
|
QList<QString> names(m_channels.keys());
|
||||||
|
QList<QString>::iterator it;
|
||||||
|
for (it = names.begin(); it != names.end(); it++) {
|
||||||
|
if (m_channels.value(*it) == object)
|
||||||
|
m_channels.remove(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Handle a queue that has just been declared and given a new name. The
|
||||||
|
* caller is assumed to be a QAmqpQueue instance.
|
||||||
|
*/
|
||||||
|
void QAmqpChannelHash::queueDeclared()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (queue)
|
||||||
|
put(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Store a channel in the hash. The channel is assumed
|
||||||
|
* to be named at the time of storage. This hooks the 'destroyed' signal
|
||||||
|
* so the channel can be removed from our list.
|
||||||
|
*/
|
||||||
|
void QAmqpChannelHash::put(const QString& name, QAmqpChannel* channel)
|
||||||
|
{
|
||||||
|
connect(channel, SIGNAL(destroyed(QObject*)), this, SLOT(channelDestroyed(QObject*)));
|
||||||
|
m_channels[name] = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ts=4 sw=4 et */
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Alexey Shcherbakov
|
||||||
|
* Copyright (C) 2014-2015 Matt Broadstone
|
||||||
|
* Contact: https://github.com/mbroadst/qamqp
|
||||||
|
*
|
||||||
|
* This file is part of the QAMQP Library.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
#ifndef QAMQPCHANNELHASH_P_H
|
||||||
|
#define QAMQPCHANNELHASH_P_H
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
class QAmqpChannel;
|
||||||
|
class QAmqpQueue;
|
||||||
|
class QAmqpExchange;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* QAmqpChannelHash is a container for storing queues and exchanges for later
|
||||||
|
* retrieval. When the objects are destroyed, they are automatically removed
|
||||||
|
* from the container.
|
||||||
|
*/
|
||||||
|
class QAmqpChannelHash : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* Retrieve a pointer to the named channel.
|
||||||
|
*
|
||||||
|
* A NULL string is assumed to be equivalent to "" for the purpose
|
||||||
|
* of retrieving the nameless (default) exchange.
|
||||||
|
*
|
||||||
|
* \param[in] name The name of the channel to retrieve.
|
||||||
|
* \retval NULL Channel does not exist.
|
||||||
|
*/
|
||||||
|
QAmqpChannel* get(const QString& name) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Return true if the named channel exists.
|
||||||
|
*/
|
||||||
|
bool contains(const QString& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of channels tracked by this hash
|
||||||
|
*/
|
||||||
|
QStringList channels() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Store an exchange in the hash. The nameless exchange is stored under
|
||||||
|
* the name "".
|
||||||
|
*/
|
||||||
|
void put(QAmqpExchange* exchange);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Store a queue in the hash. If the queue is nameless, we hook its
|
||||||
|
* declared signal and store it when the queue receives a name from the
|
||||||
|
* broker, otherwise we store it under the name given.
|
||||||
|
*/
|
||||||
|
void put(QAmqpQueue* queue);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
/*!
|
||||||
|
* Handle destruction of a channel. Do a full garbage collection run.
|
||||||
|
*/
|
||||||
|
void channelDestroyed(QObject* object);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Handle a queue that has just been declared and given a new name. The
|
||||||
|
* caller is assumed to be a QAmqpQueue instance.
|
||||||
|
*/
|
||||||
|
void queueDeclared();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
* Store a channel in the hash. This hooks the 'destroyed' signal
|
||||||
|
* so the channel can be removed from our list.
|
||||||
|
*/
|
||||||
|
void put(const QString& name, QAmqpChannel* channel);
|
||||||
|
|
||||||
|
/*! A collection of channels. Key is the channel's "name". */
|
||||||
|
QHash<QString, QAmqpChannel*> m_channels;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* vim: set ts=4 sw=4 et */
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,959 @@
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QSslSocket>
|
||||||
|
#include <QtEndian>
|
||||||
|
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpexchange_p.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
#include "qamqpqueue_p.h"
|
||||||
|
#include "qamqpauthenticator.h"
|
||||||
|
#include "qamqptable.h"
|
||||||
|
#include "qamqpclient_p.h"
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
|
||||||
|
QAmqpClientPrivate::QAmqpClientPrivate(QAmqpClient *q)
|
||||||
|
: port(AMQP_PORT),
|
||||||
|
host(AMQP_HOST),
|
||||||
|
virtualHost(AMQP_VHOST),
|
||||||
|
autoReconnect(false),
|
||||||
|
reconnectFixedTimeout(false),
|
||||||
|
timeout(0),
|
||||||
|
connecting(false),
|
||||||
|
useSsl(false),
|
||||||
|
socket(0),
|
||||||
|
closed(false),
|
||||||
|
connected(false),
|
||||||
|
channelMax(0),
|
||||||
|
heartbeatDelay(0),
|
||||||
|
frameMax(AMQP_FRAME_MAX),
|
||||||
|
error(QAMQP::NoError),
|
||||||
|
q_ptr(q)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<QAmqpMessage::PropertyHash>();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpClientPrivate::~QAmqpClientPrivate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::init()
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpClient);
|
||||||
|
initSocket();
|
||||||
|
heartbeatTimer = new QTimer(q);
|
||||||
|
QObject::connect(heartbeatTimer, SIGNAL(timeout()), q, SLOT(_q_heartbeat()));
|
||||||
|
reconnectTimer = new QTimer(q);
|
||||||
|
reconnectTimer->setSingleShot(true);
|
||||||
|
QObject::connect(reconnectTimer, SIGNAL(timeout()), q, SLOT(_q_connect()));
|
||||||
|
|
||||||
|
authenticator = QSharedPointer<QAmqpAuthenticator>(
|
||||||
|
new QAmqpPlainAuthenticator(QString::fromLatin1(AMQP_LOGIN), QString::fromLatin1(AMQP_PSWD)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::initSocket()
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpClient);
|
||||||
|
socket = new QSslSocket(q);
|
||||||
|
socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
|
||||||
|
socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
|
||||||
|
QObject::connect(socket, SIGNAL(connected()), q, SLOT(_q_socketConnected()));
|
||||||
|
QObject::connect(socket, SIGNAL(disconnected()), q, SLOT(_q_socketDisconnected()));
|
||||||
|
QObject::connect(socket, SIGNAL(readyRead()), q, SLOT(_q_readyRead()));
|
||||||
|
#if QT_VERSION >= 0x060000
|
||||||
|
QObject::connect(socket,
|
||||||
|
SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
|
||||||
|
q,
|
||||||
|
SLOT(_q_socketError(QAbstractSocket::SocketError)));
|
||||||
|
QObject::connect(socket,
|
||||||
|
SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
|
||||||
|
q,
|
||||||
|
SIGNAL(socketErrorOccurred(QAbstractSocket::SocketError)));
|
||||||
|
#else
|
||||||
|
QObject::connect(socket,
|
||||||
|
SIGNAL(error(QAbstractSocket::SocketError)),
|
||||||
|
q,
|
||||||
|
SLOT(_q_socketError(QAbstractSocket::SocketError)));
|
||||||
|
QObject::connect(socket,
|
||||||
|
SIGNAL(error(QAbstractSocket::SocketError)),
|
||||||
|
q,
|
||||||
|
SIGNAL(socketErrorOccurred(QAbstractSocket::SocketError)));
|
||||||
|
#endif
|
||||||
|
QObject::connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
|
||||||
|
q, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)));
|
||||||
|
QObject::connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
|
||||||
|
q, SIGNAL(sslErrors(QList<QSslError>)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::resetChannelState()
|
||||||
|
{
|
||||||
|
foreach (QString exchangeName, exchanges.channels()) {
|
||||||
|
QAmqpExchange *exchange =
|
||||||
|
qobject_cast<QAmqpExchange*>(exchanges.get(exchangeName));
|
||||||
|
if (exchange) exchange->d_ptr->resetInternalState();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (QString queueName, queues.channels()) {
|
||||||
|
QAmqpQueue *queue =
|
||||||
|
qobject_cast<QAmqpQueue*>(queues.get(queueName));
|
||||||
|
if (queue) queue->d_ptr->resetInternalState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::setUsername(const QString &username)
|
||||||
|
{
|
||||||
|
QAmqpAuthenticator *auth = authenticator.data();
|
||||||
|
if (auth && auth->type() == QLatin1String("AMQPLAIN")) {
|
||||||
|
QAmqpPlainAuthenticator *a = static_cast<QAmqpPlainAuthenticator*>(auth);
|
||||||
|
a->setLogin(username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::setPassword(const QString &password)
|
||||||
|
{
|
||||||
|
QAmqpAuthenticator *auth = authenticator.data();
|
||||||
|
if (auth && auth->type() == QLatin1String("AMQPLAIN")) {
|
||||||
|
QAmqpPlainAuthenticator *a = static_cast<QAmqpPlainAuthenticator*>(auth);
|
||||||
|
a->setPassword(password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::parseConnectionString(const QString &uri)
|
||||||
|
{
|
||||||
|
QUrl connectionString = QUrl::fromUserInput(uri);
|
||||||
|
|
||||||
|
if (connectionString.scheme() != AMQP_SCHEME &&
|
||||||
|
connectionString.scheme() != AMQP_SSL_SCHEME) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "invalid scheme: " << connectionString.scheme();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
useSsl = (connectionString.scheme() == AMQP_SSL_SCHEME);
|
||||||
|
port = connectionString.port((useSsl ? AMQP_SSL_PORT : AMQP_PORT));
|
||||||
|
host = connectionString.host();
|
||||||
|
|
||||||
|
QString vhost = connectionString.path();
|
||||||
|
if (vhost.startsWith("/") && vhost.size() > 1)
|
||||||
|
vhost = vhost.mid(1);
|
||||||
|
virtualHost = vhost;
|
||||||
|
setPassword(connectionString.password());
|
||||||
|
setUsername(connectionString.userName());
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::_q_connect()
|
||||||
|
{
|
||||||
|
if (reconnectTimer)
|
||||||
|
reconnectTimer->stop();
|
||||||
|
if (socket->state() != QAbstractSocket::UnconnectedState) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "socket already connected, disconnecting..";
|
||||||
|
_q_disconnect();
|
||||||
|
// We need to explicitly close connection here because either way it will not be closed until we receive closeOk
|
||||||
|
closeConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
qAmqpDebug() << "connecting to host: " << host << ", port: " << port;
|
||||||
|
if (useSsl)
|
||||||
|
socket->connectToHostEncrypted(host, port);
|
||||||
|
else
|
||||||
|
socket->connectToHost(host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::_q_disconnect()
|
||||||
|
{
|
||||||
|
if (reconnectTimer)
|
||||||
|
reconnectTimer->stop();
|
||||||
|
if (socket->state() == QAbstractSocket::UnconnectedState) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "already disconnected";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.clear();
|
||||||
|
close(200, "client disconnect");
|
||||||
|
}
|
||||||
|
|
||||||
|
// private slots
|
||||||
|
void QAmqpClientPrivate::_q_socketConnected()
|
||||||
|
{
|
||||||
|
if (reconnectTimer)
|
||||||
|
reconnectTimer->stop();
|
||||||
|
if(reconnectFixedTimeout == false)
|
||||||
|
timeout = 0;
|
||||||
|
char header[8] = {'A', 'M', 'Q', 'P', 0, 0, 9, 1};
|
||||||
|
socket->write(header, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::_q_socketDisconnected()
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpClient);
|
||||||
|
buffer.clear();
|
||||||
|
resetChannelState();
|
||||||
|
if (connected)
|
||||||
|
connected = false;
|
||||||
|
Q_EMIT q->disconnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::_q_heartbeat()
|
||||||
|
{
|
||||||
|
QAmqpHeartbeatFrame frame;
|
||||||
|
sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::_q_socketError(QAbstractSocket::SocketError error)
|
||||||
|
{
|
||||||
|
if(reconnectFixedTimeout == false)
|
||||||
|
{
|
||||||
|
if (timeout <= 0) {
|
||||||
|
timeout = 1000;
|
||||||
|
} else {
|
||||||
|
if (timeout < 120000)
|
||||||
|
timeout *= 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (error) {
|
||||||
|
case QAbstractSocket::ConnectionRefusedError:
|
||||||
|
case QAbstractSocket::RemoteHostClosedError:
|
||||||
|
case QAbstractSocket::SocketTimeoutError:
|
||||||
|
case QAbstractSocket::NetworkError:
|
||||||
|
case QAbstractSocket::ProxyConnectionClosedError:
|
||||||
|
case QAbstractSocket::ProxyConnectionRefusedError:
|
||||||
|
case QAbstractSocket::ProxyConnectionTimeoutError:
|
||||||
|
|
||||||
|
default:
|
||||||
|
qAmqpDebug() << "socket error: " << socket->errorString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// per spec, on any error we need to close the socket immediately
|
||||||
|
// and send no more data. only try to send the close message if we
|
||||||
|
// are actively connected
|
||||||
|
if (socket->state() == QAbstractSocket::ConnectedState ||
|
||||||
|
socket->state() == QAbstractSocket::ConnectingState) {
|
||||||
|
socket->abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorString = socket->errorString();
|
||||||
|
|
||||||
|
if (autoReconnect && reconnectTimer) {
|
||||||
|
qAmqpDebug() << "trying to reconnect after: " << timeout << "ms";
|
||||||
|
reconnectTimer->start(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::_q_readyRead()
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpClient);
|
||||||
|
|
||||||
|
while (socket->bytesAvailable() >= QAmqpFrame::HEADER_SIZE) {
|
||||||
|
unsigned char headerData[QAmqpFrame::HEADER_SIZE];
|
||||||
|
socket->peek((char*)headerData, QAmqpFrame::HEADER_SIZE);
|
||||||
|
const quint32 payloadSize = qFromBigEndian<quint32>(headerData + 3);
|
||||||
|
const qint64 readSize = QAmqpFrame::HEADER_SIZE + payloadSize + QAmqpFrame::FRAME_END_SIZE;
|
||||||
|
|
||||||
|
if (socket->bytesAvailable() < readSize)
|
||||||
|
return;
|
||||||
|
|
||||||
|
buffer.resize(readSize);
|
||||||
|
socket->read(buffer.data(), readSize);
|
||||||
|
const char *bufferData = buffer.constData();
|
||||||
|
const quint8 type = *(quint8*)&bufferData[0];
|
||||||
|
const quint8 magic = *(quint8*)&bufferData[QAmqpFrame::HEADER_SIZE + payloadSize];
|
||||||
|
if (Q_UNLIKELY(magic != QAmqpFrame::FRAME_END)) {
|
||||||
|
close(QAMQP::UnexpectedFrameError, "wrong end of frame");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream streamB(&buffer, QIODevice::ReadOnly);
|
||||||
|
switch (static_cast<QAmqpFrame::FrameType>(type)) {
|
||||||
|
case QAmqpFrame::Method:
|
||||||
|
{
|
||||||
|
QAmqpMethodFrame frame;
|
||||||
|
streamB >> frame;
|
||||||
|
|
||||||
|
if (Q_UNLIKELY(frame.size() > frameMax)) {
|
||||||
|
close(QAMQP::FrameError, "frame size too large");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame.methodClass() == QAmqpFrame::Connection) {
|
||||||
|
_q_method(frame);
|
||||||
|
} else {
|
||||||
|
foreach (QAmqpMethodFrameHandler *methodHandler, methodHandlersByChannel[frame.channel()])
|
||||||
|
methodHandler->_q_method(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAmqpFrame::Header:
|
||||||
|
{
|
||||||
|
QAmqpContentFrame frame;
|
||||||
|
streamB >> frame;
|
||||||
|
|
||||||
|
if (Q_UNLIKELY(frame.size() > frameMax)) {
|
||||||
|
close(QAMQP::FrameError, "frame size too large");
|
||||||
|
return;
|
||||||
|
} else if (Q_UNLIKELY(frame.channel() <= 0)) {
|
||||||
|
close(QAMQP::ChannelError, "channel number must be greater than zero");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (QAmqpContentFrameHandler *methodHandler, contentHandlerByChannel[frame.channel()])
|
||||||
|
methodHandler->_q_content(frame);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAmqpFrame::Body:
|
||||||
|
{
|
||||||
|
QAmqpContentBodyFrame frame;
|
||||||
|
streamB >> frame;
|
||||||
|
|
||||||
|
if (Q_UNLIKELY(frame.size() > frameMax)) {
|
||||||
|
close(QAMQP::FrameError, "frame size too large");
|
||||||
|
return;
|
||||||
|
} else if (Q_UNLIKELY(frame.channel() <= 0)) {
|
||||||
|
close(QAMQP::ChannelError, "channel number must be greater than zero");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (QAmqpContentBodyFrameHandler *methodHandler, bodyHandlersByChannel[frame.channel()])
|
||||||
|
methodHandler->_q_body(frame);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAmqpFrame::Heartbeat:
|
||||||
|
{
|
||||||
|
QAmqpMethodFrame frame;
|
||||||
|
streamB >> frame;
|
||||||
|
|
||||||
|
if (Q_UNLIKELY(frame.channel() != 0)) {
|
||||||
|
close(QAMQP::FrameError, "heartbeat must have channel id zero");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qAmqpDebug("AMQP: Heartbeat");
|
||||||
|
Q_EMIT q->heartbeat();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qAmqpDebug() << "AMQP: Unknown frame type: " << type;
|
||||||
|
close(QAMQP::FrameError, "invalid frame type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::sendFrame(const QAmqpFrame &frame)
|
||||||
|
{
|
||||||
|
if (socket->state() != QAbstractSocket::ConnectedState) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "socket not connected: " << socket->state();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream stream(socket);
|
||||||
|
stream << frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::closeConnection()
|
||||||
|
{
|
||||||
|
qAmqpDebug("AMQP: closing connection");
|
||||||
|
|
||||||
|
connected = false;
|
||||||
|
if (reconnectTimer)
|
||||||
|
reconnectTimer->stop();
|
||||||
|
if (heartbeatTimer)
|
||||||
|
heartbeatTimer->stop();
|
||||||
|
socket->disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpClientPrivate::_q_method(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_ASSERT(frame.methodClass() == QAmqpFrame::Connection);
|
||||||
|
if (frame.methodClass() != QAmqpFrame::Connection)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (closed) {
|
||||||
|
if (frame.id() == QAmqpClientPrivate::miCloseOk)
|
||||||
|
closeOk(frame);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (QAmqpClientPrivate::MethodId(frame.id())) {
|
||||||
|
case QAmqpClientPrivate::miStart:
|
||||||
|
start(frame);
|
||||||
|
break;
|
||||||
|
case QAmqpClientPrivate::miSecure:
|
||||||
|
secure(frame);
|
||||||
|
break;
|
||||||
|
case QAmqpClientPrivate::miTune:
|
||||||
|
tune(frame);
|
||||||
|
break;
|
||||||
|
case QAmqpClientPrivate::miOpenOk:
|
||||||
|
openOk(frame);
|
||||||
|
break;
|
||||||
|
case QAmqpClientPrivate::miClose:
|
||||||
|
close(frame);
|
||||||
|
break;
|
||||||
|
case QAmqpClientPrivate::miCloseOk:
|
||||||
|
closeOk(frame);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qAmqpDebug("Unknown method-id %d", frame.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::start(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
quint8 version_major = 0;
|
||||||
|
quint8 version_minor = 0;
|
||||||
|
stream >> version_major >> version_minor;
|
||||||
|
|
||||||
|
QAmqpTable table;
|
||||||
|
stream >> table;
|
||||||
|
|
||||||
|
QStringList mechanisms =
|
||||||
|
QAmqpFrame::readAmqpField(stream, QAmqpMetaType::LongString).toString().split(' ');
|
||||||
|
QString locales = QAmqpFrame::readAmqpField(stream, QAmqpMetaType::LongString).toString();
|
||||||
|
|
||||||
|
qAmqpDebug("-> connection#start( version_major=%d, version_minor=%d, mechanisms=(%s), locales=%s )",
|
||||||
|
version_major, version_minor, qPrintable(mechanisms.join(",")), qPrintable(locales));
|
||||||
|
|
||||||
|
if (!mechanisms.contains(authenticator->type())) {
|
||||||
|
socket->disconnectFromHost();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::secure(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_UNUSED(frame)
|
||||||
|
qAmqpDebug("-> connection#secure()");
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::tune(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
qint16 channel_max = 0,
|
||||||
|
heartbeat_delay = 0;
|
||||||
|
qint32 frame_max = 0;
|
||||||
|
|
||||||
|
stream >> channel_max;
|
||||||
|
stream >> frame_max;
|
||||||
|
stream >> heartbeat_delay;
|
||||||
|
|
||||||
|
if (!frameMax)
|
||||||
|
frameMax = frame_max;
|
||||||
|
channelMax = !channelMax ? channel_max : qMax(channel_max, channelMax);
|
||||||
|
heartbeatDelay = !heartbeatDelay ? heartbeat_delay: heartbeatDelay;
|
||||||
|
|
||||||
|
qAmqpDebug("-> connection#tune( channel_max=%d, frame_max=%d, heartbeat=%d )",
|
||||||
|
channelMax, frameMax, heartbeatDelay);
|
||||||
|
|
||||||
|
if (heartbeatTimer) {
|
||||||
|
heartbeatTimer->setInterval(heartbeatDelay * 1000);
|
||||||
|
if (heartbeatTimer->interval())
|
||||||
|
heartbeatTimer->start();
|
||||||
|
else
|
||||||
|
heartbeatTimer->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
tuneOk();
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::openOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpClient);
|
||||||
|
Q_UNUSED(frame)
|
||||||
|
qAmqpDebug("-> connection#openOk()");
|
||||||
|
connected = true;
|
||||||
|
Q_EMIT q->connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::closeOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_UNUSED(frame)
|
||||||
|
qAmqpDebug("-> connection#closeOk()");
|
||||||
|
closeConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::close(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpClient);
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
qint16 code = 0, classId, methodId;
|
||||||
|
stream >> code;
|
||||||
|
QString text = QAmqpFrame::readAmqpField(stream, QAmqpMetaType::ShortString).toString();
|
||||||
|
stream >> classId;
|
||||||
|
stream >> methodId;
|
||||||
|
|
||||||
|
qAmqpDebug("-> connection#close( reply-code=%d, reply-text=%s, class-id=%d, method-id:%d )",
|
||||||
|
code, qPrintable(text), classId, methodId);
|
||||||
|
|
||||||
|
QAMQP::Error checkError = static_cast<QAMQP::Error>(code);
|
||||||
|
if (checkError != QAMQP::NoError) {
|
||||||
|
error = checkError;
|
||||||
|
errorString = qPrintable(text);
|
||||||
|
Q_EMIT q->error(error);
|
||||||
|
|
||||||
|
// if it was a force disconnect, simulate receiving a closeOk
|
||||||
|
if (checkError == QAMQP::ConnectionForcedError) {
|
||||||
|
closeConnection();
|
||||||
|
if (autoReconnect) {
|
||||||
|
qAmqpDebug() << "trying to reconnect after: " << timeout << "ms";
|
||||||
|
QTimer::singleShot(timeout, q, SLOT(_q_connect()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connected = false;
|
||||||
|
Q_EMIT q->disconnected();
|
||||||
|
|
||||||
|
// complete handshake
|
||||||
|
QAmqpMethodFrame closeOkFrame(QAmqpFrame::Connection, QAmqpClientPrivate::miCloseOk);
|
||||||
|
qAmqpDebug("<- connection#closeOk()");
|
||||||
|
sendFrame(closeOkFrame);
|
||||||
|
closeConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::startOk()
|
||||||
|
{
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Connection, QAmqpClientPrivate::miStartOk);
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream stream(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
QAmqpTable clientProperties;
|
||||||
|
clientProperties["version"] = QString(QAMQP_VERSION);
|
||||||
|
clientProperties["platform"] = QString("Qt %1").arg(qVersion());
|
||||||
|
clientProperties["product"] = QString("QAMQP");
|
||||||
|
#if QT_VERSION >= 0x060000
|
||||||
|
clientProperties.insert(customProperties);
|
||||||
|
#else
|
||||||
|
clientProperties.unite(customProperties);
|
||||||
|
#endif
|
||||||
|
stream << clientProperties;
|
||||||
|
|
||||||
|
authenticator->write(stream);
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortString, QLatin1String("en_US"));
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
|
||||||
|
qAmqpDebug("<- connection#startOk()"); // @todo: fill this out
|
||||||
|
sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::secureOk()
|
||||||
|
{
|
||||||
|
qAmqpDebug("-> connection#secureOk()");
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::tuneOk()
|
||||||
|
{
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Connection, QAmqpClientPrivate::miTuneOk);
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream stream(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
stream << qint16(channelMax);
|
||||||
|
stream << qint32(frameMax);
|
||||||
|
stream << qint16(heartbeatDelay);
|
||||||
|
|
||||||
|
qAmqpDebug("<- connection#tuneOk( channelMax=%d, frameMax=%d, heartbeatDelay=%d )",
|
||||||
|
channelMax, frameMax, heartbeatDelay);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::open()
|
||||||
|
{
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Connection, QAmqpClientPrivate::miOpen);
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream stream(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortString, virtualHost);
|
||||||
|
|
||||||
|
stream << qint8(0);
|
||||||
|
stream << qint8(0);
|
||||||
|
|
||||||
|
qAmqpDebug("<- connection#open( virtualHost=%s, reserved-1=%d, reserved-2=%d )",
|
||||||
|
qPrintable(virtualHost), 0, 0);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClientPrivate::close(int code, const QString &text, int classId, int methodId)
|
||||||
|
{
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream stream(&arguments, QIODevice::WriteOnly);
|
||||||
|
stream << qint16(code);
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortString, text);
|
||||||
|
stream << qint16(classId);
|
||||||
|
stream << qint16(methodId);
|
||||||
|
|
||||||
|
qAmqpDebug("<- connection#close( reply-code=%d, reply-text=%s, class-id=%d, method-id:%d )",
|
||||||
|
code, qPrintable(text), classId, methodId);
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Connection, QAmqpClientPrivate::miClose);
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QAmqpClient::QAmqpClient(QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
d_ptr(new QAmqpClientPrivate(this))
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpClient::QAmqpClient(QAmqpClientPrivate *dd, QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
d_ptr(dd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpClient::~QAmqpClient()
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
if (d->connected)
|
||||||
|
d->_q_disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpClient::isConnected() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 QAmqpClient::port() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setPort(quint16 port)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpClient::host() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->host;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setHost(const QString &host)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpClient::virtualHost() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->virtualHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setVirtualHost(const QString &virtualHost)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->virtualHost = virtualHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpClient::username() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
const QAmqpAuthenticator *auth = d->authenticator.data();
|
||||||
|
if (auth && auth->type() == QLatin1String("AMQPLAIN")) {
|
||||||
|
const QAmqpPlainAuthenticator *a = static_cast<const QAmqpPlainAuthenticator*>(auth);
|
||||||
|
return a->login();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setUsername(const QString &username)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->setUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpClient::password() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
const QAmqpAuthenticator *auth = d->authenticator.data();
|
||||||
|
if (auth && auth->type() == QLatin1String("AMQPLAIN")) {
|
||||||
|
const QAmqpPlainAuthenticator *a = static_cast<const QAmqpPlainAuthenticator*>(auth);
|
||||||
|
return a->password();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setPassword(const QString &password)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->setPassword(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpExchange *QAmqpClient::createExchange(int channelNumber)
|
||||||
|
{
|
||||||
|
return createExchange(QString(), channelNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpExchange *QAmqpClient::createExchange(const QString &name, int channelNumber)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
QAmqpExchange *exchange;
|
||||||
|
if (d->exchanges.contains(name)) {
|
||||||
|
exchange = qobject_cast<QAmqpExchange*>(d->exchanges.get(name));
|
||||||
|
if (exchange)
|
||||||
|
return exchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
exchange = new QAmqpExchange(channelNumber, this);
|
||||||
|
d->methodHandlersByChannel[exchange->channelNumber()].append(exchange->d_func());
|
||||||
|
connect(this, SIGNAL(connected()), exchange, SLOT(_q_open()));
|
||||||
|
connect(this, SIGNAL(disconnected()), exchange, SLOT(_q_disconnected()));
|
||||||
|
exchange->d_func()->open();
|
||||||
|
|
||||||
|
if (!name.isEmpty())
|
||||||
|
exchange->setName(name);
|
||||||
|
d->exchanges.put(exchange);
|
||||||
|
return exchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpQueue *QAmqpClient::createQueue(int channelNumber)
|
||||||
|
{
|
||||||
|
return createQueue(QString(), channelNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpQueue *QAmqpClient::createQueue(const QString &name, int channelNumber)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
QAmqpQueue *queue;
|
||||||
|
if (d->queues.contains(name)) {
|
||||||
|
queue = qobject_cast<QAmqpQueue*>(d->queues.get(name));
|
||||||
|
if (queue)
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue = new QAmqpQueue(channelNumber, this);
|
||||||
|
d->methodHandlersByChannel[queue->channelNumber()].append(queue->d_func());
|
||||||
|
d->contentHandlerByChannel[queue->channelNumber()].append(queue->d_func());
|
||||||
|
d->bodyHandlersByChannel[queue->channelNumber()].append(queue->d_func());
|
||||||
|
connect(this, SIGNAL(connected()), queue, SLOT(_q_open()));
|
||||||
|
connect(this, SIGNAL(disconnected()), queue, SLOT(_q_disconnected()));
|
||||||
|
queue->d_func()->open();
|
||||||
|
|
||||||
|
if (!name.isEmpty())
|
||||||
|
queue->setName(name);
|
||||||
|
d->queues.put(queue);
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setAuth(QAmqpAuthenticator *authenticator)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->authenticator = QSharedPointer<QAmqpAuthenticator>(authenticator);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpAuthenticator *QAmqpClient::auth() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->authenticator.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpClient::autoReconnect() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->autoReconnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setAutoReconnect(bool value, int timeout)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->autoReconnect = value;
|
||||||
|
|
||||||
|
if((value == true) && (timeout > 0))
|
||||||
|
{
|
||||||
|
d->timeout = timeout;
|
||||||
|
d->reconnectFixedTimeout = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d->timeout = 0;
|
||||||
|
d->reconnectFixedTimeout = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qint16 QAmqpClient::channelMax() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->channelMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setChannelMax(qint16 channelMax)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
if (d->connected) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "can't modify value while connected";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->channelMax = channelMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 QAmqpClient::frameMax() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->frameMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setFrameMax(qint32 frameMax)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
if (d->connected) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "can't modify value while connected";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->frameMax = qMax(frameMax, AMQP_FRAME_MIN_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint16 QAmqpClient::heartbeatDelay() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->heartbeatDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setHeartbeatDelay(qint16 delay)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
if (d->connected) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "can't modify value while connected";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->heartbeatDelay = delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QAmqpClient::writeTimeout() const
|
||||||
|
{
|
||||||
|
return QAmqpFrame::writeTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setWriteTimeout(int msecs)
|
||||||
|
{
|
||||||
|
QAmqpFrame::setWriteTimeout(msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::addCustomProperty(const QString &name, const QString &value)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->customProperties.insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpClient::customProperty(const QString &name) const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->customProperties.value(name).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAbstractSocket::SocketError QAmqpClient::socketError() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->socket->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAbstractSocket::SocketState QAmqpClient::socketState() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->socket->state();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAMQP::Error QAmqpClient::error() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpClient::errorString() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->errorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSslConfiguration QAmqpClient::sslConfiguration() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpClient);
|
||||||
|
return d->socket->sslConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::setSslConfiguration(const QSslConfiguration &config)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
if (!config.isNull()) {
|
||||||
|
d->useSsl = true;
|
||||||
|
d->port = AMQP_SSL_PORT;
|
||||||
|
d->socket->setSslConfiguration(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpClient::gitVersion()
|
||||||
|
{
|
||||||
|
return QString(GIT_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::ignoreSslErrors(const QList<QSslError> &errors)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->socket->ignoreSslErrors(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::connectToHost(const QString &uri)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
if (uri.isEmpty()) {
|
||||||
|
d->_q_connect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->parseConnectionString(uri);
|
||||||
|
d->_q_connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::connectToHost(const QHostAddress &address, quint16 port)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->host = address.toString();
|
||||||
|
d->port = port;
|
||||||
|
d->_q_connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::disconnectFromHost()
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->_q_disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpClient::abort()
|
||||||
|
{
|
||||||
|
Q_D(QAmqpClient);
|
||||||
|
d->closeConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_qamqpclient.cpp"
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Alexey Shcherbakov
|
||||||
|
* Copyright (C) 2014-2015 Matt Broadstone
|
||||||
|
* Contact: https://github.com/mbroadst/qamqp
|
||||||
|
*
|
||||||
|
* This file is part of the QAMQP Library.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
#ifndef QAMQPCLIENT_H
|
||||||
|
#define QAMQPCLIENT_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QSslConfiguration>
|
||||||
|
#include <QSslError>
|
||||||
|
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
|
||||||
|
class QAmqpExchange;
|
||||||
|
class QAmqpQueue;
|
||||||
|
class QAmqpAuthenticator;
|
||||||
|
class QAmqpClientPrivate;
|
||||||
|
class QAMQP_EXPORT QAmqpClient : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(quint32 port READ port WRITE setPort)
|
||||||
|
Q_PROPERTY(QString host READ host WRITE setHost)
|
||||||
|
Q_PROPERTY(QString virtualHost READ virtualHost WRITE setVirtualHost)
|
||||||
|
Q_PROPERTY(QString user READ username WRITE setUsername)
|
||||||
|
Q_PROPERTY(QString password READ password WRITE setPassword)
|
||||||
|
Q_PROPERTY(bool autoReconnect READ autoReconnect WRITE setAutoReconnect)
|
||||||
|
Q_PROPERTY(qint16 channelMax READ channelMax WRITE setChannelMax)
|
||||||
|
Q_PROPERTY(qint32 frameMax READ frameMax WRITE setFrameMax)
|
||||||
|
Q_PROPERTY(qint16 heartbeatDelay READ heartbeatDelay() WRITE setHeartbeatDelay)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit QAmqpClient(QObject *parent = 0);
|
||||||
|
~QAmqpClient();
|
||||||
|
|
||||||
|
// properties
|
||||||
|
quint16 port() const;
|
||||||
|
void setPort(quint16 port);
|
||||||
|
|
||||||
|
QString host() const;
|
||||||
|
void setHost(const QString &host);
|
||||||
|
|
||||||
|
QString virtualHost() const;
|
||||||
|
void setVirtualHost(const QString &virtualHost);
|
||||||
|
|
||||||
|
QString username() const;
|
||||||
|
void setUsername(const QString &username);
|
||||||
|
|
||||||
|
QString password() const;
|
||||||
|
void setPassword(const QString &password);
|
||||||
|
|
||||||
|
void setAuth(QAmqpAuthenticator *auth);
|
||||||
|
QAmqpAuthenticator *auth() const;
|
||||||
|
|
||||||
|
bool autoReconnect() const;
|
||||||
|
void setAutoReconnect(bool value, int timeout = 0);
|
||||||
|
|
||||||
|
bool isConnected() const;
|
||||||
|
|
||||||
|
qint16 channelMax() const;
|
||||||
|
void setChannelMax(qint16 channelMax);
|
||||||
|
|
||||||
|
qint32 frameMax() const;
|
||||||
|
void setFrameMax(qint32 frameMax);
|
||||||
|
|
||||||
|
qint16 heartbeatDelay() const;
|
||||||
|
void setHeartbeatDelay(qint16 delay);
|
||||||
|
|
||||||
|
int writeTimeout() const;
|
||||||
|
void setWriteTimeout(int msecs);
|
||||||
|
|
||||||
|
void addCustomProperty(const QString &name, const QString &value);
|
||||||
|
QString customProperty(const QString &name) const;
|
||||||
|
|
||||||
|
QAbstractSocket::SocketError socketError() const;
|
||||||
|
QAbstractSocket::SocketState socketState() const;
|
||||||
|
|
||||||
|
QAMQP::Error error() const;
|
||||||
|
QString errorString() const;
|
||||||
|
|
||||||
|
QSslConfiguration sslConfiguration() const;
|
||||||
|
void setSslConfiguration(const QSslConfiguration &config);
|
||||||
|
|
||||||
|
static QString gitVersion();
|
||||||
|
|
||||||
|
// channels
|
||||||
|
QAmqpExchange *createExchange(int channelNumber = -1);
|
||||||
|
QAmqpExchange *createExchange(const QString &name, int channelNumber = -1);
|
||||||
|
|
||||||
|
QAmqpQueue *createQueue(int channelNumber = -1);
|
||||||
|
QAmqpQueue *createQueue(const QString &name, int channelNumber = -1);
|
||||||
|
|
||||||
|
// methods
|
||||||
|
void connectToHost(const QString &uri = QString());
|
||||||
|
void connectToHost(const QHostAddress &address, quint16 port = AMQP_PORT);
|
||||||
|
void disconnectFromHost();
|
||||||
|
void abort();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void connected();
|
||||||
|
void disconnected();
|
||||||
|
void heartbeat();
|
||||||
|
void error(QAMQP::Error error);
|
||||||
|
void socketErrorOccurred(QAbstractSocket::SocketError error);
|
||||||
|
void socketStateChanged(QAbstractSocket::SocketState state);
|
||||||
|
void sslErrors(const QList<QSslError> &errors);
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void ignoreSslErrors(const QList<QSslError> &errors);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QAmqpClient(QAmqpClientPrivate *dd, QObject *parent = 0);
|
||||||
|
|
||||||
|
Q_DISABLE_COPY(QAmqpClient)
|
||||||
|
Q_DECLARE_PRIVATE(QAmqpClient)
|
||||||
|
QScopedPointer<QAmqpClientPrivate> d_ptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_PRIVATE_SLOT(d_func(), void _q_socketConnected())
|
||||||
|
Q_PRIVATE_SLOT(d_func(), void _q_socketDisconnected())
|
||||||
|
Q_PRIVATE_SLOT(d_func(), void _q_readyRead())
|
||||||
|
Q_PRIVATE_SLOT(d_func(), void _q_socketError(QAbstractSocket::SocketError error))
|
||||||
|
Q_PRIVATE_SLOT(d_func(), void _q_heartbeat())
|
||||||
|
Q_PRIVATE_SLOT(d_func(), void _q_connect())
|
||||||
|
Q_PRIVATE_SLOT(d_func(), void _q_disconnect())
|
||||||
|
|
||||||
|
friend class QAmqpChannelPrivate;
|
||||||
|
friend class QAmqpQueuePrivate;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPCLIENT_H
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
#ifndef QAMQPCLIENT_P_H
|
||||||
|
#define QAMQPCLIENT_P_H
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QAbstractSocket>
|
||||||
|
#include <QSslError>
|
||||||
|
|
||||||
|
#include "qamqpchannelhash_p.h"
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
#include "qamqpauthenticator.h"
|
||||||
|
#include "qamqptable.h"
|
||||||
|
#include "qamqpframe_p.h"
|
||||||
|
|
||||||
|
#define METHOD_ID_ENUM(name, id) name = id, name ## Ok
|
||||||
|
|
||||||
|
class QTimer;
|
||||||
|
class QSslSocket;
|
||||||
|
class QAmqpClient;
|
||||||
|
class QAmqpQueue;
|
||||||
|
class QAmqpExchange;
|
||||||
|
class QAmqpConnection;
|
||||||
|
class QAmqpAuthenticator;
|
||||||
|
class QAMQP_EXPORT QAmqpClientPrivate : public QAmqpMethodFrameHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum MethodId {
|
||||||
|
METHOD_ID_ENUM(miStart, 10),
|
||||||
|
METHOD_ID_ENUM(miSecure, 20),
|
||||||
|
METHOD_ID_ENUM(miTune, 30),
|
||||||
|
METHOD_ID_ENUM(miOpen, 40),
|
||||||
|
METHOD_ID_ENUM(miClose, 50)
|
||||||
|
};
|
||||||
|
|
||||||
|
QAmqpClientPrivate(QAmqpClient *q);
|
||||||
|
virtual ~QAmqpClientPrivate();
|
||||||
|
|
||||||
|
virtual void init();
|
||||||
|
virtual void initSocket();
|
||||||
|
void resetChannelState();
|
||||||
|
void setUsername(const QString &username);
|
||||||
|
void setPassword(const QString &password);
|
||||||
|
void parseConnectionString(const QString &uri);
|
||||||
|
void sendFrame(const QAmqpFrame &frame);
|
||||||
|
|
||||||
|
void closeConnection();
|
||||||
|
|
||||||
|
// private slots
|
||||||
|
void _q_socketConnected();
|
||||||
|
void _q_socketDisconnected();
|
||||||
|
void _q_readyRead();
|
||||||
|
void _q_socketError(QAbstractSocket::SocketError error);
|
||||||
|
void _q_heartbeat();
|
||||||
|
virtual void _q_connect();
|
||||||
|
void _q_disconnect();
|
||||||
|
|
||||||
|
virtual bool _q_method(const QAmqpMethodFrame &frame);
|
||||||
|
|
||||||
|
// method handlers, FROM server
|
||||||
|
void start(const QAmqpMethodFrame &frame);
|
||||||
|
void secure(const QAmqpMethodFrame &frame);
|
||||||
|
void tune(const QAmqpMethodFrame &frame);
|
||||||
|
void openOk(const QAmqpMethodFrame &frame);
|
||||||
|
void closeOk(const QAmqpMethodFrame &frame);
|
||||||
|
|
||||||
|
// method handlers, TO server
|
||||||
|
void startOk();
|
||||||
|
void secureOk();
|
||||||
|
void tuneOk();
|
||||||
|
void open();
|
||||||
|
|
||||||
|
// method handlers, BOTH ways
|
||||||
|
void close(int code, const QString &text, int classId = 0, int methodId = 0);
|
||||||
|
void close(const QAmqpMethodFrame &frame);
|
||||||
|
|
||||||
|
quint16 port;
|
||||||
|
QString host;
|
||||||
|
QString virtualHost;
|
||||||
|
|
||||||
|
QSharedPointer<QAmqpAuthenticator> authenticator;
|
||||||
|
|
||||||
|
// Network
|
||||||
|
QByteArray buffer;
|
||||||
|
bool autoReconnect;
|
||||||
|
bool reconnectFixedTimeout;
|
||||||
|
int timeout;
|
||||||
|
bool connecting;
|
||||||
|
bool useSsl;
|
||||||
|
|
||||||
|
QSslSocket *socket;
|
||||||
|
QHash<quint16, QList<QAmqpMethodFrameHandler*> > methodHandlersByChannel;
|
||||||
|
QHash<quint16, QList<QAmqpContentFrameHandler*> > contentHandlerByChannel;
|
||||||
|
QHash<quint16, QList<QAmqpContentBodyFrameHandler*> > bodyHandlersByChannel;
|
||||||
|
|
||||||
|
// Connection
|
||||||
|
bool closed;
|
||||||
|
bool connected;
|
||||||
|
QPointer<QTimer> heartbeatTimer;
|
||||||
|
QPointer<QTimer> reconnectTimer;
|
||||||
|
QAmqpTable customProperties;
|
||||||
|
qint16 channelMax;
|
||||||
|
qint16 heartbeatDelay;
|
||||||
|
qint32 frameMax;
|
||||||
|
|
||||||
|
QAMQP::Error error;
|
||||||
|
QString errorString;
|
||||||
|
|
||||||
|
/*! Exchange objects */
|
||||||
|
QAmqpChannelHash exchanges;
|
||||||
|
|
||||||
|
/*! Named queue objects */
|
||||||
|
QAmqpChannelHash queues;
|
||||||
|
|
||||||
|
QAmqpClient * const q_ptr;
|
||||||
|
Q_DECLARE_PUBLIC(QAmqpClient)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPCLIENT_P_H
|
||||||
|
|
@ -0,0 +1,369 @@
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpexchange_p.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
|
||||||
|
QString QAmqpExchangePrivate::typeToString(QAmqpExchange::ExchangeType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QAmqpExchange::Direct: return QLatin1String("direct");
|
||||||
|
case QAmqpExchange::FanOut: return QLatin1String("fanout");
|
||||||
|
case QAmqpExchange::Topic: return QLatin1String("topic");
|
||||||
|
case QAmqpExchange::Headers: return QLatin1String("headers");
|
||||||
|
}
|
||||||
|
|
||||||
|
return QLatin1String("direct");
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpExchangePrivate::QAmqpExchangePrivate(QAmqpExchange *q)
|
||||||
|
: QAmqpChannelPrivate(q),
|
||||||
|
delayedDeclare(false),
|
||||||
|
declared(false),
|
||||||
|
nextDeliveryTag(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchangePrivate::resetInternalState()
|
||||||
|
{
|
||||||
|
QAmqpChannelPrivate::resetInternalState();
|
||||||
|
delayedDeclare = false;
|
||||||
|
declared = false;
|
||||||
|
nextDeliveryTag = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchangePrivate::declare()
|
||||||
|
{
|
||||||
|
if (!opened) {
|
||||||
|
delayedDeclare = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "attempting to declare an unnamed exchange, aborting...";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Exchange, QAmqpExchangePrivate::miDeclare);
|
||||||
|
frame.setChannel(channelNumber);
|
||||||
|
|
||||||
|
QByteArray args;
|
||||||
|
QDataStream stream(&args, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
stream << qint16(0); //reserved 1
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortString, name);
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortString, type);
|
||||||
|
|
||||||
|
stream << qint8(options);
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::Hash, arguments);
|
||||||
|
|
||||||
|
qAmqpDebug("<- exchange#declare( name=%s, type=%s, passive=%d, durable=%d, no-wait=%d )",
|
||||||
|
qPrintable(name), qPrintable(type),
|
||||||
|
options.testFlag(QAmqpExchange::Passive), options.testFlag(QAmqpExchange::Durable),
|
||||||
|
options.testFlag(QAmqpExchange::NoWait));
|
||||||
|
|
||||||
|
frame.setArguments(args);
|
||||||
|
sendFrame(frame);
|
||||||
|
delayedDeclare = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpExchangePrivate::_q_method(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpExchange);
|
||||||
|
if (QAmqpChannelPrivate::_q_method(frame))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (frame.methodClass() == QAmqpFrame::Basic) {
|
||||||
|
switch (frame.id()) {
|
||||||
|
case bmAck:
|
||||||
|
case bmNack:
|
||||||
|
handleAckOrNack(frame);
|
||||||
|
break;
|
||||||
|
case bmReturn: basicReturn(frame); break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame.methodClass() == QAmqpFrame::Confirm) {
|
||||||
|
if (frame.id() == cmConfirmOk) {
|
||||||
|
Q_EMIT q->confirmsEnabled();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame.methodClass() == QAmqpFrame::Exchange) {
|
||||||
|
switch (frame.id()) {
|
||||||
|
case miDeclareOk: declareOk(frame); break;
|
||||||
|
case miDeleteOk: deleteOk(frame); break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchangePrivate::declareOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_UNUSED(frame)
|
||||||
|
Q_Q(QAmqpExchange);
|
||||||
|
qAmqpDebug("-> exchange[ %s ]#declareOk()", qPrintable(name));
|
||||||
|
declared = true;
|
||||||
|
Q_EMIT q->declared();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchangePrivate::deleteOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_UNUSED(frame)
|
||||||
|
Q_Q(QAmqpExchange);
|
||||||
|
qAmqpDebug("-> exchange#deleteOk[ %s ]()", qPrintable(name));
|
||||||
|
declared = false;
|
||||||
|
Q_EMIT q->removed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchangePrivate::_q_disconnected()
|
||||||
|
{
|
||||||
|
QAmqpChannelPrivate::_q_disconnected();
|
||||||
|
qAmqpDebug() << "exchange disconnected: " << name;
|
||||||
|
delayedDeclare = false;
|
||||||
|
declared = false;
|
||||||
|
unconfirmedDeliveryTags.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchangePrivate::basicReturn(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpExchange);
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
quint16 replyCode;
|
||||||
|
stream >> replyCode;
|
||||||
|
QString replyText = QAmqpFrame::readAmqpField(stream, QAmqpMetaType::ShortString).toString();
|
||||||
|
QString exchangeName = QAmqpFrame::readAmqpField(stream, QAmqpMetaType::ShortString).toString();
|
||||||
|
QString routingKey = QAmqpFrame::readAmqpField(stream, QAmqpMetaType::ShortString).toString();
|
||||||
|
|
||||||
|
QAMQP::Error checkError = static_cast<QAMQP::Error>(replyCode);
|
||||||
|
if (checkError != QAMQP::NoError) {
|
||||||
|
error = checkError;
|
||||||
|
errorString = qPrintable(replyText);
|
||||||
|
Q_EMIT q->error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
qAmqpDebug("-> basic#return( reply-code=%d, reply-text=%s, exchange=%s, routing-key=%s )",
|
||||||
|
replyCode, qPrintable(replyText), qPrintable(exchangeName), qPrintable(routingKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchangePrivate::handleAckOrNack(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpExchange);
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
qlonglong deliveryTag =
|
||||||
|
QAmqpFrame::readAmqpField(stream, QAmqpMetaType::LongLongUint).toLongLong();
|
||||||
|
bool multiple = QAmqpFrame::readAmqpField(stream, QAmqpMetaType::Boolean).toBool();
|
||||||
|
if (frame.id() == QAmqpExchangePrivate::bmAck) {
|
||||||
|
if (deliveryTag == 0) {
|
||||||
|
unconfirmedDeliveryTags.clear();
|
||||||
|
} else {
|
||||||
|
int idx = unconfirmedDeliveryTags.indexOf(deliveryTag);
|
||||||
|
if (idx == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multiple) {
|
||||||
|
unconfirmedDeliveryTags.remove(0, idx + 1);
|
||||||
|
} else {
|
||||||
|
unconfirmedDeliveryTags.remove(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unconfirmedDeliveryTags.isEmpty())
|
||||||
|
Q_EMIT q->allMessagesDelivered();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
qAmqpDebug() << "nacked(" << deliveryTag << "), multiple=" << multiple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QAmqpExchange::QAmqpExchange(int channelNumber, QAmqpClient *parent)
|
||||||
|
: QAmqpChannel(new QAmqpExchangePrivate(this), parent)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpExchange);
|
||||||
|
d->init(channelNumber, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpExchange::~QAmqpExchange()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchange::channelOpened()
|
||||||
|
{
|
||||||
|
Q_D(QAmqpExchange);
|
||||||
|
if (d->delayedDeclare)
|
||||||
|
d->declare();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchange::channelClosed()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpExchange::ExchangeOptions QAmqpExchange::options() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpExchange);
|
||||||
|
return d->options;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpExchange::type() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpExchange);
|
||||||
|
return d->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpExchange::isDeclared() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpExchange);
|
||||||
|
return d->declared;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchange::declare(ExchangeType type, ExchangeOptions options, const QAmqpTable &args)
|
||||||
|
{
|
||||||
|
declare(QAmqpExchangePrivate::typeToString(type), options, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchange::declare(const QString &type, ExchangeOptions options, const QAmqpTable &args)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpExchange);
|
||||||
|
d->type = type;
|
||||||
|
d->options = options;
|
||||||
|
d->arguments = args;
|
||||||
|
d->declare();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchange::remove(int options)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpExchange);
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Exchange, QAmqpExchangePrivate::miDelete);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream stream(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
stream << qint16(0); //reserved 1
|
||||||
|
QAmqpFrame::writeAmqpField(stream, QAmqpMetaType::ShortString, d->name);
|
||||||
|
stream << qint8(options);
|
||||||
|
|
||||||
|
qAmqpDebug("<- exchange#delete( exchange=%s, if-unused=%d, no-wait=%d )",
|
||||||
|
qPrintable(d->name), options & QAmqpExchange::roIfUnused, options & QAmqpExchange::roNoWait);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchange::publish(const QString &message, const QString &routingKey,
|
||||||
|
const QAmqpMessage::PropertyHash &properties, int publishOptions)
|
||||||
|
{
|
||||||
|
publish(message.toUtf8(), routingKey, QLatin1String("text.plain"),
|
||||||
|
QAmqpTable(), properties, publishOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchange::publish(const QByteArray &message, const QString &routingKey,
|
||||||
|
const QString &mimeType, const QAmqpMessage::PropertyHash &properties,
|
||||||
|
int publishOptions)
|
||||||
|
{
|
||||||
|
publish(message, routingKey, mimeType, QAmqpTable(), properties, publishOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchange::publish(const QByteArray &message, const QString &routingKey,
|
||||||
|
const QString &mimeType, const QAmqpTable &headers,
|
||||||
|
const QAmqpMessage::PropertyHash &properties, int publishOptions)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpExchange);
|
||||||
|
if (d->nextDeliveryTag > 0) {
|
||||||
|
d->unconfirmedDeliveryTags.append(d->nextDeliveryTag);
|
||||||
|
d->nextDeliveryTag++;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Basic, QAmqpExchangePrivate::bmPublish);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream out(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
out << qint16(0); //reserved 1
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, d->name);
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, routingKey);
|
||||||
|
out << qint8(publishOptions);
|
||||||
|
|
||||||
|
qAmqpDebug("<- basic#publish( exchange=%s, routing-key=%s, mandatory=%d, immediate=%d )",
|
||||||
|
qPrintable(d->name), qPrintable(routingKey),
|
||||||
|
publishOptions & QAmqpExchange::poMandatory, publishOptions & QAmqpExchange::poImmediate);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
|
||||||
|
QAmqpContentFrame content(QAmqpFrame::Basic);
|
||||||
|
content.setChannel(d->channelNumber);
|
||||||
|
content.setProperty(QAmqpMessage::ContentType, mimeType);
|
||||||
|
content.setProperty(QAmqpMessage::ContentEncoding, "utf-8");
|
||||||
|
content.setProperty(QAmqpMessage::Headers, headers);
|
||||||
|
|
||||||
|
QAmqpMessage::PropertyHash::ConstIterator it;
|
||||||
|
QAmqpMessage::PropertyHash::ConstIterator itEnd = properties.constEnd();
|
||||||
|
for (it = properties.constBegin(); it != itEnd; ++it)
|
||||||
|
content.setProperty(it.key(), it.value());
|
||||||
|
content.setBodySize(message.size());
|
||||||
|
d->sendFrame(content);
|
||||||
|
|
||||||
|
int fullSize = message.size();
|
||||||
|
for (int sent = 0; sent < fullSize; sent += (d->client->frameMax() - 7)) {
|
||||||
|
QAmqpContentBodyFrame body;
|
||||||
|
QByteArray partition = message.mid(sent, (d->client->frameMax() - 7));
|
||||||
|
body.setChannel(d->channelNumber);
|
||||||
|
body.setBody(partition);
|
||||||
|
d->sendFrame(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpExchange::enableConfirms(bool noWait)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpExchange);
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Confirm, QAmqpExchangePrivate::cmConfirm);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream stream(&arguments, QIODevice::WriteOnly);
|
||||||
|
stream << qint8(noWait ? 1 : 0);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
|
||||||
|
// for tracking acks and nacks
|
||||||
|
if (d->nextDeliveryTag == 0) d->nextDeliveryTag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpExchange::waitForConfirms(int msecs)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpExchange);
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
connect(this, SIGNAL(allMessagesDelivered()), &loop, SLOT(quit()));
|
||||||
|
QTimer::singleShot(msecs, &loop, SLOT(quit()));
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
return (d->unconfirmedDeliveryTags.isEmpty());
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Alexey Shcherbakov
|
||||||
|
* Copyright (C) 2014-2015 Matt Broadstone
|
||||||
|
* Contact: https://github.com/mbroadst/qamqp
|
||||||
|
*
|
||||||
|
* This file is part of the QAMQP Library.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
#ifndef QAMQPEXCHANGE_H
|
||||||
|
#define QAMQPEXCHANGE_H
|
||||||
|
|
||||||
|
#include "qamqptable.h"
|
||||||
|
#include "qamqpchannel.h"
|
||||||
|
#include "qamqpmessage.h"
|
||||||
|
|
||||||
|
class QAmqpClient;
|
||||||
|
class QAmqpQueue;
|
||||||
|
class QAmqpClientPrivate;
|
||||||
|
class QAmqpExchangePrivate;
|
||||||
|
class QAMQP_EXPORT QAmqpExchange : public QAmqpChannel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString type READ type CONSTANT)
|
||||||
|
Q_PROPERTY(ExchangeOptions options READ options CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~QAmqpExchange();
|
||||||
|
|
||||||
|
enum ExchangeType {
|
||||||
|
Direct,
|
||||||
|
FanOut,
|
||||||
|
Topic,
|
||||||
|
Headers
|
||||||
|
};
|
||||||
|
QString type() const;
|
||||||
|
|
||||||
|
enum PublishOption {
|
||||||
|
poNoOptions = 0x0,
|
||||||
|
poMandatory = 0x01,
|
||||||
|
poImmediate = 0x02
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(PublishOptions, PublishOption)
|
||||||
|
|
||||||
|
enum RemoveOption {
|
||||||
|
roForce = 0x0,
|
||||||
|
roIfUnused = 0x01,
|
||||||
|
roNoWait = 0x04
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(RemoveOptions, RemoveOption)
|
||||||
|
|
||||||
|
enum ExchangeOption {
|
||||||
|
NoOptions = 0x0,
|
||||||
|
Passive = 0x01,
|
||||||
|
Durable = 0x02,
|
||||||
|
AutoDelete = 0x04,
|
||||||
|
Internal = 0x08,
|
||||||
|
NoWait = 0x10
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(ExchangeOptions, ExchangeOption)
|
||||||
|
ExchangeOptions options() const;
|
||||||
|
Q_ENUM(ExchangeOptions)
|
||||||
|
|
||||||
|
bool isDeclared() const;
|
||||||
|
|
||||||
|
void enableConfirms(bool noWait = false);
|
||||||
|
bool waitForConfirms(int msecs = 30000);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void declared();
|
||||||
|
void removed();
|
||||||
|
|
||||||
|
void confirmsEnabled();
|
||||||
|
void allMessagesDelivered();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
// AMQP Exchange
|
||||||
|
void declare(QAmqpExchange::ExchangeType type = Direct,
|
||||||
|
QAmqpExchange::ExchangeOptions options = NoOptions,
|
||||||
|
const QAmqpTable &args = QAmqpTable());
|
||||||
|
void declare(const QString &type,
|
||||||
|
QAmqpExchange::ExchangeOptions options = NoOptions,
|
||||||
|
const QAmqpTable &args = QAmqpTable());
|
||||||
|
void remove(int options = roIfUnused|roNoWait);
|
||||||
|
|
||||||
|
// AMQP Basic
|
||||||
|
void publish(const QString &message, const QString &routingKey,
|
||||||
|
const QAmqpMessage::PropertyHash &properties = QAmqpMessage::PropertyHash(),
|
||||||
|
int publishOptions = poNoOptions);
|
||||||
|
void publish(const QByteArray &message, const QString &routingKey, const QString &mimeType,
|
||||||
|
const QAmqpMessage::PropertyHash &properties = QAmqpMessage::PropertyHash(),
|
||||||
|
int publishOptions = poNoOptions);
|
||||||
|
void publish(const QByteArray &message, const QString &routingKey,
|
||||||
|
const QString &mimeType, const QAmqpTable &headers,
|
||||||
|
const QAmqpMessage::PropertyHash &properties = QAmqpMessage::PropertyHash(),
|
||||||
|
int publishOptions = poNoOptions);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void channelOpened();
|
||||||
|
virtual void channelClosed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit QAmqpExchange(int channelNumber = -1, QAmqpClient *parent = 0);
|
||||||
|
|
||||||
|
Q_DISABLE_COPY(QAmqpExchange)
|
||||||
|
Q_DECLARE_PRIVATE(QAmqpExchange)
|
||||||
|
friend class QAmqpClient;
|
||||||
|
friend class QAmqpClientPrivate;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(QAmqpExchange::ExchangeOptions)
|
||||||
|
Q_DECLARE_METATYPE(QAmqpExchange::ExchangeType)
|
||||||
|
|
||||||
|
#endif // QAMQPEXCHANGE_H
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef QAMQPEXCHANGE_P_H
|
||||||
|
#define QAMQPEXCHANGE_P_H
|
||||||
|
|
||||||
|
#include "qamqptable.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpchannel_p.h"
|
||||||
|
|
||||||
|
class QAmqpExchangePrivate: public QAmqpChannelPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum MethodId {
|
||||||
|
METHOD_ID_ENUM(miDeclare, 10),
|
||||||
|
METHOD_ID_ENUM(miDelete, 20)
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ConfirmMethod {
|
||||||
|
METHOD_ID_ENUM(cmConfirm, 10)
|
||||||
|
};
|
||||||
|
|
||||||
|
QAmqpExchangePrivate(QAmqpExchange *q);
|
||||||
|
static QString typeToString(QAmqpExchange::ExchangeType type);
|
||||||
|
|
||||||
|
virtual void resetInternalState();
|
||||||
|
|
||||||
|
void declare();
|
||||||
|
|
||||||
|
// method handler related
|
||||||
|
virtual void _q_disconnected();
|
||||||
|
virtual bool _q_method(const QAmqpMethodFrame &frame);
|
||||||
|
void declareOk(const QAmqpMethodFrame &frame);
|
||||||
|
void deleteOk(const QAmqpMethodFrame &frame);
|
||||||
|
void basicReturn(const QAmqpMethodFrame &frame);
|
||||||
|
void handleAckOrNack(const QAmqpMethodFrame &frame);
|
||||||
|
|
||||||
|
QString type;
|
||||||
|
QAmqpExchange::ExchangeOptions options;
|
||||||
|
bool delayedDeclare;
|
||||||
|
bool declared;
|
||||||
|
qlonglong nextDeliveryTag;
|
||||||
|
QVector<qlonglong> unconfirmedDeliveryTags;
|
||||||
|
|
||||||
|
Q_DECLARE_PUBLIC(QAmqpExchange)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPEXCHANGE_P_H
|
||||||
|
|
@ -0,0 +1,486 @@
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
#include "qamqptable.h"
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
#include "qamqpframe_p.h"
|
||||||
|
|
||||||
|
QReadWriteLock QAmqpFrame::lock_;
|
||||||
|
int QAmqpFrame::writeTimeout_ = 1000;
|
||||||
|
|
||||||
|
QAmqpFrame::QAmqpFrame(FrameType type)
|
||||||
|
: size_(0),
|
||||||
|
type_(type),
|
||||||
|
channel_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpFrame::FrameType QAmqpFrame::type() const
|
||||||
|
{
|
||||||
|
return static_cast<QAmqpFrame::FrameType>(type_);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpFrame::~QAmqpFrame()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpFrame::setChannel(quint16 channel)
|
||||||
|
{
|
||||||
|
channel_ = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QAmqpFrame::writeTimeout()
|
||||||
|
{
|
||||||
|
QReadLocker locker(&lock_);
|
||||||
|
return writeTimeout_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpFrame::setWriteTimeout(int msecs)
|
||||||
|
{
|
||||||
|
QWriteLocker locker(&lock_);
|
||||||
|
writeTimeout_ = msecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 QAmqpFrame::channel() const
|
||||||
|
{
|
||||||
|
return channel_;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 QAmqpFrame::size() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void QAmqpFrame::readEnd(QDataStream &stream)
|
||||||
|
{
|
||||||
|
unsigned char end_ = 0;
|
||||||
|
stream.readRawData(reinterpret_cast<char*>(&end_), sizeof(end_));
|
||||||
|
if (end_ != FRAME_END)
|
||||||
|
qAmqpDebug("Wrong end of frame");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
QDataStream &operator<<(QDataStream &stream, const QAmqpFrame &frame)
|
||||||
|
{
|
||||||
|
// write header
|
||||||
|
stream << frame.type_;
|
||||||
|
stream << frame.channel_;
|
||||||
|
stream << frame.size();
|
||||||
|
|
||||||
|
frame.writePayload(stream);
|
||||||
|
|
||||||
|
// write end
|
||||||
|
stream << qint8(QAmqpFrame::FRAME_END);
|
||||||
|
|
||||||
|
int writeTimeout = QAmqpFrame::writeTimeout();
|
||||||
|
if(writeTimeout >= -1)
|
||||||
|
{
|
||||||
|
stream.device()->waitForBytesWritten(writeTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream &operator>>(QDataStream &stream, QAmqpFrame &frame)
|
||||||
|
{
|
||||||
|
stream >> frame.type_;
|
||||||
|
stream >> frame.channel_;
|
||||||
|
stream >> frame.size_;
|
||||||
|
frame.readPayload(stream);
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QAmqpMethodFrame::QAmqpMethodFrame()
|
||||||
|
: QAmqpFrame(QAmqpFrame::Method),
|
||||||
|
methodClass_(0),
|
||||||
|
id_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame::QAmqpMethodFrame(MethodClass methodClass, qint16 id)
|
||||||
|
: QAmqpFrame(QAmqpFrame::Method),
|
||||||
|
methodClass_(methodClass),
|
||||||
|
id_(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpFrame::MethodClass QAmqpMethodFrame::methodClass() const
|
||||||
|
{
|
||||||
|
return MethodClass(methodClass_);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint16 QAmqpMethodFrame::id() const
|
||||||
|
{
|
||||||
|
return id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 QAmqpMethodFrame::size() const
|
||||||
|
{
|
||||||
|
return sizeof(id_) + sizeof(methodClass_) + arguments_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpMethodFrame::setArguments(const QByteArray &data)
|
||||||
|
{
|
||||||
|
arguments_ = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray QAmqpMethodFrame::arguments() const
|
||||||
|
{
|
||||||
|
return arguments_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpMethodFrame::readPayload(QDataStream &stream)
|
||||||
|
{
|
||||||
|
stream >> methodClass_;
|
||||||
|
stream >> id_;
|
||||||
|
|
||||||
|
arguments_.resize(size_ - (sizeof(id_) + sizeof(methodClass_)));
|
||||||
|
stream.readRawData(arguments_.data(), arguments_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpMethodFrame::writePayload(QDataStream &stream) const
|
||||||
|
{
|
||||||
|
stream << quint16(methodClass_);
|
||||||
|
stream << quint16(id_);
|
||||||
|
stream.writeRawData(arguments_.data(), arguments_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QVariant QAmqpFrame::readAmqpField(QDataStream &s, QAmqpMetaType::ValueType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QAmqpMetaType::Boolean:
|
||||||
|
{
|
||||||
|
quint8 octet = 0;
|
||||||
|
s >> octet;
|
||||||
|
return QVariant::fromValue<bool>(octet > 0);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::ShortShortUint:
|
||||||
|
{
|
||||||
|
quint8 octet = 0;
|
||||||
|
s >> octet;
|
||||||
|
return QVariant::fromValue<int>(octet);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::ShortUint:
|
||||||
|
{
|
||||||
|
quint16 tmp_value = 0;
|
||||||
|
s >> tmp_value;
|
||||||
|
return QVariant::fromValue<uint>(tmp_value);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::LongUint:
|
||||||
|
{
|
||||||
|
quint32 tmp_value = 0;
|
||||||
|
s >> tmp_value;
|
||||||
|
return QVariant::fromValue<uint>(tmp_value);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::LongLongUint:
|
||||||
|
{
|
||||||
|
qulonglong v = 0 ;
|
||||||
|
s >> v;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::ShortString:
|
||||||
|
{
|
||||||
|
qint8 size = 0;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
s >> size;
|
||||||
|
buffer.resize(size);
|
||||||
|
s.readRawData(buffer.data(), buffer.size());
|
||||||
|
return QString::fromLatin1(buffer.data(), size);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::LongString:
|
||||||
|
{
|
||||||
|
quint32 size = 0;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
s >> size;
|
||||||
|
buffer.resize(size);
|
||||||
|
s.readRawData(buffer.data(), buffer.size());
|
||||||
|
return QString::fromUtf8(buffer.data(), buffer.size());
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::Timestamp:
|
||||||
|
{
|
||||||
|
qulonglong tmp_value;
|
||||||
|
s >> tmp_value;
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
|
||||||
|
return QDateTime::fromSecsSinceEpoch(tmp_value);
|
||||||
|
#else
|
||||||
|
return QDateTime::fromTime_t(tmp_value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::Hash:
|
||||||
|
{
|
||||||
|
QAmqpTable table;
|
||||||
|
s >> table;
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::Void:
|
||||||
|
return QVariant();
|
||||||
|
default:
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "unsupported value type: " << type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpFrame::writeAmqpField(QDataStream &s, QAmqpMetaType::ValueType type, const QVariant &value)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QAmqpMetaType::Boolean:
|
||||||
|
s << (value.toBool() ? qint8(1) : qint8(0));
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::ShortShortUint:
|
||||||
|
s << qint8(value.toUInt());
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::ShortUint:
|
||||||
|
s << quint16(value.toUInt());
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::LongUint:
|
||||||
|
s << quint32(value.toUInt());
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::LongLongUint:
|
||||||
|
s << qulonglong(value.toULongLong());
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::ShortString:
|
||||||
|
{
|
||||||
|
QString str = value.toString();
|
||||||
|
if (str.length() >= 256) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "invalid shortstr length: " << str.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
s << quint8(str.length());
|
||||||
|
s.writeRawData(str.toUtf8().data(), str.length());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::LongString:
|
||||||
|
{
|
||||||
|
QString str = value.toString();
|
||||||
|
s << quint32(str.length());
|
||||||
|
s.writeRawData(str.toLatin1().data(), str.length());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::Timestamp:
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
|
||||||
|
s << qulonglong(value.toDateTime().toSecsSinceEpoch());
|
||||||
|
#else
|
||||||
|
s << qulonglong(value.toDateTime().toTime_t());
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::Hash:
|
||||||
|
{
|
||||||
|
QAmqpTable table(value.toHash());
|
||||||
|
s << table;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "unsupported value type: " << type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QAmqpContentFrame::QAmqpContentFrame()
|
||||||
|
: QAmqpFrame(QAmqpFrame::Header)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpContentFrame::QAmqpContentFrame(QAmqpFrame::MethodClass methodClass)
|
||||||
|
: QAmqpFrame(QAmqpFrame::Header)
|
||||||
|
{
|
||||||
|
methodClass_ = methodClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpFrame::MethodClass QAmqpContentFrame::methodClass() const
|
||||||
|
{
|
||||||
|
return MethodClass(methodClass_);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 QAmqpContentFrame::size() const
|
||||||
|
{
|
||||||
|
QDataStream out(&buffer_, QIODevice::WriteOnly);
|
||||||
|
buffer_.clear();
|
||||||
|
out << qint16(methodClass_);
|
||||||
|
out << qint16(0); //weight
|
||||||
|
out << qlonglong(bodySize_);
|
||||||
|
|
||||||
|
qint16 prop_ = 0;
|
||||||
|
foreach (int p, properties_.keys())
|
||||||
|
prop_ |= p;
|
||||||
|
out << prop_;
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::ContentType)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortString, properties_[QAmqpMessage::ContentType]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::ContentEncoding)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortString, properties_[QAmqpMessage::ContentEncoding]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::Headers)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::Hash, properties_[QAmqpMessage::Headers]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::DeliveryMode)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortShortUint, properties_[QAmqpMessage::DeliveryMode]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::Priority)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortShortUint, properties_[QAmqpMessage::Priority]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::CorrelationId)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortString, properties_[QAmqpMessage::CorrelationId]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::ReplyTo)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortString, properties_[QAmqpMessage::ReplyTo]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::Expiration)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortString, properties_[QAmqpMessage::Expiration]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::MessageId)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortString, properties_[QAmqpMessage::MessageId]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::Timestamp)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::Timestamp, properties_[QAmqpMessage::Timestamp]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::Type)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortString, properties_[QAmqpMessage::Type]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::UserId)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortString, properties_[QAmqpMessage::UserId]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::AppId)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortString, properties_[QAmqpMessage::AppId]);
|
||||||
|
|
||||||
|
if (prop_ & QAmqpMessage::ClusterID)
|
||||||
|
writeAmqpField(out, QAmqpMetaType::ShortString, properties_[QAmqpMessage::ClusterID]);
|
||||||
|
|
||||||
|
return buffer_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong QAmqpContentFrame::bodySize() const
|
||||||
|
{
|
||||||
|
return bodySize_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpContentFrame::setBodySize(qlonglong size)
|
||||||
|
{
|
||||||
|
bodySize_ = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpContentFrame::setProperty(QAmqpMessage::Property prop, const QVariant &value)
|
||||||
|
{
|
||||||
|
properties_[prop] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant QAmqpContentFrame::property(QAmqpMessage::Property prop) const
|
||||||
|
{
|
||||||
|
return properties_.value(prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpContentFrame::writePayload(QDataStream &out) const
|
||||||
|
{
|
||||||
|
out.writeRawData(buffer_.data(), buffer_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpContentFrame::readPayload(QDataStream &in)
|
||||||
|
{
|
||||||
|
in >> methodClass_;
|
||||||
|
in.skipRawData(2); //weight
|
||||||
|
in >> bodySize_;
|
||||||
|
qint16 flags_ = 0;
|
||||||
|
in >> flags_;
|
||||||
|
if (flags_ & QAmqpMessage::ContentType)
|
||||||
|
properties_[QAmqpMessage::ContentType] = readAmqpField(in, QAmqpMetaType::ShortString);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::ContentEncoding)
|
||||||
|
properties_[QAmqpMessage::ContentEncoding] = readAmqpField(in, QAmqpMetaType::ShortString);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::Headers)
|
||||||
|
properties_[QAmqpMessage::Headers] = readAmqpField(in, QAmqpMetaType::Hash);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::DeliveryMode)
|
||||||
|
properties_[QAmqpMessage::DeliveryMode] = readAmqpField(in, QAmqpMetaType::ShortShortUint);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::Priority)
|
||||||
|
properties_[QAmqpMessage::Priority] = readAmqpField(in, QAmqpMetaType::ShortShortUint);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::CorrelationId)
|
||||||
|
properties_[QAmqpMessage::CorrelationId] = readAmqpField(in, QAmqpMetaType::ShortString);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::ReplyTo)
|
||||||
|
properties_[QAmqpMessage::ReplyTo] = readAmqpField(in, QAmqpMetaType::ShortString);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::Expiration)
|
||||||
|
properties_[QAmqpMessage::Expiration] = readAmqpField(in, QAmqpMetaType::ShortString);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::MessageId)
|
||||||
|
properties_[QAmqpMessage::MessageId] = readAmqpField(in, QAmqpMetaType::ShortString);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::Timestamp)
|
||||||
|
properties_[QAmqpMessage::Timestamp] = readAmqpField(in, QAmqpMetaType::Timestamp);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::Type)
|
||||||
|
properties_[QAmqpMessage::Type] = readAmqpField(in, QAmqpMetaType::ShortString);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::UserId)
|
||||||
|
properties_[QAmqpMessage::UserId] = readAmqpField(in, QAmqpMetaType::ShortString);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::AppId)
|
||||||
|
properties_[QAmqpMessage::AppId] = readAmqpField(in, QAmqpMetaType::ShortString);
|
||||||
|
|
||||||
|
if (flags_ & QAmqpMessage::ClusterID)
|
||||||
|
properties_[QAmqpMessage::ClusterID] = readAmqpField(in, QAmqpMetaType::ShortString);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QAmqpContentBodyFrame::QAmqpContentBodyFrame()
|
||||||
|
: QAmqpFrame(QAmqpFrame::Body)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpContentBodyFrame::setBody(const QByteArray &data)
|
||||||
|
{
|
||||||
|
body_ = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray QAmqpContentBodyFrame::body() const
|
||||||
|
{
|
||||||
|
return body_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpContentBodyFrame::writePayload(QDataStream &out) const
|
||||||
|
{
|
||||||
|
out.writeRawData(body_.data(), body_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpContentBodyFrame::readPayload(QDataStream &in)
|
||||||
|
{
|
||||||
|
body_.resize(size_);
|
||||||
|
in.readRawData(body_.data(), body_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 QAmqpContentBodyFrame::size() const
|
||||||
|
{
|
||||||
|
return body_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QAmqpHeartbeatFrame::QAmqpHeartbeatFrame()
|
||||||
|
: QAmqpFrame(QAmqpFrame::Heartbeat)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpHeartbeatFrame::readPayload(QDataStream &stream)
|
||||||
|
{
|
||||||
|
Q_UNUSED(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpHeartbeatFrame::writePayload(QDataStream &stream) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(stream)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
#ifndef QAMQPFRAME_P_H
|
||||||
|
#define QAMQPFRAME_P_H
|
||||||
|
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
#include "qamqpmessage.h"
|
||||||
|
|
||||||
|
class QAmqpFramePrivate;
|
||||||
|
class QAmqpFrame
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const qint64 HEADER_SIZE = 7;
|
||||||
|
static const qint64 FRAME_END_SIZE = 1;
|
||||||
|
static const quint8 FRAME_END = 0xCE;
|
||||||
|
|
||||||
|
enum FrameType
|
||||||
|
{
|
||||||
|
Method = 1,
|
||||||
|
Header = 2,
|
||||||
|
Body = 3,
|
||||||
|
Heartbeat = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MethodClass
|
||||||
|
{
|
||||||
|
Connection = 10,
|
||||||
|
Channel = 20,
|
||||||
|
Exchange = 40,
|
||||||
|
Queue = 50,
|
||||||
|
Basic = 60,
|
||||||
|
Confirm = 85,
|
||||||
|
Tx = 90
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~QAmqpFrame();
|
||||||
|
|
||||||
|
FrameType type() const;
|
||||||
|
|
||||||
|
quint16 channel() const;
|
||||||
|
void setChannel(quint16 channel);
|
||||||
|
|
||||||
|
static int writeTimeout();
|
||||||
|
static void setWriteTimeout(int msecs);
|
||||||
|
|
||||||
|
virtual qint32 size() const;
|
||||||
|
|
||||||
|
static QVariant readAmqpField(QDataStream &s, QAmqpMetaType::ValueType type);
|
||||||
|
static void writeAmqpField(QDataStream &s, QAmqpMetaType::ValueType type, const QVariant &value);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit QAmqpFrame(FrameType type);
|
||||||
|
virtual void writePayload(QDataStream &stream) const = 0;
|
||||||
|
virtual void readPayload(QDataStream &stream) = 0;
|
||||||
|
|
||||||
|
qint32 size_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
qint8 type_;
|
||||||
|
quint16 channel_;
|
||||||
|
|
||||||
|
static QReadWriteLock lock_;
|
||||||
|
static int writeTimeout_;
|
||||||
|
|
||||||
|
friend QDataStream &operator<<(QDataStream &stream, const QAmqpFrame &frame);
|
||||||
|
friend QDataStream &operator>>(QDataStream &stream, QAmqpFrame &frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
QDataStream &operator<<(QDataStream &, const QAmqpFrame &frame);
|
||||||
|
QDataStream &operator>>(QDataStream &, QAmqpFrame &frame);
|
||||||
|
|
||||||
|
class QAMQP_EXPORT QAmqpMethodFrame : public QAmqpFrame
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QAmqpMethodFrame();
|
||||||
|
QAmqpMethodFrame(MethodClass methodClass, qint16 id);
|
||||||
|
|
||||||
|
qint16 id() const;
|
||||||
|
MethodClass methodClass() const;
|
||||||
|
|
||||||
|
virtual qint32 size() const;
|
||||||
|
|
||||||
|
QByteArray arguments() const;
|
||||||
|
void setArguments(const QByteArray &data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writePayload(QDataStream &stream) const;
|
||||||
|
void readPayload(QDataStream &stream);
|
||||||
|
|
||||||
|
short methodClass_;
|
||||||
|
qint16 id_;
|
||||||
|
QByteArray arguments_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QAmqpContentFrame : public QAmqpFrame
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QAmqpContentFrame();
|
||||||
|
QAmqpContentFrame(MethodClass methodClass);
|
||||||
|
|
||||||
|
MethodClass methodClass() const;
|
||||||
|
|
||||||
|
virtual qint32 size() const;
|
||||||
|
|
||||||
|
QVariant property(QAmqpMessage::Property prop) const;
|
||||||
|
void setProperty(QAmqpMessage::Property prop, const QVariant &value);
|
||||||
|
|
||||||
|
qlonglong bodySize() const;
|
||||||
|
void setBodySize(qlonglong size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writePayload(QDataStream &stream) const;
|
||||||
|
void readPayload(QDataStream &stream);
|
||||||
|
friend class QAmqpQueuePrivate;
|
||||||
|
|
||||||
|
short methodClass_;
|
||||||
|
qint16 id_;
|
||||||
|
mutable QByteArray buffer_;
|
||||||
|
QAmqpMessage::PropertyHash properties_;
|
||||||
|
qlonglong bodySize_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QAmqpContentBodyFrame : public QAmqpFrame
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QAmqpContentBodyFrame();
|
||||||
|
|
||||||
|
void setBody(const QByteArray &data);
|
||||||
|
QByteArray body() const;
|
||||||
|
|
||||||
|
virtual qint32 size() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writePayload(QDataStream &stream) const;
|
||||||
|
void readPayload(QDataStream &stream);
|
||||||
|
|
||||||
|
QByteArray body_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QAmqpHeartbeatFrame : public QAmqpFrame
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QAmqpHeartbeatFrame();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writePayload(QDataStream &stream) const;
|
||||||
|
void readPayload(QDataStream &stream);
|
||||||
|
};
|
||||||
|
|
||||||
|
class QAmqpMethodFrameHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool _q_method(const QAmqpMethodFrame &frame) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QAmqpContentFrameHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void _q_content(const QAmqpContentFrame &frame) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QAmqpContentBodyFrameHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void _q_body(const QAmqpContentBodyFrame &frame) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPFRAME_P_H
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Alexey Shcherbakov
|
||||||
|
* Copyright (C) 2014-2015 Matt Broadstone
|
||||||
|
* Contact: https://github.com/mbroadst/qamqp
|
||||||
|
*
|
||||||
|
* This file is part of the QAMQP Library.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
#ifndef QAMQPGLOBAL_H
|
||||||
|
#define QAMQPGLOBAL_H
|
||||||
|
|
||||||
|
#include <QMetaType>
|
||||||
|
|
||||||
|
#define AMQP_SCHEME "amqp"
|
||||||
|
#define AMQP_SSL_SCHEME "amqps"
|
||||||
|
#define AMQP_PORT 5672
|
||||||
|
#define AMQP_SSL_PORT 5671
|
||||||
|
#define AMQP_HOST "192.168.46.100"
|
||||||
|
#define AMQP_VHOST "/"
|
||||||
|
#define AMQP_LOGIN "ecl3000"
|
||||||
|
#define AMQP_PSWD "ecl3000"
|
||||||
|
|
||||||
|
#define AMQP_FRAME_MAX 131072
|
||||||
|
#define AMQP_FRAME_MIN_SIZE 4096
|
||||||
|
|
||||||
|
#define AMQP_BASIC_CONTENT_TYPE_FLAG (1 << 15)
|
||||||
|
#define AMQP_BASIC_CONTENT_ENCODING_FLAG (1 << 14)
|
||||||
|
#define AMQP_BASIC_HEADERS_FLAG (1 << 13)
|
||||||
|
#define AMQP_BASIC_DELIVERY_MODE_FLAG (1 << 12)
|
||||||
|
#define AMQP_BASIC_PRIORITY_FLAG (1 << 11)
|
||||||
|
#define AMQP_BASIC_CORRELATION_ID_FLAG (1 << 10)
|
||||||
|
#define AMQP_BASIC_REPLY_TO_FLAG (1 << 9)
|
||||||
|
#define AMQP_BASIC_EXPIRATION_FLAG (1 << 8)
|
||||||
|
#define AMQP_BASIC_MESSAGE_ID_FLAG (1 << 7)
|
||||||
|
#define AMQP_BASIC_TIMESTAMP_FLAG (1 << 6)
|
||||||
|
#define AMQP_BASIC_TYPE_FLAG (1 << 5)
|
||||||
|
#define AMQP_BASIC_USER_ID_FLAG (1 << 4)
|
||||||
|
#define AMQP_BASIC_APP_ID_FLAG (1 << 3)
|
||||||
|
#define AMQP_BASIC_CLUSTER_ID_FLAG (1 << 2)
|
||||||
|
|
||||||
|
#define QAMQP_VERSION "0.6.0"
|
||||||
|
|
||||||
|
#define AMQP_CONNECTION_FORCED 320
|
||||||
|
|
||||||
|
#ifdef QAMQP_SHARED
|
||||||
|
# ifdef QAMQP_BUILD
|
||||||
|
# define QAMQP_EXPORT Q_DECL_EXPORT
|
||||||
|
# else
|
||||||
|
# define QAMQP_EXPORT Q_DECL_IMPORT
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define QAMQP_EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define qAmqpDebug if (qEnvironmentVariableIsEmpty("QAMQP_DEBUG")); else qDebug
|
||||||
|
|
||||||
|
namespace QAmqpMetaType {
|
||||||
|
|
||||||
|
enum ValueType
|
||||||
|
{
|
||||||
|
Invalid = -1,
|
||||||
|
|
||||||
|
// basic AMQP types
|
||||||
|
Boolean,
|
||||||
|
ShortUint,
|
||||||
|
LongUint,
|
||||||
|
LongLongUint,
|
||||||
|
ShortString,
|
||||||
|
LongString,
|
||||||
|
|
||||||
|
// field-value types
|
||||||
|
ShortShortInt,
|
||||||
|
ShortShortUint,
|
||||||
|
ShortInt,
|
||||||
|
LongInt,
|
||||||
|
LongLongInt,
|
||||||
|
Float,
|
||||||
|
Double,
|
||||||
|
Decimal,
|
||||||
|
Array,
|
||||||
|
Timestamp,
|
||||||
|
Hash,
|
||||||
|
Bytes,
|
||||||
|
Void
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QAmqpMetaType
|
||||||
|
|
||||||
|
namespace QAMQP {
|
||||||
|
|
||||||
|
enum Error
|
||||||
|
{
|
||||||
|
NoError = 0,
|
||||||
|
ContentTooLargeError = 311,
|
||||||
|
NoRouteError = 312,
|
||||||
|
NoConsumersError = 313,
|
||||||
|
ConnectionForcedError = 320,
|
||||||
|
InvalidPathError = 402,
|
||||||
|
AccessRefusedError = 403,
|
||||||
|
NotFoundError = 404,
|
||||||
|
ResourceLockedError = 405,
|
||||||
|
PreconditionFailedError = 406,
|
||||||
|
FrameError = 501,
|
||||||
|
SyntaxError = 502,
|
||||||
|
CommandInvalidError = 503,
|
||||||
|
ChannelError = 504,
|
||||||
|
UnexpectedFrameError = 505,
|
||||||
|
ResourceError = 506,
|
||||||
|
NotAllowedError = 530,
|
||||||
|
NotImplementedError = 540,
|
||||||
|
InternalError = 541
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Decimal
|
||||||
|
{
|
||||||
|
qint8 scale;
|
||||||
|
quint32 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QAMQP
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QAMQP::Error)
|
||||||
|
Q_DECLARE_METATYPE(QAMQP::Decimal)
|
||||||
|
|
||||||
|
#endif // QAMQPGLOBAL_H
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
#include "qamqpmessage.h"
|
||||||
|
#include "qamqpmessage_p.h"
|
||||||
|
|
||||||
|
QAmqpMessagePrivate::QAmqpMessagePrivate()
|
||||||
|
: deliveryTag(0),
|
||||||
|
leftSize(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QAmqpMessage::QAmqpMessage()
|
||||||
|
: d(new QAmqpMessagePrivate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMessage::QAmqpMessage(const QAmqpMessage &other)
|
||||||
|
: d(other.d)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMessage::~QAmqpMessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMessage &QAmqpMessage::operator=(const QAmqpMessage &other)
|
||||||
|
{
|
||||||
|
d = other.d;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpMessage::operator==(const QAmqpMessage &message) const
|
||||||
|
{
|
||||||
|
if (message.d == d)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return (message.d->deliveryTag == d->deliveryTag &&
|
||||||
|
message.d->redelivered == d->redelivered &&
|
||||||
|
message.d->exchangeName == d->exchangeName &&
|
||||||
|
message.d->routingKey == d->routingKey &&
|
||||||
|
message.d->payload == d->payload &&
|
||||||
|
message.d->properties == d->properties &&
|
||||||
|
message.d->headers == d->headers &&
|
||||||
|
message.d->leftSize == d->leftSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpMessage::isValid() const
|
||||||
|
{
|
||||||
|
return d->deliveryTag != 0 &&
|
||||||
|
!d->exchangeName.isNull() &&
|
||||||
|
!d->routingKey.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong QAmqpMessage::deliveryTag() const
|
||||||
|
{
|
||||||
|
return d->deliveryTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpMessage::isRedelivered() const
|
||||||
|
{
|
||||||
|
return d->redelivered;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpMessage::exchangeName() const
|
||||||
|
{
|
||||||
|
return d->exchangeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpMessage::routingKey() const
|
||||||
|
{
|
||||||
|
return d->routingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray QAmqpMessage::payload() const
|
||||||
|
{
|
||||||
|
return d->payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpMessage::hasProperty(Property property) const
|
||||||
|
{
|
||||||
|
return d->properties.contains(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpMessage::setProperty(Property property, const QVariant &value)
|
||||||
|
{
|
||||||
|
d->properties.insert(property, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant QAmqpMessage::property(Property property, const QVariant &defaultValue) const
|
||||||
|
{
|
||||||
|
return d->properties.value(property, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpMessage::hasHeader(const QString &header) const
|
||||||
|
{
|
||||||
|
return d->headers.contains(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpMessage::setHeader(const QString &header, const QVariant &value)
|
||||||
|
{
|
||||||
|
d->headers.insert(header, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant QAmqpMessage::header(const QString &header, const QVariant &defaultValue) const
|
||||||
|
{
|
||||||
|
return d->headers.value(header, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<QString, QVariant> QAmqpMessage::headers() const
|
||||||
|
{
|
||||||
|
return d->headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const QAmqpMessage &message, uint seed)
|
||||||
|
{
|
||||||
|
Q_UNUSED(seed);
|
||||||
|
return qHash(message.deliveryTag()) ^
|
||||||
|
qHash(message.isRedelivered()) ^
|
||||||
|
qHash(message.exchangeName()) ^
|
||||||
|
qHash(message.routingKey()) ^
|
||||||
|
qHash(message.payload());
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Alexey Shcherbakov
|
||||||
|
* Copyright (C) 2014-2015 Matt Broadstone
|
||||||
|
* Contact: https://github.com/mbroadst/qamqp
|
||||||
|
*
|
||||||
|
* This file is part of the QAMQP Library.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
#ifndef QAMQPMESSAGE_H
|
||||||
|
#define QAMQPMESSAGE_H
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QSharedDataPointer>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
|
||||||
|
class QAmqpMessagePrivate;
|
||||||
|
class QAMQP_EXPORT QAmqpMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QAmqpMessage();
|
||||||
|
QAmqpMessage(const QAmqpMessage &other);
|
||||||
|
QAmqpMessage &operator=(const QAmqpMessage &other);
|
||||||
|
~QAmqpMessage();
|
||||||
|
|
||||||
|
inline void swap(QAmqpMessage &other) { qSwap(d, other.d); }
|
||||||
|
|
||||||
|
bool operator==(const QAmqpMessage &message) const;
|
||||||
|
inline bool operator!=(const QAmqpMessage &message) const { return !(operator==(message)); }
|
||||||
|
|
||||||
|
enum Property {
|
||||||
|
ContentType = AMQP_BASIC_CONTENT_TYPE_FLAG,
|
||||||
|
ContentEncoding = AMQP_BASIC_CONTENT_ENCODING_FLAG,
|
||||||
|
Headers = AMQP_BASIC_HEADERS_FLAG,
|
||||||
|
DeliveryMode = AMQP_BASIC_DELIVERY_MODE_FLAG,
|
||||||
|
Priority = AMQP_BASIC_PRIORITY_FLAG,
|
||||||
|
CorrelationId = AMQP_BASIC_CORRELATION_ID_FLAG,
|
||||||
|
ReplyTo = AMQP_BASIC_REPLY_TO_FLAG,
|
||||||
|
Expiration = AMQP_BASIC_EXPIRATION_FLAG,
|
||||||
|
MessageId = AMQP_BASIC_MESSAGE_ID_FLAG,
|
||||||
|
Timestamp = AMQP_BASIC_TIMESTAMP_FLAG,
|
||||||
|
Type = AMQP_BASIC_TYPE_FLAG,
|
||||||
|
UserId = AMQP_BASIC_USER_ID_FLAG,
|
||||||
|
AppId = AMQP_BASIC_APP_ID_FLAG,
|
||||||
|
ClusterID = AMQP_BASIC_CLUSTER_ID_FLAG
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(Properties, Property)
|
||||||
|
typedef QHash<Property, QVariant> PropertyHash;
|
||||||
|
|
||||||
|
bool hasProperty(Property property) const;
|
||||||
|
void setProperty(Property property, const QVariant &value);
|
||||||
|
QVariant property(Property property, const QVariant &defaultValue = QVariant()) const;
|
||||||
|
|
||||||
|
bool hasHeader(const QString &header) const;
|
||||||
|
void setHeader(const QString &header, const QVariant &value);
|
||||||
|
QVariant header(const QString &header, const QVariant &defaultValue = QVariant()) const;
|
||||||
|
QHash<QString, QVariant> headers() const;
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
bool isRedelivered() const;
|
||||||
|
qlonglong deliveryTag() const;
|
||||||
|
QString exchangeName() const;
|
||||||
|
QString routingKey() const;
|
||||||
|
QByteArray payload() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedDataPointer<QAmqpMessagePrivate> d;
|
||||||
|
friend class QAmqpQueuePrivate;
|
||||||
|
friend class QAmqpQueue;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QAmqpMessage::PropertyHash)
|
||||||
|
Q_DECLARE_SHARED(QAmqpMessage)
|
||||||
|
|
||||||
|
// NOTE: needed only for MSVC support, don't depend on this hash
|
||||||
|
QAMQP_EXPORT uint qHash(const QAmqpMessage &key, uint seed = 0);
|
||||||
|
|
||||||
|
#endif // QAMQPMESSAGE_H
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef QAMQPMESSAGE_P_H
|
||||||
|
#define QAMQPMESSAGE_P_H
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QSharedData>
|
||||||
|
|
||||||
|
#include "qamqpframe_p.h"
|
||||||
|
#include "qamqpmessage.h"
|
||||||
|
|
||||||
|
class QAmqpMessagePrivate : public QSharedData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QAmqpMessagePrivate();
|
||||||
|
|
||||||
|
qlonglong deliveryTag;
|
||||||
|
bool redelivered;
|
||||||
|
QString exchangeName;
|
||||||
|
QString routingKey;
|
||||||
|
QByteArray payload;
|
||||||
|
QHash<QAmqpMessage::Property, QVariant> properties;
|
||||||
|
QHash<QString, QVariant> headers;
|
||||||
|
int leftSize;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPMESSAGE_P_H
|
||||||
|
|
@ -0,0 +1,667 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpclient_p.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
#include "qamqpqueue_p.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpmessage_p.h"
|
||||||
|
#include "qamqptable.h"
|
||||||
|
using namespace QAMQP;
|
||||||
|
|
||||||
|
QAmqpQueuePrivate::QAmqpQueuePrivate(QAmqpQueue *q)
|
||||||
|
: QAmqpChannelPrivate(q),
|
||||||
|
delayedDeclare(false),
|
||||||
|
declared(false),
|
||||||
|
recievingMessage(false),
|
||||||
|
consuming(false),
|
||||||
|
consumeRequested(false),
|
||||||
|
messageCount(0),
|
||||||
|
consumerCount(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpQueuePrivate::~QAmqpQueuePrivate()
|
||||||
|
{
|
||||||
|
if (!client.isNull()) {
|
||||||
|
QAmqpClientPrivate *priv = client->d_func();
|
||||||
|
priv->contentHandlerByChannel[channelNumber].removeAll(this);
|
||||||
|
priv->bodyHandlersByChannel[channelNumber].removeAll(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::resetInternalState()
|
||||||
|
{
|
||||||
|
QAmqpChannelPrivate::resetInternalState();
|
||||||
|
delayedDeclare = false;
|
||||||
|
declared = false;
|
||||||
|
recievingMessage = false;
|
||||||
|
consuming = false;
|
||||||
|
consumeRequested = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpQueuePrivate::_q_method(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpQueue);
|
||||||
|
if (QAmqpChannelPrivate::_q_method(frame))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (frame.methodClass() == QAmqpFrame::Queue) {
|
||||||
|
switch (frame.id()) {
|
||||||
|
case miDeclareOk:
|
||||||
|
declareOk(frame);
|
||||||
|
break;
|
||||||
|
case miDeleteOk:
|
||||||
|
deleteOk(frame);
|
||||||
|
break;
|
||||||
|
case miBindOk:
|
||||||
|
bindOk(frame);
|
||||||
|
break;
|
||||||
|
case miUnbindOk:
|
||||||
|
unbindOk(frame);
|
||||||
|
break;
|
||||||
|
case miPurgeOk:
|
||||||
|
purgeOk(frame);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame.methodClass() == QAmqpFrame::Basic) {
|
||||||
|
switch(frame.id()) {
|
||||||
|
case bmConsumeOk:
|
||||||
|
consumeOk(frame);
|
||||||
|
break;
|
||||||
|
case bmDeliver:
|
||||||
|
deliver(frame);
|
||||||
|
break;
|
||||||
|
case bmGetOk:
|
||||||
|
getOk(frame);
|
||||||
|
break;
|
||||||
|
case bmGetEmpty:
|
||||||
|
Q_EMIT q->empty();
|
||||||
|
break;
|
||||||
|
case bmCancelOk:
|
||||||
|
cancelOk(frame);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::_q_content(const QAmqpContentFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpQueue);
|
||||||
|
Q_ASSERT(frame.channel() == channelNumber);
|
||||||
|
if (frame.channel() != channelNumber)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!currentMessage.isValid()) {
|
||||||
|
qAmqpDebug() << "received content-header without delivered message";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMessage.d->leftSize = frame.bodySize();
|
||||||
|
QAmqpMessage::PropertyHash::ConstIterator it;
|
||||||
|
QAmqpMessage::PropertyHash::ConstIterator itEnd = frame.properties_.constEnd();
|
||||||
|
for (it = frame.properties_.constBegin(); it != itEnd; ++it) {
|
||||||
|
QAmqpMessage::Property property = (it.key());
|
||||||
|
if (property == QAmqpMessage::Headers)
|
||||||
|
currentMessage.d->headers = (it.value()).toHash();
|
||||||
|
currentMessage.d->properties[property] = it.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMessage.d->leftSize == 0) {
|
||||||
|
// message with an empty body
|
||||||
|
q->enqueue(currentMessage);
|
||||||
|
Q_EMIT q->messageReceived();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::_q_body(const QAmqpContentBodyFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpQueue);
|
||||||
|
Q_ASSERT(frame.channel() == channelNumber);
|
||||||
|
if (frame.channel() != channelNumber)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!currentMessage.isValid()) {
|
||||||
|
qAmqpDebug() << "received content-body without delivered message";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMessage.d->payload.append(frame.body());
|
||||||
|
currentMessage.d->leftSize -= frame.body().size();
|
||||||
|
if (currentMessage.d->leftSize == 0) {
|
||||||
|
q->enqueue(currentMessage);
|
||||||
|
Q_EMIT q->messageReceived();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::declareOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpQueue);
|
||||||
|
declared = true;
|
||||||
|
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
name = QAmqpFrame::readAmqpField(stream, QAmqpMetaType::ShortString).toString();
|
||||||
|
|
||||||
|
stream >> messageCount >> consumerCount;
|
||||||
|
|
||||||
|
qAmqpDebug("-> queue#declareOk( queue-name=%s, message-count=%d, consumer-count=%d )",
|
||||||
|
qPrintable(name), messageCount, consumerCount);
|
||||||
|
|
||||||
|
Q_EMIT q->declared();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::purgeOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpQueue);
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
|
||||||
|
stream >> messageCount;
|
||||||
|
|
||||||
|
qAmqpDebug("-> queue#purgeOk( queue-name=%s, message-count=%d )",
|
||||||
|
qPrintable(name), messageCount);
|
||||||
|
|
||||||
|
Q_EMIT q->purged(messageCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::deleteOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpQueue);
|
||||||
|
declared = false;
|
||||||
|
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
stream >> messageCount;
|
||||||
|
|
||||||
|
qAmqpDebug("-> queue#deleteOk( queue-name=%s, message-count=%d )",
|
||||||
|
qPrintable(name), messageCount);
|
||||||
|
|
||||||
|
|
||||||
|
Q_EMIT q->removed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::bindOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_UNUSED(frame)
|
||||||
|
Q_Q(QAmqpQueue);
|
||||||
|
qAmqpDebug("-> queue[ %s ]#bindOk()", qPrintable(name));
|
||||||
|
Q_EMIT q->bound();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::unbindOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_UNUSED(frame)
|
||||||
|
Q_Q(QAmqpQueue);
|
||||||
|
qAmqpDebug("-> queue[ %s ]#unbindOk()", qPrintable(name));
|
||||||
|
Q_EMIT q->unbound();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::getOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
qAmqpDebug("-> queue[ %s ]#getOk()", qPrintable(name));
|
||||||
|
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream in(&data, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
QAmqpMessage message;
|
||||||
|
message.d->deliveryTag = QAmqpFrame::readAmqpField(in, QAmqpMetaType::LongLongUint).toLongLong();
|
||||||
|
message.d->redelivered = QAmqpFrame::readAmqpField(in, QAmqpMetaType::Boolean).toBool();
|
||||||
|
message.d->exchangeName = QAmqpFrame::readAmqpField(in, QAmqpMetaType::ShortString).toString();
|
||||||
|
message.d->routingKey = QAmqpFrame::readAmqpField(in, QAmqpMetaType::ShortString).toString();
|
||||||
|
currentMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::consumeOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpQueue);
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||||
|
consumerTag = QAmqpFrame::readAmqpField(stream, QAmqpMetaType::ShortString).toString();
|
||||||
|
consuming = true;
|
||||||
|
consumeRequested = false;
|
||||||
|
|
||||||
|
qAmqpDebug("-> queue[ %s ]#consumeOk( consumer-tag=%s )", qPrintable(name), qPrintable(consumerTag));
|
||||||
|
|
||||||
|
Q_EMIT q->consuming(consumerTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::deliver(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO;
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream in(&data, QIODevice::ReadOnly);
|
||||||
|
QString consumer = QAmqpFrame::readAmqpField(in, QAmqpMetaType::ShortString).toString();
|
||||||
|
if (consumerTag != consumer) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "invalid consumer tag: " << consumer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMessage message;
|
||||||
|
message.d->deliveryTag = QAmqpFrame::readAmqpField(in, QAmqpMetaType::LongLongUint).toLongLong();
|
||||||
|
message.d->redelivered = QAmqpFrame::readAmqpField(in, QAmqpMetaType::Boolean).toBool();
|
||||||
|
message.d->exchangeName = QAmqpFrame::readAmqpField(in, QAmqpMetaType::ShortString).toString();
|
||||||
|
message.d->routingKey = QAmqpFrame::readAmqpField(in, QAmqpMetaType::ShortString).toString();
|
||||||
|
currentMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::declare()
|
||||||
|
{
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Queue, QAmqpQueuePrivate::miDeclare);
|
||||||
|
frame.setChannel(channelNumber);
|
||||||
|
|
||||||
|
QByteArray args;
|
||||||
|
QDataStream out(&args, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
out << qint16(0); //reserved 1
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, name);
|
||||||
|
out << qint8(options);
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::Hash, arguments);
|
||||||
|
|
||||||
|
qAmqpDebug("<- queue#declare( queue=%s, passive=%d, durable=%d, exclusive=%d, auto-delete=%d, no-wait=%d )",
|
||||||
|
qPrintable(name), options & QAmqpQueue::Passive, options & QAmqpQueue::Durable,
|
||||||
|
options & QAmqpQueue::Exclusive, options & QAmqpQueue::AutoDelete,
|
||||||
|
options & QAmqpQueue::NoWait);
|
||||||
|
|
||||||
|
frame.setArguments(args);
|
||||||
|
sendFrame(frame);
|
||||||
|
|
||||||
|
if (delayedDeclare)
|
||||||
|
delayedDeclare = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueuePrivate::cancelOk(const QAmqpMethodFrame &frame)
|
||||||
|
{
|
||||||
|
Q_Q(QAmqpQueue);
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO;
|
||||||
|
QByteArray data = frame.arguments();
|
||||||
|
QDataStream in(&data, QIODevice::ReadOnly);
|
||||||
|
QString consumer = QAmqpFrame::readAmqpField(in, QAmqpMetaType::ShortString).toString();
|
||||||
|
if (consumerTag != consumer) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "invalid consumer tag: " << consumer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qAmqpDebug("-> queue[ %s ]#cancelOk( consumer-tag=%s )", qPrintable(name), qPrintable(consumerTag));
|
||||||
|
|
||||||
|
consumerTag.clear();
|
||||||
|
consuming = false;
|
||||||
|
consumeRequested = false;
|
||||||
|
Q_EMIT q->cancelled(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QAmqpQueue::QAmqpQueue(int channelNumber, QAmqpClient *parent)
|
||||||
|
: QAmqpChannel(new QAmqpQueuePrivate(this), parent)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
d->init(channelNumber, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpQueue::~QAmqpQueue()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::channelOpened()
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
if (d->delayedDeclare)
|
||||||
|
d->declare();
|
||||||
|
|
||||||
|
if (!d->delayedBindings.isEmpty()) {
|
||||||
|
typedef QPair<QString, QString> BindingPair;
|
||||||
|
foreach(BindingPair binding, d->delayedBindings)
|
||||||
|
bind(binding.first, binding.second);
|
||||||
|
d->delayedBindings.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::channelClosed()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int QAmqpQueue::options() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpQueue);
|
||||||
|
return d->options;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 QAmqpQueue::messageCount() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpQueue);
|
||||||
|
return d->messageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 QAmqpQueue::consumerCount() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpQueue);
|
||||||
|
return d->consumerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::declare(int options, const QAmqpTable &arguments)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
d->options = options;
|
||||||
|
d->arguments = arguments;
|
||||||
|
|
||||||
|
if (!d->opened) {
|
||||||
|
d->delayedDeclare = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->declare();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::remove(int options)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
if (!d->declared) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "trying to remove undeclared queue, aborting...";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Queue, QAmqpQueuePrivate::miDelete);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream out(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
out << qint16(0); //reserved 1
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, d->name);
|
||||||
|
out << qint8(options);
|
||||||
|
|
||||||
|
qAmqpDebug("<- queue#delete( queue=%s, if-unused=%d, if-empty=%d )",
|
||||||
|
qPrintable(d->name), options & QAmqpQueue::roIfUnused, options & QAmqpQueue::roIfEmpty);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::purge()
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
|
||||||
|
if (!d->opened)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Queue, QAmqpQueuePrivate::miPurge);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream out(&arguments, QIODevice::WriteOnly);
|
||||||
|
out << qint16(0); //reserved 1
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, d->name);
|
||||||
|
out << qint8(0); // no-wait
|
||||||
|
|
||||||
|
qAmqpDebug("<- queue#purge( queue=%s, no-wait=%d )", qPrintable(d->name), 0);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::bind(QAmqpExchange *exchange, const QString &key)
|
||||||
|
{
|
||||||
|
if (!exchange) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "invalid exchange provided";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(exchange->name(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::bind(const QString &exchangeName, const QString &key)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
if (!d->opened) {
|
||||||
|
d->delayedBindings.append(QPair<QString,QString>(exchangeName, key));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Queue, QAmqpQueuePrivate::miBind);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream out(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
out << qint16(0); // reserved 1
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, d->name);
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, exchangeName);
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, key);
|
||||||
|
|
||||||
|
out << qint8(0); // no-wait
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::Hash, QAmqpTable());
|
||||||
|
|
||||||
|
qAmqpDebug("<- queue#bind( queue=%s, exchange=%s, routing-key=%s, no-wait=%d )",
|
||||||
|
qPrintable(d->name), qPrintable(exchangeName), qPrintable(key),
|
||||||
|
0);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::unbind(QAmqpExchange *exchange, const QString &key)
|
||||||
|
{
|
||||||
|
if (!exchange) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "invalid exchange provided";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind(exchange->name(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::unbind(const QString &exchangeName, const QString &key)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
if (!d->opened) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "queue is not open";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Queue, QAmqpQueuePrivate::miUnbind);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream out(&arguments, QIODevice::WriteOnly);
|
||||||
|
out << qint16(0); //reserved 1
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, d->name);
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, exchangeName);
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, key);
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::Hash, QAmqpTable());
|
||||||
|
|
||||||
|
qAmqpDebug("<- queue#unbind( queue=%s, exchange=%s, routing-key=%s )",
|
||||||
|
qPrintable(d->name), qPrintable(exchangeName), qPrintable(key));
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpQueue::consume(int options)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
if (!d->opened) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "queue is not open";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d->consumeRequested) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "already attempting to consume";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d->consuming) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "already consuming with tag: " << d->consumerTag;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Basic, QAmqpQueuePrivate::bmConsume);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream out(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
out << qint16(0); //reserved 1
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, d->name);
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, d->consumerTag);
|
||||||
|
|
||||||
|
out << qint8(options);
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::Hash, QAmqpTable());
|
||||||
|
|
||||||
|
qAmqpDebug("<- basic#consume( queue=%s, consumer-tag=%s, no-local=%d, no-ack=%d, exclusive=%d, no-wait=%d )",
|
||||||
|
qPrintable(d->name), qPrintable(d->consumerTag),
|
||||||
|
options & QAmqpQueue::coNoLocal, options & QAmqpQueue::coNoAck,
|
||||||
|
options & QAmqpQueue::coExclusive, options & QAmqpQueue::coNoWait);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
d->consumeRequested = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::setConsumerTag(const QString &consumerTag)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
d->consumerTag = consumerTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAmqpQueue::consumerTag() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpQueue);
|
||||||
|
return d->consumerTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpQueue::isConsuming() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpQueue);
|
||||||
|
return d->consuming;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpQueue::isDeclared() const
|
||||||
|
{
|
||||||
|
Q_D(const QAmqpQueue);
|
||||||
|
return d->declared;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::get(bool noAck)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
if (!d->opened) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "channel is not open";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Basic, QAmqpQueuePrivate::bmGet);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream out(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
out << qint16(0); //reserved 1
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, d->name);
|
||||||
|
out << qint8(noAck ? 1 : 0); // no-ack
|
||||||
|
|
||||||
|
qAmqpDebug("<- basic#get( queue=%s, no-ack=%d )", qPrintable(d->name), noAck);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::ack(const QAmqpMessage &message)
|
||||||
|
{
|
||||||
|
ack(message.deliveryTag(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::ack(qlonglong deliveryTag, bool multiple)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
if (!d->opened) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "channel is not open";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Basic, QAmqpQueuePrivate::bmAck);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream out(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
out << deliveryTag;
|
||||||
|
out << qint8(multiple ? 1 : 0); // multiple
|
||||||
|
|
||||||
|
qAmqpDebug("<- basic#ack( delivery-tag=%llu, multiple=%d )", deliveryTag, multiple);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::reject(const QAmqpMessage &message, bool requeue)
|
||||||
|
{
|
||||||
|
reject(message.deliveryTag(), requeue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpQueue::reject(qlonglong deliveryTag, bool requeue)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
if (!d->opened) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "channel is not open";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Basic, QAmqpQueuePrivate::bmReject);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream out(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
out << deliveryTag;
|
||||||
|
out << qint8(requeue ? 1 : 0);
|
||||||
|
|
||||||
|
qAmqpDebug("<- basic#reject( delivery-tag=%llu, requeue=%d )", deliveryTag, requeue);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAmqpQueue::cancel(bool noWait)
|
||||||
|
{
|
||||||
|
Q_D(QAmqpQueue);
|
||||||
|
if (!d->consuming) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "not consuming!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d->consumerTag.isEmpty()) {
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "consuming with an empty consumer tag, failing...";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMethodFrame frame(QAmqpFrame::Basic, QAmqpQueuePrivate::bmCancel);
|
||||||
|
frame.setChannel(d->channelNumber);
|
||||||
|
|
||||||
|
QByteArray arguments;
|
||||||
|
QDataStream out(&arguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
QAmqpFrame::writeAmqpField(out, QAmqpMetaType::ShortString, d->consumerTag);
|
||||||
|
out << (noWait ? qint8(0x01) : qint8(0x0));
|
||||||
|
|
||||||
|
qAmqpDebug("<- basic#cancel( consumer-tag=%s, no-wait=%d )", qPrintable(d->consumerTag), noWait);
|
||||||
|
|
||||||
|
frame.setArguments(arguments);
|
||||||
|
d->sendFrame(frame);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Alexey Shcherbakov
|
||||||
|
* Copyright (C) 2014-2015 Matt Broadstone
|
||||||
|
* Contact: https://github.com/mbroadst/qamqp
|
||||||
|
*
|
||||||
|
* This file is part of the QAMQP Library.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
#ifndef QAMQPQUEUE_H
|
||||||
|
#define QAMQPQUEUE_H
|
||||||
|
|
||||||
|
#include <QQueue>
|
||||||
|
|
||||||
|
#include "qamqpchannel.h"
|
||||||
|
#include "qamqpmessage.h"
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
#include "qamqptable.h"
|
||||||
|
|
||||||
|
class QAmqpClient;
|
||||||
|
class QAmqpClientPrivate;
|
||||||
|
class QAmqpExchange;
|
||||||
|
class QAmqpQueuePrivate;
|
||||||
|
class QAMQP_EXPORT QAmqpQueue : public QAmqpChannel, public QQueue<QAmqpMessage>
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(int options READ options CONSTANT)
|
||||||
|
Q_PROPERTY(QString consumerTag READ consumerTag WRITE setConsumerTag)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum QueueOption {
|
||||||
|
NoOptions = 0x0,
|
||||||
|
Passive = 0x01,
|
||||||
|
Durable = 0x02,
|
||||||
|
Exclusive = 0x04,
|
||||||
|
AutoDelete = 0x08,
|
||||||
|
NoWait = 0x10
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(QueueOptions, QueueOption)
|
||||||
|
Q_ENUM(QueueOption)
|
||||||
|
Q_ENUM(QueueOptions)
|
||||||
|
int options() const;
|
||||||
|
|
||||||
|
enum ConsumeOption {
|
||||||
|
coNoLocal = 0x01,
|
||||||
|
coNoAck = 0x02,
|
||||||
|
coExclusive = 0x04,
|
||||||
|
coNoWait = 0x08
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(ConsumeOptions, ConsumeOption)
|
||||||
|
Q_ENUM(ConsumeOption)
|
||||||
|
|
||||||
|
enum RemoveOption {
|
||||||
|
roForce = 0x0,
|
||||||
|
roIfUnused = 0x01,
|
||||||
|
roIfEmpty = 0x02,
|
||||||
|
roNoWait = 0x04
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(RemoveOptions, RemoveOption)
|
||||||
|
Q_ENUM(RemoveOption)
|
||||||
|
|
||||||
|
~QAmqpQueue();
|
||||||
|
|
||||||
|
bool isConsuming() const;
|
||||||
|
bool isDeclared() const;
|
||||||
|
|
||||||
|
void setConsumerTag(const QString &consumerTag);
|
||||||
|
QString consumerTag() const;
|
||||||
|
|
||||||
|
qint32 messageCount() const;
|
||||||
|
qint32 consumerCount() const;
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void declared();
|
||||||
|
void bound();
|
||||||
|
void unbound();
|
||||||
|
void removed();
|
||||||
|
void purged(int messageCount);
|
||||||
|
|
||||||
|
void messageReceived();
|
||||||
|
void empty();
|
||||||
|
void consuming(const QString &consumerTag);
|
||||||
|
void cancelled(const QString &consumerTag);
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
// AMQP Queue
|
||||||
|
void declare(int options = Durable|AutoDelete, const QAmqpTable &arguments = QAmqpTable());
|
||||||
|
void bind(const QString &exchangeName, const QString &key);
|
||||||
|
void bind(QAmqpExchange *exchange, const QString &key);
|
||||||
|
void unbind(const QString &exchangeName, const QString &key);
|
||||||
|
void unbind(QAmqpExchange *exchange, const QString &key);
|
||||||
|
void purge();
|
||||||
|
void remove(int options = roIfUnused|roIfEmpty|roNoWait);
|
||||||
|
|
||||||
|
// AMQP Basic
|
||||||
|
bool consume(int options = NoOptions);
|
||||||
|
void get(bool noAck = true);
|
||||||
|
bool cancel(bool noWait = false);
|
||||||
|
void ack(const QAmqpMessage &message);
|
||||||
|
void ack(qlonglong deliveryTag, bool multiple);
|
||||||
|
void reject(const QAmqpMessage &message, bool requeue);
|
||||||
|
void reject(qlonglong deliveryTag, bool requeue);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// reimp Channel
|
||||||
|
virtual void channelOpened();
|
||||||
|
virtual void channelClosed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit QAmqpQueue(int channelNumber = -1, QAmqpClient *parent = 0);
|
||||||
|
|
||||||
|
Q_DISABLE_COPY(QAmqpQueue)
|
||||||
|
Q_DECLARE_PRIVATE(QAmqpQueue)
|
||||||
|
friend class QAmqpClient;
|
||||||
|
friend class QAmqpClientPrivate;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPQUEUE_H
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
#ifndef QAMQPQUEUE_P_H
|
||||||
|
#define QAMQPQUEUE_P_H
|
||||||
|
|
||||||
|
#include <QQueue>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "qamqpchannel_p.h"
|
||||||
|
|
||||||
|
class QAmqpQueuePrivate: public QAmqpChannelPrivate,
|
||||||
|
public QAmqpContentFrameHandler,
|
||||||
|
public QAmqpContentBodyFrameHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum MethodId {
|
||||||
|
METHOD_ID_ENUM(miDeclare, 10),
|
||||||
|
METHOD_ID_ENUM(miBind, 20),
|
||||||
|
METHOD_ID_ENUM(miUnbind, 50),
|
||||||
|
METHOD_ID_ENUM(miPurge, 30),
|
||||||
|
METHOD_ID_ENUM(miDelete, 40)
|
||||||
|
};
|
||||||
|
|
||||||
|
QAmqpQueuePrivate(QAmqpQueue *q);
|
||||||
|
~QAmqpQueuePrivate();
|
||||||
|
|
||||||
|
virtual void resetInternalState();
|
||||||
|
|
||||||
|
void declare();
|
||||||
|
virtual bool _q_method(const QAmqpMethodFrame &frame);
|
||||||
|
|
||||||
|
// AMQP Queue method handlers
|
||||||
|
void declareOk(const QAmqpMethodFrame &frame);
|
||||||
|
void deleteOk(const QAmqpMethodFrame &frame);
|
||||||
|
void purgeOk(const QAmqpMethodFrame &frame);
|
||||||
|
void bindOk(const QAmqpMethodFrame &frame);
|
||||||
|
void unbindOk(const QAmqpMethodFrame &frame);
|
||||||
|
void consumeOk(const QAmqpMethodFrame &frame);
|
||||||
|
|
||||||
|
// AMQP Basic method handlers
|
||||||
|
virtual void _q_content(const QAmqpContentFrame &frame);
|
||||||
|
virtual void _q_body(const QAmqpContentBodyFrame &frame);
|
||||||
|
void deliver(const QAmqpMethodFrame &frame);
|
||||||
|
void getOk(const QAmqpMethodFrame &frame);
|
||||||
|
void cancelOk(const QAmqpMethodFrame &frame);
|
||||||
|
|
||||||
|
QString type;
|
||||||
|
int options;
|
||||||
|
bool delayedDeclare;
|
||||||
|
bool declared;
|
||||||
|
QQueue<QPair<QString, QString> > delayedBindings;
|
||||||
|
|
||||||
|
QString consumerTag;
|
||||||
|
bool recievingMessage;
|
||||||
|
QAmqpMessage currentMessage;
|
||||||
|
bool consuming;
|
||||||
|
bool consumeRequested;
|
||||||
|
|
||||||
|
qint32 messageCount;
|
||||||
|
qint32 consumerCount;
|
||||||
|
|
||||||
|
Q_DECLARE_PUBLIC(QAmqpQueue)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPQUEUE_P_H
|
||||||
|
|
@ -0,0 +1,371 @@
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QIODevice>
|
||||||
|
|
||||||
|
#include "qamqpframe_p.h"
|
||||||
|
#include "qamqptable.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* field value types according to: https://www.rabbitmq.com/amqp-0-9-1-errata.html
|
||||||
|
t - Boolean
|
||||||
|
b - Signed 8-bit
|
||||||
|
Unsigned 8-bit
|
||||||
|
s - Signed 16-bit
|
||||||
|
Unsigned 16-bit
|
||||||
|
I - Signed 32-bit
|
||||||
|
Unsigned 32-bit
|
||||||
|
l - Signed 64-bit
|
||||||
|
Unsigned 64-bit
|
||||||
|
f - 32-bit float
|
||||||
|
d - 64-bit float
|
||||||
|
D - Decimal
|
||||||
|
S - Long string
|
||||||
|
A - Array
|
||||||
|
T - Timestamp (u64)
|
||||||
|
F - Nested Table
|
||||||
|
V - Void
|
||||||
|
x - Byte array
|
||||||
|
*/
|
||||||
|
|
||||||
|
QAmqpMetaType::ValueType valueTypeForOctet(qint8 octet)
|
||||||
|
{
|
||||||
|
switch (octet) {
|
||||||
|
case 't': return QAmqpMetaType::Boolean;
|
||||||
|
case 'b': return QAmqpMetaType::ShortShortInt;
|
||||||
|
case 's': return QAmqpMetaType::ShortInt;
|
||||||
|
case 'I': return QAmqpMetaType::LongInt;
|
||||||
|
case 'l': return QAmqpMetaType::LongLongInt;
|
||||||
|
case 'f': return QAmqpMetaType::Float;
|
||||||
|
case 'd': return QAmqpMetaType::Double;
|
||||||
|
case 'D': return QAmqpMetaType::Decimal;
|
||||||
|
case 'S': return QAmqpMetaType::LongString;
|
||||||
|
case 'A': return QAmqpMetaType::Array;
|
||||||
|
case 'T': return QAmqpMetaType::Timestamp;
|
||||||
|
case 'F': return QAmqpMetaType::Hash;
|
||||||
|
case 'V': return QAmqpMetaType::Void;
|
||||||
|
case 'x': return QAmqpMetaType::Bytes;
|
||||||
|
default:
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "invalid octet received: " << char(octet);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QAmqpMetaType::Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint8 valueTypeToOctet(QAmqpMetaType::ValueType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QAmqpMetaType::Boolean: return 't';
|
||||||
|
case QAmqpMetaType::ShortShortInt: return 'b';
|
||||||
|
case QAmqpMetaType::ShortInt: return 's';
|
||||||
|
case QAmqpMetaType::LongInt: return 'I';
|
||||||
|
case QAmqpMetaType::LongLongInt: return 'l';
|
||||||
|
case QAmqpMetaType::Float: return 'f';
|
||||||
|
case QAmqpMetaType::Double: return 'd';
|
||||||
|
case QAmqpMetaType::Decimal: return 'D';
|
||||||
|
case QAmqpMetaType::LongString: return 'S';
|
||||||
|
case QAmqpMetaType::Array: return 'A';
|
||||||
|
case QAmqpMetaType::Timestamp: return 'T';
|
||||||
|
case QAmqpMetaType::Hash: return 'F';
|
||||||
|
case QAmqpMetaType::Void: return 'V';
|
||||||
|
case QAmqpMetaType::Bytes: return 'x';
|
||||||
|
default:
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "invalid type received: " << char(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'V';
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpTable::writeFieldValue(QDataStream &stream, const QVariant &value)
|
||||||
|
{
|
||||||
|
QAmqpMetaType::ValueType type;
|
||||||
|
switch (value.userType()) {
|
||||||
|
case QMetaType::Bool:
|
||||||
|
type = QAmqpMetaType::Boolean;
|
||||||
|
break;
|
||||||
|
case QMetaType::QByteArray:
|
||||||
|
type = QAmqpMetaType::Bytes;
|
||||||
|
break;
|
||||||
|
case QMetaType::Int:
|
||||||
|
{
|
||||||
|
int i = qAbs(value.toInt());
|
||||||
|
if (i <= qint8(SCHAR_MAX)) {
|
||||||
|
type = QAmqpMetaType::ShortShortInt;
|
||||||
|
} else if (i <= qint16(SHRT_MAX)) {
|
||||||
|
type = QAmqpMetaType::ShortInt;
|
||||||
|
} else {
|
||||||
|
type = QAmqpMetaType::LongInt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QMetaType::UShort:
|
||||||
|
type = QAmqpMetaType::ShortInt;
|
||||||
|
break;
|
||||||
|
case QMetaType::UInt:
|
||||||
|
{
|
||||||
|
int i = value.toInt();
|
||||||
|
if (i <= qint8(SCHAR_MAX)) {
|
||||||
|
type = QAmqpMetaType::ShortShortInt;
|
||||||
|
} else if (i <= qint16(SHRT_MAX)) {
|
||||||
|
type = QAmqpMetaType::ShortInt;
|
||||||
|
} else {
|
||||||
|
type = QAmqpMetaType::LongInt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QMetaType::LongLong:
|
||||||
|
case QMetaType::ULongLong:
|
||||||
|
type = QAmqpMetaType::LongLongInt;
|
||||||
|
break;
|
||||||
|
case QMetaType::QString:
|
||||||
|
type = QAmqpMetaType::LongString;
|
||||||
|
break;
|
||||||
|
case QMetaType::QDateTime:
|
||||||
|
type = QAmqpMetaType::Timestamp;
|
||||||
|
break;
|
||||||
|
case QMetaType::Double:
|
||||||
|
type = value.toDouble() > FLT_MAX ? QAmqpMetaType::Double : QAmqpMetaType::Float;
|
||||||
|
break;
|
||||||
|
case QMetaType::QVariantHash:
|
||||||
|
type = QAmqpMetaType::Hash;
|
||||||
|
break;
|
||||||
|
case QMetaType::QVariantList:
|
||||||
|
type = QAmqpMetaType::Array;
|
||||||
|
break;
|
||||||
|
case QMetaType::Void:
|
||||||
|
type = QAmqpMetaType::Void;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (value.userType() == qMetaTypeId<QAMQP::Decimal>()) {
|
||||||
|
type = QAmqpMetaType::Decimal;
|
||||||
|
break;
|
||||||
|
} else if (!value.isValid()) {
|
||||||
|
type = QAmqpMetaType::Void;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "unhandled type: " << value.userType();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the field value type, a requirement for field tables only
|
||||||
|
stream << valueTypeToOctet(type);
|
||||||
|
writeFieldValue(stream, type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAmqpTable::writeFieldValue(QDataStream &stream, QAmqpMetaType::ValueType type, const QVariant &value)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QAmqpMetaType::Boolean:
|
||||||
|
case QAmqpMetaType::ShortShortUint:
|
||||||
|
case QAmqpMetaType::ShortUint:
|
||||||
|
case QAmqpMetaType::LongUint:
|
||||||
|
case QAmqpMetaType::LongLongUint:
|
||||||
|
case QAmqpMetaType::ShortString:
|
||||||
|
case QAmqpMetaType::LongString:
|
||||||
|
case QAmqpMetaType::Timestamp:
|
||||||
|
case QAmqpMetaType::Hash:
|
||||||
|
return QAmqpFrame::writeAmqpField(stream, type, value);
|
||||||
|
|
||||||
|
case QAmqpMetaType::ShortShortInt:
|
||||||
|
stream << qint8(value.toInt());
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::ShortInt:
|
||||||
|
stream << qint16(value.toInt());
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::LongInt:
|
||||||
|
stream << qint32(value.toInt());
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::LongLongInt:
|
||||||
|
stream << qlonglong(value.toLongLong());
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::Float:
|
||||||
|
{
|
||||||
|
float g = value.toFloat();
|
||||||
|
QDataStream::FloatingPointPrecision oldPrecision = stream.floatingPointPrecision();
|
||||||
|
stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
|
||||||
|
stream << g;
|
||||||
|
stream.setFloatingPointPrecision(oldPrecision);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::Double:
|
||||||
|
{
|
||||||
|
double g = value.toDouble();
|
||||||
|
QDataStream::FloatingPointPrecision oldPrecision = stream.floatingPointPrecision();
|
||||||
|
stream.setFloatingPointPrecision(QDataStream::DoublePrecision);
|
||||||
|
stream << g;
|
||||||
|
stream.setFloatingPointPrecision(oldPrecision);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::Decimal:
|
||||||
|
{
|
||||||
|
QAMQP::Decimal v(value.value<QAMQP::Decimal>());
|
||||||
|
stream << v.scale;
|
||||||
|
stream << v.value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::Array:
|
||||||
|
{
|
||||||
|
QByteArray buffer;
|
||||||
|
QDataStream arrayStream(&buffer, QIODevice::WriteOnly);
|
||||||
|
QVariantList array(value.toList());
|
||||||
|
for (int i = 0; i < array.size(); ++i)
|
||||||
|
writeFieldValue(arrayStream, array.at(i));
|
||||||
|
|
||||||
|
if (buffer.isEmpty()) {
|
||||||
|
stream << qint32(0);
|
||||||
|
} else {
|
||||||
|
stream << buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::Bytes:
|
||||||
|
{
|
||||||
|
QByteArray ba = value.toByteArray();
|
||||||
|
stream << quint32(ba.length());
|
||||||
|
stream.writeRawData(ba.data(), ba.length());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAmqpMetaType::Void:
|
||||||
|
stream << qint32(0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "unhandled type: " << type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant QAmqpTable::readFieldValue(QDataStream &stream, QAmqpMetaType::ValueType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QAmqpMetaType::Boolean:
|
||||||
|
case QAmqpMetaType::ShortShortUint:
|
||||||
|
case QAmqpMetaType::ShortUint:
|
||||||
|
case QAmqpMetaType::LongUint:
|
||||||
|
case QAmqpMetaType::LongLongUint:
|
||||||
|
case QAmqpMetaType::ShortString:
|
||||||
|
case QAmqpMetaType::LongString:
|
||||||
|
case QAmqpMetaType::Timestamp:
|
||||||
|
case QAmqpMetaType::Hash:
|
||||||
|
return QAmqpFrame::readAmqpField(stream, type);
|
||||||
|
|
||||||
|
case QAmqpMetaType::ShortShortInt:
|
||||||
|
{
|
||||||
|
char octet;
|
||||||
|
stream.readRawData(&octet, sizeof(octet));
|
||||||
|
return QVariant::fromValue<int>(octet);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::ShortInt:
|
||||||
|
{
|
||||||
|
qint16 tmp_value = 0;
|
||||||
|
stream >> tmp_value;
|
||||||
|
return QVariant::fromValue<int>(tmp_value);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::LongInt:
|
||||||
|
{
|
||||||
|
qint32 tmp_value = 0;
|
||||||
|
stream >> tmp_value;
|
||||||
|
return QVariant::fromValue<int>(tmp_value);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::LongLongInt:
|
||||||
|
{
|
||||||
|
qlonglong v = 0 ;
|
||||||
|
stream >> v;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::Float:
|
||||||
|
{
|
||||||
|
float tmp_value;
|
||||||
|
QDataStream::FloatingPointPrecision precision = stream.floatingPointPrecision();
|
||||||
|
stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
|
||||||
|
stream >> tmp_value;
|
||||||
|
stream.setFloatingPointPrecision(precision);
|
||||||
|
return QVariant::fromValue<float>(tmp_value);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::Double:
|
||||||
|
{
|
||||||
|
double tmp_value;
|
||||||
|
QDataStream::FloatingPointPrecision precision = stream.floatingPointPrecision();
|
||||||
|
stream.setFloatingPointPrecision(QDataStream::DoublePrecision);
|
||||||
|
stream >> tmp_value;
|
||||||
|
stream.setFloatingPointPrecision(precision);
|
||||||
|
return QVariant::fromValue<double>(tmp_value);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::Decimal:
|
||||||
|
{
|
||||||
|
QAMQP::Decimal v;
|
||||||
|
stream >> v.scale;
|
||||||
|
stream >> v.value;
|
||||||
|
return QVariant::fromValue<QAMQP::Decimal>(v);
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::Array:
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
quint32 size = 0;
|
||||||
|
stream >> size;
|
||||||
|
data.resize(size);
|
||||||
|
stream.readRawData(data.data(), data.size());
|
||||||
|
|
||||||
|
qint8 type = 0;
|
||||||
|
QVariantList result;
|
||||||
|
QDataStream arrayStream(&data, QIODevice::ReadOnly);
|
||||||
|
while (!arrayStream.atEnd()) {
|
||||||
|
arrayStream >> type;
|
||||||
|
result.append(readFieldValue(arrayStream, valueTypeForOctet(type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::Bytes:
|
||||||
|
{
|
||||||
|
QByteArray bytes;
|
||||||
|
quint32 length = 0;
|
||||||
|
stream >> length;
|
||||||
|
bytes.resize(length);
|
||||||
|
stream.readRawData(bytes.data(), bytes.size());
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
case QAmqpMetaType::Void:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qAmqpDebug() << Q_FUNC_INFO << "unhandled type: " << type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream &operator<<(QDataStream &stream, const QAmqpTable &table)
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
QDataStream s(&data, QIODevice::WriteOnly);
|
||||||
|
QAmqpTable::ConstIterator it;
|
||||||
|
QAmqpTable::ConstIterator itEnd = table.constEnd();
|
||||||
|
for (it = table.constBegin(); it != itEnd; ++it) {
|
||||||
|
QAmqpTable::writeFieldValue(s, QAmqpMetaType::ShortString, it.key());
|
||||||
|
QAmqpTable::writeFieldValue(s, it.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.isEmpty()) {
|
||||||
|
stream << qint32(0);
|
||||||
|
} else {
|
||||||
|
stream << data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream &operator>>(QDataStream &stream, QAmqpTable &table)
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
stream >> data;
|
||||||
|
QDataStream tableStream(&data, QIODevice::ReadOnly);
|
||||||
|
while (!tableStream.atEnd()) {
|
||||||
|
qint8 octet = 0;
|
||||||
|
QString field = QAmqpFrame::readAmqpField(tableStream, QAmqpMetaType::ShortString).toString();
|
||||||
|
tableStream >> octet;
|
||||||
|
table[field] = QAmqpTable::readFieldValue(tableStream, valueTypeForOctet(octet));
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Alexey Shcherbakov
|
||||||
|
* Copyright (C) 2014-2015 Matt Broadstone
|
||||||
|
* Contact: https://github.com/mbroadst/qamqp
|
||||||
|
*
|
||||||
|
* This file is part of the QAMQP Library.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*/
|
||||||
|
#ifndef QAMQPTABLE_H
|
||||||
|
#define QAMQPTABLE_H
|
||||||
|
|
||||||
|
#include <QVariantHash>
|
||||||
|
|
||||||
|
#include "qamqpglobal.h"
|
||||||
|
|
||||||
|
class QAMQP_EXPORT QAmqpTable : public QVariantHash
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QAmqpTable() {}
|
||||||
|
inline QAmqpTable(const QVariantHash &variantHash)
|
||||||
|
: QVariantHash(variantHash)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeFieldValue(QDataStream &stream, const QVariant &value);
|
||||||
|
static void writeFieldValue(QDataStream &stream, QAmqpMetaType::ValueType type, const QVariant &value);
|
||||||
|
static QVariant readFieldValue(QDataStream &stream, QAmqpMetaType::ValueType type);
|
||||||
|
};
|
||||||
|
|
||||||
|
QAMQP_EXPORT QDataStream &operator<<(QDataStream &, const QAmqpTable &table);
|
||||||
|
QAMQP_EXPORT QDataStream &operator>>(QDataStream &, QAmqpTable &table);
|
||||||
|
Q_DECLARE_METATYPE(QAmqpTable)
|
||||||
|
|
||||||
|
#endif // QAMQPTABLE_H
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS = \
|
||||||
|
qamqpclient \
|
||||||
|
qamqpexchange \
|
||||||
|
qamqpqueue \
|
||||||
|
qamqpchannel
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
include($${DEPTH}/tests/tests.pri)
|
||||||
|
|
||||||
|
TARGET = tst_qamqpchannel
|
||||||
|
SOURCES = tst_qamqpchannel.cpp
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
#include "signalspy.h"
|
||||||
|
#include "qamqptestcase.h"
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class tst_QAMQPChannel : public TestCase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void init();
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
void close();
|
||||||
|
void resume();
|
||||||
|
void sharedChannel();
|
||||||
|
void defineWithChannelNumber();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<QAmqpClient> client;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QAMQPChannel::init()
|
||||||
|
{
|
||||||
|
client.reset(new QAmqpClient);
|
||||||
|
client->connectToHost();
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(connected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPChannel::cleanup()
|
||||||
|
{
|
||||||
|
if (client->isConnected()) {
|
||||||
|
client->disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPChannel::close()
|
||||||
|
{
|
||||||
|
// exchange
|
||||||
|
QAmqpExchange *exchange = client->createExchange("test-close-channel");
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(opened())));
|
||||||
|
exchange->declare(QAmqpExchange::Direct);
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(declared())));
|
||||||
|
exchange->close();
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(closed())));
|
||||||
|
exchange->reopen();
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(opened())));
|
||||||
|
exchange->remove(QAmqpExchange::roForce);
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(removed())));
|
||||||
|
|
||||||
|
// queue
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-close-channel");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(opened())));
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
queue->close();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(closed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPChannel::resume()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-resume");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(opened())));
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
queue->resume();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(resumed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPChannel::sharedChannel()
|
||||||
|
{
|
||||||
|
QString routingKey = "test-shared-channel";
|
||||||
|
QAmqpQueue *queue = client->createQueue(routingKey);
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange("", queue->channelNumber());
|
||||||
|
defaultExchange->publish("first message", routingKey);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(messageReceived())));
|
||||||
|
QAmqpMessage message = queue->dequeue();
|
||||||
|
verifyStandardMessageHeaders(message, routingKey);
|
||||||
|
QCOMPARE(message.payload(), QByteArray("first message"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPChannel::defineWithChannelNumber()
|
||||||
|
{
|
||||||
|
QString routingKey = "test-specific-channel-number";
|
||||||
|
QAmqpQueue *queue = client->createQueue(routingKey, 25);
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
QCOMPARE(queue->channelNumber(), 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_QAMQPChannel)
|
||||||
|
#include "tst_qamqpchannel.moc"
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/certs">
|
||||||
|
<file alias="ca-cert.pem">../../files/certs/testca/cacert.pem</file>
|
||||||
|
<file alias="client-cert.pem">../../files/certs/client/cert.pem</file>
|
||||||
|
<file alias="client-key.pem">../../files/certs/client/key.pem</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
include($${DEPTH}/tests/tests.pri)
|
||||||
|
|
||||||
|
TARGET = tst_qamqpclient
|
||||||
|
SOURCES = tst_qamqpclient.cpp
|
||||||
|
RESOURCES = certs.qrc
|
||||||
|
|
@ -0,0 +1,331 @@
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QSslKey>
|
||||||
|
|
||||||
|
#include "qamqptestcase.h"
|
||||||
|
#include "qamqpauthenticator.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
#include "qamqpclient_p.h"
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
|
||||||
|
class tst_QAMQPClient : public TestCase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void connect();
|
||||||
|
void connectProperties();
|
||||||
|
void connectHostAddress();
|
||||||
|
void connectDisconnect();
|
||||||
|
void invalidAuthenticationMechanism();
|
||||||
|
void tune();
|
||||||
|
void socketError();
|
||||||
|
void validateUri_data();
|
||||||
|
void validateUri();
|
||||||
|
void issue38();
|
||||||
|
void issue38_take2();
|
||||||
|
|
||||||
|
public Q_SLOTS: // temporarily disabled
|
||||||
|
void autoReconnect();
|
||||||
|
void autoReconnectTimeout();
|
||||||
|
void sslConnect();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSslConfiguration createSslConfiguration();
|
||||||
|
void issue38_helper(QAmqpClient *client);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
QSslConfiguration tst_QAMQPClient::createSslConfiguration()
|
||||||
|
{
|
||||||
|
QList<QSslCertificate> caCerts =
|
||||||
|
QSslCertificate::fromPath(QLatin1String(":/certs/ca-cert.pem"));
|
||||||
|
QList<QSslCertificate> localCerts =
|
||||||
|
QSslCertificate::fromPath(QLatin1String(":/certs/client-cert.pem"));
|
||||||
|
QFile keyFile( QLatin1String(":/certs/client-key.pem"));
|
||||||
|
keyFile.open(QIODevice::ReadOnly);
|
||||||
|
QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
||||||
|
keyFile.close();
|
||||||
|
|
||||||
|
QSslConfiguration sslConfiguration;
|
||||||
|
sslConfiguration.setCaCertificates(caCerts);
|
||||||
|
sslConfiguration.setLocalCertificate(localCerts.first());
|
||||||
|
sslConfiguration.setPrivateKey(key);
|
||||||
|
sslConfiguration.setProtocol(QSsl::SecureProtocols);
|
||||||
|
return sslConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::connect()
|
||||||
|
{
|
||||||
|
QAmqpClient client;
|
||||||
|
client.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected())));
|
||||||
|
|
||||||
|
QCOMPARE(client.host(), QLatin1String(AMQP_HOST));
|
||||||
|
QCOMPARE(client.port(), quint16(AMQP_PORT));
|
||||||
|
QCOMPARE(client.virtualHost(), QLatin1String(AMQP_VHOST));
|
||||||
|
QCOMPARE(client.username(), QLatin1String(AMQP_LOGIN));
|
||||||
|
QCOMPARE(client.password(), QLatin1String(AMQP_PSWD));
|
||||||
|
QCOMPARE(client.autoReconnect(), false);
|
||||||
|
|
||||||
|
client.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::sslConnect()
|
||||||
|
{
|
||||||
|
QAmqpClient client;
|
||||||
|
client.setSslConfiguration(createSslConfiguration());
|
||||||
|
QObject::connect(&client, SIGNAL(sslErrors(QList<QSslError>)),
|
||||||
|
&client, SLOT(ignoreSslErrors(QList<QSslError>)));
|
||||||
|
|
||||||
|
client.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::connectProperties()
|
||||||
|
{
|
||||||
|
QAmqpClient client;
|
||||||
|
client.setHost("localhost");
|
||||||
|
client.setPort(5672);
|
||||||
|
client.setVirtualHost("/");
|
||||||
|
client.setUsername("guest");
|
||||||
|
client.setPassword("guest");
|
||||||
|
client.setAutoReconnect(false);
|
||||||
|
client.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected())));
|
||||||
|
client.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::connectHostAddress()
|
||||||
|
{
|
||||||
|
QAmqpClient client;
|
||||||
|
client.connectToHost(QHostAddress::LocalHost, 5672);
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected())));
|
||||||
|
client.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::connectDisconnect()
|
||||||
|
{
|
||||||
|
QAmqpClient client;
|
||||||
|
client.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected())));
|
||||||
|
client.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvalidAuthenticator : public QAmqpAuthenticator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual QString type() const { return "CRAZYAUTH"; }
|
||||||
|
virtual void write(QDataStream &out) {
|
||||||
|
Q_UNUSED(out);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QAMQPClient::invalidAuthenticationMechanism()
|
||||||
|
{
|
||||||
|
QAmqpClient client;
|
||||||
|
client.setAuth(new InvalidAuthenticator);
|
||||||
|
client.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::autoReconnect()
|
||||||
|
{
|
||||||
|
// TODO: this is a fairly crude way of testing this, research
|
||||||
|
// better alternatives
|
||||||
|
|
||||||
|
QAmqpClient client;
|
||||||
|
client.setAutoReconnect(true);
|
||||||
|
client.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected())));
|
||||||
|
QProcess::execute("rabbitmqctl", QStringList() << "stop_app");
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(disconnected())));
|
||||||
|
QProcess::execute("rabbitmqctl", QStringList() << "start_app");
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected()), 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::autoReconnectTimeout()
|
||||||
|
{
|
||||||
|
// TODO: this is a fairly crude way of testing this, research
|
||||||
|
// better alternatives
|
||||||
|
|
||||||
|
QAmqpClient client;
|
||||||
|
client.setAutoReconnect(true, 3);
|
||||||
|
client.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected()), 60));
|
||||||
|
qDebug() <<"connected" ;
|
||||||
|
QProcess::execute("rabbitmqctl", QStringList() << "stop_app");
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(disconnected()), 60));
|
||||||
|
qDebug() <<"disconnected" ;
|
||||||
|
QProcess::execute("rabbitmqctl", QStringList() << "start_app");
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected()), 60));
|
||||||
|
qDebug() <<"connected" ;
|
||||||
|
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(disconnected()), 60));
|
||||||
|
QProcess::execute("rabbitmqctl", QStringList() << "start_app");
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected()), 60));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::tune()
|
||||||
|
{
|
||||||
|
QAmqpClient client;
|
||||||
|
client.setChannelMax(15);
|
||||||
|
client.setFrameMax(5000);
|
||||||
|
client.setHeartbeatDelay(600);
|
||||||
|
|
||||||
|
client.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected())));
|
||||||
|
QCOMPARE((int) client.channelMax(), 2047);
|
||||||
|
QCOMPARE((int)client.heartbeatDelay(), 600);
|
||||||
|
QCOMPARE((int)client.frameMax(), 5000);
|
||||||
|
|
||||||
|
client.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::socketError()
|
||||||
|
{
|
||||||
|
QAmqpClient client;
|
||||||
|
client.connectToHost("amqp://127.0.0.1:56725/");
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(socketErrorOccurred(QAbstractSocket::SocketError))));
|
||||||
|
QCOMPARE(client.socketError(), QAbstractSocket::ConnectionRefusedError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::validateUri_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("uri");
|
||||||
|
QTest::addColumn<QString>("expectedUsername");
|
||||||
|
QTest::addColumn<QString>("expectedPassword");
|
||||||
|
QTest::addColumn<QString>("expectedHost");
|
||||||
|
QTest::addColumn<quint16>("expectedPort");
|
||||||
|
QTest::addColumn<QString>("expectedVirtualHost");
|
||||||
|
|
||||||
|
QTest::newRow("default vhost") << "amqp://guest:guest@192.168.1.10:5672/"
|
||||||
|
<< "guest" << "guest" << "192.168.1.10" << quint16(5672) << "/";
|
||||||
|
QTest::newRow("standard") << "amqp://user:pass@host:10000/vhost"
|
||||||
|
<< "user" << "pass" << "host" << quint16(10000) << "vhost";
|
||||||
|
QTest::newRow("urlencoded") << "amqp://user%61:%61pass@ho%61st:10000/v%2fhost"
|
||||||
|
<< "usera" << "apass" << "hoast" << quint16(10000) << "v/host";
|
||||||
|
QTest::newRow("empty") << "amqp://" << "" << "" << "" << quint16(AMQP_PORT) << "";
|
||||||
|
QTest::newRow("empty2") << "amqp://:@/" << "" << "" << "" << quint16(AMQP_PORT) << "/";
|
||||||
|
QTest::newRow("onlyuser") << "amqp://user@" << "user" << "" << "" << quint16(AMQP_PORT) << "";
|
||||||
|
QTest::newRow("userpass") << "amqp://user:pass@" << "user" << "pass" << "" << quint16(AMQP_PORT) << "";
|
||||||
|
QTest::newRow("onlyhost") << "amqp://host" << "" << "" << "host" << quint16(AMQP_PORT) << "";
|
||||||
|
QTest::newRow("onlyport") << "amqp://:10000" << "" << "" << "" << quint16(10000) << "";
|
||||||
|
QTest::newRow("onlyvhost") << "amqp:///vhost" << "" << "" << "" << quint16(AMQP_PORT) << "vhost";
|
||||||
|
QTest::newRow("urlencodedvhost") << "amqp://host/%2f"
|
||||||
|
<< "" << "" << "host" << quint16(AMQP_PORT) << "/";
|
||||||
|
QTest::newRow("ipv6") << "amqp://[::1]" << "" << "" << "::1" << quint16(AMQP_PORT) << "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::validateUri()
|
||||||
|
{
|
||||||
|
QFETCH(QString, uri);
|
||||||
|
QFETCH(QString, expectedUsername);
|
||||||
|
QFETCH(QString, expectedPassword);
|
||||||
|
QFETCH(QString, expectedHost);
|
||||||
|
QFETCH(quint16, expectedPort);
|
||||||
|
QFETCH(QString, expectedVirtualHost);
|
||||||
|
|
||||||
|
QAmqpClientPrivate clientPrivate(0);
|
||||||
|
// fake init
|
||||||
|
clientPrivate.authenticator = QSharedPointer<QAmqpAuthenticator>(
|
||||||
|
new QAmqpPlainAuthenticator(QString::fromLatin1(AMQP_LOGIN), QString::fromLatin1(AMQP_PSWD)));
|
||||||
|
|
||||||
|
// test parsing
|
||||||
|
clientPrivate.parseConnectionString(uri);
|
||||||
|
QAmqpPlainAuthenticator *auth =
|
||||||
|
static_cast<QAmqpPlainAuthenticator*>(clientPrivate.authenticator.data());
|
||||||
|
|
||||||
|
QCOMPARE(auth->login(), expectedUsername);
|
||||||
|
QCOMPARE(auth->password(), expectedPassword);
|
||||||
|
QCOMPARE(clientPrivate.host, expectedHost);
|
||||||
|
QCOMPARE(clientPrivate.port, expectedPort);
|
||||||
|
QCOMPARE(clientPrivate.virtualHost, expectedVirtualHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::issue38_helper(QAmqpClient *client)
|
||||||
|
{
|
||||||
|
// connect
|
||||||
|
client->connectToHost();
|
||||||
|
QVERIFY(waitForSignal(client, SIGNAL(connected())));
|
||||||
|
|
||||||
|
// create then declare, remove and close queue
|
||||||
|
QAmqpQueue *queue = client->createQueue();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(opened())));
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
queue->remove(QAmqpExchange::roForce);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(removed())));
|
||||||
|
queue->close();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(closed())));
|
||||||
|
queue->deleteLater();
|
||||||
|
|
||||||
|
// disconnect
|
||||||
|
client->disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(client, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::issue38()
|
||||||
|
{
|
||||||
|
QAmqpClient client;
|
||||||
|
issue38_helper(&client);
|
||||||
|
issue38_helper(&client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPClient::issue38_take2()
|
||||||
|
{
|
||||||
|
QAmqpClient client;
|
||||||
|
client.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected())));
|
||||||
|
QAmqpExchange *exchange = client.createExchange("myexchange");
|
||||||
|
exchange->declare(QAmqpExchange::Topic);
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(declared())));
|
||||||
|
QAmqpQueue *queue = client.createQueue();
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
queue->bind(exchange, "routingKeyin");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(bound())));
|
||||||
|
queue->consume(QAmqpQueue::coNoAck);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(consuming(QString))));
|
||||||
|
exchange->publish("test message", "routingKeyout");
|
||||||
|
|
||||||
|
// delete everything
|
||||||
|
queue->unbind(exchange, "routingKeyin");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(unbound())));
|
||||||
|
queue->close();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(closed())));
|
||||||
|
queue->deleteLater();
|
||||||
|
|
||||||
|
exchange->close();
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(closed())));
|
||||||
|
exchange->deleteLater();
|
||||||
|
|
||||||
|
client.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&client,SIGNAL(disconnected())));
|
||||||
|
|
||||||
|
// repeat the connection and create again here
|
||||||
|
client.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&client, SIGNAL(connected())));
|
||||||
|
exchange = client.createExchange("myexchange");
|
||||||
|
exchange->declare(QAmqpExchange::Topic);
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(declared())));
|
||||||
|
queue = client.createQueue();
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
queue->bind(exchange, "routingKeyin");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(bound())));
|
||||||
|
queue->consume(QAmqpQueue::coNoAck);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(consuming(QString))));
|
||||||
|
exchange->publish("test message", "routingKeyout");
|
||||||
|
client.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&client,SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_QAMQPClient)
|
||||||
|
#include "tst_qamqpclient.moc"
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
include($${DEPTH}/tests/tests.pri)
|
||||||
|
|
||||||
|
TARGET = tst_qamqpexchange
|
||||||
|
SOURCES = tst_qamqpexchange.cpp
|
||||||
|
|
@ -0,0 +1,246 @@
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
#include "signalspy.h"
|
||||||
|
#include "qamqptestcase.h"
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class tst_QAMQPExchange : public TestCase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void init();
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
void standardTypes_data();
|
||||||
|
void standardTypes();
|
||||||
|
void invalidStandardDeclaration_data();
|
||||||
|
void invalidStandardDeclaration();
|
||||||
|
void invalidDeclaration();
|
||||||
|
void invalidRedeclaration();
|
||||||
|
void removeIfUnused();
|
||||||
|
void invalidMandatoryRouting();
|
||||||
|
void invalidImmediateRouting();
|
||||||
|
void confirmsSupport();
|
||||||
|
void confirmDontLoseMessages();
|
||||||
|
void passiveDeclareNotFound();
|
||||||
|
void cleanupOnDeletion();
|
||||||
|
void testQueuedPublish();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<QAmqpClient> client;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::init()
|
||||||
|
{
|
||||||
|
client.reset(new QAmqpClient);
|
||||||
|
client->connectToHost();
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(connected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::cleanup()
|
||||||
|
{
|
||||||
|
if (client->isConnected()) {
|
||||||
|
client->disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::standardTypes_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QAmqpExchange::ExchangeType>("type");
|
||||||
|
QTest::addColumn<bool>("delayedDeclaration");
|
||||||
|
|
||||||
|
QTest::newRow("direct") << QAmqpExchange::Direct << false;
|
||||||
|
QTest::newRow("direct-delayed") << QAmqpExchange::Direct << true;
|
||||||
|
QTest::newRow("fanout") << QAmqpExchange::FanOut << false;
|
||||||
|
QTest::newRow("fanout-delayed") << QAmqpExchange::FanOut << true;
|
||||||
|
QTest::newRow("topic") << QAmqpExchange::Topic << false;
|
||||||
|
QTest::newRow("topic-delayed") << QAmqpExchange::Topic << true;
|
||||||
|
QTest::newRow("headers") << QAmqpExchange::Headers << false;
|
||||||
|
QTest::newRow("headers-delayed") << QAmqpExchange::Headers << true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::standardTypes()
|
||||||
|
{
|
||||||
|
QFETCH(QAmqpExchange::ExchangeType, type);
|
||||||
|
QFETCH(bool, delayedDeclaration);
|
||||||
|
|
||||||
|
QAmqpExchange *exchange = client->createExchange("test");
|
||||||
|
if (!delayedDeclaration)
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(opened())));
|
||||||
|
|
||||||
|
exchange->declare(type);
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(declared())));
|
||||||
|
exchange->remove(QAmqpExchange::roForce);
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::invalidStandardDeclaration_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("exchangeName");
|
||||||
|
QTest::addColumn<QAmqpExchange::ExchangeType>("type");
|
||||||
|
QTest::addColumn<QAMQP::Error>("error");
|
||||||
|
|
||||||
|
QTest::newRow("amq.direct") << "amq.direct" << QAmqpExchange::Direct << QAMQP::PreconditionFailedError;
|
||||||
|
QTest::newRow("amq.fanout") << "amq.fanout" << QAmqpExchange::FanOut << QAMQP::PreconditionFailedError;
|
||||||
|
QTest::newRow("amq.headers") << "amq.headers" << QAmqpExchange::Headers << QAMQP::PreconditionFailedError;
|
||||||
|
QTest::newRow("amq.match") << "amq.match" << QAmqpExchange::Headers << QAMQP::PreconditionFailedError;
|
||||||
|
QTest::newRow("amq.topic") << "amq.topic" << QAmqpExchange::Topic << QAMQP::PreconditionFailedError;
|
||||||
|
QTest::newRow("amq.reserved") << "amq.reserved" << QAmqpExchange::Direct << QAMQP::AccessRefusedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::invalidStandardDeclaration()
|
||||||
|
{
|
||||||
|
QFETCH(QString, exchangeName);
|
||||||
|
QFETCH(QAmqpExchange::ExchangeType, type);
|
||||||
|
QFETCH(QAMQP::Error, error);
|
||||||
|
|
||||||
|
QAmqpExchange *exchange = client->createExchange(exchangeName);
|
||||||
|
exchange->declare(type);
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(exchange->error(), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::invalidDeclaration()
|
||||||
|
{
|
||||||
|
QAmqpExchange *exchange = client->createExchange("test-invalid-declaration");
|
||||||
|
exchange->declare("invalidExchangeType");
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(client->error(), QAMQP::CommandInvalidError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::invalidRedeclaration()
|
||||||
|
{
|
||||||
|
QAmqpExchange *exchange = client->createExchange("test-invalid-redeclaration");
|
||||||
|
exchange->declare(QAmqpExchange::Direct);
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(declared())));
|
||||||
|
|
||||||
|
QAmqpExchange *redeclared = client->createExchange("test-invalid-redeclaration");
|
||||||
|
redeclared->declare(QAmqpExchange::FanOut);
|
||||||
|
QVERIFY(waitForSignal(redeclared, SIGNAL(error(QAMQP::Error))));
|
||||||
|
|
||||||
|
// this is per spec:
|
||||||
|
// QCOMPARE(redeclared->error(), QAMQP::NotAllowedError);
|
||||||
|
|
||||||
|
// this is for rabbitmq:
|
||||||
|
QCOMPARE(redeclared->error(), QAMQP::PreconditionFailedError);
|
||||||
|
|
||||||
|
// Server has probably closed the channel on us, if so, re-open it.
|
||||||
|
if (!exchange->isOpen()) {
|
||||||
|
exchange->reopen();
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(opened())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
exchange->remove();
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::removeIfUnused()
|
||||||
|
{
|
||||||
|
QAmqpExchange *exchange = client->createExchange("test-if-unused-exchange");
|
||||||
|
exchange->declare(QAmqpExchange::Direct, QAmqpExchange::AutoDelete);
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(declared())));
|
||||||
|
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-if-unused-queue");
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
queue->bind("test-if-unused-exchange", "testRoutingKey");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(bound())));
|
||||||
|
|
||||||
|
exchange->remove(QAmqpExchange::roIfUnused);
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(exchange->error(), QAMQP::PreconditionFailedError);
|
||||||
|
QVERIFY(!exchange->errorString().isEmpty());
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
queue->remove(QAmqpQueue::roForce);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::invalidMandatoryRouting()
|
||||||
|
{
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
defaultExchange->publish("some message", "unroutable-key", QAmqpMessage::PropertyHash(), QAmqpExchange::poMandatory);
|
||||||
|
QVERIFY(waitForSignal(defaultExchange, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(defaultExchange->error(), QAMQP::NoRouteError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::invalidImmediateRouting()
|
||||||
|
{
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
defaultExchange->publish("some message", "unroutable-key", QAmqpMessage::PropertyHash(), QAmqpExchange::poImmediate);
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(client->error(), QAMQP::NotImplementedError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::confirmsSupport()
|
||||||
|
{
|
||||||
|
QAmqpExchange *exchange = client->createExchange("confirm-test");
|
||||||
|
exchange->enableConfirms();
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(confirmsEnabled())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::confirmDontLoseMessages()
|
||||||
|
{
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
defaultExchange->enableConfirms();
|
||||||
|
QVERIFY(waitForSignal(defaultExchange, SIGNAL(confirmsEnabled())));
|
||||||
|
|
||||||
|
QAmqpMessage::PropertyHash properties;
|
||||||
|
properties[QAmqpMessage::DeliveryMode] = "2"; // make message persistent
|
||||||
|
|
||||||
|
for (int i = 0; i < 10000; ++i)
|
||||||
|
defaultExchange->publish("noop", "confirms-test", properties);
|
||||||
|
QVERIFY(defaultExchange->waitForConfirms());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::passiveDeclareNotFound()
|
||||||
|
{
|
||||||
|
QAmqpExchange *nonExistentExchange = client->createExchange("this-does-not-exist");
|
||||||
|
nonExistentExchange->declare(QAmqpExchange::Direct, QAmqpExchange::Passive);
|
||||||
|
QVERIFY(waitForSignal(nonExistentExchange, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(nonExistentExchange->error(), QAMQP::NotFoundError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::cleanupOnDeletion()
|
||||||
|
{
|
||||||
|
// create, declare, and close the wrong way
|
||||||
|
QAmqpExchange *exchange = client->createExchange("test-deletion");
|
||||||
|
exchange->declare();
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(declared())));
|
||||||
|
exchange->close();
|
||||||
|
exchange->deleteLater();
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(destroyed())));
|
||||||
|
|
||||||
|
// now create, declare, and close the right way
|
||||||
|
exchange = client->createExchange("test-deletion");
|
||||||
|
exchange->declare();
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(declared())));
|
||||||
|
exchange->close();
|
||||||
|
QVERIFY(waitForSignal(exchange, SIGNAL(closed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPExchange::testQueuedPublish()
|
||||||
|
{
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
defaultExchange->enableConfirms();
|
||||||
|
QVERIFY(waitForSignal(defaultExchange, SIGNAL(confirmsEnabled())));
|
||||||
|
|
||||||
|
QAmqpMessage::PropertyHash properties;
|
||||||
|
properties[QAmqpMessage::DeliveryMode] = "2"; // make message persistent
|
||||||
|
for (int i = 0; i < 10000; ++i) {
|
||||||
|
QMetaObject::invokeMethod(defaultExchange, "publish", Qt::QueuedConnection,
|
||||||
|
Q_ARG(QString, "noop"), Q_ARG(QString, "confirms-test"),
|
||||||
|
Q_ARG(QAmqpMessage::PropertyHash, properties));
|
||||||
|
}
|
||||||
|
|
||||||
|
QVERIFY(defaultExchange->waitForConfirms());
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_QAMQPExchange)
|
||||||
|
#include "tst_qamqpexchange.moc"
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
include($${DEPTH}/tests/tests.pri)
|
||||||
|
|
||||||
|
TARGET = tst_qamqpqueue
|
||||||
|
SOURCES = tst_qamqpqueue.cpp
|
||||||
|
|
@ -0,0 +1,708 @@
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
#include "qamqptestcase.h"
|
||||||
|
#include "signalspy.h"
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
|
||||||
|
class tst_QAMQPQueue : public TestCase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void init();
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
void defaultExchange();
|
||||||
|
void standardExchanges_data();
|
||||||
|
void standardExchanges();
|
||||||
|
void invalidDeclaration_data();
|
||||||
|
void invalidDeclaration();
|
||||||
|
void invalidBind();
|
||||||
|
void unnamed();
|
||||||
|
void exclusiveAccess();
|
||||||
|
void exclusiveRemoval();
|
||||||
|
void notFound();
|
||||||
|
void remove();
|
||||||
|
void removeIfUnused();
|
||||||
|
void removeIfEmpty();
|
||||||
|
void bindUnbind();
|
||||||
|
void delayedBind();
|
||||||
|
void purge();
|
||||||
|
void canOnlyStartConsumingOnce();
|
||||||
|
void ensureConsumeOnlySentOnce();
|
||||||
|
void cancel();
|
||||||
|
void invalidCancelBecauseNotConsuming();
|
||||||
|
void invalidCancelBecauseInvalidConsumerTag();
|
||||||
|
void getEmpty();
|
||||||
|
void get();
|
||||||
|
void verifyContentEncodingIssue33();
|
||||||
|
void defineQos();
|
||||||
|
void invalidQos();
|
||||||
|
void qos();
|
||||||
|
void invalidRoutingKey();
|
||||||
|
void tableFieldDataTypes();
|
||||||
|
void messageProperties();
|
||||||
|
void emptyMessage();
|
||||||
|
void cleanupOnDeletion();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<QAmqpClient> client;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::init()
|
||||||
|
{
|
||||||
|
client.reset(new QAmqpClient);
|
||||||
|
client->connectToHost();
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(connected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::cleanup()
|
||||||
|
{
|
||||||
|
if (client->isConnected()) {
|
||||||
|
client->disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::defaultExchange()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-default-exchange");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
defaultExchange->publish("first message", "test-default-exchange");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(messageReceived())));
|
||||||
|
QAmqpMessage message = queue->dequeue();
|
||||||
|
verifyStandardMessageHeaders(message, "test-default-exchange");
|
||||||
|
QCOMPARE(message.payload(), QByteArray("first message"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::standardExchanges_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("exchange");
|
||||||
|
QTest::addColumn<bool>("delayedDeclaration");
|
||||||
|
|
||||||
|
QTest::newRow("amq.direct") << "amq.direct" << false;
|
||||||
|
QTest::newRow("amq.direct-delayed") << "amq.direct" << true;
|
||||||
|
QTest::newRow("amq.fanout") << "amq.fanout" << false;
|
||||||
|
QTest::newRow("amq.fanout-delayed") << "amq.fanout" << true;
|
||||||
|
QTest::newRow("amq.headers") << "amq.headers" << false;
|
||||||
|
QTest::newRow("amq.headers-delayed") << "amq.headers" << true;
|
||||||
|
QTest::newRow("amq.match") << "amq.match" << false;
|
||||||
|
QTest::newRow("amq.match-delayed") << "amq.match" << true;
|
||||||
|
QTest::newRow("amq.topic") << "amq.topic" << false;
|
||||||
|
QTest::newRow("amq.topic-delayed") << "amq.topic" << true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::standardExchanges()
|
||||||
|
{
|
||||||
|
QFETCH(QString, exchange);
|
||||||
|
QFETCH(bool, delayedDeclaration);
|
||||||
|
|
||||||
|
QString queueName = QString("test-%1").arg(exchange);
|
||||||
|
QString routingKey = QString("testRoutingKey-%1").arg(exchange);
|
||||||
|
|
||||||
|
QAmqpQueue *queue = client->createQueue(queueName);
|
||||||
|
if (!delayedDeclaration)
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(opened())));
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
queue->bind(exchange, routingKey);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(bound())));
|
||||||
|
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange(exchange);
|
||||||
|
defaultExchange->publish("test message", routingKey);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(messageReceived())));
|
||||||
|
QAmqpMessage message = queue->dequeue();
|
||||||
|
verifyStandardMessageHeaders(message, routingKey, exchange);
|
||||||
|
QCOMPARE(message.payload(), QByteArray("test message"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::invalidDeclaration_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("queueName");
|
||||||
|
QTest::addColumn<QAMQP::Error>("error");
|
||||||
|
|
||||||
|
QTest::newRow("amq.direct") << "amq.direct" << QAMQP::AccessRefusedError;
|
||||||
|
QTest::newRow("amq.fanout") << "amq.fanout" << QAMQP::AccessRefusedError;
|
||||||
|
QTest::newRow("amq.headers") << "amq.headers" << QAMQP::AccessRefusedError;
|
||||||
|
QTest::newRow("amq.match") << "amq.match" << QAMQP::AccessRefusedError;
|
||||||
|
QTest::newRow("amq.topic") << "amq.topic" << QAMQP::AccessRefusedError;
|
||||||
|
QTest::newRow("amq.reserved") << "amq.reserved" << QAMQP::AccessRefusedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::invalidDeclaration()
|
||||||
|
{
|
||||||
|
QFETCH(QString, queueName);
|
||||||
|
QFETCH(QAMQP::Error, error);
|
||||||
|
|
||||||
|
QAmqpQueue *queue = client->createQueue(queueName);
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(queue->error(), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::invalidBind()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-invalid-bind");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
queue->bind("non-existant-exchange", "routingKey");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(queue->error(), QAMQP::NotFoundError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::unnamed()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue();
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
QVERIFY(!queue->name().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::exclusiveAccess()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-exclusive-queue");
|
||||||
|
queue->declare(QAmqpQueue::Exclusive);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
QVERIFY(queue->options() & QAmqpQueue::Exclusive);
|
||||||
|
|
||||||
|
QAmqpClient secondClient;
|
||||||
|
secondClient.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&secondClient, SIGNAL(connected())));
|
||||||
|
QAmqpQueue *passiveQueue = secondClient.createQueue("test-exclusive-queue");
|
||||||
|
passiveQueue->declare(QAmqpQueue::Passive);
|
||||||
|
QVERIFY(waitForSignal(passiveQueue, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(passiveQueue->error(), QAMQP::ResourceLockedError);
|
||||||
|
|
||||||
|
secondClient.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&secondClient, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::exclusiveRemoval()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-exclusive-queue");
|
||||||
|
queue->declare(QAmqpQueue::Exclusive);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
QVERIFY(queue->options() & QAmqpQueue::Exclusive);
|
||||||
|
client.data()->disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(disconnected())));
|
||||||
|
|
||||||
|
// create a new client and try to access the queue that should
|
||||||
|
// no longer exist
|
||||||
|
QAmqpClient secondClient;
|
||||||
|
secondClient.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&secondClient, SIGNAL(connected())));
|
||||||
|
QAmqpQueue *passiveQueue = secondClient.createQueue("test-exclusive-queue");
|
||||||
|
passiveQueue->declare(QAmqpQueue::Passive);
|
||||||
|
QVERIFY(waitForSignal(passiveQueue, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(passiveQueue->error(), QAMQP::NotFoundError);
|
||||||
|
secondClient.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&secondClient, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::notFound()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-not-found");
|
||||||
|
queue->declare(QAmqpQueue::Passive);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(queue->error(), QAMQP::NotFoundError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::remove()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-remove");
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
queue->remove(QAmqpQueue::roIfEmpty|QAmqpQueue::roIfUnused);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::removeIfUnused()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-remove-if-unused");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
queue->remove(QAmqpQueue::roIfUnused);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(queue->error(), QAMQP::PreconditionFailedError);
|
||||||
|
QVERIFY(!queue->errorString().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::removeIfEmpty()
|
||||||
|
{
|
||||||
|
// declare the queue and send messages to it
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-remove-if-empty");
|
||||||
|
queue->declare(QAmqpQueue::Durable);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
QVERIFY(queue->options() & QAmqpQueue::Durable);
|
||||||
|
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
defaultExchange->publish("first message", "test-remove-if-empty");
|
||||||
|
|
||||||
|
// create a second client and try to delete the queue
|
||||||
|
{
|
||||||
|
QAmqpClient secondClient;
|
||||||
|
secondClient.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&secondClient, SIGNAL(connected())));
|
||||||
|
QAmqpQueue *testDeleteQueue = secondClient.createQueue("test-remove-if-empty");
|
||||||
|
testDeleteQueue->declare(QAmqpQueue::Passive);
|
||||||
|
QVERIFY(waitForSignal(testDeleteQueue, SIGNAL(declared())));
|
||||||
|
QVERIFY(testDeleteQueue->options() & QAmqpQueue::Passive);
|
||||||
|
|
||||||
|
testDeleteQueue->remove(QAmqpQueue::roIfEmpty);
|
||||||
|
QVERIFY(waitForSignal(testDeleteQueue, SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(testDeleteQueue->error(), QAMQP::PreconditionFailedError);
|
||||||
|
QVERIFY(!testDeleteQueue->errorString().isEmpty());
|
||||||
|
|
||||||
|
secondClient.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&secondClient, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up queue
|
||||||
|
queue->remove(QAmqpQueue::roForce);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::bindUnbind()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-bind-unbind");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
queue->bind("amq.topic", "routingKey");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(bound())));
|
||||||
|
queue->unbind("amq.topic", "routingKey");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(unbound())));
|
||||||
|
|
||||||
|
QAmqpExchange *amqTopic = client->createExchange("amq.topic");
|
||||||
|
amqTopic->declare(QAmqpExchange::Direct, QAmqpExchange::Passive);
|
||||||
|
QVERIFY(waitForSignal(amqTopic, SIGNAL(declared())));
|
||||||
|
queue->bind(amqTopic, "routingKey");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(bound())));
|
||||||
|
queue->unbind(amqTopic, "routingKey");
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(unbound())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::delayedBind()
|
||||||
|
{
|
||||||
|
client->disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(disconnected())));
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-delayed-bind");
|
||||||
|
queue->declare();
|
||||||
|
queue->bind("amq.topic", "routingKey");
|
||||||
|
|
||||||
|
client->connectToHost();
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(connected())));
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(bound())));
|
||||||
|
|
||||||
|
// clean up queue
|
||||||
|
queue->remove(QAmqpQueue::roForce);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::purge()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-purge");
|
||||||
|
queue->declare(QAmqpQueue::Durable);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
QVERIFY(queue->options() & QAmqpQueue::Durable);
|
||||||
|
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
defaultExchange->publish("first message", "test-purge");
|
||||||
|
defaultExchange->publish("second message", "test-purge");
|
||||||
|
defaultExchange->publish("third message", "test-purge");
|
||||||
|
|
||||||
|
// create second client to listen to messages and attempt purge
|
||||||
|
{
|
||||||
|
QAmqpClient secondClient;
|
||||||
|
secondClient.connectToHost();
|
||||||
|
QVERIFY(waitForSignal(&secondClient, SIGNAL(connected())));
|
||||||
|
QAmqpQueue *testPurgeQueue = secondClient.createQueue("test-purge");
|
||||||
|
testPurgeQueue->declare(QAmqpQueue::Passive);
|
||||||
|
QVERIFY(waitForSignal(testPurgeQueue, SIGNAL(declared())));
|
||||||
|
QVERIFY(testPurgeQueue->options() & QAmqpQueue::Passive);
|
||||||
|
|
||||||
|
QSignalSpy spy(testPurgeQueue, SIGNAL(purged(int)));
|
||||||
|
testPurgeQueue->purge();
|
||||||
|
QVERIFY(waitForSignal(testPurgeQueue, SIGNAL(purged(int))));
|
||||||
|
QCOMPARE(spy.count(), 1);
|
||||||
|
QCOMPARE(testPurgeQueue->size(), 0);
|
||||||
|
QList<QVariant> arguments = spy.takeFirst();
|
||||||
|
QCOMPARE(arguments.at(0).toInt(), 3);
|
||||||
|
|
||||||
|
secondClient.disconnectFromHost();
|
||||||
|
QVERIFY(waitForSignal(&secondClient, SIGNAL(disconnected())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up queue
|
||||||
|
queue->remove(QAmqpQueue::roForce);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::canOnlyStartConsumingOnce()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-single-consumer");
|
||||||
|
QSignalSpy spy(queue, SIGNAL(consuming(QString)));
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
QCOMPARE(queue->consume(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::ensureConsumeOnlySentOnce()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-single-consumer");
|
||||||
|
QSignalSpy spy(queue, SIGNAL(consuming(QString)));
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
|
||||||
|
// try to consume twice
|
||||||
|
QVERIFY(queue->consume());
|
||||||
|
QCOMPARE(queue->consume(), false);
|
||||||
|
QVERIFY(spy.wait());
|
||||||
|
QCOMPARE(spy.size(), 1);
|
||||||
|
|
||||||
|
// clean up queue
|
||||||
|
queue->remove(QAmqpQueue::roForce);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::cancel()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-cancel");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
QString consumerTag = queue->consumerTag();
|
||||||
|
QSignalSpy cancelSpy(queue, SIGNAL(cancelled(QString)));
|
||||||
|
QVERIFY(queue->cancel());
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(cancelled(QString))));
|
||||||
|
QVERIFY(!cancelSpy.isEmpty());
|
||||||
|
QList<QVariant> arguments = cancelSpy.takeFirst();
|
||||||
|
QCOMPARE(arguments.at(0).toString(), consumerTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::invalidCancelBecauseNotConsuming()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-invalid-cancel-because-not-consuming");
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
QCOMPARE(queue->cancel(), false);
|
||||||
|
|
||||||
|
// clean up queue
|
||||||
|
queue->remove(QAmqpQueue::roForce);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::invalidCancelBecauseInvalidConsumerTag()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-invalid-cancel-because-invalid-consumer-tag");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
queue->setConsumerTag(QString());
|
||||||
|
QCOMPARE(queue->cancel(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::getEmpty()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-get-empty");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
queue->get();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(empty())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::get()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-get");
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
|
||||||
|
const int messageCount = 200;
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
for (int i = 0; i < messageCount; ++i) {
|
||||||
|
QString expected = QString("message %1").arg(i);
|
||||||
|
defaultExchange->publish(expected, "test-get");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < messageCount; ++i) {
|
||||||
|
QString expected = QString("message %1").arg(i);
|
||||||
|
queue->get(false);
|
||||||
|
if (!waitForSignal(queue, SIGNAL(messageReceived()))) {
|
||||||
|
// NOTE: this is here instead of waiting for messages to be
|
||||||
|
// available with a sleep above. It makes the test a little
|
||||||
|
// longer if there's a miss, look into a proper fix in the future
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAmqpMessage message = queue->dequeue();
|
||||||
|
verifyStandardMessageHeaders(message, "test-get");
|
||||||
|
QCOMPARE(message.payload(), expected.toUtf8());
|
||||||
|
queue->ack(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
queue->get(false);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(empty())));
|
||||||
|
|
||||||
|
// clean up queue
|
||||||
|
queue->remove(QAmqpQueue::roForce);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::verifyContentEncodingIssue33()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-issue-33");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
QAmqpMessage::PropertyHash properties;
|
||||||
|
properties.insert(QAmqpMessage::ContentEncoding, "fakeContentEncoding");
|
||||||
|
defaultExchange->publish("some data", "test-issue-33", properties);
|
||||||
|
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(messageReceived())));
|
||||||
|
QAmqpMessage message = queue->dequeue();
|
||||||
|
verifyStandardMessageHeaders(message, "test-issue-33");
|
||||||
|
QVERIFY(message.hasProperty(QAmqpMessage::ContentEncoding));
|
||||||
|
QString contentType = message.property(QAmqpMessage::ContentEncoding).toString();
|
||||||
|
QCOMPARE(contentType, QLatin1String("fakeContentEncoding"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::defineQos()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-define-qos");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
queue->qos(10);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(qosDefined())));
|
||||||
|
QCOMPARE(queue->prefetchCount(), qint16(10));
|
||||||
|
QCOMPARE(queue->prefetchSize(), 0);
|
||||||
|
|
||||||
|
// clean up queue
|
||||||
|
queue->remove(QAmqpQueue::roForce);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(removed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::invalidQos()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-invalid-define-qos");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
queue->qos(10, 10);
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(client->error(), QAMQP::NotImplementedError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::qos()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-qos");
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
|
||||||
|
queue->qos(1);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(qosDefined())));
|
||||||
|
QCOMPARE(queue->prefetchCount(), qint16(1));
|
||||||
|
QCOMPARE(queue->prefetchSize(), 0);
|
||||||
|
QVERIFY(queue->consume());
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(consuming(QString))));
|
||||||
|
|
||||||
|
// load up the queue
|
||||||
|
const int messageCount = 10;
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
for (int i = 0; i < messageCount; ++i) {
|
||||||
|
QString message = QString("message %1").arg(i);
|
||||||
|
defaultExchange->publish(message, "test-qos");
|
||||||
|
}
|
||||||
|
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(messageReceived())));
|
||||||
|
int messageReceivedCount = 0;
|
||||||
|
while (!queue->isEmpty()) {
|
||||||
|
QString expected = QString("message %1").arg(messageReceivedCount);
|
||||||
|
QAmqpMessage message = queue->dequeue();
|
||||||
|
verifyStandardMessageHeaders(message, "test-qos");
|
||||||
|
QCOMPARE(message.payload(), expected.toUtf8());
|
||||||
|
queue->ack(message);
|
||||||
|
messageReceivedCount++;
|
||||||
|
|
||||||
|
if (messageReceivedCount < messageCount)
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(messageReceived())));
|
||||||
|
}
|
||||||
|
|
||||||
|
QCOMPARE(messageReceivedCount, messageCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::invalidRoutingKey()
|
||||||
|
{
|
||||||
|
QString routingKey = QString("%1").arg('1', 256, QLatin1Char('0'));
|
||||||
|
QAmqpQueue *queue = client->createQueue(routingKey);
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(client.data(), SIGNAL(error(QAMQP::Error))));
|
||||||
|
QCOMPARE(client->error(), QAMQP::FrameError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::tableFieldDataTypes()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-table-field-data-types");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
QAMQP::Decimal decimal;
|
||||||
|
decimal.scale = 2;
|
||||||
|
decimal.value = 12345;
|
||||||
|
QVariant decimalVariant = QVariant::fromValue<QAMQP::Decimal>(decimal);
|
||||||
|
|
||||||
|
QAmqpTable nestedTable;
|
||||||
|
nestedTable.insert("boolean", true);
|
||||||
|
nestedTable.insert("long-int", qint32(-65536));
|
||||||
|
|
||||||
|
QVariantList array;
|
||||||
|
array.append(true);
|
||||||
|
array.append(qint32(-65536));
|
||||||
|
|
||||||
|
QDateTime timestamp = QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
QAmqpTable headers;
|
||||||
|
headers.insert("boolean", true);
|
||||||
|
headers.insert("short-short-int", qint8(-15));
|
||||||
|
headers.insert("short-short-uint", quint8(15));
|
||||||
|
headers.insert("short-int", qint16(-256));
|
||||||
|
headers.insert("short-uint", QVariant::fromValue(quint16(256)));
|
||||||
|
headers.insert("long-int", qint32(-65536));
|
||||||
|
headers.insert("long-uint", quint32(65536));
|
||||||
|
headers.insert("long-long-int", qint64(-2147483648));
|
||||||
|
headers.insert("long-long-uint", quint64(2147483648));
|
||||||
|
headers.insert("float", 230.7);
|
||||||
|
headers.insert("double", double(FLT_MAX));
|
||||||
|
headers.insert("decimal-value", decimalVariant);
|
||||||
|
headers.insert("short-string", QLatin1String("test"));
|
||||||
|
headers.insert("long-string", QLatin1String("test"));
|
||||||
|
headers.insert("timestamp", timestamp);
|
||||||
|
headers.insert("nested-table", nestedTable);
|
||||||
|
headers.insert("array", array);
|
||||||
|
headers.insert("bytes", QByteArray("abcdefg1234567"));
|
||||||
|
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
defaultExchange->publish("dummy", "test-table-field-data-types", "text.plain", headers);
|
||||||
|
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(messageReceived())));
|
||||||
|
QAmqpMessage message = queue->dequeue();
|
||||||
|
|
||||||
|
QCOMPARE(message.header("boolean").toBool(), true);
|
||||||
|
QCOMPARE(qint8(message.header("short-short-int").toInt()), qint8(-15));
|
||||||
|
QCOMPARE(quint8(message.header("short-short-uint").toUInt()), quint8(15));
|
||||||
|
QCOMPARE(qint16(message.header("short-int").toInt()), qint16(-256));
|
||||||
|
QCOMPARE(quint16(message.header("short-uint").toUInt()), quint16(256));
|
||||||
|
QCOMPARE(qint32(message.header("long-int").toInt()), qint32(-65536));
|
||||||
|
QCOMPARE(quint32(message.header("long-uint").toUInt()), quint32(65536));
|
||||||
|
QCOMPARE(qint64(message.header("long-long-int").toLongLong()), qint64(-2147483648));
|
||||||
|
QCOMPARE(quint64(message.header("long-long-uint").toLongLong()), quint64(2147483648));
|
||||||
|
QCOMPARE(message.header("float").toFloat(), float(230.7));
|
||||||
|
QCOMPARE(message.header("double").toDouble(), double(FLT_MAX));
|
||||||
|
QCOMPARE(message.header("short-string").toString(), QLatin1String("test"));
|
||||||
|
QCOMPARE(message.header("long-string").toString(), QLatin1String("test"));
|
||||||
|
QCOMPARE(message.header("bytes").toByteArray(), QByteArray("abcdefg1234567"));
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
|
||||||
|
QCOMPARE(message.header("timestamp").toDateTime().toSecsSinceEpoch(),
|
||||||
|
timestamp.toSecsSinceEpoch());
|
||||||
|
#else
|
||||||
|
QCOMPARE(message.header("timestamp").toDateTime().toTime_t(), timestamp.toTime_t());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QVERIFY(message.hasHeader("nested-table"));
|
||||||
|
QAmqpTable compareTable(message.header("nested-table").toHash());
|
||||||
|
foreach (QString key, nestedTable.keys()) {
|
||||||
|
QVERIFY(compareTable.contains(key));
|
||||||
|
QCOMPARE(nestedTable.value(key), compareTable.value(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
QVERIFY(message.hasHeader("array"));
|
||||||
|
QVariantList compareArray = message.header("array").toList();
|
||||||
|
QCOMPARE(array, compareArray);
|
||||||
|
|
||||||
|
QAMQP::Decimal receivedDecimal = message.header("decimal-value").value<QAMQP::Decimal>();
|
||||||
|
QCOMPARE(receivedDecimal.scale, qint8(2));
|
||||||
|
QCOMPARE(receivedDecimal.value, quint32(12345));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::messageProperties()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-message-properties");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
QDateTime timestamp = QDateTime::currentDateTime();
|
||||||
|
QAmqpMessage::PropertyHash properties;
|
||||||
|
properties.insert(QAmqpMessage::ContentType, "some-content-type");
|
||||||
|
properties.insert(QAmqpMessage::ContentEncoding, "some-content-encoding");
|
||||||
|
properties.insert(QAmqpMessage::DeliveryMode, 2);
|
||||||
|
properties.insert(QAmqpMessage::Priority, 5);
|
||||||
|
properties.insert(QAmqpMessage::CorrelationId, 42);
|
||||||
|
properties.insert(QAmqpMessage::ReplyTo, "another-queue");
|
||||||
|
properties.insert(QAmqpMessage::MessageId, "some-message-id");
|
||||||
|
properties.insert(QAmqpMessage::Expiration, "60000");
|
||||||
|
properties.insert(QAmqpMessage::Timestamp, timestamp);
|
||||||
|
properties.insert(QAmqpMessage::Type, "some-message-type");
|
||||||
|
properties.insert(QAmqpMessage::UserId, "guest");
|
||||||
|
properties.insert(QAmqpMessage::AppId, "some-app-id");
|
||||||
|
properties.insert(QAmqpMessage::ClusterID, "some-cluster-id");
|
||||||
|
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
defaultExchange->publish("dummy", "test-message-properties", properties);
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(messageReceived())));
|
||||||
|
QAmqpMessage message = queue->dequeue();
|
||||||
|
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::ContentType).toString(), QLatin1String("some-content-type"));
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::ContentEncoding).toString(), QLatin1String("some-content-encoding"));
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::DeliveryMode).toInt(), 2);
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::Priority).toInt(), 5);
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::CorrelationId).toInt(), 42);
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::ReplyTo).toString(), QLatin1String("another-queue"));
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::MessageId).toString(), QLatin1String("some-message-id"));
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::Expiration).toString(), QLatin1String("60000"));
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::Type).toString(), QLatin1String("some-message-type"));
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::UserId).toString(), QLatin1String("guest"));
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::AppId).toString(), QLatin1String("some-app-id"));
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::ClusterID).toString(), QLatin1String("some-cluster-id"));
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::Timestamp).toDateTime().toSecsSinceEpoch(),
|
||||||
|
timestamp.toSecsSinceEpoch());
|
||||||
|
#else
|
||||||
|
QCOMPARE(message.property(QAmqpMessage::Timestamp).toDateTime().toTime_t(),
|
||||||
|
timestamp.toTime_t());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::emptyMessage()
|
||||||
|
{
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-issue-43");
|
||||||
|
declareQueueAndVerifyConsuming(queue);
|
||||||
|
|
||||||
|
QAmqpExchange *defaultExchange = client->createExchange();
|
||||||
|
defaultExchange->publish("", "test-issue-43");
|
||||||
|
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(messageReceived())));
|
||||||
|
QAmqpMessage message = queue->dequeue();
|
||||||
|
verifyStandardMessageHeaders(message, "test-issue-43");
|
||||||
|
QVERIFY(message.payload().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QAMQPQueue::cleanupOnDeletion()
|
||||||
|
{
|
||||||
|
// create, declare, and close the wrong way
|
||||||
|
QAmqpQueue *queue = client->createQueue("test-deletion");
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
queue->close();
|
||||||
|
queue->deleteLater();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(destroyed())));
|
||||||
|
|
||||||
|
// now create, declare, and close the right way
|
||||||
|
queue = client->createQueue("test-deletion");
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
queue->close();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(closed())));
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_QAMQPQueue)
|
||||||
|
#include "tst_qamqpqueue.moc"
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef QAMQPTESTCASE_H
|
||||||
|
#define QAMQPTESTCASE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTestEventLoop>
|
||||||
|
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class TestCase : public QObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestCase() {}
|
||||||
|
virtual ~TestCase() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool waitForSignal(QObject *obj, const char *signal, int delay = 5)
|
||||||
|
{
|
||||||
|
QObject::connect(obj, signal, &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||||
|
QPointer<QObject> safe = obj;
|
||||||
|
|
||||||
|
QTestEventLoop::instance().enterLoop(delay);
|
||||||
|
if (!safe.isNull())
|
||||||
|
QObject::disconnect(safe, signal, &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||||
|
return !QTestEventLoop::instance().timeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void declareQueueAndVerifyConsuming(QAmqpQueue *queue)
|
||||||
|
{
|
||||||
|
queue->declare();
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
|
||||||
|
QVERIFY(queue->consume());
|
||||||
|
QSignalSpy spy(queue, SIGNAL(consuming(QString)));
|
||||||
|
QVERIFY(waitForSignal(queue, SIGNAL(consuming(QString))));
|
||||||
|
QVERIFY(queue->isConsuming());
|
||||||
|
QVERIFY(!spy.isEmpty());
|
||||||
|
QList<QVariant> arguments = spy.takeFirst();
|
||||||
|
QCOMPARE(arguments.at(0).toString(), queue->consumerTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
void verifyStandardMessageHeaders(const QAmqpMessage &message, const QString &routingKey,
|
||||||
|
const QString &exchangeName = QLatin1String(""),
|
||||||
|
bool redelivered = false)
|
||||||
|
{
|
||||||
|
QCOMPARE(message.routingKey(), routingKey);
|
||||||
|
QCOMPARE(message.exchangeName(), exchangeName);
|
||||||
|
QCOMPARE(message.isRedelivered(), redelivered);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QAMQPTESTCASE_H
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef SIGNALSPY_H
|
||||||
|
#define SIGNALSPY_H
|
||||||
|
|
||||||
|
namespace QAMQP {
|
||||||
|
namespace Test {
|
||||||
|
|
||||||
|
bool waitForSignal(QObject *obj, const char *signal, int delay)
|
||||||
|
{
|
||||||
|
QObject::connect(obj, signal, &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||||
|
QPointer<QObject> safe = obj;
|
||||||
|
|
||||||
|
QTestEventLoop::instance().enterLoop(delay);
|
||||||
|
if (!safe.isNull())
|
||||||
|
QObject::disconnect(safe, signal, &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||||
|
return !QTestEventLoop::instance().timeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Test
|
||||||
|
} // namespace QAMQP
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5zCCAc+gAwIBAgIBAjANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl
|
||||||
|
c3RDQTAeFw0xNDA4MjcxOTI2MDVaFw0xNTA4MjcxOTI2MDVaMCoxFzAVBgNVBAMM
|
||||||
|
Dm1icm9hZHN0LWJ1aWxkMQ8wDQYDVQQKDAZjbGllbnQwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQDrzmzjgxNXxXX+fgfjB5Pt+YbO2uQR9PDADUyk+8Kw
|
||||||
|
/v1xjZBqKSHaBJLMv2nHlfGM8p92XQoepWKtG4z49UsT6MMppfUnZ/TO6LgUuJtw
|
||||||
|
FaVYdJmzK8SPvsQ331id9f4grgMTiff+i6hM2Bb9Jq83/jnglrBm8T4KHjPjJXQi
|
||||||
|
MN8d7ZkV2bo2vFQcO/KNTODntqINp5+OFPboyjDbMoMgUTqnXJBQsWwA9EVq2JYs
|
||||||
|
FYtA5xsqk0yG9DBgI5ClfxESQQo6lHKYeX2KIuHVO5awPpm+wZbIeR3l5QFqQrQZ
|
||||||
|
zfw7ANsA1RK4c85jb8K0vHxX1wV3kB+2kqpi4jxm/ucnAgMBAAGjLzAtMAkGA1Ud
|
||||||
|
EwQCMAAwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3
|
||||||
|
DQEBBQUAA4IBAQAxoTOMViXAKveeYx7I0dve/Te3TXe6XTlF0iFNIMp0FB3X0OeA
|
||||||
|
Bjknf6SUxY4qYV9DsFBGtXg8irkbothVNQKrhSedb6n+OQGy5z24oJ+vWW5jCyf3
|
||||||
|
TBoWRLnHY52j/4KElNpbEddacreYY6Ft5VYLZuyXy2G18xWjUnE5EG+QkizgAWzw
|
||||||
|
w9aTxS7qyGb7/FklJhH5OA8izi4JNbIrLEcUw4ECgYihtdLnZz/ANTp4kwz7qjaj
|
||||||
|
X7+8V3h7R59/HOHglCbjtkhBVuRyz5ljTfMbCava4Za2solujAo4tRxvmhioog0t
|
||||||
|
QplQjUP4QM5jfFlD/1HXY2SzYPG0FIiRj93L
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEA685s44MTV8V1/n4H4weT7fmGztrkEfTwwA1MpPvCsP79cY2Q
|
||||||
|
aikh2gSSzL9px5XxjPKfdl0KHqVirRuM+PVLE+jDKaX1J2f0zui4FLibcBWlWHSZ
|
||||||
|
syvEj77EN99YnfX+IK4DE4n3/ouoTNgW/SavN/454JawZvE+Ch4z4yV0IjDfHe2Z
|
||||||
|
Fdm6NrxUHDvyjUzg57aiDaefjhT26Mow2zKDIFE6p1yQULFsAPRFatiWLBWLQOcb
|
||||||
|
KpNMhvQwYCOQpX8REkEKOpRymHl9iiLh1TuWsD6ZvsGWyHkd5eUBakK0Gc38OwDb
|
||||||
|
ANUSuHPOY2/CtLx8V9cFd5AftpKqYuI8Zv7nJwIDAQABAoIBAQDj32PSqIQ0uZlB
|
||||||
|
CcHNXzFRM2VW2Ki1waI1taRveuu153Q8G7WHIaCY8vp56i/qs7ftoTkARQDWhLRK
|
||||||
|
3OjqXQDkiHaw9LNoFUm5+aKKQ6vSMNjMFkHBp3YYAx3TcH5Oh73BDufiJd4FmihV
|
||||||
|
uizdDlkdHwwHQRfPIyn01SMHStZjgkOqIOkKq1Me8uggiYpTh/2sbX931cwxJnSF
|
||||||
|
EvDOLTvLjJdj6aWjupUaMvMsZDHJdtTZxl/YPV/KO49EkaOz0Ijv4mD8a0FQ5QRa
|
||||||
|
ud1xITFlFXOeZNjH3n6/+4ypIJDkXddpfZUuetoZ3DPRZY5aalKW+SGy3zqLu8qh
|
||||||
|
0VGPRQ7BAoGBAP7xNXis1ErCybdI1wvo3XIcsq7YHsssD+2IFmNomdBF5e9QzAwU
|
||||||
|
Q63WD6qmcLCSzjSm4dYZNvFL9RLWUCIkC7nkpqt0bftDw//NTdyinJo/JNf4Lprx
|
||||||
|
uji5njju+FuU83Whu3QoBXFTv7Ql09bX/6EgCfx1cWrJEHC6L3oGE3SpAoGBAOzI
|
||||||
|
5BfC+5TTbqWbjoH8ycdpjbEvyhpRKT920spa2j0kNjduryJHtq1AemLsR9NOH67h
|
||||||
|
cO5YHD9ClRMXxI4ogVbzOGqVAy3LdYXCJIV8GO/WTjjDINoPNb2+VfaHCkboS+8y
|
||||||
|
d1HwwcFbK7p3dJFNF3ppDVXsTfZZzDAQfFqa6A9PAoGAPzmYtjW+bFAEcJT65/Q3
|
||||||
|
Pv6I/b2RXXeu94yBaOPfCXzcOk6CXBiGdE0bE4o1dkTiKMKeTVdxfcQFokdOFjl0
|
||||||
|
QwTGpMy6Hc8/g2fqAGa/ia1RONJO1JRQR5MY/yucojG9cxXKBFOMjf9kEowzDhwB
|
||||||
|
RHdKoraJix8UGbDC53MsTgkCgYBepze23/Td219ByFtBTyICGwnPKMFrn8ITYpaE
|
||||||
|
2aigBFe/9PkBhRVbUIkb/kQADhzQNcKFJKe2ChG5niiugzag4X1N7d9lcQ27uI4M
|
||||||
|
5jy5szt1qVr6kFX1UZ7fe7/59GZWaiAUm194wc9LLPFmHCEkh9YS4PGRZvgexphP
|
||||||
|
R9k4NQKBgQDXYokjEt6jl67724/J8gP09oTAxZCBSweZkTHErUg8NdsUJWqBGLP9
|
||||||
|
zFg1pOfAV9gy/qKm01SdG81lWcf8sDLa3QjB4WOW6x99DH2mQ7y69tStn8B3mAVB
|
||||||
|
o8Ddf50gjv54oSqFPrF1DAbBXWOEWfeLM44zyaBR9t28bNBJM4CEiQ==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,16 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIICbzCCAVcCAQAwKjEXMBUGA1UEAwwObWJyb2Fkc3QtYnVpbGQxDzANBgNVBAoM
|
||||||
|
BmNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOvObOODE1fF
|
||||||
|
df5+B+MHk+35hs7a5BH08MANTKT7wrD+/XGNkGopIdoEksy/aceV8Yzyn3ZdCh6l
|
||||||
|
Yq0bjPj1SxPowyml9Sdn9M7ouBS4m3AVpVh0mbMrxI++xDffWJ31/iCuAxOJ9/6L
|
||||||
|
qEzYFv0mrzf+OeCWsGbxPgoeM+MldCIw3x3tmRXZuja8VBw78o1M4Oe2og2nn44U
|
||||||
|
9ujKMNsygyBROqdckFCxbAD0RWrYliwVi0DnGyqTTIb0MGAjkKV/ERJBCjqUcph5
|
||||||
|
fYoi4dU7lrA+mb7Blsh5HeXlAWpCtBnN/DsA2wDVErhzzmNvwrS8fFfXBXeQH7aS
|
||||||
|
qmLiPGb+5ycCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQAyDAY1l3GDSkLXOKId
|
||||||
|
GM0sB0Ve7tT64IsqFacp29wV15fgJH1368VOMwxiXRQVSvGQGWog0JzuX0qH12ZZ
|
||||||
|
+6zQnGhumuKtoqfwlPBFNtvFRFxQ61Dzk6RZaO5fC7ZW+cLrfcEjTh9X3ts2POwP
|
||||||
|
/iuFdr+r+422YDOmHY3gNKBYKg8MtaDUNSLSiwNEQ/CPNs3FsyObHutiMPgIKwqt
|
||||||
|
vZ2hkvvMWcYPf2dtPTS3AfMPWVP+zR4eDfeiKYoxCyYZHsvQEyYqP5P5U1elqia1
|
||||||
|
gR9WUuC6Li+7wju6ksFrrLKGPNDXvfOm3Ecqfc5JPgU+U4bJLFRT1CFEOuYRnViK
|
||||||
|
V/jK
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5zCCAc+gAwIBAgIBATANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl
|
||||||
|
c3RDQTAeFw0xNDA4MjcxOTI0MjNaFw0xNTA4MjcxOTI0MjNaMCoxFzAVBgNVBAMM
|
||||||
|
Dm1icm9hZHN0LWJ1aWxkMQ8wDQYDVQQKDAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQDDV2SFR4wdqcGTJjXoxufcRcg1QPBrsglR2rvQL40i
|
||||||
|
oF9U9QAASwtn5c1+A5pkEdOb6xOrND/qiCW1jQgBKzi9qMnL9+61Z/Xykq5Op4qj
|
||||||
|
oqf1l6DV5nyHo9DOmqMKlBUGFR1PvwRcxmtl76+ekLxRP3Z38YbJHj1FT2H/9Dno
|
||||||
|
ThoImcxiSeMI1T7yBfv5SZ4TVheRIabkRcwT5FrU3P6TkVJq2PBjH4n6cNlLAMka
|
||||||
|
Ias4Jnxip4Xg/kk9JXlfce45EAMlgEpp/6zSYQqvpESo/2elElP39sFBPvv7HNIh
|
||||||
|
si7AKzIsFlEpsUFlcBkC1SD9jxV2xVbXZssCiX3ZM5F1AgMBAAGjLzAtMAkGA1Ud
|
||||||
|
EwQCMAAwCwYDVR0PBAQDAgUgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3
|
||||||
|
DQEBBQUAA4IBAQCHXuSqK4vEDNIqZxMQiFqB4zwkz5KG3uZrbhqfHaqxxjinwlNJ
|
||||||
|
Sky9lAx2QN/sRDuk8M+8HZxRMsASIPzELMjjj19CduadkLFV4cj+0nP2m6K1li8y
|
||||||
|
RyGQpEwQi5MG2o+iQt3Ygw07KQJYhOXaifjEFJ8Q1U00KO+e9H7iLF8GrhLzmOv3
|
||||||
|
usLPIvE8dnNu+EkrC57c48g9vkzR+BWl4TA1TcJBy9r219Z4jGrIysPWJUPwhKJj
|
||||||
|
tf9Uk9oHbMkuv5Qc+NhCumkB82phIt5WxeL1mKgwKVxiZJ+4DysfD7cgni8jhq86
|
||||||
|
KZgEOMel6CekBa7ToLzUdvjU0SjT2DBBK6YD
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEogIBAAKCAQEAw1dkhUeMHanBkyY16Mbn3EXINUDwa7IJUdq70C+NIqBfVPUA
|
||||||
|
AEsLZ+XNfgOaZBHTm+sTqzQ/6ogltY0IASs4vajJy/futWf18pKuTqeKo6Kn9Zeg
|
||||||
|
1eZ8h6PQzpqjCpQVBhUdT78EXMZrZe+vnpC8UT92d/GGyR49RU9h//Q56E4aCJnM
|
||||||
|
YknjCNU+8gX7+UmeE1YXkSGm5EXME+Ra1Nz+k5FSatjwYx+J+nDZSwDJGiGrOCZ8
|
||||||
|
YqeF4P5JPSV5X3HuORADJYBKaf+s0mEKr6REqP9npRJT9/bBQT77+xzSIbIuwCsy
|
||||||
|
LBZRKbFBZXAZAtUg/Y8VdsVW12bLAol92TORdQIDAQABAoIBAFKRwEWuBoYLWW2P
|
||||||
|
uz3Xxe4P+R65gmajbNkSsky/rNK0I1fP794v2nRiaMgZUct21ZGUfk3h2hqSzg29
|
||||||
|
vWJxGJzimdoDxP0dIpMUeWV54FpmyMRBAZUoxf63ue164+v2yCQ4DJnGzltA6+i8
|
||||||
|
tek6mL9nKfZtO2ILzC5d7bi5TTjp/SXUiKG3VAFSxgxoBC9PGlL7BNFbm9JXSket
|
||||||
|
LVIWNj781pqBMEHvj9aLVG0uKpkY5jRjShHQ1a1v3l/WSDBsoVaG8xzvZSE5wd7s
|
||||||
|
Fjzk53siyzapOBhTCJc8NFoA88SfYYQfxCVrhIxpDhH6rYBMi/j99DTlj+Goi6eo
|
||||||
|
7aqEkwECgYEA7YwtwqGtPbakc5Y6shmLyniDah8xaVfrK4duGBvuoZCMek+ee2DN
|
||||||
|
WaeUKcSBBL0wWGVxnTm5MHleeadc92vF8eI/T7LKDnqPGfg9I9nteI43wCuRqKbz
|
||||||
|
YDseZnngBWFM6QnhYJrL1mH66zTAolaW8e+4U0ZsNO/Wk3RP8FrZDFUCgYEA0oPp
|
||||||
|
DIW+6i43dC9AOKxPv6lWdYOHnnh050WftR3sYfQ5FEqLln0jbio1WvaukQtPeruz
|
||||||
|
WhhAYbrkSjy1286NMjkhO3FbofiUkTgpI9YSubIchbGcem9G58IfhA41mAGzrGer
|
||||||
|
t65ip6f2jwOZkRM+t5/65iqQuGoCIWlnBpO3kKECgYB6mX6ElSz0TO9TOJXSlZyw
|
||||||
|
QsKQYsj9tYKKVLtddg0TFadq+OyygKN7QiIV7HUqHPp2pOSeYMxTWFCKOPaiO91N
|
||||||
|
mZdTatMd5eM1ZAkqF6+YKM5dQB9NC91QLTLjcMNOA4nOPGs1kK7jVm5KNk+1eTsu
|
||||||
|
YqqfUBlIuP/l2oHnavvagQKBgFPIYiE0vbXwLOvVvmaP1bF/EMT2Uyxz3nsJD7YC
|
||||||
|
sciObYUw4ftD1K0MqW2JjhJ2AOzk9U2fJ0h+HEube/l+bF2XtS02QXTmPSLKyjzT
|
||||||
|
/2HejFF9TbzAuuSUMvzYtuXHj53HKOWSxvrY810Z3q2JjkWAq1edizmKH0zy6SkJ
|
||||||
|
813hAoGAIGMMqi8HsqsvgTQebYohwkRBG+G+JPVF6rPD/+WfglnIoo4sNWNBnh6Z
|
||||||
|
e/+TkLsZR0QVnbQtStabroxxCkBkjzoDgu2Ff2mKhcFMsuNwWm/2hWHD8VLMomWi
|
||||||
|
7BK4OjVcBxOoQelmBEuwIaCiuADZBFgGEbV5yBdv8yD+ewUP/Z0=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,16 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIICbzCCAVcCAQAwKjEXMBUGA1UEAwwObWJyb2Fkc3QtYnVpbGQxDzANBgNVBAoM
|
||||||
|
BnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMNXZIVHjB2p
|
||||||
|
wZMmNejG59xFyDVA8GuyCVHau9AvjSKgX1T1AABLC2flzX4DmmQR05vrE6s0P+qI
|
||||||
|
JbWNCAErOL2oycv37rVn9fKSrk6niqOip/WXoNXmfIej0M6aowqUFQYVHU+/BFzG
|
||||||
|
a2Xvr56QvFE/dnfxhskePUVPYf/0OehOGgiZzGJJ4wjVPvIF+/lJnhNWF5EhpuRF
|
||||||
|
zBPkWtTc/pORUmrY8GMfifpw2UsAyRohqzgmfGKnheD+ST0leV9x7jkQAyWASmn/
|
||||||
|
rNJhCq+kRKj/Z6USU/f2wUE++/sc0iGyLsArMiwWUSmxQWVwGQLVIP2PFXbFVtdm
|
||||||
|
ywKJfdkzkXUCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQB3B0Rve1QC6ygBlbFj
|
||||||
|
jYSwmyIID3OYuF4RvXGIzCQsh8+aJYWe4dMUcqRLlr13PmWQlle5v5F3x903Y5QN
|
||||||
|
IeQudApEelqJMI0QLOKALZWgX5YjF+z0AW/yhAu3QufEsERG4T5x2BJVKcrSIewl
|
||||||
|
ang2Hr4isGmlXS/VVPYa0K50lv7GgHsCookOSS52/MA7r5f48EmJeC2JO0sKKKkf
|
||||||
|
sMY9urBAIeGYNvXw/JuswlnfMpuUGEBoGnByc+3SecXiHGaUBn3xXSsbL+Ht8x9D
|
||||||
|
Sa0gyGqelTqxhcOU0YcvorxIPb6Jbms2J8GEmeq25vMlRTlDKJV01F7PMo/1reXU
|
||||||
|
KEn6
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,17 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICxjCCAa6gAwIBAgIJAOZK1btq1p0yMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV
|
||||||
|
BAMTCE15VGVzdENBMB4XDTE0MDgyNzE5MjI0MloXDTE1MDgyNzE5MjI0MlowEzER
|
||||||
|
MA8GA1UEAxMITXlUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||||
|
AQC/T4RhynLiTGkcgfq6TVwKVtvRy2jAOFOG7p9g4NofVTa0HvEUjNITwPUrYQDa
|
||||||
|
OLSK6BtStss1aTeSDfkY9wejl1PZ02lgz2CeznOvWp/74Rz8Mjc7BGz3WFOqcfJt
|
||||||
|
6DyTj/wgE/lJXTVGXsojQtDChYwEPNSWmAXLz1nLj2Ac1B4uy5kVsBCXkhWcu4E8
|
||||||
|
xEaf6mIA9KvF1MWBgHkNbZ2DruYsdlA4h95+40wn2qDkm9RgmE2MyFjV8YYjwv2s
|
||||||
|
5G48+pj7o7Vg/3/QZFuoGnU8GJ4gFFVOQQRqKOUrVFrJGduiVXxqPNjTt1rfopX4
|
||||||
|
pC8GZ69NotARfrwlBflM+Dm/AgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0P
|
||||||
|
BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAJxWhxMpYywwJOkB243RhD76Y9OJ0q
|
||||||
|
RAboh03wrHpx5jgRZj4PxJrMdoDop/7mP9e/2UfIWVkVzKGH3ZPSrDaJ7bRooqgu
|
||||||
|
nR1T8yWm+/zDoKoZGl+pdc25rr+PcWbzXOuPZTukSM01AqgGuwmiRB70HzqJpV3u
|
||||||
|
IPLvkvrqUIcjpdi7ULmfB1caNzTMizviTK7b3ORG+pZUVMRmOzSkJOD1PoNXg7GQ
|
||||||
|
p5FisMP7ULL0RLtOMrqwwyCslMJ+8g9pJuCqNJEeoBQsh/lmEZANWTSYTPRIbQAM
|
||||||
|
cnhx6GDsER2zvDoTLLM4SXqWEXHsV5D06Z41fKXIDSgsSZVnzb6ZWxvd
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5zCCAc+gAwIBAgIBATANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl
|
||||||
|
c3RDQTAeFw0xNDA4MjcxOTI0MjNaFw0xNTA4MjcxOTI0MjNaMCoxFzAVBgNVBAMM
|
||||||
|
Dm1icm9hZHN0LWJ1aWxkMQ8wDQYDVQQKDAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQDDV2SFR4wdqcGTJjXoxufcRcg1QPBrsglR2rvQL40i
|
||||||
|
oF9U9QAASwtn5c1+A5pkEdOb6xOrND/qiCW1jQgBKzi9qMnL9+61Z/Xykq5Op4qj
|
||||||
|
oqf1l6DV5nyHo9DOmqMKlBUGFR1PvwRcxmtl76+ekLxRP3Z38YbJHj1FT2H/9Dno
|
||||||
|
ThoImcxiSeMI1T7yBfv5SZ4TVheRIabkRcwT5FrU3P6TkVJq2PBjH4n6cNlLAMka
|
||||||
|
Ias4Jnxip4Xg/kk9JXlfce45EAMlgEpp/6zSYQqvpESo/2elElP39sFBPvv7HNIh
|
||||||
|
si7AKzIsFlEpsUFlcBkC1SD9jxV2xVbXZssCiX3ZM5F1AgMBAAGjLzAtMAkGA1Ud
|
||||||
|
EwQCMAAwCwYDVR0PBAQDAgUgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3
|
||||||
|
DQEBBQUAA4IBAQCHXuSqK4vEDNIqZxMQiFqB4zwkz5KG3uZrbhqfHaqxxjinwlNJ
|
||||||
|
Sky9lAx2QN/sRDuk8M+8HZxRMsASIPzELMjjj19CduadkLFV4cj+0nP2m6K1li8y
|
||||||
|
RyGQpEwQi5MG2o+iQt3Ygw07KQJYhOXaifjEFJ8Q1U00KO+e9H7iLF8GrhLzmOv3
|
||||||
|
usLPIvE8dnNu+EkrC57c48g9vkzR+BWl4TA1TcJBy9r219Z4jGrIysPWJUPwhKJj
|
||||||
|
tf9Uk9oHbMkuv5Qc+NhCumkB82phIt5WxeL1mKgwKVxiZJ+4DysfD7cgni8jhq86
|
||||||
|
KZgEOMel6CekBa7ToLzUdvjU0SjT2DBBK6YD
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5zCCAc+gAwIBAgIBAjANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl
|
||||||
|
c3RDQTAeFw0xNDA4MjcxOTI2MDVaFw0xNTA4MjcxOTI2MDVaMCoxFzAVBgNVBAMM
|
||||||
|
Dm1icm9hZHN0LWJ1aWxkMQ8wDQYDVQQKDAZjbGllbnQwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQDrzmzjgxNXxXX+fgfjB5Pt+YbO2uQR9PDADUyk+8Kw
|
||||||
|
/v1xjZBqKSHaBJLMv2nHlfGM8p92XQoepWKtG4z49UsT6MMppfUnZ/TO6LgUuJtw
|
||||||
|
FaVYdJmzK8SPvsQ331id9f4grgMTiff+i6hM2Bb9Jq83/jnglrBm8T4KHjPjJXQi
|
||||||
|
MN8d7ZkV2bo2vFQcO/KNTODntqINp5+OFPboyjDbMoMgUTqnXJBQsWwA9EVq2JYs
|
||||||
|
FYtA5xsqk0yG9DBgI5ClfxESQQo6lHKYeX2KIuHVO5awPpm+wZbIeR3l5QFqQrQZ
|
||||||
|
zfw7ANsA1RK4c85jb8K0vHxX1wV3kB+2kqpi4jxm/ucnAgMBAAGjLzAtMAkGA1Ud
|
||||||
|
EwQCMAAwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3
|
||||||
|
DQEBBQUAA4IBAQAxoTOMViXAKveeYx7I0dve/Te3TXe6XTlF0iFNIMp0FB3X0OeA
|
||||||
|
Bjknf6SUxY4qYV9DsFBGtXg8irkbothVNQKrhSedb6n+OQGy5z24oJ+vWW5jCyf3
|
||||||
|
TBoWRLnHY52j/4KElNpbEddacreYY6Ft5VYLZuyXy2G18xWjUnE5EG+QkizgAWzw
|
||||||
|
w9aTxS7qyGb7/FklJhH5OA8izi4JNbIrLEcUw4ECgYihtdLnZz/ANTp4kwz7qjaj
|
||||||
|
X7+8V3h7R59/HOHglCbjtkhBVuRyz5ljTfMbCava4Za2solujAo4tRxvmhioog0t
|
||||||
|
QplQjUP4QM5jfFlD/1HXY2SzYPG0FIiRj93L
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
V 150827192423Z 01 unknown /CN=mbroadst-build/O=server
|
||||||
|
V 150827192605Z 02 unknown /CN=mbroadst-build/O=client
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
unique_subject = yes
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
unique_subject = yes
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
V 150827192423Z 01 unknown /CN=mbroadst-build/O=server
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
[ ca ]
|
||||||
|
default_ca = testca
|
||||||
|
|
||||||
|
[ testca ]
|
||||||
|
dir = .
|
||||||
|
certificate = $dir/cacert.pem
|
||||||
|
database = $dir/index.txt
|
||||||
|
new_certs_dir = $dir/certs
|
||||||
|
private_key = $dir/private/cakey.pem
|
||||||
|
serial = $dir/serial
|
||||||
|
|
||||||
|
default_crl_days = 7
|
||||||
|
default_days = 365
|
||||||
|
default_md = sha1
|
||||||
|
|
||||||
|
policy = testca_policy
|
||||||
|
x509_extensions = certificate_extensions
|
||||||
|
|
||||||
|
[ testca_policy ]
|
||||||
|
commonName = supplied
|
||||||
|
stateOrProvinceName = optional
|
||||||
|
countryName = optional
|
||||||
|
emailAddress = optional
|
||||||
|
organizationName = optional
|
||||||
|
organizationalUnitName = optional
|
||||||
|
|
||||||
|
[ certificate_extensions ]
|
||||||
|
basicConstraints = CA:false
|
||||||
|
|
||||||
|
[ req ]
|
||||||
|
default_bits = 2048
|
||||||
|
default_keyfile = ./private/cakey.pem
|
||||||
|
default_md = sha1
|
||||||
|
prompt = yes
|
||||||
|
distinguished_name = root_ca_distinguished_name
|
||||||
|
x509_extensions = root_ca_extensions
|
||||||
|
|
||||||
|
[ root_ca_distinguished_name ]
|
||||||
|
commonName = hostname
|
||||||
|
|
||||||
|
[ root_ca_extensions ]
|
||||||
|
basicConstraints = CA:true
|
||||||
|
keyUsage = keyCertSign, cRLSign
|
||||||
|
|
||||||
|
[ client_ca_extensions ]
|
||||||
|
basicConstraints = CA:false
|
||||||
|
keyUsage = digitalSignature
|
||||||
|
extendedKeyUsage = 1.3.6.1.5.5.7.3.2
|
||||||
|
|
||||||
|
[ server_ca_extensions ]
|
||||||
|
basicConstraints = CA:false
|
||||||
|
keyUsage = keyEncipherment
|
||||||
|
extendedKeyUsage = 1.3.6.1.5.5.7.3.1
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC/T4RhynLiTGkc
|
||||||
|
gfq6TVwKVtvRy2jAOFOG7p9g4NofVTa0HvEUjNITwPUrYQDaOLSK6BtStss1aTeS
|
||||||
|
DfkY9wejl1PZ02lgz2CeznOvWp/74Rz8Mjc7BGz3WFOqcfJt6DyTj/wgE/lJXTVG
|
||||||
|
XsojQtDChYwEPNSWmAXLz1nLj2Ac1B4uy5kVsBCXkhWcu4E8xEaf6mIA9KvF1MWB
|
||||||
|
gHkNbZ2DruYsdlA4h95+40wn2qDkm9RgmE2MyFjV8YYjwv2s5G48+pj7o7Vg/3/Q
|
||||||
|
ZFuoGnU8GJ4gFFVOQQRqKOUrVFrJGduiVXxqPNjTt1rfopX4pC8GZ69NotARfrwl
|
||||||
|
BflM+Dm/AgMBAAECggEAUGYEhmxkN4JRMi/VxPG52oaCPvqy/QUu5SfnRvl38W8I
|
||||||
|
XE4clrxPlQmkfyR3DT6DcVT2Fp7Ha5zaQ8EnjDxUs4VnMcXNJWhBfLvaljkJvvru
|
||||||
|
CXa5C05i1NgD4T+d2F6fBoyeMoTyYMiRGQ/A92ye+wDQxP8jgF5HIU30uL16cOJh
|
||||||
|
/Znu6JJBjYgE9g7ce8REEpi2Fru2Ixj147ge1ICW801i0Xy5susCJvH3I817GKoq
|
||||||
|
NonAn5P+5zTv9mECDnNkhRViATigrQ8DYikNewPknrmfb0IMAvF8dTnCWI4KuorD
|
||||||
|
c4TD7w/zzrpncWNCnsDgWfgq9u9Anp6bvhED0VLiMQKBgQDgewv8skGmm4xAUKdR
|
||||||
|
BsDYIUgip57qj4EmPkjypn8lzVjDUnbBhr/NAUQK5pKnrzFE2H9/H7M1zlNwk1FS
|
||||||
|
m6GYjx4DmnGAvQ0LCBs4gxlT878n7TYTxkTge69tYQ0lmmAGGajmv2G4TtVADMlG
|
||||||
|
rojrQIYoSggVkUI+AyGhHhm8xQKBgQDaLCyBUSWPOMc33AozWMm2OYJ4nFWd1A0C
|
||||||
|
SLgpR6/+D8mT3o6YRYIMmh6AUFCENAKitbKujQOaKRll5aaNWO4JohhgcuzGEj5C
|
||||||
|
4F++7SXd6E/1+gtExnOHkPJ9z3FIeSoGCDK3DmfE8H9fcMM5mFZG7OVna3arINv4
|
||||||
|
nT8s3aAMswKBgQDDM5yn3+Zg17AtGTV1qxa0mrRclkAFnkZjGBRdFNVJ7Pf72VC1
|
||||||
|
VtSgkzI0/G2Y7So9wLmVtN4ksscyBJjZ6cWqoQErhvieR0b5SdJJ4Q58R1/5ezfk
|
||||||
|
GCw6vLM+vP8urMBFbbjG9rMmDz83FCdOlGUxlQlULZQ8FPVycUykC0W8NQKBgEjA
|
||||||
|
fj7JLnsp9dS8vXIN44Wue8F4cFxm/8eJNFAfpaJU5WU3y9kfJJTLN+yV26OaLF7R
|
||||||
|
tDncsBzSI7QE9psf0pDHytUuvaH3J2fppkPmlMAA3dkqfmN6wb+tKA+oAyCltsu4
|
||||||
|
JCFC3nufrvnGgnNMR0jzajQoc7PxCylGVnDBnsNdAoGAV62Nc+T6HdZa9yDSPU5p
|
||||||
|
bNT40q0iZHmTUa6QQl+ZsRZP8u4w+RnfcUOK+QKJr43DraJgjWtwbO2VnCIMTA21
|
||||||
|
EsSGuOCEMuYLMkswOrAJfM80FalsF3I74s5TbGYaczXXOe54XZJ8tyWSP0IiwreO
|
||||||
|
+eejI2bW3rU9TfBDdpR5Ks8=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
03
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
02
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "[
|
||||||
|
{rabbit, [
|
||||||
|
{ssl_listeners, [5671]},
|
||||||
|
{ssl_options, [{cacertfile,'${TRAVIS_BUILD_DIR}/tests/files/certs/testca/cacert.pem'},
|
||||||
|
{certfile,'${TRAVIS_BUILD_DIR}/tests/files/certs/server/cert.pem'},
|
||||||
|
{keyfile, '${TRAVIS_BUILD_DIR}/tests/files/certs/server/key.pem'},
|
||||||
|
{verify,verify_peer},
|
||||||
|
{fail_if_no_peer_cert,false}]}
|
||||||
|
]}
|
||||||
|
]." >> rabbitmq.config
|
||||||
|
|
||||||
|
sudo CONFIG_FILE=$PWD RABBITMQ_NODENAME=test-rabbitmq rabbitmq-server -detached
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ "$QT_SELECT" = "qt4" ]; then
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install libqt4-dev
|
||||||
|
else
|
||||||
|
sudo add-apt-repository -y ppa:canonical-qt5-edgers/ubuntu1204-qt5
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install qtbase5-dev
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
lcov --capture --directory . --output-file coverage-gcov.info --no-external
|
||||||
|
lcov --output-file coverage-gcov.info --remove coverage-gcov.info 'moc_*.cpp' '*.moc*' '.*rcc*' '*3rdparty*'
|
||||||
|
genhtml coverage-gcov.info --output-directory doc/coverage
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH} $${PWD}/common
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
|
||||||
|
unix:!macx:QMAKE_RPATHDIR += $${OUT_PWD}/$${DEPTH}/src
|
||||||
|
macx {
|
||||||
|
QMAKE_RPATHDIR += @loader_path/$${DEPTH}/src
|
||||||
|
QMAKE_LFLAGS += -Wl,-rpath,@loader_path/$${DEPTH}/src
|
||||||
|
}
|
||||||
|
|
||||||
|
QT = core network testlib
|
||||||
|
QT -= gui
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
CONFIG += testcase no_testcase_installs
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$${PWD}/common/signalspy.h \
|
||||||
|
$${PWD}/common/qamqptestcase.h
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS = \
|
||||||
|
auto
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS = \
|
||||||
|
send \
|
||||||
|
receive
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class Receiver : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Receiver(QObject *parent = 0) : QObject(parent) {
|
||||||
|
m_client.setAutoReconnect(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void start() {
|
||||||
|
connect(&m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
|
||||||
|
m_client.connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void clientConnected() {
|
||||||
|
QAmqpQueue *queue = m_client.createQueue("hello");
|
||||||
|
disconnect(queue, 0, 0, 0); // in case this is a reconnect
|
||||||
|
connect(queue, SIGNAL(declared()), this, SLOT(queueDeclared()));
|
||||||
|
queue->declare();
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueDeclared() {
|
||||||
|
QAmqpQueue *queue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!queue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
connect(queue, SIGNAL(messageReceived()), this, SLOT(messageReceived()));
|
||||||
|
|
||||||
|
// queue->consume(QAmqpQueue::coNoAck);
|
||||||
|
// queue->consume(QAmqpQueue::coNoLocal);
|
||||||
|
|
||||||
|
qint32 sizeQueue = queue->messageCount();
|
||||||
|
while (sizeQueue--) {
|
||||||
|
queue->get(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << " [*] Waiting for messages. To exit press CTRL+C";
|
||||||
|
|
||||||
|
queue->ack(3, false); // Acknowledgement the 3rd message.
|
||||||
|
|
||||||
|
queue->reopen();
|
||||||
|
|
||||||
|
// m_client.disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void messageReceived() {
|
||||||
|
QAmqpQueue *queue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!queue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QAmqpMessage message = queue->dequeue();
|
||||||
|
qDebug() << " [x] Received in" << message.payload();
|
||||||
|
|
||||||
|
int input=0;
|
||||||
|
// std::scanf("%d", &input);
|
||||||
|
qDebug() << " [x] Received out, " << message.deliveryTag() << " | " << message.payload() << " , input = " << input;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAmqpClient m_client;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
qDebug() << " Recieve starts ... ";
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
Receiver receiver;
|
||||||
|
receiver.start();
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH}
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class Sender : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Sender(QObject *parent = 0) : QObject(parent) {}
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void start() {
|
||||||
|
connect(&m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
|
||||||
|
connect(&m_client, SIGNAL(disconnected()), qApp, SLOT(quit()));
|
||||||
|
m_client.connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void clientConnected() {
|
||||||
|
QAmqpQueue *queue = m_client.createQueue("hello");
|
||||||
|
connect(queue, SIGNAL(declared()), this, SLOT(queueDeclared()));
|
||||||
|
queue->declare();
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueDeclared() {
|
||||||
|
QAmqpQueue *queue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!queue)
|
||||||
|
return;
|
||||||
|
QAmqpExchange *defaultExchange = m_client.createExchange();
|
||||||
|
defaultExchange->publish("Hello World! A", "hello");
|
||||||
|
qDebug() << " [x] Sent 'Hello World! A'";
|
||||||
|
defaultExchange->publish("Hello World! B", "hello");
|
||||||
|
qDebug() << " [x] Sent 'Hello World! B'";
|
||||||
|
defaultExchange->publish("Hello World! C", "hello");
|
||||||
|
qDebug() << " [x] Sent 'Hello World! C'";
|
||||||
|
defaultExchange->publish("Hello World! D", "hello");
|
||||||
|
qDebug() << " [x] Sent 'Hello World! D'";
|
||||||
|
defaultExchange->publish("Hello World! E", "hello");
|
||||||
|
qDebug() << " [x] Sent 'Hello World! E'";
|
||||||
|
m_client.disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAmqpClient m_client;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
Sender sender;
|
||||||
|
sender.start();
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH}
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH}
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class LogEmitter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
LogEmitter(QObject *parent = 0) : QObject(parent) {}
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void start() {
|
||||||
|
connect(&m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
|
||||||
|
connect(&m_client, SIGNAL(disconnected()), qApp, SLOT(quit()));
|
||||||
|
m_client.connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void clientConnected() {
|
||||||
|
QAmqpExchange *exchange = m_client.createExchange("logs");
|
||||||
|
connect(exchange, SIGNAL(declared()), this, SLOT(exchangeDeclared()));
|
||||||
|
exchange->declare(QAmqpExchange::FanOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exchangeDeclared() {
|
||||||
|
QAmqpExchange *exchange = qobject_cast<QAmqpExchange*>(sender());
|
||||||
|
if (!exchange)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString message;
|
||||||
|
if (qApp->arguments().size() < 2)
|
||||||
|
message = "info: Hello World!";
|
||||||
|
else
|
||||||
|
message = qApp->arguments().at(1);
|
||||||
|
exchange->publish(message, "");
|
||||||
|
qDebug() << " [x] Sent " << message;
|
||||||
|
m_client.disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAmqpClient m_client;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
LogEmitter logEmitter;
|
||||||
|
logEmitter.start();
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS = \
|
||||||
|
receive_logs \
|
||||||
|
emit_log
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class LogReceiver : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
LogReceiver(QObject *parent = 0) : QObject(parent) {}
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void start() {
|
||||||
|
connect(&m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
|
||||||
|
m_client.connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void clientConnected() {
|
||||||
|
QAmqpExchange *exchange = m_client.createExchange("logs");
|
||||||
|
connect(exchange, SIGNAL(declared()), this, SLOT(exchangeDeclared()));
|
||||||
|
exchange->declare(QAmqpExchange::FanOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exchangeDeclared() {
|
||||||
|
QAmqpQueue *temporaryQueue = m_client.createQueue();
|
||||||
|
connect(temporaryQueue, SIGNAL(declared()), this, SLOT(queueDeclared()));
|
||||||
|
connect(temporaryQueue, SIGNAL(bound()), this, SLOT(queueBound()));
|
||||||
|
connect(temporaryQueue, SIGNAL(messageReceived()), this, SLOT(messageReceived()));
|
||||||
|
temporaryQueue->declare(QAmqpQueue::Exclusive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueDeclared() {
|
||||||
|
QAmqpQueue *temporaryQueue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!temporaryQueue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
temporaryQueue->bind("logs", temporaryQueue->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueBound() {
|
||||||
|
QAmqpQueue *temporaryQueue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!temporaryQueue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qDebug() << " [*] Waiting for logs. To exit press CTRL+C";
|
||||||
|
temporaryQueue->consume(QAmqpQueue::coNoAck);
|
||||||
|
}
|
||||||
|
|
||||||
|
void messageReceived() {
|
||||||
|
QAmqpQueue *temporaryQueue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!temporaryQueue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QAmqpMessage message = temporaryQueue->dequeue();
|
||||||
|
qDebug() << " [x] " << message.payload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAmqpClient m_client;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
LogReceiver logReceiver;
|
||||||
|
logReceiver.start();
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH}
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH}
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class DirectLogEmitter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
DirectLogEmitter(QObject *parent = 0) : QObject(parent) {}
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void start() {
|
||||||
|
connect(&m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
|
||||||
|
connect(&m_client, SIGNAL(disconnected()), qApp, SLOT(quit()));
|
||||||
|
m_client.connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void clientConnected() {
|
||||||
|
QAmqpExchange *direct_logs = m_client.createExchange("direct_logs");
|
||||||
|
connect(direct_logs, SIGNAL(declared()), this, SLOT(exchangeDeclared()));
|
||||||
|
direct_logs->declare(QAmqpExchange::Direct);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exchangeDeclared() {
|
||||||
|
QAmqpExchange *direct_logs = qobject_cast<QAmqpExchange*>(sender());
|
||||||
|
if (!direct_logs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QStringList args = qApp->arguments();
|
||||||
|
args.takeFirst(); // remove executable name
|
||||||
|
|
||||||
|
QString severity = (args.isEmpty() ? "info" : args.first());
|
||||||
|
QString message;
|
||||||
|
if (args.size() > 1) {
|
||||||
|
args.takeFirst();
|
||||||
|
message = args.join(" ");
|
||||||
|
} else {
|
||||||
|
message = "Hello World!";
|
||||||
|
}
|
||||||
|
|
||||||
|
direct_logs->publish(message, severity);
|
||||||
|
qDebug(" [x] Sent %s:%s", severity.toLatin1().constData(), message.toLatin1().constData());
|
||||||
|
m_client.disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAmqpClient m_client;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
DirectLogEmitter logEmitter;
|
||||||
|
logEmitter.start();
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class DirectLogReceiver : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
DirectLogReceiver(QObject *parent = 0) : QObject(parent) {}
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void start(const QStringList &severities) {
|
||||||
|
m_severities = severities;
|
||||||
|
connect(&m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
|
||||||
|
m_client.connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void clientConnected() {
|
||||||
|
QAmqpExchange *exchange = m_client.createExchange("direct_logs");
|
||||||
|
connect(exchange, SIGNAL(declared()), this, SLOT(exchangeDeclared()));
|
||||||
|
exchange->declare(QAmqpExchange::Direct);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exchangeDeclared() {
|
||||||
|
QAmqpQueue *temporaryQueue = m_client.createQueue();
|
||||||
|
connect(temporaryQueue, SIGNAL(declared()), this, SLOT(queueDeclared()));
|
||||||
|
connect(temporaryQueue, SIGNAL(messageReceived()), this, SLOT(messageReceived()));
|
||||||
|
temporaryQueue->declare(QAmqpQueue::Exclusive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueDeclared() {
|
||||||
|
QAmqpQueue *temporaryQueue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!temporaryQueue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// start consuming
|
||||||
|
temporaryQueue->consume(QAmqpQueue::coNoAck);
|
||||||
|
|
||||||
|
foreach (QString severity, m_severities)
|
||||||
|
temporaryQueue->bind("direct_logs", severity);
|
||||||
|
qDebug() << " [*] Waiting for logs. To exit press CTRL+C";
|
||||||
|
}
|
||||||
|
|
||||||
|
void messageReceived() {
|
||||||
|
QAmqpQueue *temporaryQueue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!temporaryQueue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QAmqpMessage message = temporaryQueue->dequeue();
|
||||||
|
qDebug() << " [x] " << message.routingKey() << ":" << message.payload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAmqpClient m_client;
|
||||||
|
QStringList m_severities;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
QStringList severities = app.arguments().mid(1);
|
||||||
|
if (severities.isEmpty()) {
|
||||||
|
qDebug("usage: %s [info] [warning] [error]", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectLogReceiver logReceiver;
|
||||||
|
logReceiver.start(severities);
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH}
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS = \
|
||||||
|
emit_log_direct \
|
||||||
|
receive_logs_direct
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS = \
|
||||||
|
rpc_server \
|
||||||
|
rpc_client
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
#include "fibonaccirpcclient.h"
|
||||||
|
|
||||||
|
FibonacciRpcClient::FibonacciRpcClient(QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_client(0),
|
||||||
|
m_responseQueue(0),
|
||||||
|
m_defaultExchange(0)
|
||||||
|
{
|
||||||
|
m_client = new QAmqpClient(this);
|
||||||
|
connect(m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
|
||||||
|
}
|
||||||
|
|
||||||
|
FibonacciRpcClient::~FibonacciRpcClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FibonacciRpcClient::connectToServer()
|
||||||
|
{
|
||||||
|
QEventLoop loop;
|
||||||
|
connect(this, SIGNAL(connected()), &loop, SLOT(quit()));
|
||||||
|
m_client->connectToHost();
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
return m_client->isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FibonacciRpcClient::call(int number)
|
||||||
|
{
|
||||||
|
qDebug() << " [x] Requesting fib(" << number << ")";
|
||||||
|
m_correlationId = QUuid::createUuid().toString();
|
||||||
|
QAmqpMessage::PropertyHash properties;
|
||||||
|
properties.insert(QAmqpMessage::ReplyTo, m_responseQueue->name());
|
||||||
|
properties.insert(QAmqpMessage::CorrelationId, m_correlationId);
|
||||||
|
|
||||||
|
m_defaultExchange->publish(QByteArray::number(number), "rpc_queue", properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FibonacciRpcClient::clientConnected()
|
||||||
|
{
|
||||||
|
m_responseQueue = m_client->createQueue();
|
||||||
|
connect(m_responseQueue, SIGNAL(declared()), this, SLOT(queueDeclared()));
|
||||||
|
connect(m_responseQueue, SIGNAL(messageReceived()), this, SLOT(responseReceived()));
|
||||||
|
m_responseQueue->declare(QAmqpQueue::Exclusive | QAmqpQueue::AutoDelete);
|
||||||
|
m_defaultExchange = m_client->createExchange();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FibonacciRpcClient::queueDeclared()
|
||||||
|
{
|
||||||
|
m_responseQueue->consume();
|
||||||
|
Q_EMIT connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FibonacciRpcClient::responseReceived()
|
||||||
|
{
|
||||||
|
QAmqpMessage message = m_responseQueue->dequeue();
|
||||||
|
if (message.property(QAmqpMessage::CorrelationId).toString() != m_correlationId) {
|
||||||
|
// requeue message, it wasn't meant for us
|
||||||
|
m_responseQueue->reject(message, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << " [.] Got " << message.payload();
|
||||||
|
qApp->quit();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef FIBONACCIRPCCLIENT_H
|
||||||
|
#define FIBONACCIRPCCLIENT_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QAmqpQueue;
|
||||||
|
class QAmqpExchange;
|
||||||
|
class QAmqpClient;
|
||||||
|
class FibonacciRpcClient : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit FibonacciRpcClient(QObject *parent = 0);
|
||||||
|
~FibonacciRpcClient();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void connected();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
bool connectToServer();
|
||||||
|
void call(int number);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void clientConnected();
|
||||||
|
void queueDeclared();
|
||||||
|
void responseReceived();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAmqpClient *m_client;
|
||||||
|
QAmqpQueue *m_responseQueue;
|
||||||
|
QAmqpExchange *m_defaultExchange;
|
||||||
|
QString m_correlationId;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FIBONACCIRPCCLIENT_H
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include "fibonaccirpcclient.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
FibonacciRpcClient client;
|
||||||
|
if (!client.connectToServer())
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
client.call(30);
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH}
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
fibonaccirpcclient.h
|
||||||
|
SOURCES += \
|
||||||
|
fibonaccirpcclient.cpp \
|
||||||
|
main.cpp
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include "server.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
Server server;
|
||||||
|
server.listen();
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH}
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
server.h
|
||||||
|
SOURCES += \
|
||||||
|
server.cpp \
|
||||||
|
main.cpp
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
|
||||||
|
#include "server.h"
|
||||||
|
|
||||||
|
Server::Server(QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_client(0),
|
||||||
|
m_rpcQueue(0),
|
||||||
|
m_defaultExchange(0)
|
||||||
|
{
|
||||||
|
m_client = new QAmqpClient(this);
|
||||||
|
connect(m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Server::~Server()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::listen()
|
||||||
|
{
|
||||||
|
m_client->connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Server::fib(int n)
|
||||||
|
{
|
||||||
|
if (n == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (n == 1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return fib(n - 1) + fib(n - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::clientConnected()
|
||||||
|
{
|
||||||
|
m_rpcQueue = m_client->createQueue("rpc_queue");
|
||||||
|
connect(m_rpcQueue, SIGNAL(declared()), this, SLOT(queueDeclared()));
|
||||||
|
connect(m_rpcQueue, SIGNAL(qosDefined()), this, SLOT(qosDefined()));
|
||||||
|
connect(m_rpcQueue, SIGNAL(messageReceived()), this, SLOT(processRpcMessage()));
|
||||||
|
m_rpcQueue->declare();
|
||||||
|
|
||||||
|
m_defaultExchange = m_client->createExchange();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::queueDeclared()
|
||||||
|
{
|
||||||
|
m_rpcQueue->qos(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::qosDefined()
|
||||||
|
{
|
||||||
|
m_rpcQueue->consume();
|
||||||
|
qDebug() << " [x] Awaiting RPC requests";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::processRpcMessage()
|
||||||
|
{
|
||||||
|
QAmqpMessage rpcMessage = m_rpcQueue->dequeue();
|
||||||
|
int n = rpcMessage.payload().toInt();
|
||||||
|
|
||||||
|
int response = fib(n);
|
||||||
|
m_rpcQueue->ack(rpcMessage);
|
||||||
|
|
||||||
|
QString replyTo = rpcMessage.property(QAmqpMessage::ReplyTo).toString();
|
||||||
|
QAmqpMessage::PropertyHash properties;
|
||||||
|
properties.insert(QAmqpMessage::CorrelationId, rpcMessage.property(QAmqpMessage::CorrelationId));
|
||||||
|
m_defaultExchange->publish(QByteArray::number(response), replyTo, properties);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef SERVER_H
|
||||||
|
#define SERVER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QAmqpQueue;
|
||||||
|
class QAmqpExchange;
|
||||||
|
class QAmqpClient;
|
||||||
|
class Server : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Server(QObject *parent = 0);
|
||||||
|
~Server();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void listen();
|
||||||
|
int fib(int n);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void clientConnected();
|
||||||
|
void queueDeclared();
|
||||||
|
void qosDefined();
|
||||||
|
void processRpcMessage();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAmqpClient *m_client;
|
||||||
|
QAmqpQueue *m_rpcQueue;
|
||||||
|
QAmqpExchange *m_defaultExchange;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SERVER_H
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH}
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class TopicLogEmitter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
TopicLogEmitter(QObject *parent = 0) : QObject(parent) {}
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void start() {
|
||||||
|
connect(&m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
|
||||||
|
connect(&m_client, SIGNAL(disconnected()), qApp, SLOT(quit()));
|
||||||
|
m_client.connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void clientConnected() {
|
||||||
|
QAmqpExchange *topic_logs = m_client.createExchange("topic_logs");
|
||||||
|
connect(topic_logs, SIGNAL(declared()), this, SLOT(exchangeDeclared()));
|
||||||
|
topic_logs->declare(QAmqpExchange::Topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exchangeDeclared() {
|
||||||
|
QAmqpExchange *topic_logs = qobject_cast<QAmqpExchange*>(sender());
|
||||||
|
if (!topic_logs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QStringList args = qApp->arguments();
|
||||||
|
args.takeFirst(); // remove executable name
|
||||||
|
|
||||||
|
QString routingKey = (args.isEmpty() ? "anonymous.info" : args.first());
|
||||||
|
QString message;
|
||||||
|
if (args.size() > 1) {
|
||||||
|
args.takeFirst();
|
||||||
|
message = args.join(" ");
|
||||||
|
} else {
|
||||||
|
message = "Hello World!";
|
||||||
|
}
|
||||||
|
|
||||||
|
topic_logs->publish(message, routingKey);
|
||||||
|
qDebug(" [x] Sent %s:%s", routingKey.toLatin1().constData(), message.toLatin1().constData());
|
||||||
|
m_client.disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAmqpClient m_client;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
TopicLogEmitter logEmitter;
|
||||||
|
logEmitter.start();
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "qamqpclient.h"
|
||||||
|
#include "qamqpexchange.h"
|
||||||
|
#include "qamqpqueue.h"
|
||||||
|
|
||||||
|
class TopicLogReceiver : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
TopicLogReceiver(QObject *parent = 0) : QObject(parent) {}
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void start(const QStringList &bindingKeys) {
|
||||||
|
m_bindingKeys = bindingKeys;
|
||||||
|
connect(&m_client, SIGNAL(connected()), this, SLOT(clientConnected()));
|
||||||
|
m_client.connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void clientConnected() {
|
||||||
|
QAmqpExchange *topic_logs = m_client.createExchange("topic_logs");
|
||||||
|
connect(topic_logs, SIGNAL(declared()), this, SLOT(exchangeDeclared()));
|
||||||
|
topic_logs->declare(QAmqpExchange::Topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exchangeDeclared() {
|
||||||
|
QAmqpQueue *temporaryQueue = m_client.createQueue();
|
||||||
|
connect(temporaryQueue, SIGNAL(declared()), this, SLOT(queueDeclared()));
|
||||||
|
connect(temporaryQueue, SIGNAL(bound()), this, SLOT(queueBound()));
|
||||||
|
connect(temporaryQueue, SIGNAL(messageReceived()), this, SLOT(messageReceived()));
|
||||||
|
temporaryQueue->declare(QAmqpQueue::Exclusive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueDeclared() {
|
||||||
|
QAmqpQueue *temporaryQueue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!temporaryQueue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (QString bindingKey, m_bindingKeys)
|
||||||
|
temporaryQueue->bind("topic_logs", bindingKey);
|
||||||
|
qDebug() << " [*] Waiting for logs. To exit press CTRL+C";
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueBound() {
|
||||||
|
QAmqpQueue *temporaryQueue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!temporaryQueue)
|
||||||
|
return;
|
||||||
|
temporaryQueue->consume(QAmqpQueue::coNoAck);
|
||||||
|
}
|
||||||
|
|
||||||
|
void messageReceived() {
|
||||||
|
QAmqpQueue *temporaryQueue = qobject_cast<QAmqpQueue*>(sender());
|
||||||
|
if (!temporaryQueue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QAmqpMessage message = temporaryQueue->dequeue();
|
||||||
|
qDebug() << " [x] " << message.routingKey() << ":" << message.payload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAmqpClient m_client;
|
||||||
|
QStringList m_bindingKeys;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
QStringList bindingKeys = app.arguments().mid(1);
|
||||||
|
if (bindingKeys.isEmpty()) {
|
||||||
|
qDebug("usage: %s [binding_key] ...", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TopicLogReceiver logReceiver;
|
||||||
|
logReceiver.start(bindingKeys);
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
DEPTH = ../../..
|
||||||
|
include($${DEPTH}/qamqp.pri)
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
INCLUDEPATH += $${QAMQP_INCLUDEPATH}
|
||||||
|
LIBS += -L$${DEPTH}/src $${QAMQP_LIBS}
|
||||||
|
macx:CONFIG -= app_bundle
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS = \
|
||||||
|
emit_log_topic \
|
||||||
|
receive_logs_topic
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS = \
|
||||||
|
helloworld \
|
||||||
|
workqueues \
|
||||||
|
pubsub \
|
||||||
|
routing \
|
||||||
|
topics \
|
||||||
|
rpc
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue