Showing preview only (1,780K chars total). Download the full file or copy to clipboard to get everything.
Repository: halfgaar/FlashMQ
Branch: master
Commit: af7ab0c2be24
Files: 235
Total size: 1.7 MB
Directory structure:
gitextract_eh9hwlw7/
├── .build-ci.sh
├── .clang-format
├── .clang-format.original
├── .dockerignore
├── .get-os-codename-and-stamp.sh
├── .github/
│ └── workflows/
│ ├── building.yml
│ ├── codestyle.yml
│ ├── docker.yml
│ ├── linting.yml
│ ├── testing.yml
│ └── testing_non_sse.yml
├── .gitignore
├── CMakeLists.shared
├── CMakeLists.txt
├── Dockerfile
├── FlashMQTests/
│ ├── CMakeLists.txt
│ ├── UTF-8-test.txt
│ ├── bridgeprefixtests.cpp
│ ├── conffiletemp.cpp
│ ├── conffiletemp.h
│ ├── configtests.cpp
│ ├── dnstests.cpp
│ ├── filecloser.cpp
│ ├── filecloser.h
│ ├── flashmqtempdir.cpp
│ ├── flashmqtempdir.h
│ ├── main.cpp
│ ├── mainappasfork.cpp
│ ├── mainappasfork.h
│ ├── mainappinthread.cpp
│ ├── mainappinthread.h
│ ├── maintests.cpp
│ ├── maintests.h
│ ├── plugins/
│ │ ├── curlfunctions.cpp
│ │ ├── curlfunctions.h
│ │ ├── test_plugin.cpp
│ │ ├── test_plugin.h
│ │ └── test_plugin.pro
│ ├── plugintests.cpp
│ ├── retaintests.cpp
│ ├── run-make-from-ci.sh
│ ├── run-tests-from-ci.sh
│ ├── sharedsubscriptionstests.cpp
│ ├── subscriptionidtests.cpp
│ ├── testhelpers.cpp
│ ├── testhelpers.h
│ ├── testinitializer.cpp
│ ├── testinitializer.h
│ ├── tst_maintests.cpp
│ ├── utiltests.cpp
│ ├── websockettests.cpp
│ └── willtests.cpp
├── LICENSE
├── README.md
├── acksender.cpp
├── acksender.h
├── acltree.cpp
├── acltree.h
├── backgroundworker.cpp
├── backgroundworker.h
├── bindaddr.cpp
├── bindaddr.h
├── bridgeconfig.cpp
├── bridgeconfig.h
├── bridgeinfodb.cpp
├── bridgeinfodb.h
├── build.sh
├── checkedsharedptr.h
├── checkedweakptr.h
├── cirbuf.cpp
├── cirbuf.h
├── client.cpp
├── client.h
├── clientacceptqueue.cpp
├── clientacceptqueue.h
├── configfileparser.cpp
├── configfileparser.h
├── debian/
│ ├── conffiles
│ ├── flashmq.service
│ ├── postinst
│ ├── postrm
│ ├── preinst
│ └── prerm
├── derivablecounter.cpp
├── derivablecounter.h
├── dnsresolver.cpp
├── dnsresolver.h
├── driftcounter.cpp
├── driftcounter.h
├── enums.h
├── evpencodectxmanager.cpp
├── evpencodectxmanager.h
├── examples/
│ └── plugin_libcurl/
│ ├── CMakeLists.txt
│ ├── LICENSE
│ ├── README.md
│ ├── build.sh
│ ├── src/
│ │ ├── authenticatingclient.cpp
│ │ ├── authenticatingclient.h
│ │ ├── curl_functions.cpp
│ │ ├── curl_functions.h
│ │ ├── plugin_libcurl.cpp
│ │ ├── pluginstate.cpp
│ │ └── pluginstate.h
│ └── vendor/
│ ├── flashmq_plugin.h
│ └── flashmq_public.h
├── exceptions.cpp
├── exceptions.h
├── fdmanaged.cpp
├── fdmanaged.h
├── flags.h
├── flashmq.conf
├── flashmq_plugin.cpp
├── flashmq_plugin.h
├── flashmq_plugin_deprecated.h
├── flashmq_public.h
├── flashmqtestclient.cpp
├── flashmqtestclient.h
├── fmqmain.cpp
├── fmqmain.h
├── fmqsockaddr.cpp
├── fmqsockaddr.h
├── fmqssl.cpp
├── fmqssl.h
├── forward_declarations.h
├── fuzz-helper.sh
├── globals.cpp
├── globals.h
├── globalstats.cpp
├── globalstats.h
├── globber.cpp
├── globber.h
├── haproxy.cpp
├── haproxy.h
├── http.cpp
├── http.h
├── iowrapper.cpp
├── iowrapper.h
├── listener.cpp
├── listener.h
├── lockedsharedptr.h
├── lockedweakptr.cpp
├── lockedweakptr.h
├── logger.cpp
├── logger.h
├── main.cpp
├── mainapp.cpp
├── mainapp.h
├── man/
│ ├── .gitignore
│ ├── Makefile
│ ├── README.md
│ ├── docbook5-refentry-xslt/
│ │ ├── docbook5-refentry-to-html5.xsl
│ │ └── docbook5-refentry-to-manpage.xsl
│ ├── flashmq-docbook5-refentry-to-html5.xsl
│ ├── flashmq-docbook5-refentry-to-manpage.xsl
│ ├── flashmq.1
│ ├── flashmq.1.dbk5
│ ├── flashmq.1.html
│ ├── flashmq.conf.5
│ ├── flashmq.conf.5.dbk5
│ ├── flashmq.conf.5.html
│ ├── refentry.colophon.dbk5
│ └── reference.dbk5
├── mosquittoauthoptcompatwrap.cpp
├── mosquittoauthoptcompatwrap.h
├── mqtt5properties.cpp
├── mqtt5properties.h
├── mqttpacket.cpp
├── mqttpacket.h
├── mutexowned.h
├── network.cpp
├── network.h
├── nocopy.cpp
├── nocopy.h
├── oneinstancelock.cpp
├── oneinstancelock.h
├── packetdatatypes.cpp
├── packetdatatypes.h
├── persistencefile.cpp
├── persistencefile.h
├── persistencefunctions.cpp
├── persistencefunctions.h
├── plugin.cpp
├── plugin.h
├── pluginloader.cpp
├── pluginloader.h
├── publishcopyfactory.cpp
├── publishcopyfactory.h
├── qospacketqueue.cpp
├── qospacketqueue.h
├── queuedtasks.cpp
├── queuedtasks.h
├── release.sh
├── retainedmessage.cpp
├── retainedmessage.h
├── retainedmessagesdb.cpp
├── retainedmessagesdb.h
├── rwlockguard.cpp
├── rwlockguard.h
├── scopedsocket.cpp
├── scopedsocket.h
├── sdnotify.cpp
├── sdnotify.h
├── session.cpp
├── session.h
├── sessionsandsubscriptionsdb.cpp
├── sessionsandsubscriptionsdb.h
├── settings.cpp
├── settings.h
├── sharedsubscribers.cpp
├── sharedsubscribers.h
├── sslctxmanager.cpp
├── sslctxmanager.h
├── subscription.cpp
├── subscription.h
├── subscriptionstore.cpp
├── subscriptionstore.h
├── threaddata.cpp
├── threaddata.h
├── threadglobals.cpp
├── threadglobals.h
├── threadlocalutils.cpp
├── threadlocalutils.h
├── threadlocked.h
├── threadloop.cpp
├── threadloop.h
├── types.cpp
├── types.h
├── unscopedlock.cpp
├── unscopedlock.h
├── utils.cpp
├── utils.h
├── variablebyteint.cpp
├── variablebyteint.h
├── x509manager.cpp
└── x509manager.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .build-ci.sh
================================================
#!/bin/bash
set -e # If any step reports a problem consider the whole build a failure
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
sudo mv linuxdeploy-x86_64.AppImage /usr/local/bin
sudo chmod +x /usr/local/bin/linuxdeploy-x86_64.AppImage
sudo apt update
sudo apt install -y shellcheck
shellcheck debian/post* debian/pre*
./build.sh
./FlashMQBuildRelease/FlashMQ --version
sudo dpkg -i ./FlashMQBuildRelease/*.deb
set +e # Prevent Travis internals from breaking our build, see https://github.com/travis-ci/travis-ci/issues/891
================================================
FILE: .clang-format
================================================
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
#BreakBeforeBraces: Allman
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: AfterColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 180
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
PackConstructorInitializers: Never
BasedOnStyle: ''
ConstructorInitializerAllOnOneLineOrOnePerLine: false
AllowAllConstructorInitializersOnNextLine: true
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyIndentedWhitespace: 0
PointerAlignment: Right
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
RequiresClausePosition: OwnLine
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: Never
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...
================================================
FILE: .clang-format.original
================================================
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
PackConstructorInitializers: BinPack
BasedOnStyle: ''
ConstructorInitializerAllOnOneLineOrOnePerLine: false
AllowAllConstructorInitializersOnNextLine: true
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyIndentedWhitespace: 0
PointerAlignment: Right
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RequiresClausePosition: OwnLine
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...
================================================
FILE: .dockerignore
================================================
Dockerfile
================================================
FILE: .get-os-codename-and-stamp.sh
================================================
#!/bin/bash
my_codename=""
my_int_version=""
if [[ -e "/etc/os-release" ]]; then
eval "$(cat "/etc/os-release")"
my_codename="$VERSION_CODENAME"
my_int_version=${VERSION_ID//./}
elif [[ -e "/etc/lsb-release" ]]; then
eval "$(cat "/etc/lsb-release")"
my_codename="$DISTRIB_CODENAME"
my_int_version=${DISTRIB_RELEASE//./}
else
1>&2 echo "Error in determing os codename"
exit 1
fi
if [[ -z "$my_codename" ]]; then
1>&2 echo "ERROR in determining OS codename"
exit 1
fi
if [[ ! "$my_int_version" =~ ^[0-9]+$ ]]; then
1>&2 echo "ERROR: int version '$my_int_version' is not an int. We need a numeric string for proper debian-revision version comparison."
exit 1
fi
# Sequence numbers makes sure that when one upgrades the OS, the package for
# the new distro version is selected.
sequence="$my_int_version"
if [[ -z "$sequence" ]]; then
1>&2 echo "ERROR: no OS sequence defined for $my_codename"
exit 2
fi
echo -n "${sequence}+${my_codename}+${EPOCHSECONDS}"
================================================
FILE: .github/workflows/building.yml
================================================
name: Building
on: [push]
jobs:
compilation:
strategy:
matrix:
include:
- os: ubuntu-24.04
friendly: clang
CXX: /usr/bin/clang++
aptpkg: clang
- os: ubuntu-24.04
friendly: gcc
CXX: /usr/bin/g++
aptpkg: build-essential
- os: ubuntu-22.04
friendly: clang
CXX: /usr/bin/clang++
aptpkg: clang
- os: ubuntu-22.04
friendly: gcc
CXX: /usr/bin/g++
aptpkg: build-essential
runs-on: ${{ matrix.os }}
name: "${{ matrix.os }}: ${{ matrix.friendly }}"
steps:
- name: Checkout
uses: actions/checkout@v3
- run: sudo apt update
# Build prerequisites
- run: sudo apt install -y xsltproc
- run: sudo apt install -y cmake libssl-dev libcurl4-openssl-dev ${{ matrix.aptpkg }}
# Build example plugin(s)
- run: CXX="${{ matrix.CXX }}" ./examples/plugin_libcurl/build.sh "Release"
# Building
- run: CXX="${{ matrix.CXX }}" ./build.sh "Release"
# Testing build results
- run: ./FlashMQBuildRelease/flashmq --version
- run: sudo dpkg -i ./FlashMQBuildRelease/*.deb
================================================
FILE: .github/workflows/codestyle.yml
================================================
name: Checking code style
on: [pull_request]
jobs:
check_cpp_codestyle:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- run: sudo apt install -y clang-format-15
- run: git fetch origin "$GITHUB_BASE_REF" --depth 1 # Github CI only checks out a single commit, so we need to fetch what to compare against
- run: CHANGES=$(git diff -U0 --no-color "origin/$GITHUB_BASE_REF" | clang-format-diff-15 -p1); [[ -z "$CHANGES" ]] || { echo "Please fix the following C++ style issues:"; echo "$CHANGES"; exit 2; }
================================================
FILE: .github/workflows/docker.yml
================================================
name: Docker
on: [push]
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- run: docker build . -t halfgaar/flashmq
- run: docker run halfgaar/flashmq /bin/flashmq --version
================================================
FILE: .github/workflows/linting.yml
================================================
name: Linting
on: [push]
jobs:
shellcheck:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- run: sudo apt install -y shellcheck
- run: shellcheck debian/post* debian/pre*
- run: find . -type f -iname '*.sh' -exec shellcheck '{}' '+'
markdownlint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- run: sudo snap install mdl
# Exclude MD013: line-length. If it looks good to the dev editing the Markdown file it's good enough for us
- run: find . -type f -iname '*.md' -exec mdl --rules "~MD013" '{}' '+'
================================================
FILE: .github/workflows/testing.yml
================================================
name: Testing
on: [push]
jobs:
compilation:
strategy:
matrix:
include:
- os: ubuntu-24.04
friendly: clang
compiler: clang++
aptpkg: clang
- os: ubuntu-24.04
friendly: gcc
compiler: g++
aptpkg: build-essential
- os: ubuntu-22.04
friendly: clang
compiler: clang++
aptpkg: clang
- os: ubuntu-22.04
friendly: gcc
compiler: g++
aptpkg: build-essential
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: FlashMQTests
name: "Normal test - ${{ matrix.os }}: ${{ matrix.friendly }}"
steps:
- name: Checkout
uses: actions/checkout@v3
- run: sudo apt update
# Build prerequisites
- run: sudo apt install -y cmake libssl-dev libcurl4-openssl-dev ${{ matrix.aptpkg }}
# Building
- run: ./run-make-from-ci.sh --compiler "${{ matrix.compiler }}"
- run: ./run-tests-from-ci.sh
================================================
FILE: .github/workflows/testing_non_sse.yml
================================================
name: TestingNoSse
on: [push]
jobs:
compilation:
strategy:
matrix:
include:
- os: ubuntu-24.04
friendly: clang
compiler: clang++
aptpkg: clang
- os: ubuntu-24.04
friendly: gcc
compiler: g++
aptpkg: build-essential
- os: ubuntu-22.04
friendly: clang
compiler: clang++
aptpkg: clang
- os: ubuntu-22.04
friendly: gcc
compiler: g++
aptpkg: build-essential
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: FlashMQTests
name: "Non-SSE test - ${{ matrix.os }}: ${{ matrix.friendly }}"
steps:
- name: Checkout
uses: actions/checkout@v3
- run: sudo apt update
# Build prerequisites
- run: sudo apt install -y cmake libssl-dev libcurl4-openssl-dev ${{ matrix.aptpkg }}
# Building
- run: ./run-make-from-ci.sh --compiler "${{ matrix.compiler }}" --extra-config "FMQ_NO_SSE=1"
- run: ./run-tests-from-ci.sh
================================================
FILE: .gitignore
================================================
*.user
build-*
FlashMQBuild*
*.swp
compile_commands.json
.clangd/
.cache/
build
FlashMQTests/build
================================================
FILE: CMakeLists.shared
================================================
# This file is shared between FlashMQ itself and FlashMQTests
# It should not contain any definitions that isn't used by both
# When building FlashMQTests:
# CMAKE_CURRENT_SOURCE_DIR -> /home/user/FlashMQ/FlashMQTests
# CMAKE_CURRENT_LIST_DIR -> /home/user/FlashMQ
#
# When building FlashMQ:
# CMAKE_CURRENT_SOURCE_DIR -> /home/user/FlashMQ
# CMAKE_CURRENT_LIST_DIR -> /home/user/FlashMQ
if (CMAKE_CURRENT_LIST_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
set(RELPATH "./")
else()
set(RELPATH "../")
endif()
message("Determined RELPATH: ${RELPATH}")
set(FLASHMQ_HEADERS
${RELPATH}forward_declarations.h
${RELPATH}mainapp.h
${RELPATH}utils.h
${RELPATH}threaddata.h
${RELPATH}client.h
${RELPATH}session.h
${RELPATH}mqttpacket.h
${RELPATH}exceptions.h
${RELPATH}types.h
${RELPATH}subscriptionstore.h
${RELPATH}rwlockguard.h
${RELPATH}retainedmessage.h
${RELPATH}cirbuf.h
${RELPATH}logger.h
${RELPATH}plugin.h
${RELPATH}configfileparser.h
${RELPATH}sslctxmanager.h
${RELPATH}iowrapper.h
${RELPATH}mosquittoauthoptcompatwrap.h
${RELPATH}settings.h
${RELPATH}listener.h
${RELPATH}unscopedlock.h
${RELPATH}scopedsocket.h
${RELPATH}bindaddr.h
${RELPATH}oneinstancelock.h
${RELPATH}evpencodectxmanager.h
${RELPATH}acltree.h
${RELPATH}enums.h
${RELPATH}threadlocalutils.h
${RELPATH}flashmq_plugin.h
${RELPATH}flashmq_plugin_deprecated.h
${RELPATH}retainedmessagesdb.h
${RELPATH}persistencefile.h
${RELPATH}sessionsandsubscriptionsdb.h
${RELPATH}qospacketqueue.h
${RELPATH}threadglobals.h
${RELPATH}threadloop.h
${RELPATH}publishcopyfactory.h
${RELPATH}variablebyteint.h
${RELPATH}mqtt5properties.h
${RELPATH}globalstats.h
${RELPATH}derivablecounter.h
${RELPATH}packetdatatypes.h
${RELPATH}haproxy.h
${RELPATH}network.h
${RELPATH}subscription.h
${RELPATH}sharedsubscribers.h
${RELPATH}pluginloader.h
${RELPATH}queuedtasks.h
${RELPATH}acksender.h
${RELPATH}bridgeconfig.h
${RELPATH}dnsresolver.h
${RELPATH}globber.h
${RELPATH}bridgeinfodb.h
${RELPATH}x509manager.h
${RELPATH}backgroundworker.h
${RELPATH}fmqmain.h
${RELPATH}driftcounter.h
${RELPATH}lockedweakptr.h
${RELPATH}lockedsharedptr.h
${RELPATH}globals.h
${RELPATH}nocopy.h
${RELPATH}fdmanaged.h
${RELPATH}checkedweakptr.h
${RELPATH}mutexowned.h
${RELPATH}http.h
${RELPATH}fmqsockaddr.h
${RELPATH}flags.h
${RELPATH}persistencefunctions.h
${RELPATH}clientacceptqueue.h
${RELPATH}checkedsharedptr.h
${RELPATH}flashmq_public.h
${RELPATH}sdnotify.h
${RELPATH}threadlocked.h
)
set(FLASHMQ_IMPLS
${RELPATH}mainapp.cpp
${RELPATH}utils.cpp
${RELPATH}threaddata.cpp
${RELPATH}client.cpp
${RELPATH}session.cpp
${RELPATH}mqttpacket.cpp
${RELPATH}exceptions.cpp
${RELPATH}types.cpp
${RELPATH}subscriptionstore.cpp
${RELPATH}rwlockguard.cpp
${RELPATH}retainedmessage.cpp
${RELPATH}cirbuf.cpp
${RELPATH}logger.cpp
${RELPATH}plugin.cpp
${RELPATH}configfileparser.cpp
${RELPATH}sslctxmanager.cpp
${RELPATH}iowrapper.cpp
${RELPATH}mosquittoauthoptcompatwrap.cpp
${RELPATH}settings.cpp
${RELPATH}listener.cpp
${RELPATH}unscopedlock.cpp
${RELPATH}scopedsocket.cpp
${RELPATH}bindaddr.cpp
${RELPATH}oneinstancelock.cpp
${RELPATH}evpencodectxmanager.cpp
${RELPATH}acltree.cpp
${RELPATH}threadlocalutils.cpp
${RELPATH}flashmq_plugin.cpp
${RELPATH}retainedmessagesdb.cpp
${RELPATH}persistencefile.cpp
${RELPATH}sessionsandsubscriptionsdb.cpp
${RELPATH}qospacketqueue.cpp
${RELPATH}threadglobals.cpp
${RELPATH}threadloop.cpp
${RELPATH}publishcopyfactory.cpp
${RELPATH}variablebyteint.cpp
${RELPATH}mqtt5properties.cpp
${RELPATH}globalstats.cpp
${RELPATH}derivablecounter.cpp
${RELPATH}packetdatatypes.cpp
${RELPATH}haproxy.cpp
${RELPATH}network.cpp
${RELPATH}subscription.cpp
${RELPATH}sharedsubscribers.cpp
${RELPATH}pluginloader.cpp
${RELPATH}queuedtasks.cpp
${RELPATH}acksender.cpp
${RELPATH}bridgeconfig.cpp
${RELPATH}dnsresolver.cpp
${RELPATH}bridgeinfodb.cpp
${RELPATH}globber.cpp
${RELPATH}x509manager.cpp
${RELPATH}backgroundworker.cpp
${RELPATH}fmqmain.cpp
${RELPATH}driftcounter.cpp
${RELPATH}lockedweakptr.cpp
${RELPATH}globals.cpp
${RELPATH}nocopy.cpp
${RELPATH}fdmanaged.cpp
${RELPATH}http.cpp
${RELPATH}fmqsockaddr.cpp
${RELPATH}fmqssl.cpp
${RELPATH}persistencefunctions.cpp
${RELPATH}clientacceptqueue.cpp
${RELPATH}sdnotify.cpp
)
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.5)
cmake_policy(SET CMP0048 NEW)
include(CheckCXXCompilerFlag)
include(CMakeLists.shared)
project(FlashMQ VERSION 1.26.1 LANGUAGES CXX)
add_definitions(-DOPENSSL_API_COMPAT=0x10100000L)
add_definitions(-DFLASHMQ_VERSION=\"${PROJECT_VERSION}\")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (FMQ_ASAN)
message("Building with ASAN.")
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
endif()
check_cxx_compiler_flag("-msse4.2" COMPILER_RT_HAS_MSSE4_2_FLAG)
if (${COMPILER_RT_HAS_MSSE4_2_FLAG})
SET(CMAKE_CXX_FLAGS "-msse4.2")
endif()
add_compile_options(-fvisibility=hidden -fvisibility-inlines-hidden)
add_link_options(-rdynamic)
add_compile_options(-Wall -Wextra)
add_executable(flashmq
${FLASHMQ_HEADERS}
${FLASHMQ_IMPLS}
main.cpp
)
target_link_libraries(flashmq pthread dl ssl crypto resolv anl)
execute_process(COMMAND ../.get-os-codename-and-stamp.sh OUTPUT_VARIABLE OS_CODENAME)
install(TARGETS flashmq
RUNTIME DESTINATION "/usr/bin/")
install(DIRECTORY DESTINATION "/var/lib/flashmq")
install(DIRECTORY DESTINATION "/var/log/flashmq")
install(FILES flashmq.conf DESTINATION "/etc/flashmq")
install(FILES debian/flashmq.service DESTINATION "/lib/systemd/system")
install(FILES man/flashmq.conf.5 DESTINATION "/usr/share/man/man5")
install(FILES man/flashmq.1 DESTINATION "/usr/share/man/man1")
SET(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/debian/conffiles;${CMAKE_CURRENT_SOURCE_DIR}/debian/preinst;${CMAKE_CURRENT_SOURCE_DIR}/debian/postinst;${CMAKE_CURRENT_SOURCE_DIR}/debian/postrm;${CMAKE_CURRENT_SOURCE_DIR}/debian/prerm")
SET(CPACK_GENERATOR "DEB")
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Wiebe Cazemier <wiebe@flashmq.org>")
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Light-weight, high performance MQTT server capable of million+ messages per second.")
SET(CPACK_PACKAGE_HOMEPAGE_URL "https://www.flashmq.org/")
SET(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
SET(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT")
SET(CPACK_DEBIAN_PACKAGE_RELEASE ${OS_CODENAME})
SET(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
SET(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
SET(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
INCLUDE(CPack)
================================================
FILE: Dockerfile
================================================
# build target, used for building the binary, providing shared libraries and could be used as a development env
FROM debian:trixie-slim AS build
# install build dependencies
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y install g++ make cmake libssl-dev file
# create flashmq user and group for runtime image below
RUN useradd --system --shell /bin/false --user-group --no-log-init flashmq
ARG BUILD_TYPE=Release
RUN echo "Building ${BUILD_TYPE} version"
WORKDIR /usr/src/app
COPY . .
RUN rm -rf FlashMQBuild* 2>/dev/null
RUN ./build.sh ${BUILD_TYPE}
# convert docker buildx platform name to Debian platform name
FROM scratch AS run-amd64
ARG PLATFORM=x86_64
ARG LD_LOCATION=/lib64/ld-linux-x86-64.so.2
FROM scratch AS run-arm64
ARG PLATFORM=aarch64
ARG LD_LOCATION=/lib/ld-linux-aarch64.so.1
# from scratch image is empty
FROM run-$TARGETARCH AS run
USER flashmq:flashmq
COPY --from=build /etc/passwd /etc/passwd
COPY --from=build /etc/group /etc/group
# copy in the shared libaries in use discovered using ldd on release binary
COPY --from=build /lib/${PLATFORM}-linux-gnu/libpthread.so.0 /lib/${PLATFORM}-linux-gnu/libpthread.so.0
COPY --from=build /lib/${PLATFORM}-linux-gnu/libdl.so.2 /lib/${PLATFORM}-linux-gnu/libdl.so.2
COPY --from=build /usr/lib/${PLATFORM}-linux-gnu/libssl.so.3 /usr/lib/${PLATFORM}-linux-gnu/libssl.so.3
COPY --from=build /usr/lib/${PLATFORM}-linux-gnu/libcrypto.so.3 /usr/lib/${PLATFORM}-linux-gnu/libcrypto.so.3
COPY --from=build /usr/lib/${PLATFORM}-linux-gnu/libstdc++.so.6 /usr/lib/${PLATFORM}-linux-gnu/libstdc++.so.6
COPY --from=build /lib/${PLATFORM}-linux-gnu/libgcc_s.so.1 /lib/${PLATFORM}-linux-gnu/libgcc_s.so.1
COPY --from=build /lib/${PLATFORM}-linux-gnu/libc.so.6 /lib/${PLATFORM}-linux-gnu/libc.so.6
COPY --from=build ${LD_LOCATION} ${LD_LOCATION}
COPY --from=build /lib/${PLATFORM}-linux-gnu/libm.so.6 /lib/${PLATFORM}-linux-gnu/libm.so.6
COPY --from=build /lib/${PLATFORM}-linux-gnu/libresolv.so.2 /lib/${PLATFORM}-linux-gnu/libresolv.so.2
COPY --from=build /lib/${PLATFORM}-linux-gnu/libanl.so.1 /lib/${PLATFORM}-linux-gnu/libanl.so.1
COPY --from=build /lib/${PLATFORM}-linux-gnu/libz.so.1 /lib/${PLATFORM}-linux-gnu/libz.so.1
COPY --from=build /lib/${PLATFORM}-linux-gnu/libzstd.so.1 /lib/${PLATFORM}-linux-gnu/libzstd.so.1
# copy in the FlashMQ binary itself
COPY --from=build /usr/src/app/FlashMQBuildRelease/flashmq /bin/flashmq
EXPOSE 1883
CMD ["/bin/flashmq"]
================================================
FILE: FlashMQTests/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.5)
cmake_policy(SET CMP0048 NEW)
include(CheckCXXCompilerFlag)
include(../CMakeLists.shared)
project(FlashMQTests VERSION 1.0.0 LANGUAGES CXX)
add_definitions(-DOPENSSL_API_COMPAT=0x10100000L)
add_definitions(-DFLASHMQ_VERSION=\"${PROJECT_VERSION}\")
add_definitions(-DTESTING)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (FMQ_NO_SSE)
add_definitions(-DFMQ_NO_SSE)
message("Building tests wihout SSE.")
else()
SET(CMAKE_CXX_FLAGS "-msse4.2")
endif()
add_link_options(-rdynamic)
add_compile_options(-Wall -Wextra -fsanitize=address)
add_link_options(-fsanitize=address)
add_library(test_plugin SHARED
plugins/test_plugin.h plugins/test_plugin.cpp
plugins/curlfunctions.h plugins/curlfunctions.cpp
)
target_link_libraries(test_plugin curl)
# Copying to a version 0.0.1 file is a bit of a hack. I'm not sure how to version it in this CMake otherwise.
add_custom_command(TARGET test_plugin POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "plugins/")
add_custom_command(TARGET test_plugin POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:test_plugin> "plugins/libtest_plugin.so.0.0.1")
# Copy in a way that always copies, to avoid unexpected staleness.
add_custom_command(TARGET test_plugin POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/UTF-8-test.txt"
"${CMAKE_BINARY_DIR}/UTF-8-test.txt")
add_custom_command(TARGET test_plugin POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/../fuzztests/plainwebsocketpacket1_handshake.dat"
"${CMAKE_BINARY_DIR}/plainwebsocketpacket1_handshake.dat")
add_executable(flashmq-tests
${FLASHMQ_HEADERS}
${FLASHMQ_IMPLS}
../flashmqtestclient.cpp ../flashmqtestclient.h
main.cpp
mainappinthread.h mainappinthread.cpp
maintests.h maintests.cpp
testhelpers.cpp testhelpers.h
flashmqtempdir.h flashmqtempdir.cpp
tst_maintests.cpp
retaintests.cpp
conffiletemp.cpp conffiletemp.h
filecloser.cpp filecloser.h
plugintests.cpp
configtests.cpp
sharedsubscriptionstests.cpp
websockettests.cpp
willtests.cpp
dnstests.cpp
utiltests.cpp
testinitializer.h testinitializer.cpp
mainappasfork.h mainappasfork.cpp
subscriptionidtests.cpp
bridgeprefixtests.cpp
)
target_compile_options(flashmq-tests PUBLIC -fvisibility=hidden -fvisibility-inlines-hidden)
target_link_options(flashmq-tests PUBLIC -rdynamic)
target_include_directories(flashmq-tests PUBLIC ..)
target_link_libraries(flashmq-tests pthread dl ssl crypto resolv anl)
================================================
FILE: FlashMQTests/bridgeprefixtests.cpp
================================================
#include "maintests.h"
#include "conffiletemp.h"
#include "flashmqtestclient.h"
#include "mainappasfork.h"
#include "testhelpers.h"
#include "flashmqtempdir.h"
void waitForMessagesOverBridge(FlashMQTestClient &one, FlashMQTestClient &two, const std::string &topic)
{
int wait_i = 0;
for(wait_i = 0; wait_i < 10; wait_i++)
{
one.publish(topic, "connectiontest", 2);
try
{
two.waitForMessageCount(1);
break;
}
catch (std::exception &ex) { }
}
if (wait_i >= 10)
throw std::runtime_error("Timeout waiting for messages over bridge");
}
void MainTests::forkingTestBridgeWithLocalAndRemotePrefix()
{
for (const std::string protocol_version : {"mqtt5", "mqtt3.1"})
{
cleanup();
ConfFileTemp confFile;
const std::string config = R"(
allow_anonymous true
log_debug false
bridge {
address ::1
port 21883
subscribe ManglerRemote/shoes 2
publish ManglerLocal/boots 2
clientid_prefix Mangler
local_prefix ManglerLocal/
remote_prefix ManglerRemote/
protocol_version %s
}
listen {
protocol mqtt
port 51183
})";
confFile.writeLine(formatString(config, protocol_version.c_str()));
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
MainAppAsFork app(args);
app.start();
app.waitForStarted(51183);
// We will consider the test server initialized here the 'remote' broker.
init();
FlashMQTestClient clientToLocalWithBridge;
clientToLocalWithBridge.start();
FlashMQTestClient clientToRemote;
clientToRemote.start();
clientToLocalWithBridge.connectClient(ProtocolVersion::Mqtt31, 51183);
clientToLocalWithBridge.subscribe("#", 1);
clientToRemote.connectClient(ProtocolVersion::Mqtt5);
clientToRemote.subscribe("#", 1);
waitForMessagesOverBridge(clientToLocalWithBridge, clientToRemote, "ManglerLocal/boots");
clientToLocalWithBridge.clearReceivedLists();
clientToRemote.clearReceivedLists();
{
clientToLocalWithBridge.publish("ManglerLocal/boots", "asdf", 2);
clientToRemote.waitForMessageCount(1);
auto ro = clientToRemote.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerRemote/boots"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "asdf");
}
clientToLocalWithBridge.clearReceivedLists();
clientToRemote.clearReceivedLists();
{
clientToRemote.publish("ManglerRemote/shoes", "are made for walking", 2);
clientToLocalWithBridge.waitForMessageCount(1);
auto ro = clientToLocalWithBridge.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerLocal/shoes"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "are made for walking");
}
}
}
/**
* @brief Test the internal packet cache; that we don't accidentally cache packets with the prefixes applied.
*
* The PublishCopyFactory was temporarily broken first to write this test.
*/
void MainTests::forkingTestBridgePrefixesOtherClientsUnaffected()
{
for (const std::string protocol_version : {"mqtt5", "mqtt3.1"})
{
cleanup();
ConfFileTemp confFile;
const std::string config = R"(
allow_anonymous true
log_debug false
bridge {
address ::1
port 21883
subscribe ManglerRemote/shoes 2
publish ManglerLocal/boots 2
clientid_prefix Mangler
local_prefix ManglerLocal/
remote_prefix ManglerRemote/
protocol_version %s
}
listen {
protocol mqtt
port 51183
})";
confFile.writeLine(formatString(config, protocol_version.c_str()));
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
MainAppAsFork app(args);
app.start();
app.waitForStarted(51183);
// We will consider the test server initialized here the 'remote' broker.
init();
FlashMQTestClient randomOtherLocalClient1;
randomOtherLocalClient1.start();
randomOtherLocalClient1.connectClient(ProtocolVersion::Mqtt5, 51183);
// Not using wild-cards because we happen to know that produces the correct delivery order to subscribers to test this.
randomOtherLocalClient1.subscribe("ManglerLocal/boots", 1);
randomOtherLocalClient1.subscribe("ManglerRemote/boots", 1);
FlashMQTestClient clientToLocalWithBridge;
clientToLocalWithBridge.start();
FlashMQTestClient clientToRemote;
clientToRemote.start();
clientToLocalWithBridge.connectClient(ProtocolVersion::Mqtt31, 51183);
clientToLocalWithBridge.subscribe("#", 1);
clientToRemote.connectClient(ProtocolVersion::Mqtt5);
clientToRemote.subscribe("#", 1);
FlashMQTestClient randomOtherLocalClient2;
randomOtherLocalClient2.start();
randomOtherLocalClient2.connectClient(ProtocolVersion::Mqtt5, 51183);
// Not using wild-cards because we happen to know that produces the correct delivery order to subscribers to test this.
randomOtherLocalClient2.subscribe("ManglerLocal/boots", 1);
randomOtherLocalClient2.subscribe("ManglerRemote/boots", 1);
FlashMQTestClient randomOtherLocalClient3;
randomOtherLocalClient3.start();
randomOtherLocalClient3.connectClient(ProtocolVersion::Mqtt5, 51183);
// For this one, we do use the wildcard subscription, just to be sure.
randomOtherLocalClient3.subscribe("ManglerLocal/#", 1);
waitForMessagesOverBridge(clientToLocalWithBridge, clientToRemote, "ManglerLocal/boots");
clientToLocalWithBridge.clearReceivedLists();
clientToRemote.clearReceivedLists();
{
clientToLocalWithBridge.publish("ManglerLocal/boots", "asdf", 2);
clientToRemote.waitForMessageCount(1);
auto ro = clientToRemote.receivedObjects.lock();
//FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerRemote/boots"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "asdf");
}
clientToLocalWithBridge.clearReceivedLists();
{
randomOtherLocalClient1.waitForMessageCount(2);
auto ro = randomOtherLocalClient1.receivedObjects.lock();
FMQ_VERIFY(std::all_of(ro->receivedPublishes.begin(), ro->receivedPublishes.end(), [](MqttPacket &p) {
return startsWith(p.getTopic(), "ManglerLocal/");
}));
}
{
randomOtherLocalClient2.waitForMessageCount(2);
auto ro = randomOtherLocalClient2.receivedObjects.lock();
FMQ_VERIFY(std::all_of(ro->receivedPublishes.begin(), ro->receivedPublishes.end(), [](MqttPacket &p) {
return startsWith(p.getTopic(), "ManglerLocal/");
}));
}
{
randomOtherLocalClient3.waitForMessageCount(2);
auto ro = randomOtherLocalClient3.receivedObjects.lock();
FMQ_VERIFY(std::all_of(ro->receivedPublishes.begin(), ro->receivedPublishes.end(), [](MqttPacket &p) {
return startsWith(p.getTopic(), "ManglerLocal/");
}));
}
}
}
void MainTests::forkingTestBridgeWithOnlyRemotePrefix()
{
for (const std::string protocol_version : {"mqtt5", "mqtt3.1"})
{
cleanup();
ConfFileTemp confFile;
const std::string config = R"(
allow_anonymous true
log_debug false
bridge {
address ::1
port 21883
subscribe ManglerRemote/shoes 2
publish boots 2
clientid_prefix Mangler
remote_prefix ManglerRemote/
protocol_version %s
}
listen {
protocol mqtt
port 51183
})";
confFile.writeLine(formatString(config, protocol_version.c_str()));
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
MainAppAsFork app(args);
app.start();
app.waitForStarted(51183);
// We will consider the test server initialized here the 'remote' broker.
init();
FlashMQTestClient clientToLocalWithBridge;
clientToLocalWithBridge.start();
FlashMQTestClient clientToRemote;
clientToRemote.start();
FlashMQTestClient receiverToLocal;
receiverToLocal.start();
FlashMQTestClient receiverToLocal2;
receiverToLocal2.start();
receiverToLocal2.connectClient(ProtocolVersion::Mqtt31, 51183);
receiverToLocal2.subscribe("#", 1);
clientToLocalWithBridge.connectClient(ProtocolVersion::Mqtt31, 51183);
clientToLocalWithBridge.subscribe("#", 1);
clientToRemote.connectClient(ProtocolVersion::Mqtt5);
clientToRemote.subscribe("#", 1);
waitForMessagesOverBridge(clientToLocalWithBridge, clientToRemote, "boots");
clientToLocalWithBridge.clearReceivedLists();
clientToRemote.clearReceivedLists();
receiverToLocal.connectClient(ProtocolVersion::Mqtt31, 51183);
receiverToLocal.subscribe("#", 1);
{
clientToLocalWithBridge.publish("boots", "asdf", 2);
clientToRemote.waitForMessageCount(1);
auto ro = clientToRemote.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerRemote/boots"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "asdf");
}
// Make sure other clients connected to the server with prefixes defined get normal topics. I.e. test packet cache.
{
receiverToLocal.waitForMessageCount(1);
auto roLocal = receiverToLocal.receivedObjects.lock();
FMQ_COMPARE(roLocal->receivedPublishes.at(0).getTopic(), std::string("boots"));
FMQ_COMPARE(roLocal->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(roLocal->receivedPublishes.at(0).getPayloadView(), "asdf");
}
{
receiverToLocal2.waitForMessageCount(1);
auto roLocal2 = receiverToLocal2.receivedObjects.lock();
FMQ_COMPARE(roLocal2->receivedPublishes.back().getTopic(), std::string("boots"));
FMQ_COMPARE(roLocal2->receivedPublishes.back().getQos(), 1);
FMQ_COMPARE(roLocal2->receivedPublishes.back().getPayloadView(), "asdf");
}
clientToLocalWithBridge.clearReceivedLists();
clientToRemote.clearReceivedLists();
{
clientToRemote.publish("ManglerRemote/shoes", "are made for walking", 2);
clientToLocalWithBridge.waitForMessageCount(1);
auto ro = clientToLocalWithBridge.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("shoes"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "are made for walking");
}
}
}
void MainTests::forkingTestBridgeWithOnlyLocalPrefix()
{
for (const std::string protocol_version : {"mqtt5", "mqtt3.1"})
{
cleanup();
ConfFileTemp confFile;
const std::string config = R"(
allow_anonymous true
log_debug false
bridge {
address ::1
port 21883
subscribe shoes 2
publish ManglerLocal/boots 2
clientid_prefix Mangler
local_prefix ManglerLocal/
protocol_version %s
}
listen {
protocol mqtt
port 51183
})";
confFile.writeLine(formatString(config, protocol_version.c_str()));
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
MainAppAsFork app(args);
app.start();
app.waitForStarted(51183);
// We will consider the test server initialized here the 'remote' broker.
init();
FlashMQTestClient clientToLocalWithBridge;
clientToLocalWithBridge.start();
FlashMQTestClient clientToRemote;
clientToRemote.start();
FlashMQTestClient senderToLocal;
senderToLocal.start();
clientToLocalWithBridge.connectClient(ProtocolVersion::Mqtt31, 51183);
clientToLocalWithBridge.subscribe("#", 1);
clientToRemote.connectClient(ProtocolVersion::Mqtt5);
clientToRemote.subscribe("#", 1);
waitForMessagesOverBridge(clientToLocalWithBridge, clientToRemote, "ManglerLocal/boots");
clientToLocalWithBridge.clearReceivedLists();
clientToRemote.clearReceivedLists();
{
clientToLocalWithBridge.publish("ManglerLocal/boots", "asdf", 2);
clientToRemote.waitForMessageCount(1);
auto ro = clientToRemote.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("boots"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "asdf");
}
clientToLocalWithBridge.clearReceivedLists();
clientToRemote.clearReceivedLists();
{
clientToRemote.publish("shoes", "are made for walking", 2);
clientToLocalWithBridge.waitForMessageCount(1);
auto ro = clientToLocalWithBridge.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerLocal/shoes"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "are made for walking");
}
// Make sure other clients connected to the server with prefixes defined get normal topics.
{
clientToLocalWithBridge.clearReceivedLists();
senderToLocal.connectClient(ProtocolVersion::Mqtt31, 51183);
senderToLocal.publish("panic", "attack", 0);
clientToLocalWithBridge.waitForMessageCount(1);
auto ro = clientToLocalWithBridge.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("panic"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 0);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "attack");
}
}
}
/**
* @brief Test that we don't apply the prefix on outgoing messages when it's the same as the topic string.
*
* When having a prefix of 'one/two/', this is to prevent a subscription like 'one/two/#', turning topic 'one/two/'
* into '', which is illegal (while empty subtopic strings, like caused by trailing slash, is legal).
*/
void MainTests::forkingTestBridgeOutgoingTopicEqualsPrefix()
{
cleanup();
ConfFileTemp confFile;
const std::string config = R"(
allow_anonymous true
log_debug false
bridge {
address ::1
port 21883
subscribe shoes 2
publish ManglerLocal/# 2
clientid_prefix Mangler
local_prefix ManglerLocal/
protocol_version mqtt5
}
listen {
protocol mqtt
port 51183
})";
confFile.writeLine(config);
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
MainAppAsFork app(args);
app.start();
app.waitForStarted(51183);
// We will consider the test server initialized here the 'remote' broker.
init();
FlashMQTestClient clientToLocalWithBridge;
clientToLocalWithBridge.start();
FlashMQTestClient clientToRemote;
clientToRemote.start();
FlashMQTestClient senderToLocal;
senderToLocal.start();
clientToLocalWithBridge.connectClient(ProtocolVersion::Mqtt31, 51183);
clientToLocalWithBridge.subscribe("#", 1);
clientToRemote.connectClient(ProtocolVersion::Mqtt5);
clientToRemote.subscribe("#", 1);
waitForMessagesOverBridge(clientToLocalWithBridge, clientToRemote, "ManglerLocal/boots");
clientToLocalWithBridge.clearReceivedLists();
clientToRemote.clearReceivedLists();
{
clientToLocalWithBridge.publish("ManglerLocal/", "WC4WQbYJb76TguUT", 2);
clientToRemote.waitForMessageCount(1);
auto ro = clientToRemote.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerLocal/"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "WC4WQbYJb76TguUT");
}
}
/**
* @brief Same as forkingTestBridgeOutgoingTopicEqualsPrefix, but then for incoming.
*/
void MainTests::forkingTestBridgeIncomingTopicEqualsPrefix()
{
cleanup();
ConfFileTemp confFile;
const std::string config = R"(
allow_anonymous true
log_debug false
bridge {
address ::1
port 21883
subscribe ManglerRemote/# 2
publish boots 2
clientid_prefix Mangler
remote_prefix ManglerRemote/
protocol_version mqtt5
}
listen {
protocol mqtt
port 51183
})";
confFile.writeLine(config);
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
MainAppAsFork app(args);
app.start();
app.waitForStarted(51183);
// We will consider the test server initialized here the 'remote' broker.
init();
FlashMQTestClient clientToLocalWithBridge;
clientToLocalWithBridge.start();
FlashMQTestClient clientToRemote;
clientToRemote.start();
clientToLocalWithBridge.connectClient(ProtocolVersion::Mqtt31, 51183);
clientToLocalWithBridge.subscribe("#", 1);
clientToRemote.connectClient(ProtocolVersion::Mqtt5);
clientToRemote.subscribe("#", 1);
waitForMessagesOverBridge(clientToLocalWithBridge, clientToRemote, "boots");
clientToLocalWithBridge.clearReceivedLists();
clientToRemote.clearReceivedLists();
{
clientToRemote.publish("ManglerRemote/", "XN0KoFeDOimgRiTs", 2);
clientToLocalWithBridge.waitForMessageCount(1);
auto ro = clientToLocalWithBridge.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerRemote/"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "XN0KoFeDOimgRiTs");
}
}
void MainTests::forkingTestBridgeZWithLocalAndRemotePrefixRetained()
{
for (const std::string protocol_version : {"mqtt5", "mqtt3.1"})
{
cleanup();
ConfFileTemp conf_file_remote;
{
const std::string config_remote = R"(
allow_anonymous true
log_debug false
listen {
protocol mqtt
port 51883
})";
conf_file_remote.writeLine(formatString(config_remote, protocol_version.c_str()));
conf_file_remote.closeFile();
}
std::vector<std::string> args_remote {"--config-file", conf_file_remote.getFilePath()};
MainAppAsFork remoteServer(args_remote);
remoteServer.start();
remoteServer.waitForStarted(51883);
FlashMQTestClient clientToRemote;
clientToRemote.start();
clientToRemote.connectClient(ProtocolVersion::Mqtt5, 51883);
{
Publish pub("ManglerRemote/shoes/retainme", "asdf", 2);
pub.retain = true;
clientToRemote.publish(pub);
}
ConfFileTemp conf_file_local;
{
const std::string config_local = R"(
allow_anonymous true
log_debug false
bridge {
address ::1
port 51883
subscribe ManglerRemote/shoes/# 2
publish ManglerLocal/boots/# 2
clientid_prefix Mangler
local_prefix ManglerLocal/
remote_prefix ManglerRemote/
protocol_version %s
}
listen {
protocol mqtt
port 21883
})";
conf_file_local.writeLine(formatString(config_local, protocol_version.c_str()));
conf_file_local.closeFile();
std::vector<std::string> args_local {"--config-file", conf_file_local.getFilePath()};
// We consider our normal test client as 'local'
init(args_local);
}
FlashMQTestClient clientToLocal;
clientToLocal.start();
clientToLocal.connectClient(ProtocolVersion::Mqtt5);
clientToLocal.subscribe("#", 1);
clientToLocal.waitForMessageCount(1);
{
auto ro = clientToLocal.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.size(), static_cast<size_t>(1));
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerLocal/shoes/retainme"));
}
clientToLocal.clearReceivedLists();
waitForMessagesOverBridge(clientToRemote, clientToLocal, "ManglerRemote/shoes/connectiontest");
{
auto ro = clientToLocal.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.size(), static_cast<size_t>(1));
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerLocal/shoes/connectiontest"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), std::string("connectiontest"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getRetain(), false);
}
{
FlashMQTestClient clientToLocal;
clientToLocal.start();
clientToLocal.connectClient(ProtocolVersion::Mqtt5);
clientToLocal.subscribe("#", 1);
clientToLocal.waitForPacketCount(1);
auto ro = clientToLocal.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.size(), static_cast<size_t>(1));
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerLocal/shoes/retainme"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "asdf");
FMQ_COMPARE(ro->receivedPublishes.at(0).getRetain(), true);
}
// Push a retained message to local
{
FlashMQTestClient clientToLocal;
clientToLocal.start();
clientToLocal.connectClient(ProtocolVersion::Mqtt5);
Publish pub("ManglerLocal/boots/retainmetoo", "zuOJHOekvdkGD9FH", 2);
pub.retain = true;
clientToLocal.publish(pub);
}
// That we then must see as retained on the remote
{
FlashMQTestClient clientToRemote;
clientToRemote.start();
clientToRemote.connectClient(ProtocolVersion::Mqtt5, 51883);
clientToRemote.subscribe("ManglerRemote/boots/#", 2);
clientToRemote.waitForMessageCount(1);
auto ro = clientToRemote.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.size(), static_cast<size_t>(1));
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerRemote/boots/retainmetoo"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 2);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "zuOJHOekvdkGD9FH");
FMQ_COMPARE(ro->receivedPublishes.at(0).getRetain(), true);
}
}
}
/**
* @brief Test if queued QoS messages have have their prefixes applied. Special care is taken in FlashMQ to support
* that (the topic_override is part of the QueuedPublish), so we need to test it.
*/
void MainTests::forkingTestBridgeWithLocalAndRemotePrefixQueuedQoS()
{
for (const std::string protocol_version : {"mqtt5", "mqtt3.1.1"})
{
cleanup();
ConfFileTemp conf_file_local;
const std::string config_local = R"(
allow_anonymous true
log_debug false
bridge {
address ::1
port 21883
subscribe ManglerRemote/shoes/# 2
publish ManglerLocal/boots/# 2
clientid_prefix Mangler
remote_clean_start false
local_clean_start false
remote_session_expiry_interval 300
local_session_expiry_interval 300
local_prefix ManglerLocal/
remote_prefix ManglerRemote/
protocol_version %s
}
listen {
protocol mqtt
port 51883
})";
conf_file_local.writeLine(formatString(config_local, protocol_version.c_str()));
conf_file_local.closeFile();
std::vector<std::string> args_local {"--config-file", conf_file_local.getFilePath()};
MainAppAsFork localServer(args_local);
localServer.start();
localServer.waitForStarted(51883);
FlashMQTestClient clientToLocalWithBridge;
clientToLocalWithBridge.start();
clientToLocalWithBridge.connectClient(ProtocolVersion::Mqtt5, 51883);
FlashMQTempDir remote_server_storage_dir;
ConfFileTemp conf_file_remote;
const std::string config_remote = R"(
allow_anonymous true
log_debug false
storage_dir %s
listen {
protocol mqtt
port 21883
}
)";
conf_file_remote.writeLine(formatString(config_remote, remote_server_storage_dir.getPath().c_str()));
conf_file_remote.closeFile();
std::vector<std::string> args_remote {"--config-file", conf_file_remote.getFilePath()};
// Bring the remote on-line
init(args_remote);
FlashMQTestClient clientToRemote;
clientToRemote.start();
clientToRemote.connectClient(ProtocolVersion::Mqtt5);
clientToRemote.subscribe("#", 1);
waitForMessagesOverBridge(clientToLocalWithBridge, clientToRemote, "ManglerLocal/boots/connectiontest");
{
auto ro = clientToRemote.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.size(), static_cast<size_t>(1));
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerRemote/boots/connectiontest"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), std::string("connectiontest"));
}
clientToRemote.clearReceivedLists();
// Stop the remote
cleanup();
Publish pub("ManglerLocal/boots/queue_me", "late to the party", 1);
clientToLocalWithBridge.publish(pub);
std::cout << "Starting remote server again" << std::endl;
// Start the remote again
init(args_remote);
{
FlashMQTestClient clientToRemote;
clientToRemote.start();
clientToRemote.connectClient(ProtocolVersion::Mqtt5, false, 1000, [](Connect &c) {
c.clientid = "QueuedReceiver_666";
});
clientToRemote.subscribe("+/boots/queue_me", 1);
clientToRemote.waitForMessageCount(1, 10);
auto ro = clientToRemote.receivedObjects.lock();
FMQ_COMPARE(ro->receivedPublishes.size(), static_cast<size_t>(1));
FMQ_COMPARE(ro->receivedPublishes.at(0).getTopic(), std::string("ManglerRemote/boots/queue_me"));
FMQ_COMPARE(ro->receivedPublishes.at(0).getQos(), 1);
FMQ_COMPARE(ro->receivedPublishes.at(0).getPayloadView(), "late to the party");
}
}
}
================================================
FILE: FlashMQTests/conffiletemp.cpp
================================================
#include "conffiletemp.h"
#include <vector>
#include <unistd.h>
#include <stdexcept>
ConfFileTemp::ConfFileTemp()
{
const std::string templateName("/tmp/flashmqconf_XXXXXX");
std::vector<char> nameBuf(templateName.size() + 1, 0);
std::copy(templateName.begin(), templateName.end(), nameBuf.begin());
this->fd = mkstemp(nameBuf.data());
if (this->fd < 0)
{
throw std::runtime_error("mkstemp error.");
}
this->filePath = nameBuf.data();
}
ConfFileTemp::~ConfFileTemp()
{
closeFile();
if (!this->filePath.empty())
unlink(this->filePath.c_str());
}
const std::string &ConfFileTemp::getFilePath() const
{
if (fd > 0)
throw std::runtime_error("You first need to close the file before using it.");
return this->filePath;
}
void ConfFileTemp::writeLine(const std::string &line)
{
if (write(this->fd, line.c_str(), line.size()) < 0)
throw std::runtime_error("Config file write failed");
if (write(this->fd, "\n", 1) < 0)
throw std::runtime_error("Config file write failed");
}
void ConfFileTemp::closeFile()
{
if (this->fd < 0)
return;
close(this->fd);
this->fd = -1;
}
================================================
FILE: FlashMQTests/conffiletemp.h
================================================
#ifndef CONFFILETEMP_H
#define CONFFILETEMP_H
#include <string>
class ConfFileTemp
{
int fd = -1;
std::string filePath;
public:
ConfFileTemp();
~ConfFileTemp();
const std::string &getFilePath() const;
void writeLine(const std::string &line);
void closeFile();
ConfFileTemp &operator=(const ConfFileTemp &other) = delete;
ConfFileTemp(const ConfFileTemp &other) = delete;
ConfFileTemp(ConfFileTemp &&other) = delete;
};
#endif // CONFFILETEMP_H
================================================
FILE: FlashMQTests/configtests.cpp
================================================
#include "maintests.h"
#include "testhelpers.h"
#include "conffiletemp.h"
#include "exceptions.h"
#include "settings.h"
void MainTests::test_loading_second_value()
{
/* this is expected to work*/
{
ConfFileTemp config;
config.writeLine("bridge {");
config.writeLine(" address localhost");
config.writeLine(" publish send/this 1"); // this value should be different from the default (0)
config.writeLine("}");
config.closeFile();
ConfigFileParser parser(config.getFilePath());
parser.loadFile(false);
Settings settings = parser.getSettings();
std::list<BridgeConfig> bridges = settings.stealBridges();
FMQ_VERIFY(!bridges.empty());
BridgeConfig &bridge = bridges.front();
FMQ_COMPARE(bridge.publishes[0].topic, "send/this");
FMQ_COMPARE(bridge.publishes[0].qos, (uint8_t)1);
}
/* this is expecte to fail because "address" doesn't take a second value */
{
ConfFileTemp config;
config.writeLine("bridge {");
config.writeLine(" address localhost thisisnotok");
config.writeLine(" publish send/this 1");
config.writeLine("}");
config.closeFile();
ConfigFileParser parser(config.getFilePath());
try
{
parser.loadFile(false);
FMQ_FAIL("The config parser is too liberal");
}
catch (ConfigFileException &ex)
{
/* Excellent, what we wanted */
}
}
}
void MainTests::test_parsing_numbers()
{
/* this should work: 180 */
{
ConfFileTemp config;
config.writeLine("expire_sessions_after_seconds 180");
config.closeFile();
ConfigFileParser parser(config.getFilePath());
parser.loadFile(false);
Settings settings = parser.getSettings();
FMQ_COMPARE(settings.expireSessionsAfterSeconds, (uint32_t)180);
}
/* this should fail: 180days */
{
ConfFileTemp config;
config.writeLine("expire_sessions_after_seconds 180days");
config.closeFile();
ConfigFileParser parser(config.getFilePath());
try
{
parser.loadFile(false);
FMQ_FAIL("The parser was too liberal");
}
catch (ConfigFileException&)
{
/* Good! This is where we want to end up in */
}
}
/* this should also fail: 180 days */
{
ConfFileTemp config;
config.writeLine("expire_sessions_after_seconds 180 days");
config.closeFile();
ConfigFileParser parser(config.getFilePath());
try
{
parser.loadFile(false);
FMQ_FAIL("The parser was too liberal");
}
catch (ConfigFileException&)
{
/* Good! This is where we want to end up in */
}
}
/* Last one that should fail: 180 days and a bit */
{
ConfFileTemp config;
config.writeLine("expire_sessions_after_seconds 180 days and a bit more");
config.closeFile();
ConfigFileParser parser(config.getFilePath());
try
{
parser.loadFile(false);
FMQ_FAIL("The parser was too liberal");
}
catch (ConfigFileException&)
{
/* Good! This is where we want to end up in */
}
}
}
void MainTests::testStringDistances()
{
FMQ_COMPARE(distanceBetweenStrings("", ""), (unsigned int)0);
FMQ_COMPARE(distanceBetweenStrings("dog", ""), (unsigned int)3);
FMQ_COMPARE(distanceBetweenStrings("", "dog"), (unsigned int)3);
FMQ_COMPARE(distanceBetweenStrings("dog", "horse"), (unsigned int)4);
FMQ_COMPARE(distanceBetweenStrings("horse", "dog"), (unsigned int)4);
FMQ_COMPARE(distanceBetweenStrings("industry", "interest"), (unsigned int)6);
FMQ_COMPARE(distanceBetweenStrings("kitten", "sitting"), (unsigned int)3);
FMQ_COMPARE(distanceBetweenStrings("uninformed", "uniformed"), (unsigned int)1);
}
void MainTests::testConfigSuggestion()
{
// User made a small typo: 'session' instead of 'sessions'
{
ConfFileTemp config;
config.writeLine("expire_session_after_seconds 180");
config.closeFile();
ConfigFileParser parser(config.getFilePath());
try
{
parser.loadFile(false);
FMQ_FAIL("The parser is too liberal");
}
catch (ConfigFileException &ex)
{
FMQ_COMPARE(ex.what(), "Config key 'expire_session_after_seconds' is not valid (here). Did you mean: expire_sessions_after_seconds ?");
}
}
// User entered gibberish. Let's not suggest gibberish back
{
ConfFileTemp config;
config.writeLine("foobarbaz 180");
config.closeFile();
ConfigFileParser parser(config.getFilePath());
try
{
parser.loadFile(false);
FMQ_FAIL("The parser is too liberal");
}
catch (ConfigFileException &ex)
{
FMQ_COMPARE(ex.what(), "Config key 'foobarbaz' is not valid (here).");
}
}
}
void MainTests::testFlags()
{
Flags<PersistenceDataToSave> flags;
FMQ_VERIFY(flags.hasNone());
flags.setAll();
FMQ_VERIFY(flags.hasAll());
flags.clearFlag(PersistenceDataToSave::BridgeInfo);
FMQ_VERIFY(flags.hasFlagSet(PersistenceDataToSave::SessionsAndSubscriptions));
FMQ_VERIFY(flags.hasFlagSet(PersistenceDataToSave::RetainedMessages));
FMQ_VERIFY(!flags.hasFlagSet(PersistenceDataToSave::BridgeInfo));
flags.clearFlag(PersistenceDataToSave::RetainedMessages);
FMQ_VERIFY(flags.hasFlagSet(PersistenceDataToSave::SessionsAndSubscriptions));
FMQ_VERIFY(!flags.hasFlagSet(PersistenceDataToSave::RetainedMessages));
FMQ_VERIFY(!flags.hasFlagSet(PersistenceDataToSave::BridgeInfo));
flags.clearFlag(PersistenceDataToSave::SessionsAndSubscriptions);
FMQ_VERIFY(!flags.hasFlagSet(PersistenceDataToSave::SessionsAndSubscriptions));
FMQ_VERIFY(!flags.hasFlagSet(PersistenceDataToSave::RetainedMessages));
FMQ_VERIFY(!flags.hasFlagSet(PersistenceDataToSave::BridgeInfo));
flags.setFlag(PersistenceDataToSave::SessionsAndSubscriptions);
FMQ_VERIFY(flags.hasFlagSet(PersistenceDataToSave::SessionsAndSubscriptions));
FMQ_VERIFY(!flags.hasFlagSet(PersistenceDataToSave::RetainedMessages));
FMQ_VERIFY(!flags.hasFlagSet(PersistenceDataToSave::BridgeInfo));
flags.setFlag(PersistenceDataToSave::RetainedMessages);
FMQ_VERIFY(flags.hasFlagSet(PersistenceDataToSave::SessionsAndSubscriptions));
FMQ_VERIFY(flags.hasFlagSet(PersistenceDataToSave::RetainedMessages));
FMQ_VERIFY(!flags.hasFlagSet(PersistenceDataToSave::BridgeInfo));
flags.setFlag(PersistenceDataToSave::BridgeInfo);
FMQ_VERIFY(flags.hasFlagSet(PersistenceDataToSave::SessionsAndSubscriptions));
FMQ_VERIFY(flags.hasFlagSet(PersistenceDataToSave::RetainedMessages));
FMQ_VERIFY(flags.hasFlagSet(PersistenceDataToSave::BridgeInfo));
flags.clearAll();
FMQ_VERIFY(flags.hasNone());
}
================================================
FILE: FlashMQTests/dnstests.cpp
================================================
#include "maintests.h"
#include "testhelpers.h"
#include "utils.h"
void MainTests::testDnsResolver()
{
try
{
DnsResolver resolver;
resolver.query("demo.flashmq.org", ListenerProtocol::IPv46, std::chrono::milliseconds(5000));
int count = 0;
while (++count < 100)
{
std::list<FMQSockaddr> results = resolver.getResult();
if (!results.empty())
{
QVERIFY(std::any_of(results.begin(), results.end(), [](FMQSockaddr &x){return x.getText() == "89.188.6.194";}));
QVERIFY(std::any_of(results.begin(), results.end(), [](FMQSockaddr &x){return x.getText() == "2a01:1b0:7996:418:83:137:146:230";}));
break;
}
usleep(10000);
}
if (count >= 100)
QVERIFY(false);
}
catch (std::exception &ex)
{
QVERIFY2(false, ex.what());
}
}
void MainTests::testDnsResolverDontCancel()
{
try
{
DnsResolver resolver;
resolver.query("demo.flashmq.org", ListenerProtocol::IPv46, std::chrono::milliseconds(5000));
resolver.query("demo.flashmq.org", ListenerProtocol::IPv46, std::chrono::milliseconds(5000));
int count = 0;
while (++count < 100)
{
std::list<FMQSockaddr> results = resolver.getResult();
if (!results.empty())
{
QVERIFY(std::any_of(results.begin(), results.end(), [](FMQSockaddr &x){return x.getText() == "89.188.6.194";}));
QVERIFY(std::any_of(results.begin(), results.end(), [](FMQSockaddr &x){return x.getText() == "2a01:1b0:7996:418:83:137:146:230";}));
break;
}
usleep(10000);
}
if (count >= 100)
QVERIFY(false);
}
catch (std::exception &ex)
{
QVERIFY2(false, ex.what());
}
}
void MainTests::testDnsResolverSecondQuery()
{
try
{
DnsResolver resolver;
for (int i = 0; i < 2; i++)
{
resolver.query("demo.flashmq.org", ListenerProtocol::IPv46, std::chrono::milliseconds(5000));
int count = 0;
while (++count < 100)
{
std::list<FMQSockaddr> results = resolver.getResult();
if (!results.empty())
{
QVERIFY(std::any_of(results.begin(), results.end(), [](FMQSockaddr &x){return x.getText() == "89.188.6.194";}));
QVERIFY(std::any_of(results.begin(), results.end(), [](FMQSockaddr &x){return x.getText() == "2a01:1b0:7996:418:83:137:146:230";}));
break;
}
usleep(10000);
}
if (count >= 100)
QVERIFY(false);
}
}
catch (std::exception &ex)
{
QVERIFY2(false, ex.what());
}
}
void MainTests::testDnsResolverInvalid()
{
try
{
DnsResolver resolver;
const std::string rnd = getSecureRandomString(16);
const std::string domain = rnd + ".flashmq.org";
resolver.query(domain, ListenerProtocol::IPv46, std::chrono::milliseconds(5000));
int count = 0;
while (count++ < 60)
{
std::list<FMQSockaddr> results = resolver.getResult();
if (!results.empty())
{
for (const auto &r : results)
{
std::cerr << "Wrong DNS result: " << r.getText() << std::endl;
}
QVERIFY2(false, "A DNS result was returned when we expected nothing.");
break;
}
usleep(100000);
}
QVERIFY2(false, "It took too long to get a result. That's weird, because we should have gotten a timeout exception.");
}
catch (std::exception &ex)
{
const std::string err = str_tolower(ex.what());
std::cout << "For reference, the error that we're scanning: " << err << std::endl;
QVERIFY(strContains(err, "name or service not known"));
}
}
void MainTests::testGetResultWhenThereIsNone()
{
try
{
DnsResolver resolver;
std::list<FMQSockaddr> results = resolver.getResult();
QVERIFY(results.empty());
QVERIFY(false);
}
catch (std::exception &ex)
{
std::string err = str_tolower(ex.what());
QVERIFY(strContains(err, "no dns query in progress"));
}
}
================================================
FILE: FlashMQTests/filecloser.cpp
================================================
#include "filecloser.h"
#include <unistd.h>
FileCloser::FileCloser(int fd) :
fd(fd)
{
}
FileCloser::~FileCloser()
{
if (fd >= 0)
close(fd);
fd = -1;
}
================================================
FILE: FlashMQTests/filecloser.h
================================================
#ifndef FILECLOSER_H
#define FILECLOSER_H
class FileCloser
{
int fd = -1;
public:
FileCloser(int fd);
~FileCloser();
};
#endif // FILECLOSER_H
================================================
FILE: FlashMQTests/flashmqtempdir.cpp
================================================
#include "flashmqtempdir.h"
#include <sys/types.h>
#include <unistd.h>
#include <filesystem>
#include "utils.h"
FlashMQTempDir::FlashMQTempDir()
{
const std::string templateName(std::filesystem::temp_directory_path() / "flashmq_test_XXXXXX");
std::vector<char> nameBuf(templateName.size() + 1, 0);
std::copy(templateName.begin(), templateName.end(), nameBuf.begin());
this->path = std::string(mkdtemp(nameBuf.data()));
}
FlashMQTempDir::~FlashMQTempDir()
{
if (this->path.empty() || !strContains(this->path, "flashmq_test_"))
return;
// Not pretty, but whatever works...
int pid = fork();
if (pid == 0)
{
execlp("rm", "rm", "-rf", "--", this->path.c_str(), (char*)NULL);
}
}
const std::filesystem::path &FlashMQTempDir::getPath() const
{
return this->path;
}
================================================
FILE: FlashMQTests/flashmqtempdir.h
================================================
#ifndef FLASHMQTEMPDIR_H
#define FLASHMQTEMPDIR_H
#include <filesystem>
#include <stdlib.h>
#include <string>
#include <vector>
class FlashMQTempDir
{
std::filesystem::path path;
public:
FlashMQTempDir();
~FlashMQTempDir();
const std::filesystem::path &getPath() const;
};
#endif // FLASHMQTEMPDIR_H
================================================
FILE: FlashMQTests/main.cpp
================================================
#include <iostream>
#include <vector>
#include "maintests.h"
void printHelp(const std::string &arg0)
{
std::cout << std::endl;
std::cout << "Usage: " << arg0 << " [ --skip-tests-with-internet ] [ --skip-server-tests ] " << " <tests> " << std::endl;
}
int main(int argc, char *argv[])
{
bool skip_tests_with_internet = false;
bool skip_server_tests = false;
bool abort_on_first_fail = false;
std::vector<std::string> tests;
bool option_list_terminated = false;
for (int i = 1; i < argc ; i++)
{
const std::string name(argv[i]);
if (option_list_terminated)
tests.push_back(name);
else if (name == "--")
option_list_terminated = true;
else if (name == "--help")
{
printHelp(argv[0]);
return 1;
}
else if (name == "--skip-tests-with-internet")
skip_tests_with_internet = true;
else if (name == "--skip-server-tests")
skip_server_tests = true;
else if (name == "--abort-on-first-fail")
abort_on_first_fail = true;
else if (name.find("--") == 0)
{
std::cerr << "Unknown argument " << name << std::endl;
printHelp(argv[0]);
return 1;
}
else
tests.push_back(name);
}
MainTests maintests;
if (!maintests.test(skip_tests_with_internet, skip_server_tests, abort_on_first_fail, tests))
return 1;
return 0;
}
================================================
FILE: FlashMQTests/mainappasfork.cpp
================================================
#include "mainappasfork.h"
#include <mainapp.h>
#include "signal.h"
#include "fmqmain.h"
#include "sys/wait.h"
std::string MainAppAsFork::getConfigFileFromArgs(const std::vector<std::string> &args)
{
std::string result = "";
bool next = false;
for(const std::string &arg : args)
{
if (arg == "--config-file")
{
next = true;
continue;
}
if (next)
{
result = arg;
break;
}
}
return result;
}
MainAppAsFork::MainAppAsFork()
{
defaultConf.writeLine("allow_anonymous true");
defaultConf.closeFile();
args.push_back("--config-file");
args.push_back(defaultConf.getFilePath());
}
MainAppAsFork::MainAppAsFork(const std::vector<std::string> &args) :
args(args)
{
}
MainAppAsFork::~MainAppAsFork()
{
this->stop();
}
void MainAppAsFork::start()
{
// We must not have threads when we fork.
Logger::getInstance()->quit();
pid_t pid = fork();
if (pid < 0)
throw std::runtime_error("What the fork?");
if (pid == 0)
{
try
{
std::list<std::vector<char>> argCopies;
const std::string programName = "FlashMQTests";
std::vector<char> programNameCopy(programName.size() + 1, 0);
std::copy(programName.begin(), programName.end(), programNameCopy.begin());
argCopies.push_back(std::move(programNameCopy));
for (const std::string &arg : args)
{
std::vector<char> copyArg(arg.size() + 1, 0);
std::copy(arg.begin(), arg.end(), copyArg.begin());
argCopies.push_back(std::move(copyArg));
}
char *argv[256];
memset(argv, 0, 256*sizeof (char*));
int i = 0;
for (std::vector<char> © : argCopies)
{
argv[i++] = copy.data();
}
int r = fmqmain(i, argv);
::exit(r);
}
catch (std::exception &ex)
{
std::cout << "The forked process threw an exception: " << ex.what() << std::endl;
std::cerr << "The forked process threw an exception: " << ex.what() << std::endl;
}
// Does not call destructors.
abort();
}
this->child = pid;
}
void MainAppAsFork::stop()
{
if (this->child <= 0)
return;
kill(this->child, SIGTERM);
int status = 0;
waitpid(this->child, &status, 0);
this->child = -1;
}
void MainAppAsFork::waitForStarted(int port)
{
int sockfd = check<std::runtime_error>(socket(AF_INET, SOCK_STREAM, 0));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
struct sockaddr *addr2 = reinterpret_cast<sockaddr*>(&addr);
bool timeout = false;
int n = 0;
while (connect(sockfd, addr2, sizeof(struct sockaddr_in)) != 0)
{
std::this_thread::sleep_for(std::chrono::milliseconds(20));
if (n++ > 100)
{
timeout = true;
break;
}
}
close(sockfd);
if (timeout)
throw std::runtime_error("Forked mainapp failed to start?");
}
================================================
FILE: FlashMQTests/mainappasfork.h
================================================
#ifndef MAINAPPASFORK_H
#define MAINAPPASFORK_H
#include <vector>
#include <string>
#include <unistd.h>
#include "conffiletemp.h"
/**
* @brief The MainAppAsFork class provides a way to run the FlashMQ server in a separate process for tests. This is convenient for isolation,
* running multiple versions (bridging) and avoids conflicts with (thread) globals. But, it can also be inconvenient, because you
* don't have access to the internal state (for assertions) and on abnormal exist the child processes may linger (although we
* could devise stuff against that).
*/
class MainAppAsFork
{
pid_t child = -1;
std::vector<std::string> args;
ConfFileTemp defaultConf;
public:
static std::string getConfigFileFromArgs(const std::vector<std::string> &args);
MainAppAsFork();
MainAppAsFork(const std::vector<std::string> &args);
~MainAppAsFork();
void start();
void stop();
void waitForStarted(int port=21883);
};
#endif // MAINAPPASFORK_H
================================================
FILE: FlashMQTests/mainappinthread.cpp
================================================
#include "mainappinthread.h"
MainAppInThread::MainAppInThread()
{
}
MainAppInThread::MainAppInThread(const std::vector<std::string> &args) :
mArgs(args)
{
}
MainAppInThread::~MainAppInThread()
{
stopApp();
}
void MainAppInThread::start()
{
const auto args = mArgs;
auto thread_task = [this, args]()
{
std::list<std::vector<char>> argCopies;
const std::string programName = "FlashMQTests";
std::vector<char> programNameCopy(programName.size() + 1, 0);
std::copy(programName.begin(), programName.end(), programNameCopy.begin());
argCopies.push_back(std::move(programNameCopy));
for (const std::string &arg : args)
{
std::vector<char> copyArg(arg.size() + 1, 0);
std::copy(arg.begin(), arg.end(), copyArg.begin());
argCopies.push_back(std::move(copyArg));
}
char *argv[256];
memset(argv, 0, 256*sizeof (char*));
int i = 0;
for (std::vector<char> © : argCopies)
{
argv[i++] = copy.data();
}
std::shared_ptr<MainApp> mainapp;
{
auto mainapp_locked = mMainApp.lock();
std::shared_ptr<MainApp> &mainapp_member = *mainapp_locked;
mainapp_member = MainApp::initMainApp(i, argv);
mainapp = mainapp_member;
}
// A hack: when I supply args I probably define a config for auth stuff.
if (args.empty())
mainapp->settings.allowAnonymous = true;
mainapp->start();
};
this->thread = std::thread(thread_task);
}
void MainAppInThread::stopApp()
{
{
auto mainapp_locked = mMainApp.lock();
std::shared_ptr<MainApp> &mainapp = *mainapp_locked;
if (mainapp)
mainapp->quit();
}
if (this->thread.joinable())
this->thread.join();
}
void MainAppInThread::waitForStarted()
{
auto started_f = [this]
{
std::shared_ptr<MainApp> mainapp;
{
auto mainapp_locked = mMainApp.lock();
mainapp = *mainapp_locked;
}
if (!mainapp)
return false;
return mainapp->getStarted();
};
int n = 0;
while(!started_f())
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
if (n++ > 500)
throw std::runtime_error("Waiting for app to start failed.");
}
}
================================================
FILE: FlashMQTests/mainappinthread.h
================================================
#ifndef MAINAPPINTHREAD_H
#define MAINAPPINTHREAD_H
#include <thread>
#include "mainapp.h"
#include "mutexowned.h"
class MainAppInThread
{
std::thread thread;
const std::vector<std::string> mArgs;
MutexOwned<std::shared_ptr<MainApp>> mMainApp;
public:
MainAppInThread();
MainAppInThread(const std::vector<std::string> &args);
~MainAppInThread();
void start();
void stopApp();
void waitForStarted();
};
#endif // MAINAPPINTHREAD_H
================================================
FILE: FlashMQTests/maintests.cpp
================================================
#include <unistd.h>
#include "maintests.h"
#include "testhelpers.h"
#include "testinitializer.h"
#include "threadglobals.h"
void MainTests::testAsserts()
{
{
auto a = [] {
assert_count = 0;
assert_fail_count = 0;
QCOMPARE(1, 1);
};
a();
if (assert_count != 1 || assert_fail_count != 0)
throw std::exception();
}
{
auto a = [] {
assert_count = 0;
assert_fail_count = 0;
QCOMPARE(1, 2);
};
a();
if (assert_count != 1 || assert_fail_count != 1)
throw std::exception();
}
{
auto a = [] {
assert_count = 0;
assert_fail_count = 0;
QVERIFY(true);
};
a();
if (assert_count != 1 || assert_fail_count != 0)
throw std::exception();
}
{
auto a = [] {
assert_count = 0;
assert_fail_count = 0;
QVERIFY(false);
};
a();
if (assert_count != 1 || assert_fail_count != 1)
throw std::exception();
}
{
auto a = [] {
assert_count = 0;
assert_fail_count = 0;
QVERIFY2(true, "");
};
a();
if (assert_count != 1 || assert_fail_count != 0)
throw std::exception();
}
{
auto a = [] {
assert_count = 0;
assert_fail_count = 0;
QVERIFY2(false, "");
};
a();
if (assert_count != 1 || assert_fail_count != 1)
throw std::exception();
}
{
auto a = [] {
assert_count = 0;
assert_fail_count = 0;
QFAIL("");
};
a();
if (assert_count != 1 || assert_fail_count != 1)
throw std::exception();
}
{
auto a = [] {
assert_count = 0;
assert_fail_count = 0;
MYCASTCOMPARE(static_cast<ssize_t>(1), static_cast<size_t>(1));
};
a();
if (assert_count != 1 || assert_fail_count != 0)
throw std::exception();
}
{
auto a = [] {
assert_count = 0;
assert_fail_count = 0;
MYCASTCOMPARE(static_cast<ssize_t>(1), static_cast<size_t>(2));
};
a();
if (assert_count != 1 || assert_fail_count != 1)
throw std::exception();
}
}
void MainTests::initBeforeEachTest(const std::vector<std::string> &args, bool startServer)
{
mainApp.reset();
if (startServer)
{
mainApp = std::make_unique<MainAppInThread>(args);
mainApp->start();
mainApp->waitForStarted();
}
// We test functions directly that the server normally only calls from worker threads, in which thread data is available. This is kind of a dummy-fix, until
// we actually need correct thread data at those points (at this point, it's only to increase message counters).
this->dummyThreadData = std::make_shared<ThreadData>(666, settings, pluginLoader, std::weak_ptr<MainApp>());
ThreadGlobals::assignThreadData(dummyThreadData);
ThreadGlobals::assignSettings(&this->settings);
}
void MainTests::initBeforeEachTest(bool startServer)
{
std::vector<std::string> args;
initBeforeEachTest(args, startServer);
}
void MainTests::cleanupAfterEachTest()
{
if (this->mainApp)
this->mainApp->stopApp();
this->mainApp.reset();
}
void MainTests::registerFunction(const std::string &name, std::function<void ()> f, bool requiresServer, bool requiresInternet)
{
TestFunction &tf = testFunctions[name];
tf.f = f;
tf.requiresServer = requiresServer;
tf.requiresInternet = requiresInternet;
}
void MainTests::testDummy()
{
FMQ_COMPARE(1,1);
}
MainTests::MainTests()
{
static int instanceCount = 0;
if (instanceCount++ > 0)
throw std::runtime_error("Don't instantiate this more than once.");
asserts_print = false;
testAsserts();
asserts_print = true;
assert_count = 0;
assert_fail_count = 0;
/*
* Forking tests need to be done first (by alphabet), otherwise you break everything with DNS. This is a limitation
* of getaddrinfo_a(). So, name them appropriately.
*/
REGISTER_FUNCTION(forkingTestForkingTestServer);
REGISTER_FUNCTION(forkingTestSaveAndLoadDelayedWill);
REGISTER_FUNCTION(forkingTestBridgeWithLocalAndRemotePrefix);
REGISTER_FUNCTION(forkingTestBridgePrefixesOtherClientsUnaffected);
REGISTER_FUNCTION(forkingTestBridgeWithOnlyRemotePrefix);
REGISTER_FUNCTION(forkingTestBridgeWithOnlyLocalPrefix);
REGISTER_FUNCTION(forkingTestBridgeOutgoingTopicEqualsPrefix);
REGISTER_FUNCTION(forkingTestBridgeIncomingTopicEqualsPrefix);
REGISTER_FUNCTION(forkingTestBridgeWithLocalAndRemotePrefixQueuedQoS);
REGISTER_FUNCTION(forkingTestBridgeZWithLocalAndRemotePrefixRetained);
REGISTER_FUNCTION(testDummy);
REGISTER_FUNCTION3(test_circbuf);
REGISTER_FUNCTION3(test_circbuf_unwrapped_doubling);
REGISTER_FUNCTION3(test_circbuf_wrapped_doubling);
REGISTER_FUNCTION3(test_circbuf_full_wrapped_buffer_doubling);
REGISTER_FUNCTION3(test_cirbuf_vector_methods);
REGISTER_FUNCTION3(test_validSubscribePath);
REGISTER_FUNCTION(test_retained);
REGISTER_FUNCTION(test_retained_double_set);
REGISTER_FUNCTION(test_retained_mode_drop);
REGISTER_FUNCTION(test_retained_mode_downgrade);
REGISTER_FUNCTION(test_retained_mode_no_retain);
REGISTER_FUNCTION(test_retained_changed);
REGISTER_FUNCTION(test_retained_removed);
REGISTER_FUNCTION(test_retained_tree);
REGISTER_FUNCTION(test_retained_global_expire);
REGISTER_FUNCTION(test_retained_per_message_expire);
REGISTER_FUNCTION(test_retained_tree_purging);
REGISTER_FUNCTION(testRetainAsPublished);
REGISTER_FUNCTION(testRetainAsPublishedNegative);
REGISTER_FUNCTION(testRetainedParentOfWildcard);
REGISTER_FUNCTION(testRetainedWildcard);
REGISTER_FUNCTION(testRetainedAclReadCheck);
REGISTER_FUNCTION(testRetainHandlingDontGiveRetain);
REGISTER_FUNCTION(testRetainHandlingDontGiveRetainOnExistingSubscription);
REGISTER_FUNCTION(test_various_packet_sizes);
REGISTER_FUNCTION3(test_acl_tree);
REGISTER_FUNCTION3(test_acl_tree2);
REGISTER_FUNCTION3(test_acl_patterns_username);
REGISTER_FUNCTION3(test_acl_patterns_clientid);
REGISTER_FUNCTION(test_loading_acl_file);
REGISTER_FUNCTION3(test_loading_second_value);
REGISTER_FUNCTION3(test_parsing_numbers);
REGISTER_FUNCTION3(test_validUtf8Generic);
#ifndef FMQ_NO_SSE
REGISTER_FUNCTION3(test_sse_split);
REGISTER_FUNCTION3(test_validUtf8Sse);
REGISTER_FUNCTION3(test_utf8_nonchars);
REGISTER_FUNCTION3(test_utf8_overlong);
REGISTER_FUNCTION3(test_utf8_compare_implementation);
#endif
REGISTER_FUNCTION3(testPacketInt16Parse);
REGISTER_FUNCTION3(testRetainedMessageDB);
REGISTER_FUNCTION3(testRetainedMessageDBNotPresent);
REGISTER_FUNCTION3(testRetainedMessageDBEmptyList);
REGISTER_FUNCTION3(testSavingSessions);
REGISTER_FUNCTION3(testParsePacket);
REGISTER_FUNCTION3(testbufferToMqttPacketsFuzz);
REGISTER_FUNCTION(testDowngradeQoSOnSubscribeQos2to2);
REGISTER_FUNCTION(testDowngradeQoSOnSubscribeQos2to1);
REGISTER_FUNCTION(testDowngradeQoSOnSubscribeQos2to0);
REGISTER_FUNCTION(testDowngradeQoSOnSubscribeQos1to1);
REGISTER_FUNCTION(testDowngradeQoSOnSubscribeQos1to0);
REGISTER_FUNCTION(testDowngradeQoSOnSubscribeQos0to0);
REGISTER_FUNCTION(testNotMessingUpQosLevels);
REGISTER_FUNCTION(testUnSubscribe);
REGISTER_FUNCTION(testUnsubscribeNonExistingWildcard);
REGISTER_FUNCTION(testBasicsWithFlashMQTestClient);
REGISTER_FUNCTION(testDontRemoveSessionGivenToNewClientWithSameId);
REGISTER_FUNCTION(testKeepSubscriptionOnKickingOutExistingClientWithCleanSessionFalse);
REGISTER_FUNCTION(testPickUpSessionWithSubscriptionsAfterDisconnect);
REGISTER_FUNCTION(testMqtt3will);
REGISTER_FUNCTION(testMqtt3NoWillOnDisconnect);
REGISTER_FUNCTION(testMqtt5NoWillOnDisconnect);
REGISTER_FUNCTION(testMqtt5DelayedWill);
REGISTER_FUNCTION(testMqtt5DelayedWillAlwaysOnSessionEnd);
REGISTER_FUNCTION(testWillOnSessionTakeOvers);
REGISTER_FUNCTION(testOverrideWillDelayOnSessionDestructionByTakeOver);
REGISTER_FUNCTION(testDisabledWills);
REGISTER_FUNCTION(testMqtt5DelayedWillsDisabled);
REGISTER_FUNCTION3(testStringDistances);
REGISTER_FUNCTION3(testConfigSuggestion);
REGISTER_FUNCTION3(testFlags);
REGISTER_FUNCTION(testIncomingTopicAlias);
REGISTER_FUNCTION(testOutgoingTopicAlias);
REGISTER_FUNCTION(testOutgoingTopicAliasBeyondMax);
REGISTER_FUNCTION(testOutgoingTopicAliasStoredPublishes);
REGISTER_FUNCTION(testReceivingRetainedMessageWithQoS);
REGISTER_FUNCTION(testQosDowngradeOnOfflineClients);
REGISTER_FUNCTION(testPacketOrderOnSessionPickup);
REGISTER_FUNCTION(testUserProperties);
REGISTER_FUNCTION(testMessageExpiry);
REGISTER_FUNCTION(testExpiredQueuedMessages);
REGISTER_FUNCTION(testQoSPublishQueue);
REGISTER_FUNCTION3(testQoSPublishQueueMemoryLeak);
REGISTER_FUNCTION3(testTimePointToAge);
REGISTER_FUNCTION(testMosquittoPasswordFile);
REGISTER_FUNCTION(testOverrideAllowAnonymousToTrue);
REGISTER_FUNCTION(testOverrideAllowAnonymousToFalse);
REGISTER_FUNCTION(testKeepAllowAnonymousFalse);
REGISTER_FUNCTION(testAllowAnonymousWithoutPasswordsLoaded);
REGISTER_FUNCTION3(testAddrMatchesSubnetIpv4);
REGISTER_FUNCTION3(testAddrMatchesSubnetIpv6);
REGISTER_FUNCTION(testSharedSubscribersUnit);
REGISTER_FUNCTION(testSharedSubscribers);
REGISTER_FUNCTION(testDisconnectedSharedSubscribers);
REGISTER_FUNCTION(testUnsubscribedSharedSubscribers);
REGISTER_FUNCTION(testSharedSubscribersSurviveRestart);
REGISTER_FUNCTION(testSharedSubscriberDoesntGetRetainedMessages);
REGISTER_FUNCTION(testExtendedAuthOneStepSucceed);
REGISTER_FUNCTION(testExtendedAuthOneStepDeny);
REGISTER_FUNCTION(testExtendedAuthOneStepBadAuthMethod);
REGISTER_FUNCTION(testExtendedAuthTwoStep);
REGISTER_FUNCTION(testExtendedAuthTwoStepSecondStepFail);
REGISTER_FUNCTION(testExtendedReAuth);
REGISTER_FUNCTION(testExtendedReAuthTwoStep);
REGISTER_FUNCTION(testExtendedReAuthFail);
REGISTER_FUNCTION(testSimpleAuthAsync);
REGISTER_FUNCTION(testFailedAsyncClientCrashOnSession);
REGISTER_FUNCTION(testAsyncWithImmediateFollowUpPackets);
REGISTER_FUNCTION(testAsyncWithException);
REGISTER_FUNCTION(testPluginAuthFail);
REGISTER_FUNCTION(testPluginAuthSucceed);
REGISTER_FUNCTION(testPluginOnDisconnect);
REGISTER_FUNCTION(testPluginGetClientAddress);
REGISTER_FUNCTION(testChangePublish);
REGISTER_FUNCTION(testClientRemovalByPlugin);
REGISTER_FUNCTION(testSubscriptionRemovalByPlugin);
REGISTER_FUNCTION(testPublishByPlugin);
REGISTER_FUNCTION(testWillDenialByPlugin);
REGISTER_FUNCTION(testPluginMainInit);
REGISTER_FUNCTION2(testAsyncCurl, true, true);
REGISTER_FUNCTION(testSubscribeWithoutRetainedDelivery);
REGISTER_FUNCTION(testDontUpgradeWildcardDenyMode);
REGISTER_FUNCTION(testAlsoDontApproveOnErrorInPluginWithWildcardDenyMode);
REGISTER_FUNCTION(testDenyWildcardSubscription);
REGISTER_FUNCTION(testUserPropertiesPresent);
REGISTER_FUNCTION(testPublishInThread);
REGISTER_FUNCTION(testPublishToItself);
REGISTER_FUNCTION(testNoLocalPublishToItself);
REGISTER_FUNCTION3(testTopicMatchingInSubscriptionTree);
REGISTER_FUNCTION2(testDnsResolver, false, true);
REGISTER_FUNCTION2(testDnsResolverDontCancel, false, true);
REGISTER_FUNCTION2(testDnsResolverSecondQuery, false, true);
REGISTER_FUNCTION2(testDnsResolverInvalid, false, true);
REGISTER_FUNCTION(testGetResultWhenThereIsNone);
REGISTER_FUNCTION(testWebsocketPing);
REGISTER_FUNCTION(testWebsocketCorruptLengthFrame);
REGISTER_FUNCTION(testWebsocketHugePing);
REGISTER_FUNCTION(testWebsocketManyBigPingFrames);
REGISTER_FUNCTION(testWebsocketClose);
REGISTER_FUNCTION3(testStartsWith);
REGISTER_FUNCTION3(testStringValuesParsing);
REGISTER_FUNCTION3(testStringValuesParsingEscaping);
REGISTER_FUNCTION3(testStringValuesFuzz);
REGISTER_FUNCTION3(testStringValuesInvalid);
REGISTER_FUNCTION2(testPreviouslyValidConfigFile, false, false);
REGISTER_FUNCTION3(testNoCopy);
REGISTER_FUNCTION3(testBase64);
REGISTER_FUNCTION(testSessionTakeoverOtherUsername);
REGISTER_FUNCTION(testCorrelationData);
REGISTER_FUNCTION(testSubscriptionIdOnlineClient);
REGISTER_FUNCTION(testSubscriptionIdOfflineClient);
REGISTER_FUNCTION(testSubscriptionIdRetainedMessages);
REGISTER_FUNCTION(testSubscriptionIdSharedSubscriptions);
REGISTER_FUNCTION(testSubscriptionIdChange);
REGISTER_FUNCTION(testSubscriptionIdOverlappingSubscriptions);
}
bool MainTests::test(bool skip_tests_with_internet, bool skip_server_tests, bool abort_on_first_fail, const std::vector<std::string> &tests)
{
int testCount = 0;
int testPassCount = 0;
int testFailCount = 0;
int testExceptionCount = 0;
std::map<std::string, TestFunction> *selectedTests = &this->testFunctions;
std::map<std::string, TestFunction> subset;
for(const std::string &test_name : tests)
{
auto pos = this->testFunctions.find(test_name);
if (pos == this->testFunctions.end())
{
std::cerr << "Test '" << test_name << "' not found." << std::endl;
return false;
}
subset[test_name] = pos->second;
}
if (!subset.empty())
{
selectedTests = ⊂
}
std::vector<std::string> failedTests;
for (const auto &pair : *selectedTests)
{
if (abort_on_first_fail && testFailCount > 0)
break;
const TestFunction &tf = pair.second;
if (skip_tests_with_internet && tf.requiresInternet)
continue;
if (skip_server_tests && tf.requiresServer)
continue;
testCount++;
try
{
std::cout << CYAN << "INIT" << COLOR_END << ": " << pair.first << std::endl;
if (!isatty(2))
std::cerr << "INIT: " << pair.first << std::endl;
TestInitializer testInitializer(this);
testInitializer.init(tf.requiresServer);
const int failCountBefore = assert_fail_count;
const int assertCountBefore = assert_count;
std::cout << CYAN << "RUN" << COLOR_END << ": " << pair.first << std::endl;
if (!isatty(2))
std::cerr << "RUN: " << pair.first << std::endl;
tf.f();
const int failCountAfter = assert_fail_count;
const int assertCountAfter = assert_count;
testInitializer.cleanup();
if (assertCountBefore == assertCountAfter)
{
std::cout << RED << "FAIL" << COLOR_END << ": " << pair.first << ": no asserts performed" << std::endl;
testFailCount++;
failedTests.push_back(pair.first);
}
else if (failCountBefore != failCountAfter)
{
std::cout << RED << "FAIL" << COLOR_END << ": " << pair.first << std::endl;
testFailCount++;
failedTests.push_back(pair.first);
}
else
{
std::cout << GREEN << "PASS" << COLOR_END << ": " << pair.first << std::endl;
testPassCount++;
}
}
catch (std::exception &ex)
{
// TODO: get details
testFailCount++;
testExceptionCount++;
failedTests.push_back(pair.first);
std::cout << RED << "FAIL EXCEPTION" << COLOR_END << ": " << pair.first << ": " << ex.what() << std::endl;
}
std::cout << std::endl;
}
Logger::getInstance()->quit();
std::cout << "Tests run: " << testCount << ". Passed: " << testPassCount << ". Failed: "
<< testFailCount << " (of which " << testExceptionCount << " exceptions). Total assertions: "
<< assert_count << "." << std::endl;
std::cout << std::endl << std::endl;
if (testCount == 0)
{
std::cout << std::endl << RED << "No tests ran." << COLOR_END << std::endl;
return false;
}
else if (assert_fail_count == 0 && testFailCount == 0)
{
std::cout << std::endl << GREEN << "TESTS PASSED" << COLOR_END << std::endl;
return true;
}
else
{
std::cout << "Failed tests: " << std::endl;
for (const std::string &test_name : failedTests)
{
std::cout << " - " << test_name << std::endl;
}
std::cout << std::endl << RED << "TESTS FAILED" << COLOR_END << std::endl;
return false;
}
}
================================================
FILE: FlashMQTests/maintests.h
================================================
#ifndef MAINTESTS_H
#define MAINTESTS_H
#include <memory>
#include <unordered_map>
#include <functional>
#include "mainappinthread.h"
#define REGISTER_FUNCTION(name) registerFunction(#name, std::bind(&MainTests::name, this))
#define REGISTER_FUNCTION2(name, server, internet) registerFunction(#name, std::bind(&MainTests::name, this), server, internet)
#define REGISTER_FUNCTION3(name) registerFunction(#name, std::bind(&MainTests::name, this), false, false)
struct TestFunction
{
std::function<void()> f;
bool requiresServer = true;
bool requiresInternet = false;
};
class MainTests
{
friend class TestInitializer;
std::shared_ptr<ThreadData> dummyThreadData;
std::unique_ptr<MainAppInThread> mainApp;
Settings settings;
std::shared_ptr<PluginLoader> pluginLoader = std::make_shared<PluginLoader>();
std::map<std::string, TestFunction> testFunctions;
void testAsserts();
void initBeforeEachTest(const std::vector<std::string> &args, bool startServer=true);
void initBeforeEachTest(bool startServer=true);
void cleanupAfterEachTest();
void registerFunction(const std::string &name, std::function<void ()> f, bool requiresServer=true, bool requiresInternet=false);
// Compatability for porting the tests away from Qt. The function names are too vague so want to phase them out.
void init(const std::vector<std::string> &args) { initBeforeEachTest(args);}
void init() {initBeforeEachTest();}
void cleanup() {cleanupAfterEachTest();}
void testParsePacketHelper(const std::string &topic, uint8_t from_qos, bool retain);
void testTopicMatchingInSubscriptionTreeHelper(const std::string &subscribe_topic, const std::string &publish_topic, int match_count=1);
void testDummy();
void test_circbuf();
void test_circbuf_unwrapped_doubling();
void test_circbuf_wrapped_doubling();
void test_circbuf_full_wrapped_buffer_doubling();
void test_cirbuf_vector_methods();
void test_validSubscribePath();
/**
* Retain tests
*/
void test_retained();
void test_retained_double_set();
void test_retained_mode_drop();
void test_retained_mode_downgrade();
void test_retained_mode_no_retain();
void test_retained_changed();
void test_retained_removed();
void test_retained_tree();
void test_retained_global_expire();
void test_retained_per_message_expire();
void test_retained_tree_purging();
void testRetainAsPublished();
void testRetainAsPublishedNegative();
void testRetainedParentOfWildcard();
void testRetainedWildcard();
void testRetainedAclReadCheck();
void testRetainHandlingDontGiveRetain();
void testRetainHandlingDontGiveRetainOnExistingSubscription();
void test_various_packet_sizes();
void test_acl_tree();
void test_acl_tree2();
void test_acl_patterns_username();
void test_acl_patterns_clientid();
void test_loading_acl_file();
void test_loading_second_value();
void test_parsing_numbers();
void test_validUtf8Generic();
#ifndef FMQ_NO_SSE
void test_sse_split();
void test_validUtf8Sse();
void test_utf8_nonchars();
void test_utf8_overlong();
void test_utf8_compare_implementation();
#endif
void testPacketInt16Parse();
void testRetainedMessageDB();
void testRetainedMessageDBNotPresent();
void testRetainedMessageDBEmptyList();
void testSavingSessions();
void testParsePacket();
void testbufferToMqttPacketsFuzz();
void testDowngradeQoSOnSubscribeQos2to2();
void testDowngradeQoSOnSubscribeQos2to1();
void testDowngradeQoSOnSubscribeQos2to0();
void testDowngradeQoSOnSubscribeQos1to1();
void testDowngradeQoSOnSubscribeQos1to0();
void testDowngradeQoSOnSubscribeQos0to0();
void testNotMessingUpQosLevels();
void testUnSubscribe();
void testUnsubscribeNonExistingWildcard();
void testBasicsWithFlashMQTestClient();
void testDontRemoveSessionGivenToNewClientWithSameId();
void testKeepSubscriptionOnKickingOutExistingClientWithCleanSessionFalse();
void testPickUpSessionWithSubscriptionsAfterDisconnect();
/**
* Will tests.
*/
void testMqtt3will();
void testMqtt3NoWillOnDisconnect();
void testMqtt5NoWillOnDisconnect();
void testMqtt5DelayedWill();
void testMqtt5DelayedWillAlwaysOnSessionEnd();
void testWillOnSessionTakeOvers();
void testOverrideWillDelayOnSessionDestructionByTakeOver();
void testDisabledWills();
void testMqtt5DelayedWillsDisabled();
void testStringDistances();
void testConfigSuggestion();
void testFlags();
void testIncomingTopicAlias();
void testOutgoingTopicAlias();
void testOutgoingTopicAliasBeyondMax();
void testOutgoingTopicAliasStoredPublishes();
void testReceivingRetainedMessageWithQoS();
void testQosDowngradeOnOfflineClients();
void testPacketOrderOnSessionPickup();
void testUserProperties();
void testMessageExpiry();
void testExpiredQueuedMessages();
void testQoSPublishQueue();
void testQoSPublishQueueMemoryLeak();
void testTimePointToAge();
void testMosquittoPasswordFile();
void testOverrideAllowAnonymousToTrue();
void testOverrideAllowAnonymousToFalse();
void testKeepAllowAnonymousFalse();
void testAllowAnonymousWithoutPasswordsLoaded();
void testAddrMatchesSubnetIpv4();
void testAddrMatchesSubnetIpv6();
/**
* Shared subscriptions tests
*/
void testSharedSubscribersUnit();
void testSharedSubscribers();
void testDisconnectedSharedSubscribers();
void testUnsubscribedSharedSubscribers();
void testSharedSubscribersSurviveRestart();
void testSharedSubscriberDoesntGetRetainedMessages();
/**
* Plugin tests
*/
void testExtendedAuthOneStepSucceed();
void testExtendedAuthOneStepDeny();
void testExtendedAuthOneStepBadAuthMethod();
void testExtendedAuthTwoStep();
void testExtendedAuthTwoStepSecondStepFail();
void testExtendedReAuth();
void testExtendedReAuthTwoStep();
void testExtendedReAuthFail();
void testSimpleAuthAsync();
void testFailedAsyncClientCrashOnSession();
void testAsyncWithImmediateFollowUpPackets();
void testAsyncWithException();
void testPluginAuthFail();
void testPluginAuthSucceed();
void testPluginOnDisconnect();
void testPluginGetClientAddress();
void testChangePublish();
void testClientRemovalByPlugin();
void testSubscriptionRemovalByPlugin();
void testPublishByPlugin();
void testWillDenialByPlugin();
void testPluginMainInit();
void testAsyncCurl();
void testSubscribeWithoutRetainedDelivery();
void testDontUpgradeWildcardDenyMode();
void testAlsoDontApproveOnErrorInPluginWithWildcardDenyMode();
void testDenyWildcardSubscription();
void testUserPropertiesPresent();
void testPublishInThread();
void testPublishToItself();
void testNoLocalPublishToItself();
void testTopicMatchingInSubscriptionTree();
void testDnsResolver();
void testDnsResolverDontCancel();
void testDnsResolverSecondQuery();
void testDnsResolverInvalid();
void testGetResultWhenThereIsNone();
void testWebsocketPing();
void testWebsocketCorruptLengthFrame();
void testWebsocketHugePing();
void testWebsocketManyBigPingFrames();
void testWebsocketClose();
void testStartsWith();
void forkingTestForkingTestServer();
void testStringValuesParsing();
void testStringValuesParsingEscaping();
void testStringValuesFuzz();
void testStringValuesInvalid();
void testPreviouslyValidConfigFile();
void forkingTestSaveAndLoadDelayedWill();
void testBase64();
void testNoCopy();
void testSessionTakeoverOtherUsername();
void testCorrelationData();
void testSubscriptionIdOnlineClient();
void testSubscriptionIdOfflineClient();
void testSubscriptionIdRetainedMessages();
void testSubscriptionIdSharedSubscriptions();
void testSubscriptionIdChange();
void testSubscriptionIdOverlappingSubscriptions();
void forkingTestBridgeWithLocalAndRemotePrefix();
void forkingTestBridgePrefixesOtherClientsUnaffected();
void forkingTestBridgeWithOnlyRemotePrefix();
void forkingTestBridgeWithOnlyLocalPrefix();
void forkingTestBridgeOutgoingTopicEqualsPrefix();
void forkingTestBridgeIncomingTopicEqualsPrefix();
void forkingTestBridgeZWithLocalAndRemotePrefixRetained();
void forkingTestBridgeWithLocalAndRemotePrefixQueuedQoS();
public:
MainTests();
bool test(bool skip_tests_with_internet, bool skip_server_tests, bool abort_on_first_fail, const std::vector<std::string> &tests);
};
#endif // MAINTESTS_H
================================================
FILE: FlashMQTests/plugins/curlfunctions.cpp
================================================
#include "curlfunctions.h"
#include <sys/epoll.h>
#include "../../flashmq_plugin.h"
#include "test_plugin.h"
#include <cstring>
/**
* @brief This is curl telling us what events to watch for.
* @param easy
* @param s
* @param what
* @param clientp
* @param socketp
* @return
*/
int socket_event_watch_notification(CURL *easy, curl_socket_t s, int what, void *clientp, void *socketp)
{
(void)easy;
(void)clientp;
(void)socketp;
if (what == CURL_POLL_REMOVE)
flashmq_poll_remove_fd(s);
else
{
int events = 0;
if (what == CURL_POLL_IN)
events |= EPOLLIN;
else if (what == CURL_POLL_OUT)
events |= EPOLLOUT;
else if (what == CURL_POLL_INOUT)
events = EPOLLIN | EPOLLOUT;
else
return 1;
flashmq_poll_add_fd(s, events, std::weak_ptr<void>());
}
return 0;
}
void check_all_active_curls(TestPluginData *p, CURLM *curlMulti)
{
CURLMsg *msg;
int msgs_left;
while((msg = curl_multi_info_read(curlMulti, &msgs_left)))
{
if (msg->msg == CURLMSG_DONE)
{
CURL *easy = msg->easy_handle;
AuthenticatingClient *c = nullptr;
curl_easy_getinfo(easy, CURLINFO_PRIVATE, &c);
flashmq_logf(LOG_INFO, "Libcurl said: %s", curl_easy_strerror(msg->data.result));
std::string answer(c->response.data(), std::min<int>(9, c->response.size()));
if (answer == "<!doctype")
flashmq_continue_async_authentication_v4(c->client, AuthResult::success, std::string(), std::string(), 0);
else
flashmq_continue_async_authentication_v4(c->client, AuthResult::login_denied, std::string(), std::string(), 0);
// Normally we have to have something in the AuthenticatingClient to look up which request to delete, but we only have one here.
p->curlTestClient.reset();
}
}
}
void call_timed_curl_multi_socket_action(CURLM *multi, TestPluginData *p)
{
p->current_timer = 0;
int a = 0;
int rc = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &a);
/* Curl says: "When this function returns error, the state of all transfers are uncertain and they cannot be
* continued. curl_multi_socket_action should not be called again on the same multi handle after an error has
* been returned, unless first removing all the handles and adding new ones."
*/
if (rc != CURLM_OK)
{
// This would normally be removing all our pending requests, but we only have one here.
p->curlTestClient.reset();
return;
}
check_all_active_curls(p, multi);
}
int timer_callback(CURLM *multi, long timeout_ms, void *clientp)
{
TestPluginData *p = static_cast<TestPluginData*>(clientp);
// We also remove the last known task before it executes if curl tells us to install a new one. This
// is suggested by the unclear and incomplete example at https://curl.se/libcurl/c/CURLMOPT_TIMERFUNCTION.html.
if (timeout_ms == -1 || p->current_timer > 0)
{
flashmq_remove_task(p->current_timer);
p->current_timer = 0;
}
if (timeout_ms >= 0)
{
auto f = std::bind(&call_timed_curl_multi_socket_action, multi, p);
p->current_timer = flashmq_add_task(f, timeout_ms);
}
return CURLM_OK;
}
size_t curl_write_cb(char *data, size_t n, size_t l, void *userp)
{
AuthenticatingClient *ac = static_cast<AuthenticatingClient*>(userp);
int pos = ac->response.size();
ac->response.resize(ac->response.size() + n*l);
std::memcpy(&ac->response[pos], data, n*l);
return n*l;
}
================================================
FILE: FlashMQTests/plugins/curlfunctions.h
================================================
#ifndef CURLFUNCTIONS_H
#define CURLFUNCTIONS_H
#include <curl/curl.h>
#include "test_plugin.h"
int socket_event_watch_notification(CURL *easy, curl_socket_t s, int what, void *clientp, void *socketp);
void check_all_active_curls(TestPluginData *p, CURLM *curlMulti);
void call_timed_curl_multi_socket_action(CURLM *multi, TestPluginData *p);
int timer_callback(CURLM *multi, long timeout_ms, void *clientp);
size_t curl_write_cb(char *data, size_t n, size_t l, void *userp);
#endif // CURLFUNCTIONS_H
================================================
FILE: FlashMQTests/plugins/test_plugin.cpp
================================================
#include <functional>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <stdexcept>
#include <optional>
#include "../../flashmq_plugin.h"
#include "test_plugin.h"
#include <curl/curl.h>
#include <sys/epoll.h>
#include "curlfunctions.h"
TestPluginData::TestPluginData() :
curlMulti(curl_multi_init(), curl_multi_cleanup)
{
if (!curlMulti)
throw std::runtime_error("Curl failed to init");
curl_multi_setopt(curlMulti.get(), CURLMOPT_SOCKETFUNCTION, socket_event_watch_notification);
curl_multi_setopt(curlMulti.get(), CURLMOPT_TIMERFUNCTION, timer_callback);
curl_multi_setopt(curlMulti.get(), CURLMOPT_TIMERDATA, this);
}
TestPluginData::~TestPluginData()
{
if (this->t.joinable())
t.join();
}
void get_auth_result_delayed(std::weak_ptr<Client> client, AuthResult result)
{
usleep(500000);
flashmq_continue_async_authentication_v4(client, result, "", "", 0);
}
int flashmq_plugin_version()
{
return FLASHMQ_PLUGIN_VERSION;
}
void flashmq_plugin_allocate_thread_memory(void **thread_data, std::unordered_map<std::string, std::string> &plugin_opts)
{
TestPluginData *p = new TestPluginData();
*thread_data = p;
(void)plugin_opts;
}
void flashmq_plugin_deallocate_thread_memory(void *thread_data, std::unordered_map<std::string, std::string> &plugin_opts)
{
(void)plugin_opts;
TestPluginData *p = static_cast<TestPluginData*>(thread_data);
delete p;
}
void flashmq_plugin_poll_event_received(void *thread_data, int fd, uint32_t events, const std::weak_ptr<void> &ptr)
{
(void)ptr;
TestPluginData *p = static_cast<TestPluginData*>(thread_data);
int new_events = CURL_CSELECT_ERR;
if (events & EPOLLIN)
{
new_events &= ~CURL_CSELECT_ERR;
new_events |= CURL_CSELECT_IN;
}
if (events & EPOLLOUT)
{
new_events &= ~CURL_CSELECT_ERR;
new_events |= CURL_CSELECT_OUT;
}
int n = -1;
if (curl_multi_socket_action(p->curlMulti.get(), fd, new_events, &n) != CURLM_OK)
{
p->curlTestClient.reset();
return;
}
check_all_active_curls(p, p->curlMulti.get());
}
void flashmq_plugin_init(void *thread_data, std::unordered_map<std::string, std::string> &plugin_opts, bool reloading)
{
(void)thread_data;
(void)plugin_opts;
(void)reloading;
TestPluginData *p = static_cast<TestPluginData*>(thread_data);
if (plugin_opts.find("main_init was here") != plugin_opts.end())
p->main_init_ran = true;
}
void flashmq_plugin_deinit(void *thread_data, std::unordered_map<std::string, std::string> &plugin_opts, bool reloading)
{
(void)thread_data;
(void)plugin_opts;
(void)reloading;
}
void flashmq_plugin_periodic_event(void *thread_data)
{
(void)thread_data;
}
AuthResult flashmq_plugin_login_check(void *thread_data, const std::string &clientid, const std::string &username, const std::string &password,
const std::vector<std::pair<std::string, std::string>> *userProperties, const std::weak_ptr<Client> &client)
{
(void)thread_data;
(void)clientid;
(void)username;
(void)password;
(void)userProperties;
(void)client;
if (username.find("async") == 0)
{
TestPluginData *p = static_cast<TestPluginData*>(thread_data);
p->c = client;
AuthResult result = password == "success" ? AuthResult::success : AuthResult::login_denied;
auto delayedResult = std::bind(&get_auth_result_delayed, p->c, result);
if (p->t.joinable())
p->t.join();
p->t = std::thread(delayedResult);
return AuthResult::async;
}
if (username == "failme")
return AuthResult::login_denied;
if (username == "getaddress")
{
struct sockaddr_storage addr_mem;
struct sockaddr *addr = reinterpret_cast<sockaddr*>(&addr_mem);
socklen_t addrlen = sizeof(addr_mem);
std::string text;
flashmq_get_client_address_v4(client, &text, addr, &addrlen);
sockaddr sockaddr_after;
std::memcpy(&sockaddr_after, addr, std::min<socklen_t>(addrlen, sizeof(sockaddr)));
flashmq_publish_message("getaddresstest/address", 0, false, text);
if (sockaddr_after.sa_family == AF_INET)
{
flashmq_publish_message("getaddresstest/family", 0, false, "AF_INET");
}
}
if (username == "curl")
{
TestPluginData *p = static_cast<TestPluginData*>(thread_data);
p->curlTestClient = std::make_unique<AuthenticatingClient>();
p->curlTestClient->client = client;
curl_easy_setopt(p->curlTestClient->easy_handle.get(), CURLOPT_WRITEFUNCTION, curl_write_cb);
curl_easy_setopt(p->curlTestClient->easy_handle.get(), CURLOPT_WRITEDATA, p->curlTestClient.get());
curl_easy_setopt(p->curlTestClient->easy_handle.get(), CURLOPT_PRIVATE, p->curlTestClient.get());
// Keep in mind that DNS resovling may be blocking too. You could perhaps resolve the DNS once and use the result.
curl_easy_setopt(p->curlTestClient->easy_handle.get(), CURLOPT_URL, "http://www.google.com/");
p->curlTestClient->addToMulti(p->curlMulti);
return AuthResult::async;
}
return AuthResult::success;
}
void publish_in_thread()
{
flashmq_publish_message("topic/from/thread", 0, false, "payload from thread");
}
AuthResult flashmq_plugin_acl_check(
void *thread_data, const AclAccess access, const std::string &clientid, const std::string &username,
const std::string &topic, const std::vector<std::string> &subtopics, const std::string &shareName,
std::string_view payload, const uint8_t qos, const bool retain,
const std::optional<std::string> &correlationData, const std::optional<std::string> &responseTopic,
const std::optional<std::string> &contentType,
const std::optional<std::chrono::time_point<std::chrono::steady_clock>> expiresAt,
const std::vector<std::pair<std::string, std::string>> *userProperties)
{
(void)thread_data;
(void)access;
(void)clientid;
(void)username;
(void)subtopics;
(void)qos;
(void)retain;
(void)correlationData;
(void)responseTopic;
(void)userProperties;
(void)shareName;
(void)contentType;
(void)expiresAt;
if (clientid == "return_error")
return AuthResult::error;
if (access == AclAccess::subscribe && clientid == "success_without_retained_delivery")
return AuthResult::success_without_retained_delivery;
if (clientid == "test_user_without_retain_as_published_CswU21YA" && access == AclAccess::read)
assert(!retain);
if (clientid == "test_user_with_retain_as_published_v8sIeCvI" && access == AclAccess::read)
assert(retain);
assert(access == AclAccess::subscribe || !payload.empty());
if (access == AclAccess::register_will && topic == "will/disallowed")
return AuthResult::acl_denied;
if (topic == "removeclient" || topic == "removeclientandsession")
{
std::weak_ptr<Session> ses;
flashmq_get_session_pointer(clientid, username, ses);
flashmq_plugin_remove_client_v4(ses, topic == "removeclientandsession", ServerDisconnectReasons::NormalDisconnect);
}
if (clientid == "unsubscribe" && access == AclAccess::write)
{
std::weak_ptr<Session> session;
flashmq_get_session_pointer(clientid, username, session);
flashmq_plugin_remove_subscription_v4(session, topic);
}
if (clientid == "generate_publish")
{
flashmq_logf(LOG_INFO, "Publishing from plugin.");
const std::string topic = "generated/topic";
const std::string payload = "money";
flashmq_publish_message(topic, 0, false, payload);
}
if ((access == AclAccess::read || access == AclAccess::write) && topic == "test_user_property")
{
assert(userProperties);
}
if (topic == "publish_in_thread" && access == AclAccess::write)
{
std::thread t(publish_in_thread);
pthread_setname_np(t.native_handle(), "PubInThread");
t.join();
}
return AuthResult::success;
}
AuthResult flashmq_plugin_extended_auth(void *thread_data, const std::string &clientid, ExtendedAuthStage stage, const std::string &authMethod,
const std::string &authData, const std::vector<std::pair<std::string, std::string>> *userProperties, std::string &returnData,
std::string &username, const std::weak_ptr<Client> &client)
{
(void)thread_data;
(void)stage;
(void)authMethod;
(void)authData;
(void)username;
(void)clientid;
(void)userProperties;
(void)returnData;
(void)client;
if (authMethod == "always_good_passing_back_the_auth_data")
{
if (authData == "actually not good.")
return AuthResult::login_denied;
returnData = authData;
return AuthResult::success;
}
if (authMethod == "always_fail")
{
return AuthResult::login_denied;
}
if (authMethod == "two_step")
{
if (authData == "Hello")
returnData = "Hello back";
if (authData == "grant me already!")
{
returnData = "OK, if you insist.";
return AuthResult::success;
}
else if (authData == "whoops, wrong data.")
return AuthResult::login_denied;
else
return AuthResult::auth_continue;
}
return AuthResult::auth_method_not_supported;
}
bool flashmq_plugin_alter_publish(
void *thread_data, const std::string &clientid, std::string &topic, const std::vector<std::string> &subtopics,
std::string_view payload, uint8_t &qos, bool &retain, std::optional<std::string> &correlationData,
std::optional<std::string> &responseTopic, std::optional<std::string> &contentType,
std::vector<std::pair<std::string, std::string>> *userProperties)
{
(void)thread_data;
(void)clientid;
(void)subtopics;
(void)qos;
(void)retain;
(void)correlationData;
(void)responseTopic;
(void)userProperties;
(void)contentType;
TestPluginData *p = static_cast<TestPluginData*>(thread_data);
assert(!payload.empty());
if (topic == "changeme")
{
topic = "changed";
qos = 2;
return true;
}
if (topic == "check_main_init_presence" && p->main_init_ran)
{
topic = "check_main_init_presence_confirmed";
return true;
}
return false;
}
void flashmq_plugin_client_disconnected(void *thread_data, const std::string &clientid)
{
(void)thread_data;
flashmq_logf(LOG_INFO, "flashmq_plugin_client_disconnected called for '%s'", clientid.c_str());
flashmq_publish_message("disconnect/confirmed", 0, false, "adsf");
}
void flashmq_plugin_main_init(std::unordered_map<std::string, std::string> &plugin_opts)
{
(void)plugin_opts;
flashmq_logf(LOG_INFO, "The tester was here.");
// The plugin_opts aren't const. I don't know if that was a mistake or not anymore, but it works in my favor now.
plugin_opts["main_init was here"] = "true";
if (curl_global_init(CURL_GLOBAL_ALL) != 0)
throw std::runtime_error("Global curl init failed to init");
}
void flashmq_plugin_main_deinit(std::unordered_map<std::string, std::string> &plugin_opts)
{
(void)plugin_opts;
curl_global_cleanup();
}
AuthenticatingClient::AuthenticatingClient() :
easy_handle(curl_easy_init(), curl_easy_cleanup)
{
}
AuthenticatingClient::~AuthenticatingClient()
{
auto x = registeredAtMultiHandle.lock();
if (x)
{
curl_multi_remove_handle(x.get(), easy_handle.get());
}
}
void AuthenticatingClient::addToMulti(std::shared_ptr<CURLM> &curlMulti)
{
if (curl_multi_add_handle(curlMulti.get(), easy_handle.get()) != CURLM_OK)
throw std::runtime_error("curl_multi_add_handle failed");
registeredAtMultiHandle = curlMulti;
}
================================================
FILE: FlashMQTests/plugins/test_plugin.h
================================================
#ifndef TESTPLUGIN_H
#define TESTPLUGIN_H
#include <memory>
#include <vector>
#include <thread>
#include "../../forward_declarations.h"
#include <curl/curl.h>
struct AuthenticatingClient
{
std::weak_ptr<Client> client;
std::vector<char> response;
std::unique_ptr<CURL, void(*)(CURL*)> easy_handle;
std::weak_ptr<CURLM> registeredAtMultiHandle;
public:
AuthenticatingClient();
~AuthenticatingClient();
void addToMulti(std::shared_ptr<CURLM> &curlMulti);
};
class TestPluginData
{
public:
std::thread t;
std::weak_ptr<Client> c;
bool main_init_ran = false;
std::shared_ptr<CURLM> curlMulti;
uint32_t current_timer = 0;
// Normally we keep some kind of indexed record of requests, but in our test plugin, we just track one.
std::unique_ptr<AuthenticatingClient> curlTestClient;
public:
TestPluginData();
~TestPluginData();
};
#endif // TESTPLUGIN_H
================================================
FILE: FlashMQTests/plugins/test_plugin.pro
================================================
QT -= gui
CONFIG += c++17
TARGET = test_plugin
TEMPLATE = lib
VERSION=0.0.1
LIBS += -lcurl
HEADERS += test_plugin.h \
curlfunctions.h
SOURCES += test_plugin.cpp \
curlfunctions.cpp
================================================
FILE: FlashMQTests/plugintests.cpp
================================================
#include "maintests.h"
#include "testhelpers.h"
#include "conffiletemp.h"
#include "flashmqtestclient.h"
#include <sys/sysinfo.h>
void MainTests::testWillDenialByPlugin()
{
std::vector<ProtocolVersion> versions { ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5 };
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
std::unique_ptr<FlashMQTestClient> sender = std::make_unique<FlashMQTestClient>();
sender->start();
std::shared_ptr<WillPublish> will = std::make_shared<WillPublish>();
will->topic = "will/allowed";
will->payload = "mypayload";
sender->setWill(will);
sender->connectClient(ProtocolVersion::Mqtt311);
FlashMQTestClient receiver;
receiver.start();
receiver.connectClient(ProtocolVersion::Mqtt311);
receiver.subscribe("will/+", 0);
sender.reset();
receiver.waitForMessageCount(1);
{
auto ro = receiver.receivedObjects.lock();
MqttPacket pubPack = ro->receivedPublishes.front();
std::shared_ptr<Client> client = receiver.getClient();
pubPack.parsePublishData(client);
QCOMPARE(pubPack.getPublishData().topic, "will/allowed");
QCOMPARE(pubPack.getPublishData().payload, "mypayload");
QCOMPARE(pubPack.getPublishData().qos, 0);
}
receiver.clearReceivedLists();
// Now set a will that we will deny.
{
sender = std::make_unique<FlashMQTestClient>();
sender->start();
std::shared_ptr<WillPublish> will2 = std::make_shared<WillPublish>();
will2->topic = "will/disallowed";
will2->payload = "mypayload";
sender->setWill(will2);
sender->connectClient(ProtocolVersion::Mqtt311);
sender.reset();
usleep(500000);
receiver.waitForMessageCount(0);
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.empty());
}
}
void MainTests::testPluginAuthFail()
{
std::vector<ProtocolVersion> versions { ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5 };
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
for (ProtocolVersion &version : versions)
{
FlashMQTestClient client;
client.start();
client.connectClient(version, false, 120, [](Connect &connect) {
connect.username = "failme";
connect.password = "boo";
});
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
if (version >= ProtocolVersion::Mqtt5)
QVERIFY(connAckData.reasonCode == ReasonCodes::NotAuthorized);
else
QVERIFY(static_cast<uint8_t>(connAckData.reasonCode) == 5);
}
}
void MainTests::testPluginAuthSucceed()
{
std::vector<ProtocolVersion> versions { ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5 };
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
for (ProtocolVersion &version : versions)
{
FlashMQTestClient client;
client.start();
client.connectClient(version, false, 120, [](Connect &connect) {
connect.username = "passme";
connect.password = "boo";
});
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::Success);
}
}
void MainTests::testExtendedAuthOneStepSucceed()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.username = "me";
connect.password = "me me";
connect.authenticationMethod = "always_good_passing_back_the_auth_data";
connect.authenticationData = "I have a proposal to put to ye.";
});
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::Success);
QVERIFY(connAckData.authData == "I have a proposal to put to ye.");
}
void MainTests::testExtendedAuthOneStepDeny()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.username = "me";
connect.password = "me me";
connect.authenticationMethod = "always_fail";
});
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::NotAuthorized);
}
void MainTests::testExtendedAuthOneStepBadAuthMethod()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.username = "me";
connect.password = "me me";
connect.authenticationMethod = "doesnt_exist";
});
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::BadAuthenticationMethod);
}
void MainTests::testExtendedAuthTwoStep()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.username = "me";
connect.password = "me me";
connect.authenticationMethod = "two_step";
connect.authenticationData = "Hello";
});
{
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
AuthPacketData authData = ro->receivedPackets.front().parseAuthData();
QVERIFY(authData.reasonCode == ReasonCodes::ContinueAuthentication);
QVERIFY(authData.data == "Hello back");
}
client.clearReceivedLists();
const Auth auth(ReasonCodes::ContinueAuthentication, "two_step", "grant me already!");
client.writeAuth(auth);
client.waitForConnack();
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::Success);
QVERIFY(connAckData.authData == "OK, if you insist.");
}
void MainTests::testExtendedAuthTwoStepSecondStepFail()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.username = "me";
connect.password = "me me";
connect.authenticationMethod = "two_step";
connect.authenticationData = "Hello";
});
{
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
AuthPacketData authData = ro->receivedPackets.front().parseAuthData();
QVERIFY(authData.reasonCode == ReasonCodes::ContinueAuthentication);
QVERIFY(authData.data == "Hello back");
}
client.clearReceivedLists();
const Auth auth(ReasonCodes::ContinueAuthentication, "two_step", "whoops, wrong data.");
client.writeAuth(auth);
client.waitForConnack();
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::NotAuthorized);
}
void MainTests::testExtendedReAuth()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.username = "me";
connect.password = "me me";
connect.authenticationMethod = "always_good_passing_back_the_auth_data";
connect.authenticationData = "Santa Claus";
});
{
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::Success);
}
client.clearReceivedLists();
// Then reauth.
Auth auth(ReasonCodes::ContinueAuthentication, "always_good_passing_back_the_auth_data", "Again Santa Claus");
client.writeAuth(auth);
client.waitForConnack();
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
AuthPacketData authData = ro->receivedPackets.front().parseAuthData();
QVERIFY(authData.reasonCode == ReasonCodes::Success);
QVERIFY(authData.data == "Again Santa Claus");
}
void MainTests::testExtendedReAuthTwoStep()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.username = "me";
connect.password = "me me";
connect.authenticationMethod = "two_step";
connect.authenticationData = "Hello";
});
{
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
AuthPacketData authData = ro->receivedPackets.front().parseAuthData();
QVERIFY(authData.reasonCode == ReasonCodes::ContinueAuthentication);
QVERIFY(authData.data == "Hello back");
}
client.clearReceivedLists();
const Auth auth(ReasonCodes::ContinueAuthentication, "two_step", "grant me already!");
client.writeAuth(auth);
client.waitForConnack();
{
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::Success);
QVERIFY(connAckData.authData == "OK, if you insist.");
}
client.clearReceivedLists();
// Then reauth.
const Auth reauth(ReasonCodes::ReAuthenticate, "two_step", "Hello");
client.writeAuth(reauth);
client.waitForConnack();
{
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
AuthPacketData reauthData = ro->receivedPackets.front().parseAuthData();
QVERIFY(reauthData.reasonCode == ReasonCodes::ContinueAuthentication);
QVERIFY(reauthData.data == "Hello back");
}
client.clearReceivedLists();
const Auth reauthFinish(ReasonCodes::ContinueAuthentication, "two_step", "grant me already!");
client.writeAuth(reauthFinish);
client.waitForConnack();
{
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
AuthPacketData reauthFinishData = ro->receivedPackets.front().parseAuthData();
QVERIFY(reauthFinishData.reasonCode == ReasonCodes::Success);
QVERIFY(reauthFinishData.data == "OK, if you insist.");
}
}
void MainTests::testExtendedReAuthFail()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.username = "me";
connect.password = "me me";
connect.authenticationMethod = "always_good_passing_back_the_auth_data";
connect.authenticationData = "I have a proposal to put to ye.";
});
{
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::Success);
QVERIFY(connAckData.authData == "I have a proposal to put to ye.");
}
client.clearReceivedLists();
// Then reauth.
{
const Auth reauth(ReasonCodes::ReAuthenticate, "always_good_passing_back_the_auth_data", "actually not good.");
client.writeAuth(reauth);
client.waitForPacketCount(1);
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
QVERIFY(ro->receivedPackets.front().packetType == PacketType::DISCONNECT);
DisconnectData data = ro->receivedPackets.front().parseDisconnectData();
QVERIFY(data.reasonCode == ReasonCodes::NotAuthorized);
}
}
void MainTests::testSimpleAuthAsync()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
std::list<std::string> results { "success", "fail" };
for (std::string &result : results)
{
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [&](Connect &connect) {
connect.username = "async";
connect.password = result;
});
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
if (result == "success")
QVERIFY(connAckData.reasonCode == ReasonCodes::Success);
else
QVERIFY(connAckData.reasonCode == ReasonCodes::NotAuthorized);
}
}
/**
* There was a crash when doing session stuff with a client that was rejected by exception
* in continuationOfAuthentication (by duplicate session id between different users).
*/
void MainTests::testFailedAsyncClientCrashOnSession()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
std::list<FlashMQTestClient> clients;
clients.emplace_back();
clients.back().start();
clients.back().connectClient(ProtocolVersion::Mqtt5, false, 120, [&](Connect &connect) {
connect.username = "async1";
connect.password = "success";
connect.clientid = "duplicate";
}, 21883);
clients.emplace_back();
clients.back().start();
clients.back().connectClient(ProtocolVersion::Mqtt5, false, 120, [&](Connect &connect) {
connect.username = "async2";
connect.password = "success";
connect.clientid = "duplicate";
}, 21883, false);
// Because it crashed, we don't have events to wait for.
std::this_thread::sleep_for(std::chrono::seconds(1));
/*
* An if statement in a test is dodgy, but the underlying client was made weak after this
* test was written, so when the original problem occurs, the client hasn't disconnected nor expired.
*/
if (!clients.back().clientExpired())
{
Publish pub("sdf", "wer", 2);
MqttPacket pubPack(ProtocolVersion::Mqtt5, pub);
if (pub.qos > 0)
pubPack.setPacketId(3);
clients.back().getClient()->writeMqttPacketAndBlameThisClient(pubPack);
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// Test if the server still works after that.
clients.front().clearReceivedLists();
clients.front().publish("sdf", "sfd", 2);
auto ro = clients.front().receivedObjects.lock();
FMQ_VERIFY(!ro->receivedPackets.empty());
FMQ_COMPARE(ro->receivedPackets.back().packetType, PacketType::PUBCOMP);
}
/**
* We send a subscription directly after authentication, without waiting for it. These should be queued and dealt
* with after the login was approved.
*/
void MainTests::testAsyncWithImmediateFollowUpPackets()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [&](Connect &connect) {
connect.username = "async";
connect.password = "success";
}, 21883, false);
{
const uint16_t packet_id = 66;
std::vector<Subscribe> subs;
subs.emplace_back("our/random/topic", 0);
MqttPacket subPack(client.getProtocolVersion(), packet_id, 0, subs);
client.getClient()->writeMqttPacketAndBlameThisClient(subPack);
}
client.waitForConnack();
client.getClient()->setAuthenticated(true);
{
auto ro = client.receivedObjects.lock();
FMQ_VERIFY(ro->receivedPackets.size() >= 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::Success);
}
client.clearReceivedLists();
{
FlashMQTestClient client2;
client2.start();
client2.connectClient(ProtocolVersion::Mqtt311);
client2.publish("our/random/topic", "1Xs0QC5XInKLGHfm", 0);
}
client.waitForMessageCount(1);
{
auto ro = client.receivedObjects.lock();
MqttPacket &p = ro->receivedPublishes.front();
FMQ_COMPARE(p.getTopic(), "our/random/topic");
FMQ_COMPARE(p.getPayloadCopy(), "1Xs0QC5XInKLGHfm");
}
}
/**
* We're testing behavior that throws an internal FlashMQ exception in handling async auth, namely trying
* to take over session with a different username. An exception inside the plugin would not be a good test,
* because that is handled locally by the authentication.
*/
void MainTests::testAsyncWithException()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
// =========
for (ProtocolVersion p : {ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5})
{
FlashMQTestClient client1;
client1.start();
client1.connectClient(p, true, 600, [](Connect &connect) {
connect.clientid = "thesameclientid";
connect.username = "async0";
connect.password = "success";
});
{
auto ro = client1.receivedObjects.lock();
auto &pack = ro->receivedPackets.at(0);
FMQ_COMPARE(pack.packetType, PacketType::CONNACK);
ConnAckData ackData = pack.parseConnAckData();
FMQ_COMPARE(ackData.reasonCode, ReasonCodes::Success);
}
FlashMQTestClient client2;
client2.start();
client2.connectClient(p, true, 600, [](Connect &connect) {
connect.clientid = "thesameclientid";
connect.username = "async1";
connect.password = "success";
});
{
auto ro = client2.receivedObjects.lock();
auto &pack = ro->receivedPackets.at(0);
FMQ_COMPARE(pack.packetType, PacketType::CONNACK);
ConnAckData ackData = pack.parseConnAckData();
int expectedCode = p == ProtocolVersion::Mqtt5 ? static_cast<int>(ReasonCodes::NotAuthorized) : static_cast<int>(ConnAckReturnCodes::NotAuthorized);
FMQ_COMPARE(static_cast<int>(ackData.reasonCode), expectedCode);
}
}
}
void MainTests::testClientRemovalByPlugin()
{
std::list<std::string> methods { "removeclient", "removeclientandsession"};
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
for (std::string &method : methods)
{
cleanup();
init(args);
FlashMQTestClient sender;
sender.start();
sender.connectClient(ProtocolVersion::Mqtt5, false, 120);
const std::string sender_client_id = sender.getClientId();
FlashMQTestClient receiver;
receiver.start();
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe("#", 2);
sender.publish(method, "asdf", 0);
sender.waitForDisconnectPacket();
auto ro = sender.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
QVERIFY(ro->receivedPackets.front().packetType == PacketType::DISCONNECT);
std::shared_ptr<SubscriptionStore> store = globals->subscriptionStore;
std::shared_ptr<Session> session = store->lockSession(sender_client_id);
if (method == "removeclient")
{
QVERIFY(session);
}
else
{
QVERIFY(!session);
}
}
}
void MainTests::testSubscriptionRemovalByPlugin()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
sender.start();
sender.connectClient(ProtocolVersion::Mqtt5);
FlashMQTestClient receiver;
receiver.start();
receiver.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.clientid = "unsubscribe";
});
receiver.subscribe("a/b/c", 2);
FlashMQTestClient dummyreceiver;
dummyreceiver.start();
dummyreceiver.connectClient(ProtocolVersion::Mqtt5);
dummyreceiver.subscribe("#", 2);
sender.publish("a/b/c", "asdf", 0);
receiver.waitForMessageCount(1);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.size() == 1);
}
receiver.clearReceivedLists();
sender.clearReceivedLists();
receiver.publish("a/b/c", "sdf", 0); // Because the clientid of this client, this will unsubscribe.
dummyreceiver.clearReceivedLists();
// A hack way to make sure the relevant thread has done the unsubscribe (by doing work we can detect).
const int nprocs = get_nprocs();
for (int i = 0; i < nprocs; i++)
receiver.publish("waitforthis", "sdf", 0);
dummyreceiver.waitForPacketCount(nprocs);
receiver.clearReceivedLists();
dummyreceiver.clearReceivedLists();
sender.publish("a/b/c", "asdf", 0);
usleep(200000);
receiver.waitForMessageCount(0);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.empty());
}
}
void MainTests::testPublishByPlugin()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
sender.start();
sender.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.clientid = "generate_publish";
});
FlashMQTestClient receiver;
receiver.start();
receiver.connectClient(ProtocolVersion::Mqtt5, false, 120);
receiver.subscribe("#", 2);
sender.publish("boo", "booboo", 0);
receiver.waitForMessageCount(2);
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 2);
QVERIFY(std::any_of(ro->receivedPublishes.begin(), ro->receivedPublishes.end(), [](MqttPacket &p) {
return p.getTopic() == "boo";
}));
QVERIFY(std::any_of(ro->receivedPublishes.begin(), ro->receivedPublishes.end(), [](MqttPacket &p) {
return p.getTopic() == "generated/topic";
}));
}
void MainTests::testChangePublish()
{
std::vector<ProtocolVersion> versions { ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5 };
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
for (ProtocolVersion &version : versions)
{
FlashMQTestClient sender;
sender.start();
sender.connectClient(version, false, 120);
FlashMQTestClient receiver;
receiver.start();
receiver.connectClient(version, false, 100);
receiver.subscribe("#", 2);
FlashMQTestClient receiver_of_pattern;
receiver_of_pattern.start();
receiver_of_pattern.connectClient(version, false, 100);
receiver_of_pattern.subscribe("changed", 2);
sender.publish("changeme", "hello", 1);
receiver.waitForMessageCount(1);
{
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
MYCASTCOMPARE(ro->receivedPublishes.front().getTopic(), "changed");
MYCASTCOMPARE(ro->receivedPublishes.front().getPayloadCopy(), "hello");
MYCASTCOMPARE(ro->receivedPublishes.front().getQos(), 2);
}
receiver_of_pattern.waitForMessageCount(1);
{
auto ro = receiver_of_pattern.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
MYCASTCOMPARE(ro->receivedPublishes.front().getTopic(), "changed");
MYCASTCOMPARE(ro->receivedPublishes.front().getPayloadCopy(), "hello");
MYCASTCOMPARE(ro->receivedPublishes.front().getQos(), 2);
}
}
}
void MainTests::testPluginOnDisconnect()
{
std::vector<ProtocolVersion> versions { ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5 };
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient receiver;
receiver.start();
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe("#", 0);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5);
client.disconnect(ReasonCodes::Success);
receiver.waitForMessageCount(1);
{
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
QCOMPARE(ro->receivedPublishes.front().getTopic(), "disconnect/confirmed");
}
}
void MainTests::testPluginGetClientAddress()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient receiver;
receiver.start();
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe("getaddresstest/#", 0);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.username = "getaddress";
});
try
{
receiver.waitForMessageCount(2);
}
catch(std::exception &e)
{
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 2);
}
auto ro = receiver.receivedObjects.lock();
QCOMPARE(ro->receivedPublishes[0].getTopic(), "getaddresstest/address");
QCOMPARE(ro->receivedPublishes[0].getPayloadCopy(), "127.0.0.1");
QCOMPARE(ro->receivedPublishes[1].getTopic(), "getaddresstest/family");
QCOMPARE(ro->receivedPublishes[1].getPayloadCopy(), "AF_INET");
}
void MainTests::testPluginMainInit()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
sender.start();
sender.connectClient(ProtocolVersion::Mqtt5);
FlashMQTestClient receiver;
receiver.start();
receiver.connectClient(ProtocolVersion::Mqtt5, false, 120);
receiver.subscribe("#", 2);
sender.publish("check_main_init_presence", "booboo", 0);
receiver.waitForMessageCount(1);
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
MYCASTCOMPARE(ro->receivedPublishes.front().getTopic(), "check_main_init_presence_confirmed");
}
void MainTests::testAsyncCurl()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, false, 120, [](Connect &connect) {
connect.username = "curl";
connect.password = "boo";
});
auto ro = client.receivedObjects.lock();
QVERIFY(ro->receivedPackets.size() == 1);
ConnAckData connAckData = ro->receivedPackets.front().parseConnAckData();
QVERIFY(connAckData.reasonCode == ReasonCodes::Success);
}
void MainTests::testSubscribeWithoutRetainedDelivery()
{
// Control case without plugin loaded.
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "retained payload";
const std::string topic = "retaintopic/one/two/three";
sender.connectClient(ProtocolVersion::Mqtt5);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
receiver.connectClient(ProtocolVersion::Mqtt5, true, 0, [] (Connect &connect){
connect.clientid = "success_without_retained_delivery";
});
receiver.subscribe(topic, 0);
receiver.waitForMessageCount(1);
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
}
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "retained payload";
const std::string topic = "retaintopic/one/two/three";
sender.connectClient(ProtocolVersion::Mqtt5);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
receiver.connectClient(ProtocolVersion::Mqtt5, true, 0, [] (Connect &connect){
connect.clientid = "success_without_retained_delivery";
});
receiver.subscribe(topic, 0);
usleep(250000);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.empty());
}
sender.publish("retaintopic/one/two/three", "on-line payload", 0);
receiver.waitForMessageCount(1);
{
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
QVERIFY(ro->receivedPublishes.front().getPayloadView() == "on-line payload");
}
}
void MainTests::testDontUpgradeWildcardDenyMode()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.writeLine("minimum_wildcard_subscription_depth 2");
confFile.writeLine("wildcard_subscription_deny_mode deny_retained_only");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "retained payload";
const std::string topic = "retaintopic/one/two/three";
sender.connectClient(ProtocolVersion::Mqtt5);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe("#", 0);
usleep(250000);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.empty());
}
sender.publish("retaintopic/one/two/three", "on-line payload", 0);
receiver.waitForMessageCount(1);
{
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
QVERIFY(ro->receivedPublishes.front().getPayloadView() == "on-line payload");
}
}
void MainTests::testAlsoDontApproveOnErrorInPluginWithWildcardDenyMode()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.writeLine("minimum_wildcard_subscription_depth 2");
confFile.writeLine("wildcard_subscription_deny_mode deny_retained_only");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "retained payload";
const std::string topic = "retaintopic/one/two/three";
sender.connectClient(ProtocolVersion::Mqtt5);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
receiver.connectClient(ProtocolVersion::Mqtt5, true, 0, [] (Connect &connect){
connect.clientid = "return_error";
});
bool suback_errored = false;
try
{
receiver.subscribe("#", 0);
}
catch (SubAckIsError &ex)
{
suback_errored = true;
}
QVERIFY(suback_errored);
usleep(250000);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.empty());
}
sender.publish("retaintopic/one/two/three", "on-line payload", 0);
usleep(250000);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.empty());
}
}
void MainTests::testDenyWildcardSubscription()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.writeLine("minimum_wildcard_subscription_depth 2");
confFile.writeLine("wildcard_subscription_deny_mode deny_all");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "retained payload";
const std::string topic = "retaintopic/one/two/three";
sender.connectClient(ProtocolVersion::Mqtt5);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
receiver.connectClient(ProtocolVersion::Mqtt5, true, 0, [] (Connect &connect){
connect.clientid = "success_without_retained_delivery";
});
bool suback_errored = false;
try
{
receiver.subscribe("bla/#", 0);
}
catch (SubAckIsError &ex)
{
suback_errored = true;
}
QVERIFY(suback_errored);
}
void MainTests::testUserPropertiesPresent()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "werwer payload";
const std::string topic = "test_user_property";
sender.connectClient(ProtocolVersion::Mqtt5);
receiver.connectClient(ProtocolVersion::Mqtt5, true, 0, [] (Connect &connect){
connect.clientid = "qq5HD9s9VDomlF2l";
});
receiver.subscribe(topic, 0);
Publish pub1(topic, payload, 0);
pub1.addUserProperty("myprop", "myval");
sender.publish(pub1);
receiver.waitForMessageCount(1);
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
QVERIFY(ro->receivedPublishes.front().getPayloadView() == payload);
QVERIFY(ro->receivedPublishes.front().getUserProperties() != nullptr);
auto props = ro->receivedPublishes.front().getUserProperties();
QVERIFY(std::any_of(props->begin(), props->end(), [](std::pair<std::string, std::string> &p) {
return p.first == "myprop" && p.second == "myval";
}));
}
void MainTests::testPublishInThread()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
sender.connectClient(ProtocolVersion::Mqtt5);
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe("topic/from/thread", 0);
Publish pub1("publish_in_thread", "dummy", 0);
sender.publish(pub1);
receiver.waitForMessageCount(1);
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
QVERIFY(ro->receivedPublishes.front().getPayloadView() == "payload from thread");
}
================================================
FILE: FlashMQTests/retaintests.cpp
================================================
#include "maintests.h"
#include "flashmqtestclient.h"
#include "conffiletemp.h"
#include "testhelpers.h"
#include "utils.h"
#include "retainedmessagesdb.h"
void MainTests::test_retained()
{
std::vector<ProtocolVersion> protocols {ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5};
for (const ProtocolVersion senderVersion : protocols)
{
for (const ProtocolVersion receiverVersion : protocols)
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "We are testing";
const std::string topic = "retaintopic";
sender.connectClient(senderVersion);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
Publish pub2("dummy2", "Nobody sees this", 0);
pub2.retain = true;
sender.publish(pub2);
receiver.connectClient(receiverVersion);
receiver.subscribe("dummy", 0);
receiver.subscribe(topic, 0);
receiver.waitForMessageCount(1);
{
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
MqttPacket &msg = ro->receivedPublishes.front();
QCOMPARE(msg.getPayloadCopy(), payload);
QCOMPARE(msg.getTopic(), topic);
QVERIFY(msg.getRetain());
ro->clear();
}
sender.publish(pub1);
receiver.waitForMessageCount(1);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY2(ro->receivedPublishes.size() == 1, "There must be one message in the received list");
MqttPacket &msg2 = ro->receivedPublishes.front();
QCOMPARE(msg2.getPayloadCopy(), payload);
QCOMPARE(msg2.getTopic(), topic);
QVERIFY2(!msg2.getRetain(), "Getting a retained message while already being subscribed must be marked as normal, not retain.");
}
}
}
}
/**
* @brief MainTests::test_retained_double_set Test incepted because of different locking paths in first tree node and second tree node.
*/
void MainTests::test_retained_double_set()
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string topic = "one/two/three";
sender.connectClient(ProtocolVersion::Mqtt5);
{
Publish pub("one", "dummy node creator", 0);
pub.retain = true;
pub.qos = 1;
sender.publish(pub);
}
Publish pub1(topic, "nobody sees this", 0);
pub1.retain = true;
pub1.qos = 1;
sender.publish(pub1);
pub1.payload = "We are setting twice";
sender.publish(pub1);
{
// Confirm the retained messages are present.
FlashMQTestClient c;
c.start();
c.connectClient(ProtocolVersion::Mqtt5);
c.subscribe("one/#", 0);
c.waitForMessageCount(2);
}
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe("one/#", 0);
receiver.waitForMessageCount(2);
MYCASTCOMPARE(receiver.receivedObjects.lock()->receivedPublishes.size(), 2);
{
auto ro = receiver.receivedObjects.lock();
auto msg = std::find_if(ro->receivedPublishes.begin(), ro->receivedPublishes.end(), [](const MqttPacket &p) {return p.getTopic() == "one";});
FMQ_VERIFY(msg != ro->receivedPublishes.end());
QCOMPARE(msg->getPayloadCopy(), "dummy node creator");
QCOMPARE(msg->getTopic(), "one");
QVERIFY(msg->getRetain());
}
{
auto ro = receiver.receivedObjects.lock();
auto msg2 = std::find_if(ro->receivedPublishes.begin(), ro->receivedPublishes.end(), [&](const MqttPacket &p) {return p.getTopic() == topic;});
FMQ_VERIFY(msg2 != ro->receivedPublishes.end());
QCOMPARE(msg2->getPayloadCopy(), "We are setting twice");
QCOMPARE(msg2->getTopic(), topic);
QVERIFY(msg2->getRetain());
}
}
/**
* @brief MainTests::test_retained_disabled Copied from test_retained and adjusted
*/
void MainTests::test_retained_mode_drop()
{
ConfFileTemp confFile;
confFile.writeLine("allow_anonymous yes");
confFile.writeLine("retained_messages_mode drop");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
std::vector<ProtocolVersion> protocols {ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5};
for (const ProtocolVersion senderVersion : protocols)
{
for (const ProtocolVersion receiverVersion : protocols)
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "We are testing";
const std::string topic = "retaintopic";
sender.connectClient(senderVersion);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
Publish pub2("dummy2", "Nobody sees this", 0);
pub2.retain = true;
sender.publish(pub2);
receiver.connectClient(receiverVersion);
receiver.subscribe("dummy", 0);
receiver.subscribe(topic, 0);
usleep(250000);
receiver.waitForMessageCount(0);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY2(ro->receivedPublishes.empty(), "In drop mode, retained publishes should be stored as retained messages.");
}
receiver.clearReceivedLists();
sender.publish(pub1);
usleep(250000);
receiver.waitForMessageCount(0);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.empty());
}
}
}
}
/**
* @brief MainTests::test_retained_mode_downgrade copied from test_retained and adjusted
*/
void MainTests::test_retained_mode_downgrade()
{
ConfFileTemp confFile;
confFile.writeLine("allow_anonymous yes");
confFile.writeLine("retained_messages_mode downgrade");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
std::vector<ProtocolVersion> protocols {ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5};
for (const ProtocolVersion senderVersion : protocols)
{
for (const ProtocolVersion receiverVersion : protocols)
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "We are testing";
const std::string topic = "retaintopic";
sender.connectClient(senderVersion);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
Publish pub2("dummy2", "Nobody sees this", 0);
pub2.retain = true;
sender.publish(pub2);
receiver.connectClient(receiverVersion);
receiver.subscribe("dummy", 0);
receiver.subscribe(topic, 0, false, true);
usleep(250000);
receiver.waitForMessageCount(0);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY2(ro->receivedPublishes.empty(), "In downgrade mode, retained publishes should not be stored as retained messages.");
}
receiver.clearReceivedLists();
sender.publish(pub1);
receiver.waitForMessageCount(1);
{
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
MqttPacket &msg2 = ro->receivedPublishes.front();
QCOMPARE(msg2.getPayloadCopy(), payload);
QCOMPARE(msg2.getTopic(), topic);
QVERIFY2(!msg2.getRetain(), "Getting a retained message while already being subscribed must be marked as normal, not retain.");
}
}
}
}
/**
* @brief Tests 'enabled_without_retaining', which relays the message with the 'retain' flag set, but does not retain.
*/
void MainTests::test_retained_mode_no_retain()
{
ConfFileTemp confFile;
confFile.writeLine("allow_anonymous yes");
confFile.writeLine("retained_messages_mode enabled_without_retaining");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "We are testing";
const std::string topic = "retaintopic/foo/bar";
sender.connectClient(ProtocolVersion::Mqtt5);
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe(topic, 0, false, true);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
receiver.waitForMessageCount(1);
{
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.size() == 1);
MqttPacket &msg = ro->receivedPublishes.front();
QCOMPARE(msg.getPayloadCopy(), payload);
QCOMPARE(msg.getTopic(), topic);
QVERIFY2(msg.getRetain(), "We were supposed to have seen the retain flag, because 'retain as published' was on.");
}
FlashMQTestClient late_receiver;
late_receiver.start();
late_receiver.connectClient(ProtocolVersion::Mqtt5);
late_receiver.subscribe("#", 0);
usleep(250000);
receiver.waitForMessageCount(0);
QVERIFY2(late_receiver.receivedObjects.lock()->receivedPublishes.empty(), "In enabled_without_retaining mode, retained publishes should not be stored as retained messages.");
}
void MainTests::test_retained_changed()
{
FlashMQTestClient sender;
sender.start();
sender.connectClient(ProtocolVersion::Mqtt311);
const std::string topic = "retaintopic";
Publish p(topic, "We are testing", 0);
p.retain = true;
p.qos = 1;
sender.publish(p);
p.payload = "Changed payload";
sender.publish(p);
FlashMQTestClient receiver;
receiver.start();
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe(topic, 0);
receiver.waitForMessageCount(1);
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
MqttPacket &pack = ro->receivedPublishes.front();
QCOMPARE(pack.getPayloadCopy(), p.payload);
QVERIFY(pack.getRetain());
}
void MainTests::test_retained_removed()
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
std::string payload = "We are testing";
std::string topic = "retaintopic";
sender.connectClient(ProtocolVersion::Mqtt311);
Publish pub1(topic, payload, 1);
pub1.retain = true;
sender.publish(pub1);
pub1.payload = "";
sender.publish(pub1);
receiver.connectClient(ProtocolVersion::Mqtt311);
receiver.subscribe(topic, 0);
usleep(100000);
receiver.waitForMessageCount(0);
auto ro = receiver.receivedObjects.lock();
QVERIFY2(ro->receivedPublishes.empty(), "We erased the retained message. We shouldn't have received any.");
}
/**
* @brief MainTests::test_retained_tree tests a bug I found, where '+/+' yields different results than '#', where it should be the same.
*/
void MainTests::test_retained_tree()
{
FlashMQTestClient sender;
sender.start();
std::string payload = "We are testing";
const std::string topic1 = "TopicA/B";
const std::string topic2 = "Topic/C";
const std::string topic3 = "TopicB/C";
const std::list<std::string> topics {topic1, topic2, topic3};
sender.connectClient(ProtocolVersion::Mqtt311);
Publish p1(topic1, payload, 0);
p1.retain = true;
sender.publish(p1);
Publish p2(topic2, payload, 0);
p2.retain = true;
sender.publish(p2);
Publish p3(topic3, payload, 0);
p3.retain = true;
sender.publish(p3);
FlashMQTestClient receiver;
receiver.start();
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe("+/+", 0);
receiver.waitForMessageCount(3);
auto ro = receiver.receivedObjects.lock();
QCOMPARE(ro->receivedPublishes.size(), topics.size());
for (const std::string &s : topics)
{
bool r = std::any_of(ro->receivedPublishes.begin(), ro->receivedPublishes.end(), [&](MqttPacket &pack)
{
return pack.getTopic() == s && pack.getPayloadCopy() == payload;
});
QVERIFY2(r, formatString("%s not found in retained messages.", s.c_str()).c_str());
}
}
void MainTests::test_retained_global_expire()
{
std::vector<ProtocolVersion> versions { ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5 };
ConfFileTemp confFile;
confFile.writeLine("allow_anonymous yes");
confFile.writeLine("expire_retained_messages_after_seconds 1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
std::vector<ProtocolVersion> protocols {ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5};
for (const ProtocolVersion senderVersion : protocols)
{
for (const ProtocolVersion receiverVersion : protocols)
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
sender.connectClient(senderVersion);
Publish pub1("retaintopic/1", "We are testing", 0);
pub1.retain = true;
sender.publish(pub1);
Publish pub2("retaintopic/2", "asdf", 0);
pub2.retain = true;
sender.publish(pub2);
usleep(2000000);
receiver.connectClient(receiverVersion);
receiver.subscribe("#", 0);
usleep(500000);
receiver.waitForMessageCount(0);
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 0);
}
}
}
void MainTests::test_retained_per_message_expire()
{
std::vector<ProtocolVersion> versions { ProtocolVersion::Mqtt311, ProtocolVersion::Mqtt5 };
ConfFileTemp confFile;
confFile.writeLine("allow_anonymous yes");
confFile.writeLine("expire_retained_messages_after_seconds 10");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
sender.connectClient(ProtocolVersion::Mqtt5);
Publish pub1("retaintopic/1", "We are testing", 0);
pub1.retain = true;
sender.publish(pub1);
Publish pub2("retaintopic/2", "asdf", 0);
pub2.retain = true;
pub2.setExpireAfter(1);
sender.publish(pub2);
usleep(2000000);
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe("#", 0);
usleep(500000);
receiver.waitForMessageCount(1);
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
MqttPacket &msg = ro->receivedPublishes.front();
QCOMPARE(msg.getPayloadCopy(), "We are testing");
QCOMPARE(msg.getTopic(), "retaintopic/1");
QVERIFY(msg.getRetain());
}
void MainTests::test_retained_tree_purging()
{
std::shared_ptr<SubscriptionStore> store = globals->subscriptionStore;
int toDeleteCount = 0;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
std::string topic = formatString("retain%d/bla%d/asdf", i, j);
Publish pub(topic, "willnotexpire", 0);
if (i % 2 == 0)
{
pub.setExpireAfter(1);
pub.payload = "willexpire";
toDeleteCount++;
}
std::vector<std::string> subtopics = splitTopic(topic);
store->setRetainedMessage(pub, subtopics);
}
}
{
Publish pubStray("retain0/bla5", "willnotexpire", 0);
std::vector<std::string> subtopics = splitTopic(pubStray.topic);
store->setRetainedMessage(pubStray, subtopics);
}
const int beforeCount = store->getAllRetainedMessages().size();
usleep(2000000);
store->expireRetainedMessages();
std::vector<RetainedMessage> list;
const std::chrono::time_point<std::chrono::steady_clock> limit = std::chrono::steady_clock::now() + std::chrono::milliseconds(1000);
std::deque<std::weak_ptr<RetainedMessageNode>> deferred;
store->getRetainedMessages(store->retainedMessagesRoot.get(), list, limit, 100000, deferred);
QVERIFY(deferred.empty());
QVERIFY(std::none_of(list.begin(), list.end(), [](RetainedMessage &rm) {
return rm.publish.payload == "willexpire";
}));
QVERIFY(std::all_of(list.begin(), list.end(), [](RetainedMessage &rm) {
return rm.publish.payload == "willnotexpire";
}));
MYCASTCOMPARE(store->getAllRetainedMessages().size(), beforeCount - toDeleteCount);
}
void MainTests::testRetainAsPublished()
{
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5);
FlashMQTestClient client2;
client2.start();
client2.connectClient(ProtocolVersion::Mqtt5);
FlashMQTestClient client3;
client3.start();
client3.connectClient(ProtocolVersion::Mqtt5);
client2.subscribe("mytopic", 1, false, false);
client.subscribe("mytopic", 1, false, true);
client3.subscribe("mytopic", 1, false, false);
try
{
Publish pub("mytopic", "mypayload", 1);
pub.retain = true;
client.publish(pub);
}
catch (std::exception &ex)
{
QVERIFY2(false, ex.what());
}
try
{
client.waitForMessageCount(1);
}
catch (std::exception &ex)
{
QVERIFY2(false, ex.what());
}
auto ro = client.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
const MqttPacket &first = ro->receivedPublishes.front();
QVERIFY(first.getRetain());
}
void MainTests::testRetainAsPublishedNegative()
{
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5);
client.subscribe("mytopic", 1, false, false);
try
{
Publish pub("mytopic", "mypayload", 1);
pub.retain = true;
client.publish(pub);
}
catch (std::exception &ex)
{
QVERIFY2(false, ex.what());
}
try
{
client.waitForMessageCount(1);
}
catch (std::exception &ex)
{
QVERIFY2(false, ex.what());
}
auto ro = client.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
const MqttPacket &first = ro->receivedPublishes.front();
QVERIFY(!first.getRetain());
}
/**
* @brief MainTests::testRetainedParentOfWildcard tests whether subscribing to 'one/two/three/four/#' gives you 'one/two/three/four'.
*/
void MainTests::testRetainedParentOfWildcard()
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "We are testing testRetainedParentOfWildcard";
const std::string publish_topic = "one/two/three/four";
sender.connectClient(ProtocolVersion::Mqtt5);
Publish pub1(publish_topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe("dummy", 0);
receiver.subscribe("one/two/three/four/#", 0);
try
{
receiver.waitForMessageCount(1);
}
catch (std::exception &ex)
{
QVERIFY2(false, "Exception happened. Likely waited for retained messages, but none received.");
}
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
MqttPacket &msg = ro->receivedPublishes.front();
QCOMPARE(msg.getPayloadCopy(), payload);
QCOMPARE(msg.getTopic(), publish_topic);
QVERIFY(msg.getRetain());
}
void MainTests::testRetainedWildcard()
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "We are testing testRetainedWildcard";
const std::string publish_topic = "one/two/three/four";
sender.connectClient(ProtocolVersion::Mqtt5);
Publish pub1(publish_topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
Publish pub2("publish/into/nothing", payload, 0);
pub2.retain = true;
sender.publish(pub2);
receiver.connectClient(ProtocolVersion::Mqtt5);
receiver.subscribe("dummy", 0);
receiver.subscribe("one/two/three/#", 0);
try
{
receiver.waitForMessageCount(1);
}
catch (std::exception &ex)
{
QVERIFY2(false, "Exception happened. Likely waited for retained messages, but none received.");
}
auto ro = receiver.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
MqttPacket &msg = ro->receivedPublishes.front();
QCOMPARE(msg.getPayloadCopy(), payload);
QCOMPARE(msg.getTopic(), publish_topic);
QVERIFY(msg.getRetain());
}
/**
* @brief MainTests::testRetainedAclReadCheck tests the manipulation of the retain bit in the original incoming packet.
*
* This has to do with the optimization in CopyFactory, that under certain conditions, the original packet's vector is
* just used to write to the client.
*/
void MainTests::testRetainedAclReadCheck()
{
ConfFileTemp confFile;
confFile.writeLine("plugin plugins/libtest_plugin.so.0.0.1");
confFile.closeFile();
std::vector<std::string> args {"--config-file", confFile.getFilePath()};
cleanup();
init(args);
FlashMQTestClient client;
client.start();
client.connectClient(ProtocolVersion::Mqtt5, true, 30, [](Connect &connect) {
connect.clientid = "test_user_with_retain_as_published_v8sIeCvI";
});
client.subscribe("mytopic", 1, false, true);
FlashMQTestClient client2;
client2.start();
client2.connectClient(ProtocolVersion::Mqtt5, true, 30, [](Connect &connect) {
connect.clientid = "test_user_without_retain_as_published_CswU21YA";
});
client2.subscribe("mytopic", 1, false, false);
FlashMQTestClient publish_client;
publish_client.start();
publish_client.connectClient(ProtocolVersion::Mqtt5);
Publish pub("mytopic", "mypayload", 1);
pub.retain = true;
publish_client.publish(pub);
try
{
client.waitForMessageCount(1);
client2.waitForMessageCount(1);
}
catch (std::exception &ex)
{
QVERIFY2(false, ex.what());
}
auto ro = client.receivedObjects.lock();
auto ro2 = client2.receivedObjects.lock();
MYCASTCOMPARE(ro->receivedPublishes.size(), 1);
MYCASTCOMPARE(ro2->receivedPublishes.size(), 1);
}
void MainTests::testRetainHandlingDontGiveRetain()
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "retained payload";
const std::string topic = "retaintopic/one/two/three";
sender.connectClient(ProtocolVersion::Mqtt5);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
receiver.connectClient(ProtocolVersion::Mqtt5, true, 0);
receiver.subscribe(topic, 0, false, false, 0, RetainHandling::DoNotSendRetainedMessages);
usleep(250000);
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.empty());
}
void MainTests::testRetainHandlingDontGiveRetainOnExistingSubscription()
{
FlashMQTestClient sender;
FlashMQTestClient receiver;
sender.start();
receiver.start();
const std::string payload = "retained payload";
const std::string topic = "retaintopic/one/two/three";
sender.connectClient(ProtocolVersion::Mqtt5);
Publish pub1(topic, payload, 0);
pub1.retain = true;
sender.publish(pub1);
{
receiver.connectClient(ProtocolVersion::Mqtt5, true, 0);
receiver.subscribe(topic, 0, false, false, 0, RetainHandling::SendRetainedMessagesAtNewSubscribeOnly);
receiver.waitForMessageCount(1);
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.size() == 1);
}
receiver.clearReceivedLists();
receiver.subscribe(topic, 1, false, false, 0, RetainHandling::SendRetainedMessagesAtNewSubscribeOnly);
usleep(250000);
auto ro = receiver.receivedObjects.lock();
QVERIFY(ro->receivedPublishes.empty());
}
================================================
FILE: FlashMQTests/run-make-from-ci.sh
================================================
#!/bin/bash
extra_configs=()
compiler="g++"
while [ -n "$*" ]; do
flag=$1
value=$2
case "$flag" in
"--compiler")
compiler="$value"
shift
;;
"--extra-config")
extra_configs+=("$value")
shift
;;
"--")
shift
break
;;
*)
echo -e "unknown option $flag\n"
exit 1
;;
esac
shift
done
set -u
for item in "${extra_configs[@]}"; do
extra_configs_expanded=("${extra_configs_expanded[@]}" "-D" "$item")
done
CXX="$compiler" cmake -DCMAKE_BUILD_TYPE=Debug -S . -B buildtests "${extra_configs_expanded[@]}"
nprocs=4
if _nprocs=$(nproc); then
nprocs="$_nprocs"
fi
make -C buildtests -j "$nprocs"
================================================
FILE: FlashMQTests/run-tests-from-ci.sh
================================================
#!/bin/bash
STDERR_LOG=$(mktemp)
cd buildtests || exit 1
# Using --abort-on-first-fail because the output can be hard to find in CI when it's swamped out by the rest.
if ./flashmq-tests --abort-on-first-fail 2> "$STDERR_LOG" ; then
echo -e '\033[01;32mSUCCESS!\033[00m'
else
echo -e '\033[01;31mBummer\033[00m'
echo -e "\n\nTail of stderr:\n\n"
tail -n 200 "$STDERR_LOG"
exit 1
fi
================================================
FILE: FlashMQTests/sharedsubscriptionstests.cpp
================================================
#include "maintests.h"
#include "testhelpers.h"
#include "flashmqtestclient.h"
#include "flashmqtempdir.h"
#include "conffiletemp.h"
#include "utils.h"
#include "threadglobals.h"
void MainTests::testSharedSubscribersUnit()
{
Settings settings;
std::shared_ptr<PluginLoader> pluginLoader = std::make_shared<PluginLoader>();
std::shared_ptr<ThreadData> t(new ThreadData(0, settings, pluginLoader, std::weak_ptr<MainApp>()));
ThreadGlobals::assignThreadData(t);
std::shared_ptr<Client> c1(new Client(ClientType::Normal, -1, t, FmqSsl(), ConnectionProtocol::Mqtt, HaProxyMode::Off, nullptr, settings, false));
c1->setClientProperties(ProtocolVersion::Mqtt5, "clientid1", {}, "user1", true, 60);
std::shared_ptr<Client> c2(new Client(ClientType::Normal, -1, t, FmqSsl(), ConnectionProtocol::Mqtt, HaProxyMode::Off, nullptr, settings, false));
c2->setClientProperties(ProtocolVersion::Mqtt5, "clientid2", {}, "user2", true, 60);
std::shared_ptr<Client> c3(new Client(ClientType::Normal, -1, t, FmqSsl(), ConnectionProtocol::Mqtt, HaProxyMode::Off, nullptr, settings, false));
c3->setClientProperties(ProtocolVersion::Mqtt5, "clientid3", {}, "user3", true, 60);
std::shared_ptr<Session> ses1 = std::make_shared<Session>(c1->getClientId(), c1->getUsername(), std::optional<std::string>());
ses1->assignActiveConnection(c1);
std::shared_ptr<Session> ses2 = std::make_shared<Session>(c2->getClientId(), c2->getUsername(), std::optional<std::string>());
ses2->assignActiveConnection(c2);
std::shared_ptr<Session> ses3 = std::make_shared<Session>(c3->getClientId(), c3->getUsername(), std::optional<std::string>());
ses3->assignActiveConnection(c3);
SharedSubscribers s("dummy", {});
s[ses1->getClientId()].session = ses1;
MYCASTCOMPARE(s.members.size(), 1);
s[ses2->getClientId()].session = ses2;
MYCASTCOMPARE(s.members.size(), 2);
s[ses3->getClientId()].session = ses3;
MYCASTCOMPARE(s.members.size(), 3);
s[ses2->getClientId()].reset();
MYCASTCOMPARE(s.members.size(), 3);
QCOMPARE(*s.getNext(), s[ses1->getClientId()]);
QCOMPARE(*s.getNext(), s[ses3->getClientId()]);
s.purgeAndReIndex();
MYCASTCOMPARE(s.members.size(), 2);
// We still should get the same two active members
QCOMPARE(*s.getNext(), s[ses1->getClientId()]);
QCOMPARE(*s.getNext(), s[ses3->getClientId()]);
s.erase(ses3->getClientId());
// Now we only have one left
QCOMPARE(*s.getNext(), s[ses1->getClientId()]);
QCOMPARE(*s.getNext(), s[ses1->getClientId()]);
s.erase(ses1->getClientId());
QVERIFY(!s.empty());
s.purgeAndReIndex();
QVERIFY(s.empty());
}
void MainTests::testSharedSubscribers()
{
FlashMQTestClient receiver1;
receiver1.start();
receiver1.connectClient(ProtocolVersion::Mqtt5);
FlashMQTestClient receiver2;
receiver2.start();
receiver2.connectClient(ProtocolVersion::Mqtt5);
receiver1.subscribe("$share/ahTahHu5/one/two/three", 1);
receiver2.subscribe("$share/ahTahHu5/one/two/three", 1);
FlashMQTestClient sender;
sender.start();
sender.connectClient(ProtocolVersion::Mqtt5);
sender.publish("one/two/three", "rainy day", 1);
sender.publish("one/two/three", "sunny day", 1);
receiver1.waitForMessageCount(1);
receiver2.waitForMessageCount(1);
{
auto ro1 = receiver1.receivedObjects.lock();
auto ro2 = receiver2.receivedObjects.lock();
MYCASTCOMPARE(ro1->receivedPublishes.size(), 1);
MYCASTCOMPARE(ro2->receivedPublishes.size(), 1);
int rain = std::count_if(ro1->receivedPublishes.begin(), ro1->receivedPublishes.end(),
[](const MqttPacket &pack) { return pack.getPayloadCopy() == "rainy day";});
int sun = std::count_if(ro2->receivedPublishes.begin(), ro2->receivedPublishes.end(),
[](const MqttPacket &pack) { return pack.getPayloadCopy() == "sunny day";});
QCOMPARE(rain, 1);
QCOMPARE(sun, 1);
}
receiver1.unsubscribe("$share/ahTahHu5/one/two/three");
receiver1.clearReceivedLists();
receiver2.clearReceivedLists();
sender.publish("one/two/thre
gitextract_eh9hwlw7/ ├── .build-ci.sh ├── .clang-format ├── .clang-format.original ├── .dockerignore ├── .get-os-codename-and-stamp.sh ├── .github/ │ └── workflows/ │ ├── building.yml │ ├── codestyle.yml │ ├── docker.yml │ ├── linting.yml │ ├── testing.yml │ └── testing_non_sse.yml ├── .gitignore ├── CMakeLists.shared ├── CMakeLists.txt ├── Dockerfile ├── FlashMQTests/ │ ├── CMakeLists.txt │ ├── UTF-8-test.txt │ ├── bridgeprefixtests.cpp │ ├── conffiletemp.cpp │ ├── conffiletemp.h │ ├── configtests.cpp │ ├── dnstests.cpp │ ├── filecloser.cpp │ ├── filecloser.h │ ├── flashmqtempdir.cpp │ ├── flashmqtempdir.h │ ├── main.cpp │ ├── mainappasfork.cpp │ ├── mainappasfork.h │ ├── mainappinthread.cpp │ ├── mainappinthread.h │ ├── maintests.cpp │ ├── maintests.h │ ├── plugins/ │ │ ├── curlfunctions.cpp │ │ ├── curlfunctions.h │ │ ├── test_plugin.cpp │ │ ├── test_plugin.h │ │ └── test_plugin.pro │ ├── plugintests.cpp │ ├── retaintests.cpp │ ├── run-make-from-ci.sh │ ├── run-tests-from-ci.sh │ ├── sharedsubscriptionstests.cpp │ ├── subscriptionidtests.cpp │ ├── testhelpers.cpp │ ├── testhelpers.h │ ├── testinitializer.cpp │ ├── testinitializer.h │ ├── tst_maintests.cpp │ ├── utiltests.cpp │ ├── websockettests.cpp │ └── willtests.cpp ├── LICENSE ├── README.md ├── acksender.cpp ├── acksender.h ├── acltree.cpp ├── acltree.h ├── backgroundworker.cpp ├── backgroundworker.h ├── bindaddr.cpp ├── bindaddr.h ├── bridgeconfig.cpp ├── bridgeconfig.h ├── bridgeinfodb.cpp ├── bridgeinfodb.h ├── build.sh ├── checkedsharedptr.h ├── checkedweakptr.h ├── cirbuf.cpp ├── cirbuf.h ├── client.cpp ├── client.h ├── clientacceptqueue.cpp ├── clientacceptqueue.h ├── configfileparser.cpp ├── configfileparser.h ├── debian/ │ ├── conffiles │ ├── flashmq.service │ ├── postinst │ ├── postrm │ ├── preinst │ └── prerm ├── derivablecounter.cpp ├── derivablecounter.h ├── dnsresolver.cpp ├── dnsresolver.h ├── driftcounter.cpp ├── driftcounter.h ├── enums.h ├── evpencodectxmanager.cpp ├── evpencodectxmanager.h ├── examples/ │ └── plugin_libcurl/ │ ├── CMakeLists.txt │ ├── LICENSE │ ├── README.md │ ├── build.sh │ ├── src/ │ │ ├── authenticatingclient.cpp │ │ ├── authenticatingclient.h │ │ ├── curl_functions.cpp │ │ ├── curl_functions.h │ │ ├── plugin_libcurl.cpp │ │ ├── pluginstate.cpp │ │ └── pluginstate.h │ └── vendor/ │ ├── flashmq_plugin.h │ └── flashmq_public.h ├── exceptions.cpp ├── exceptions.h ├── fdmanaged.cpp ├── fdmanaged.h ├── flags.h ├── flashmq.conf ├── flashmq_plugin.cpp ├── flashmq_plugin.h ├── flashmq_plugin_deprecated.h ├── flashmq_public.h ├── flashmqtestclient.cpp ├── flashmqtestclient.h ├── fmqmain.cpp ├── fmqmain.h ├── fmqsockaddr.cpp ├── fmqsockaddr.h ├── fmqssl.cpp ├── fmqssl.h ├── forward_declarations.h ├── fuzz-helper.sh ├── globals.cpp ├── globals.h ├── globalstats.cpp ├── globalstats.h ├── globber.cpp ├── globber.h ├── haproxy.cpp ├── haproxy.h ├── http.cpp ├── http.h ├── iowrapper.cpp ├── iowrapper.h ├── listener.cpp ├── listener.h ├── lockedsharedptr.h ├── lockedweakptr.cpp ├── lockedweakptr.h ├── logger.cpp ├── logger.h ├── main.cpp ├── mainapp.cpp ├── mainapp.h ├── man/ │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── docbook5-refentry-xslt/ │ │ ├── docbook5-refentry-to-html5.xsl │ │ └── docbook5-refentry-to-manpage.xsl │ ├── flashmq-docbook5-refentry-to-html5.xsl │ ├── flashmq-docbook5-refentry-to-manpage.xsl │ ├── flashmq.1 │ ├── flashmq.1.dbk5 │ ├── flashmq.1.html │ ├── flashmq.conf.5 │ ├── flashmq.conf.5.dbk5 │ ├── flashmq.conf.5.html │ ├── refentry.colophon.dbk5 │ └── reference.dbk5 ├── mosquittoauthoptcompatwrap.cpp ├── mosquittoauthoptcompatwrap.h ├── mqtt5properties.cpp ├── mqtt5properties.h ├── mqttpacket.cpp ├── mqttpacket.h ├── mutexowned.h ├── network.cpp ├── network.h ├── nocopy.cpp ├── nocopy.h ├── oneinstancelock.cpp ├── oneinstancelock.h ├── packetdatatypes.cpp ├── packetdatatypes.h ├── persistencefile.cpp ├── persistencefile.h ├── persistencefunctions.cpp ├── persistencefunctions.h ├── plugin.cpp ├── plugin.h ├── pluginloader.cpp ├── pluginloader.h ├── publishcopyfactory.cpp ├── publishcopyfactory.h ├── qospacketqueue.cpp ├── qospacketqueue.h ├── queuedtasks.cpp ├── queuedtasks.h ├── release.sh ├── retainedmessage.cpp ├── retainedmessage.h ├── retainedmessagesdb.cpp ├── retainedmessagesdb.h ├── rwlockguard.cpp ├── rwlockguard.h ├── scopedsocket.cpp ├── scopedsocket.h ├── sdnotify.cpp ├── sdnotify.h ├── session.cpp ├── session.h ├── sessionsandsubscriptionsdb.cpp ├── sessionsandsubscriptionsdb.h ├── settings.cpp ├── settings.h ├── sharedsubscribers.cpp ├── sharedsubscribers.h ├── sslctxmanager.cpp ├── sslctxmanager.h ├── subscription.cpp ├── subscription.h ├── subscriptionstore.cpp ├── subscriptionstore.h ├── threaddata.cpp ├── threaddata.h ├── threadglobals.cpp ├── threadglobals.h ├── threadlocalutils.cpp ├── threadlocalutils.h ├── threadlocked.h ├── threadloop.cpp ├── threadloop.h ├── types.cpp ├── types.h ├── unscopedlock.cpp ├── unscopedlock.h ├── utils.cpp ├── utils.h ├── variablebyteint.cpp ├── variablebyteint.h ├── x509manager.cpp └── x509manager.h
SYMBOL INDEX (461 symbols across 134 files)
FILE: FlashMQTests/bridgeprefixtests.cpp
function waitForMessagesOverBridge (line 8) | void waitForMessagesOverBridge(FlashMQTestClient &one, FlashMQTestClient...
FILE: FlashMQTests/conffiletemp.h
function class (line 6) | class ConfFileTemp
FILE: FlashMQTests/filecloser.h
function class (line 5) | class FileCloser
FILE: FlashMQTests/flashmqtempdir.h
function class (line 9) | class FlashMQTempDir
FILE: FlashMQTests/main.cpp
function printHelp (line 6) | void printHelp(const std::string &arg0)
function main (line 12) | int main(int argc, char *argv[])
FILE: FlashMQTests/mainappasfork.cpp
type sockaddr_in (line 116) | struct sockaddr_in
type sockaddr_in (line 117) | struct sockaddr_in
type sockaddr (line 123) | struct sockaddr
type sockaddr_in (line 127) | struct sockaddr_in
FILE: FlashMQTests/mainappasfork.h
function class (line 15) | class MainAppAsFork
FILE: FlashMQTests/mainappinthread.h
function class (line 9) | class MainAppInThread
FILE: FlashMQTests/maintests.h
type TestFunction (line 14) | struct TestFunction
function class (line 21) | class MainTests
FILE: FlashMQTests/plugins/curlfunctions.cpp
function socket_event_watch_notification (line 16) | int socket_event_watch_notification(CURL *easy, curl_socket_t s, int wha...
function check_all_active_curls (line 43) | void check_all_active_curls(TestPluginData *p, CURLM *curlMulti)
function call_timed_curl_multi_socket_action (line 70) | void call_timed_curl_multi_socket_action(CURLM *multi, TestPluginData *p)
function timer_callback (line 91) | int timer_callback(CURLM *multi, long timeout_ms, void *clientp)
function curl_write_cb (line 111) | size_t curl_write_cb(char *data, size_t n, size_t l, void *userp)
FILE: FlashMQTests/plugins/test_plugin.cpp
function get_auth_result_delayed (line 33) | void get_auth_result_delayed(std::weak_ptr<Client> client, AuthResult re...
function flashmq_plugin_version (line 41) | int flashmq_plugin_version()
function flashmq_plugin_allocate_thread_memory (line 46) | void flashmq_plugin_allocate_thread_memory(void **thread_data, std::unor...
function flashmq_plugin_deallocate_thread_memory (line 53) | void flashmq_plugin_deallocate_thread_memory(void *thread_data, std::uno...
function flashmq_plugin_poll_event_received (line 61) | void flashmq_plugin_poll_event_received(void *thread_data, int fd, uint3...
function flashmq_plugin_init (line 90) | void flashmq_plugin_init(void *thread_data, std::unordered_map<std::stri...
function flashmq_plugin_deinit (line 102) | void flashmq_plugin_deinit(void *thread_data, std::unordered_map<std::st...
function flashmq_plugin_periodic_event (line 109) | void flashmq_plugin_periodic_event(void *thread_data)
function AuthResult (line 114) | AuthResult flashmq_plugin_login_check(void *thread_data, const std::stri...
function publish_in_thread (line 186) | void publish_in_thread()
function AuthResult (line 191) | AuthResult flashmq_plugin_acl_check(
function AuthResult (line 269) | AuthResult flashmq_plugin_extended_auth(void *thread_data, const std::st...
function flashmq_plugin_alter_publish (line 314) | bool flashmq_plugin_alter_publish(
function flashmq_plugin_client_disconnected (line 350) | void flashmq_plugin_client_disconnected(void *thread_data, const std::st...
function flashmq_plugin_main_init (line 358) | void flashmq_plugin_main_init(std::unordered_map<std::string, std::strin...
function flashmq_plugin_main_deinit (line 371) | void flashmq_plugin_main_deinit(std::unordered_map<std::string, std::str...
FILE: FlashMQTests/plugins/test_plugin.h
type AuthenticatingClient (line 11) | struct AuthenticatingClient
function class (line 26) | class TestPluginData
FILE: FlashMQTests/testhelpers.cpp
function fmq_assert (line 10) | bool fmq_assert(bool b, const char *failmsg, const char *actual, const c...
function fmq_fail (line 41) | void fmq_fail(const char *failmsg, const char *file, int line)
FILE: FlashMQTests/testhelpers.h
function fmq_compare (line 45) | inline bool fmq_compare(const std::string &s1, const std::string &s2, co...
function fmq_compare (line 53) | bool fmq_compare(const T1 &t1, const T2 &t2, const char *actual, const c...
function fmq_compare (line 58) | inline bool fmq_compare(const char *c1, const char *c2, const char *actu...
function myCastCompare (line 80) | bool myCastCompare(const T1 &t1, const T2 &t2, const char *actual, const...
FILE: FlashMQTests/testinitializer.h
function class (line 9) | class TestInitializer
FILE: FlashMQTests/tst_maintests.cpp
function testDowngradeQoSOnSubscribeHelper (line 1245) | void testDowngradeQoSOnSubscribeHelper(const uint8_t pub_qos, const uint...
type sockaddr_in (line 2953) | struct sockaddr_in
type sockaddr_in (line 2954) | struct sockaddr_in
type sockaddr_in6 (line 2977) | struct sockaddr_in6
type sockaddr_in6 (line 2978) | struct sockaddr_in6
FILE: FlashMQTests/websockettests.cpp
function pollFd (line 13) | void pollFd(int fd, bool throw_on_timeout)
function readFromSocket (line 28) | std::vector<char> readFromSocket(int fd, bool throw_on_timeout, size_t e...
FILE: acksender.h
function class (line 18) | class AckSender
FILE: acltree.cpp
function AclNode (line 22) | AclNode *AclNode::getChildren(const std::string &subtopic, bool register...
function AclNode (line 48) | const AclNode *AclNode::getChildren(const std::string &subtopic) const
function AclNode (line 55) | AclNode *AclNode::getChildrenPlus()
function AclNode (line 63) | const AclNode *AclNode::getChildrenPlus() const
function AuthResult (line 236) | AuthResult AclTree::findPermission(const std::vector<std::string> &subto...
function AclGrant (line 287) | AclGrant stringToAclGrant(const std::string &s)
FILE: acltree.h
type class (line 20) | enum class
type class (line 28) | enum class
FILE: backgroundworker.cpp
type pollfd (line 12) | struct pollfd
type pollfd (line 13) | struct pollfd
FILE: backgroundworker.h
function class (line 12) | class BackgroundWorker
FILE: bindaddr.cpp
type sockaddr_in (line 49) | struct sockaddr_in
type sockaddr_in6 (line 65) | struct sockaddr_in6
type sockaddr_un (line 81) | struct sockaddr_un
FILE: bindaddr.h
function class (line 23) | class BindAddr
FILE: bridgeconfig.cpp
function FMQSockaddr (line 97) | FMQSockaddr BridgeState::popDnsResult()
FILE: bridgeconfig.h
type class (line 14) | enum class
type BridgeTopicPath (line 21) | struct BridgeTopicPath
function class (line 36) | class BridgeClientGroupIds
function std (line 103) | const std::string &getClientid() const;
FILE: bridgeinfodb.h
type BridgeInfoForSerializing (line 13) | struct BridgeInfoForSerializing
function ReadVersion (line 27) | enum class ReadVersion
FILE: checkedweakptr.h
function class (line 6) | class __attribute__((visibility("default"))) PointerNullException : publ...
FILE: cirbuf.h
function class (line 23) | class CirBuf
FILE: client.cpp
type sockaddr (line 65) | struct sockaddr
type epoll_event (line 152) | struct epoll_event
function HaProxyStage (line 170) | HaProxyStage Client::readHaProxyData()
function ProtocolVersion (line 200) | ProtocolVersion Client::getProtocolVersion() const
function DisconnectStage (line 271) | DisconnectStage Client::readFdIntoBuffer()
function PacketDropReason (line 335) | PacketDropReason Client::writeMqttPacket(const MqttPacket &packet)
function PacketDropReason (line 378) | PacketDropReason Client::writeMqttPacketAndBlameThisClient(
function PacketDropReason (line 448) | PacketDropReason Client::writeMqttPacketAndBlameThisClient(const MqttPac...
function FMQSockaddr (line 528) | const FMQSockaddr &Client::getAddr() const
type epoll_event (line 890) | struct epoll_event
type epoll_event (line 917) | struct epoll_event
function X509ClientVerification (line 1201) | X509ClientVerification Client::getX509ClientVerification() const
function AllowListenerAnonymous (line 1211) | AllowListenerAnonymous Client::getAllowAnonymousOverride() const
FILE: client.h
type StowedClientRegistrationData (line 45) | struct StowedClientRegistrationData
type class (line 54) | enum class
type AsyncAuthResult (line 61) | struct AsyncAuthResult
function class (line 71) | class Client
FILE: clientacceptqueue.h
type ClientAcceptQueue (line 12) | struct ClientAcceptQueue
FILE: configfileparser.cpp
function full_stoi (line 37) | int full_stoi(const std::string &key, const std::string &value)
function full_stoul (line 54) | unsigned long full_stoul(const std::string &key, const std::string &valu...
type stat (line 142) | struct stat
type stat (line 179) | struct stat
function Settings (line 1435) | const Settings &ConfigFileParser::getSettings()
FILE: configfileparser.h
type class (line 25) | enum class
function newVal (line 39) | long long newVal{std::stoll(value, &len)};
function value_to_int (line 53) | long unsigned>::type
function class (line 98) | class ConfigFileParser
FILE: derivablecounter.h
function class (line 22) | class DerivableCounter
FILE: dnsresolver.cpp
type gaicb (line 58) | struct gaicb
type addrinfo (line 96) | struct addrinfo
FILE: dnsresolver.h
function class (line 24) | class DnsResolver
FILE: driftcounter.h
function class (line 14) | class DriftCounter
FILE: enums.h
type class (line 14) | enum class
type class (line 21) | enum class
type class (line 28) | enum class
type class (line 36) | enum class
type class (line 42) | enum class
type class (line 49) | enum class
type class (line 55) | enum class
FILE: evpencodectxmanager.h
type EvpEncodeCtxManager (line 17) | struct EvpEncodeCtxManager
FILE: examples/plugin_libcurl/src/authenticatingclient.h
function namespace (line 39) | namespace ExampleCurlPlugin
FILE: examples/plugin_libcurl/src/curl_functions.cpp
function socket_event_watch_notification (line 48) | int socket_event_watch_notification(CURL *easy, curl_socket_t s, int wha...
function timer_callback (line 77) | int timer_callback(CURLM *multi, long timeout_ms, void *clientp)
function call_timed_curl_multi_socket_action (line 97) | void call_timed_curl_multi_socket_action(CURLM *multi, PluginState *s)
function check_all_active_curls (line 117) | void check_all_active_curls(PluginState *p, CURLM *curlMulti)
function curl_write_cb (line 145) | size_t curl_write_cb(char *data, size_t n, size_t l, void *userp)
FILE: examples/plugin_libcurl/src/plugin_libcurl.cpp
function flashmq_plugin_version (line 41) | int flashmq_plugin_version()
function flashmq_plugin_main_init (line 46) | void flashmq_plugin_main_init(std::unordered_map<std::string, std::strin...
function flashmq_plugin_main_deinit (line 54) | void flashmq_plugin_main_deinit(std::unordered_map<std::string, std::str...
function flashmq_plugin_allocate_thread_memory (line 61) | void flashmq_plugin_allocate_thread_memory(void **thread_data, std::unor...
function flashmq_plugin_deallocate_thread_memory (line 69) | void flashmq_plugin_deallocate_thread_memory(void *thread_data, std::uno...
function flashmq_plugin_init (line 83) | void flashmq_plugin_init(void *thread_data, std::unordered_map<std::stri...
function flashmq_plugin_deinit (line 90) | void flashmq_plugin_deinit(void *thread_data, std::unordered_map<std::st...
function flashmq_plugin_poll_event_received (line 105) | void flashmq_plugin_poll_event_received(void *thread_data, int fd, uint3...
function AuthResult (line 131) | AuthResult flashmq_plugin_login_check(
function AuthResult (line 176) | AuthResult flashmq_plugin_acl_check(
FILE: examples/plugin_libcurl/src/pluginstate.h
function namespace (line 40) | namespace ExampleCurlPlugin
FILE: examples/plugin_libcurl/vendor/flashmq_public.h
type class (line 47) | enum class
type class (line 79) | enum class
type class (line 95) | enum class
function ServerDisconnectReasons (line 108) | enum class ServerDisconnectReasons
FILE: exceptions.h
function namespace (line 23) | namespace FlashMQ
FILE: fdmanaged.h
function class (line 5) | class FdManaged
FILE: flags.h
function setFlag (line 29) | void setFlag(FlagType val)
function clearFlag (line 34) | void clearFlag(FlagType val)
function clearAll (line 39) | void clearAll()
function setAll (line 44) | void setAll()
FILE: flashmq_plugin.cpp
function flashmq_logf (line 22) | void flashmq_logf(int level, const char *str, ...)
function mosquitto_log_printf (line 32) | void mosquitto_log_printf(int level, const char *fmt, ...)
function flashmq_plugin_remove_client (line 44) | void flashmq_plugin_remove_client(const std::string &clientid, bool also...
function flashmq_plugin_remove_client_v4 (line 69) | void flashmq_plugin_remove_client_v4(const std::weak_ptr<Session> &sessi...
function flashmq_plugin_remove_subscription (line 90) | void flashmq_plugin_remove_subscription(const std::string &clientid, con...
function flashmq_plugin_remove_subscription_v4 (line 107) | void flashmq_plugin_remove_subscription_v4(const std::weak_ptr<Session> ...
function flashmq_plugin_add_subscription (line 124) | bool flashmq_plugin_add_subscription(
function flashmq_continue_async_authentication (line 145) | void flashmq_continue_async_authentication(const std::weak_ptr<Client> &...
function flashmq_continue_async_authentication_v4 (line 150) | void flashmq_continue_async_authentication_v4(
function flashmq_publish_message (line 167) | void flashmq_publish_message(const std::string &topic, const uint8_t qos...
function flashmq_get_client_address (line 202) | void flashmq_get_client_address(const std::weak_ptr<Client> &client, std...
function flashmq_get_client_address_v4 (line 226) | void flashmq_get_client_address_v4(const std::weak_ptr<Client> &client, ...
function flashmq_poll_add_fd (line 249) | void flashmq_poll_add_fd(int fd, uint32_t events, const std::weak_ptr<vo...
function flashmq_poll_remove_fd (line 259) | void flashmq_poll_remove_fd(uint32_t fd)
function sockaddr (line 269) | sockaddr *FlashMQSockAddr::getAddr()
type sockaddr_in6 (line 276) | struct sockaddr_in6
function flashmq_add_task (line 279) | uint32_t flashmq_add_task(std::function<void ()> f, uint32_t delay_in_ms)
function flashmq_remove_task (line 289) | void flashmq_remove_task(uint32_t id)
function flashmq_get_session_pointer (line 299) | void flashmq_get_session_pointer(const std::string &clientid, const std:...
function flashmq_get_client_pointer (line 309) | void flashmq_get_client_pointer(const std::weak_ptr<Session> &session, s...
function flashmq_defer_thread_ready (line 316) | void flashmq_defer_thread_ready()
function flashmq_signal_thread_ready (line 329) | void flashmq_signal_thread_ready()
FILE: flashmq_plugin_deprecated.h
function class (line 11) | class API FlashMQSockAddr
FILE: flashmq_public.h
type class (line 47) | enum class
type class (line 79) | enum class
type class (line 95) | enum class
function ServerDisconnectReasons (line 108) | enum class ServerDisconnectReasons
FILE: flashmqtestclient.cpp
type sockaddr_in (line 108) | struct sockaddr_in
type sockaddr (line 119) | struct sockaddr
type sockaddr (line 129) | struct sockaddr
function ProtocolVersion (line 390) | ProtocolVersion FlashMQTestClient::getProtocolVersion()
FILE: flashmqtestclient.h
function class (line 23) | class SubAckIsError : public std::runtime_error
function class (line 32) | class FlashMQTestClient
FILE: fmqmain.cpp
function signal_handler (line 24) | void signal_handler(int signal)
function register_signal_handers (line 53) | int register_signal_handers()
function fmqmain (line 83) | int fmqmain(int argc, char *argv[])
FILE: fmqsockaddr.cpp
type sockaddr_in (line 15) | struct sockaddr_in
type sockaddr_in6 (line 19) | struct sockaddr_in6
type sockaddr_un (line 23) | struct sockaddr_un
function sockaddr (line 38) | const sockaddr *FMQSockaddr::getSockaddr() const
function socklen_t (line 48) | socklen_t FMQSockaddr::getSize() const
type sockaddr_in (line 63) | struct sockaddr_in
type sockaddr_in6 (line 70) | struct sockaddr_in6
type sockaddr_in (line 83) | struct sockaddr_in
type sockaddr_in6 (line 89) | struct sockaddr_in6
type in_addr (line 95) | struct in_addr
type in6_addr (line 112) | struct in6_addr
FILE: fmqsockaddr.h
function class (line 13) | class FMQSockaddr
FILE: fmqssl.h
function class (line 9) | class FmqSsl
FILE: globals.h
function class (line 19) | class Globals
FILE: globalstats.h
function class (line 21) | class GlobalStats
FILE: globber.h
function class (line 8) | class GlobT
function class (line 19) | class Globber
FILE: haproxy.cpp
function read_ha_proxy_helper (line 26) | size_t read_ha_proxy_helper(int fd, void *buf, size_t nbytes)
function get_ha_proxy_pp2_field (line 55) | std::optional<std::string> get_ha_proxy_pp2_field(const std::unordered_m...
function HaProxySslData (line 65) | HaProxySslData read_ha_proxy_pp2_ssl(const std::vector<unsigned char> &d...
function read_ha_proxy_pp2_tlv (line 109) | std::unordered_map<int, std::variant<std::string, HaProxySslData>> read_...
FILE: haproxy.h
type proxy_hdr_v2 (line 36) | struct proxy_hdr_v2
type proxy_ipv4_addr (line 45) | struct proxy_ipv4_addr
type proxy_ipv6_addr (line 54) | struct proxy_ipv6_addr
type proxy_unix_addr (line 63) | struct proxy_unix_addr
type proxy_ipv4_addr (line 71) | struct proxy_ipv4_addr
type proxy_ipv6_addr (line 72) | struct proxy_ipv6_addr
type proxy_unix_addr (line 73) | struct proxy_unix_addr
type HaProxySslData (line 76) | struct HaProxySslData
function HaProxyConnectionType (line 90) | enum class HaProxyConnectionType
FILE: http.cpp
function generateWebsocketAcceptString (line 12) | std::string generateWebsocketAcceptString(const std::string &websocketKey)
function generateInvalidWebsocketVersionHttpHeaders (line 30) | std::string generateInvalidWebsocketVersionHttpHeaders(const int wantedV...
function generateBadHttpRequestReponse (line 40) | std::string generateBadHttpRequestReponse(const std::string &msg)
function generateWebsocketAnswer (line 50) | std::string generateWebsocketAnswer(const std::string &acceptString, con...
function generateRedirect (line 63) | std::string generateRedirect(const std::string &location)
function parseHttpHeader (line 78) | std::optional<HttpRequest> parseHttpHeader(CirBuf &buf)
function websocketCloseCodeToString (line 195) | std::string websocketCloseCodeToString(uint16_t code)
FILE: http.h
function class (line 10) | class HttpRequest
FILE: iowrapper.cpp
function verify_callback (line 167) | static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
function WebsocketState (line 260) | WebsocketState IoWrapper::getWebsocketState() const
function X509Manager (line 265) | X509Manager IoWrapper::getPeerCertificate() const
type proxy_hdr_v2 (line 287) | struct proxy_hdr_v2
type proxy_ipv4_addr (line 355) | struct proxy_ipv4_addr
type sockaddr_in (line 358) | struct sockaddr_in
type sockaddr (line 364) | struct sockaddr
type proxy_ipv6_addr (line 368) | struct proxy_ipv6_addr
type sockaddr_in6 (line 371) | struct sockaddr_in6
type in6_addr (line 374) | struct in6_addr
type sockaddr (line 377) | struct sockaddr
FILE: iowrapper.h
type class (line 35) | enum class
type class (line 45) | enum class
type IncompleteSslWrite (line 64) | struct IncompleteSslWrite
type IncompleteWebsocketRead (line 76) | struct IncompleteWebsocketRead
type class (line 89) | enum class
type class (line 96) | enum class
function class (line 108) | class IoWrapper
FILE: listener.cpp
function X509ClientVerification (line 188) | X509ClientVerification Listener::getX509ClientVerficationMode() const
FILE: listener.h
type class (line 22) | enum class
type Listener (line 30) | struct Listener
FILE: lockedsharedptr.h
function reset (line 22) | void reset()
FILE: lockedweakptr.h
function expired (line 29) | bool expired()
FILE: logger.cpp
function Logger (line 95) | Logger *Logger::getInstance()
function StreamToLog (line 123) | StreamToLog Logger::log(int level)
function logSslError (line 391) | int logSslError(const char *str, size_t len, void *u)
FILE: logger.h
function LogLevel (line 25) | enum class LogLevel
function class (line 65) | class Logger
FILE: main.cpp
function main (line 3) | int main(int argc, char *argv[])
FILE: mainapp.cpp
type option (line 474) | struct option
type epoll_event (line 583) | struct epoll_event
type sockaddr_storage (line 746) | struct sockaddr_storage
type sockaddr (line 747) | struct sockaddr
type rlimit (line 1028) | struct rlimit
FILE: mainapp.h
function class (line 36) | class MainApp
FILE: mosquittoauthoptcompatwrap.cpp
function mosquitto_auth_opt (line 43) | mosquitto_auth_opt &mosquitto_auth_opt::operator=(const mosquitto_auth_o...
FILE: mosquittoauthoptcompatwrap.h
type mosquitto_auth_opt (line 25) | struct mosquitto_auth_opt
function size (line 42) | struct AuthOptCompatWrap
FILE: mqtt5properties.cpp
function VariableByteInt (line 31) | const VariableByteInt &Mqtt5PropertyBuilder::getVarInt()
FILE: mqtt5properties.h
function class (line 19) | class Mqtt5PropertyBuilder
FILE: mqttpacket.cpp
function HandleResult (line 541) | HandleResult MqttPacket::handle(std::shared_ptr<Client> &sender)
function ConnectData (line 609) | ConnectData MqttPacket::parseConnectData(std::shared_ptr<Client> &sender)
function ConnAckData (line 943) | ConnAckData MqttPacket::parseConnAckData()
function AuthPacketData (line 1421) | AuthPacketData MqttPacket::parseAuthData()
function DisconnectData (line 1534) | DisconnectData MqttPacket::parseDisconnectData()
function PubRecData (line 2288) | PubRecData MqttPacket::parsePubRecData()
function SubAckData (line 2400) | SubAckData MqttPacket::parseSubAckData()
function Publish (line 2781) | const Publish &MqttPacket::getPublishData()
FILE: mqttpacket.h
type class (line 29) | enum class
function class (line 40) | class MqttPacket
FILE: mutexowned.h
function class (line 6) | class __attribute__((visibility("default"))) MutexOwnedObjectNull : publ...
FILE: network.cpp
type sockaddr_in (line 20) | struct sockaddr_in
type in_addr (line 24) | struct in_addr
type sockaddr_in6 (line 41) | struct sockaddr_in6
type sockaddr_in (line 106) | struct sockaddr_in
type sockaddr_in (line 107) | struct sockaddr_in
type sockaddr_in6 (line 116) | struct sockaddr_in6
type in6_addr (line 119) | struct in6_addr
type sockaddr (line 139) | struct sockaddr
type sockaddr (line 139) | struct sockaddr
type sockaddr (line 145) | struct sockaddr
type sockaddr (line 145) | struct sockaddr
FILE: network.h
function class (line 18) | class Network
FILE: oneinstancelock.cpp
type flock (line 40) | struct flock
FILE: oneinstancelock.h
function class (line 20) | class OneInstanceLock
FILE: packetdatatypes.h
type ConnectData (line 19) | struct ConnectData
type ConnAckData (line 56) | struct ConnAckData
type AuthPacketData (line 81) | struct AuthPacketData
type DisconnectData (line 88) | struct DisconnectData
type SubAckData (line 97) | struct SubAckData
type PubRecData (line 104) | struct PubRecData
FILE: persistencefile.h
function class (line 33) | class PersistenceFileCantBeOpened : public std::runtime_error
type class (line 54) | enum class
FILE: persistencefunctions.cpp
function saveState (line 22) | void saveState(const Settings &settings, const std::list<BridgeInfoForSe...
function saveBridgeInfo (line 62) | void saveBridgeInfo(const std::string &filePath, const std::list<BridgeI...
function loadBridgeInfo (line 71) | std::list<BridgeConfig> loadBridgeInfo(Settings &settings)
function correctBackupDbPermissions (line 113) | void correctBackupDbPermissions(const std::string &dir)
FILE: plugin.cpp
function AuthResult (line 315) | AuthResult Authentication::aclCheck(Publish &publishData, std::string_vi...
function AuthResult (line 329) | AuthResult Authentication::aclCheck(
function AuthResult (line 440) | AuthResult Authentication::loginCheck(const std::string &clientid, const...
function AuthResult (line 508) | AuthResult Authentication::extendedAuth(const std::string &clientid, Ext...
type stat (line 710) | struct stat
type timespec (line 712) | struct timespec
type stat (line 811) | struct stat
type timespec (line 813) | struct timespec
function AuthResult (line 886) | AuthResult Authentication::aclCheckFromMosquittoAclFile(const std::strin...
function AuthResultToString (line 973) | std::string AuthResultToString(AuthResult r)
FILE: plugin.h
type class (line 26) | enum class
type MosquittoPasswordFileEntry (line 42) | struct MosquittoPasswordFileEntry
type mosquitto_auth_opt (line 56) | struct mosquitto_auth_opt
type mosquitto_auth_opt (line 57) | struct mosquitto_auth_opt
type mosquitto_auth_opt (line 58) | struct mosquitto_auth_opt
type mosquitto_auth_opt (line 59) | struct mosquitto_auth_opt
type AuthResult (line 69) | typedef AuthResult(*F_flashmq_plugin_acl_check_v1)(void *thread_data, co...
type AuthResult (line 72) | typedef AuthResult(*F_flashmq_plugin_login_check_v1)(void *thread_data, ...
type AuthResult (line 75) | typedef AuthResult(*F_flashmq_plugin_extended_auth_v1)(void *thread_data...
type AuthResult (line 87) | typedef AuthResult(*F_flashmq_plugin_acl_check_v2)(void *thread_data, co...
type AuthResult (line 94) | typedef AuthResult(*F_flashmq_plugin_acl_check_v3)(
type AuthResult (line 104) | typedef AuthResult(*F_flashmq_plugin_acl_check_v4)(
type AuthResult (line 119) | typedef AuthResult(*F_flashmq_plugin_acl_check_v5)(
function class (line 134) | class Authentication
FILE: pluginloader.cpp
function PluginFamily (line 110) | PluginFamily PluginLoader::getPluginFamily() const
FILE: pluginloader.h
type class (line 19) | enum class
function class (line 31) | class PluginLoader
FILE: publishcopyfactory.cpp
function MqttPacket (line 33) | MqttPacket *PublishCopyFactory::getOptimumPacket(
function Publish (line 140) | Publish PublishCopyFactory::getNewPublish(uint8_t new_max_qos, bool reta...
FILE: publishcopyfactory.h
function class (line 31) | class PublishCopyFactory
FILE: qospacketqueue.cpp
function Publish (line 30) | Publish &QueuedPublish::getPublish()
FILE: qospacketqueue.h
function class (line 25) | class QueuedPublish
FILE: queuedtasks.h
type QueuedTask (line 20) | struct QueuedTask
function class (line 36) | class QueuedTasks
FILE: retainedmessage.cpp
function RetainedMessage (line 33) | RetainedMessage &RetainedMessage::operator=(const Publish &pub)
FILE: retainedmessage.h
type RetainedMessage (line 17) | struct RetainedMessage
function namespace (line 34) | namespace std {
FILE: retainedmessagesdb.h
function ReadVersion (line 37) | enum class ReadVersion
FILE: rwlockguard.h
function class (line 18) | class RWLockGuard
FILE: scopedsocket.cpp
function ScopedSocket (line 48) | ScopedSocket &ScopedSocket::operator=(ScopedSocket &&other)
type epoll_event (line 95) | struct epoll_event
FILE: scopedsocket.h
function class (line 21) | class ScopedSocket
FILE: sdnotify.cpp
function closep (line 27) | static void closep(int *fd) {
function notify (line 35) | static int notify(const char *message) {
function notify_ready (line 88) | int notify_ready(void) {
function notify_reloading (line 92) | int notify_reloading(void) {
function notify_stopping (line 116) | int notify_stopping(void) {
FILE: session.cpp
function PacketDropReason (line 119) | PacketDropReason Session::writePacket(PublishCopyFactory ©Factory, c...
FILE: session.h
function class (line 29) | class Session
FILE: sessionsandsubscriptionsdb.cpp
function SubscriptionOptionsByte (line 71) | SubscriptionOptionsByte SubscriptionForSerializing::getSubscriptionOptio...
function SessionsAndSubscriptionsResult (line 112) | SessionsAndSubscriptionsResult SessionsAndSubscriptionsDB::readDataV3V4V...
function SessionsAndSubscriptionsResult (line 481) | SessionsAndSubscriptionsResult SessionsAndSubscriptionsDB::readData()
FILE: sessionsandsubscriptionsdb.h
type SubscriptionForSerializing (line 34) | struct SubscriptionForSerializing
type SessionsAndSubscriptionsResult (line 52) | struct SessionsAndSubscriptionsResult
function ReadVersion (line 61) | enum class ReadVersion
FILE: settings.cpp
function checkUniqueBridgeNames (line 17) | void checkUniqueBridgeNames(const std::list<BridgeConfig> &bridges)
function AuthOptCompatWrap (line 40) | AuthOptCompatWrap &Settings::getAuthOptsCompat()
type sockaddr (line 102) | struct sockaddr
type sockaddr (line 107) | struct sockaddr
FILE: settings.h
type class (line 30) | enum class
type class (line 40) | enum class
type class (line 47) | enum class
type class (line 53) | enum class
function class (line 62) | class Settings
FILE: sharedsubscribers.cpp
function Subscription (line 28) | Subscription &SharedSubscribers::operator[](const std::string &clientid)
function Subscription (line 45) | const Subscription *SharedSubscribers::getFirst() const
function Subscription (line 61) | const Subscription *SharedSubscribers::getNext()
function Subscription (line 80) | const Subscription *SharedSubscribers::getNext(size_t hash) const
FILE: sharedsubscribers.h
function class (line 24) | class SharedSubscribers
FILE: sslctxmanager.cpp
function SSL_CTX (line 26) | SSL_CTX *SslCtxManager::get() const
FILE: sslctxmanager.h
function class (line 18) | class SslCtxManager
FILE: subscription.h
type Subscription (line 17) | struct Subscription
FILE: subscriptionstore.cpp
function AddSubscriptionType (line 59) | AddSubscriptionType SubscriptionNode::addSubscriber(
function AddSubscriptionType (line 256) | AddSubscriptionType SubscriptionStore::addSubscription(
FILE: subscriptionstore.h
type ReceivingSubscriber (line 32) | struct ReceivingSubscriber
type class (line 43) | enum class
function class (line 50) | class SubscriptionNode
function isOrphaned (line 89) | bool isOrphaned() const;
FILE: threaddata.cpp
type epoll_event (line 59) | struct epoll_event
type epoll_event (line 1149) | struct epoll_event
function ThreadData (line 1395) | ThreadData *ThreadDataOwner::operator->() const
FILE: threaddata.h
type KeepAliveCheck (line 42) | struct KeepAliveCheck
type QueuedRetainedMessage (line 50) | struct QueuedRetainedMessage
type Clients (line 59) | struct Clients
type ThreadDataOwner (line 65) | struct ThreadDataOwner
function class (line 85) | class ThreadData
FILE: threadglobals.cpp
function Settings (line 38) | Settings *ThreadGlobals::getSettings()
FILE: threadglobals.h
function class (line 20) | class ThreadGlobals
FILE: threadlocalutils.h
function class (line 25) | class SimdUtils
FILE: threadloop.cpp
function do_thread_work (line 18) | void do_thread_work(std::shared_ptr<ThreadData> threadData)
FILE: threadloop.h
type ReadyClient (line 37) | struct ReadyClient
FILE: types.cpp
function RetainHandling (line 47) | RetainHandling SubscriptionOptionsByte::getRetainHandling() const
FILE: types.h
type class (line 27) | enum class
type class (line 47) | enum class
type class (line 55) | enum class
type class (line 90) | enum class
type class (line 103) | enum class
type class (line 156) | enum class
type class (line 163) | enum class
type SubscriptionOptionsByte (line 170) | struct SubscriptionOptionsByte
function class (line 183) | class ConnAck
function class (line 197) | class SubAck
function getLengthWithoutFixedHeader (line 217) | size_t getLengthWithoutFixedHeader() const;
function class (line 285) | class WillPublish : public Publish
type Connect (line 332) | struct Connect
type Subscribe (line 357) | struct Subscribe
type Unsubscribe (line 373) | struct Unsubscribe
type class (line 380) | enum class
FILE: unscopedlock.h
function class (line 22) | class UnscopedLock
FILE: utils.cpp
function split (line 41) | std::list<std::string> split(const std::string &input, const char sep, s...
function strContains (line 58) | bool strContains(const std::string &s, const std::string &needle)
function isValidUtf8Generic (line 64) | bool isValidUtf8Generic(const char *s, bool alsoCheckInvalidPublishChars)
function isValidPublishPath (line 70) | bool isValidPublishPath(const std::string &s)
function isValidSubscribePath (line 84) | bool isValidSubscribePath(const std::string &s)
function isValidShareName (line 109) | bool isValidShareName(const std::string &s)
function containsDangerousCharacters (line 123) | bool containsDangerousCharacters(const std::string &s)
function splitTopic (line 142) | std::vector<std::string> splitTopic(const std::string &topic)
function splitToVector (line 170) | std::vector<std::string> splitToVector(const std::string &input, const c...
function ltrim (line 188) | void ltrim(std::string &s)
function rtrim (line 195) | void rtrim(std::string &s)
function rtrim (line 202) | void rtrim(std::string &s, char c)
function trim (line 209) | void trim(std::string &s)
function startsWith (line 223) | bool startsWith(const std::string &s, const std::string &needle)
function endsWith (line 238) | bool endsWith(const std::string &s, const std::string &ending)
function getSecureRandomString (line 245) | std::string getSecureRandomString(const ssize_t len)
function str_tolower (line 273) | std::string str_tolower(std::string s)
function stringTruthiness (line 280) | bool stringTruthiness(const std::string &val)
function isPowerOfTwo (line 291) | bool isPowerOfTwo(int n)
function base64Decode (line 296) | std::vector<unsigned char> base64Decode(const std::string &s)
function base64Encode (line 320) | std::string base64Encode(const unsigned char *input, const int length)
function testSsl (line 334) | void testSsl(const std::string &fullchain, const std::string &privkey)
function testSslVerifyLocations (line 369) | void testSslVerifyLocations(const std::string &caFile, const std::string...
function formatString (line 389) | std::string formatString(const std::string str, ...)
function dirnameOf (line 410) | std::string_view dirnameOf(std::string_view path)
function getFileSize (line 416) | size_t getFileSize(const std::string &path)
function getFreeSpace (line 428) | uint64_t getFreeSpace(const std::string &path)
function sa_family_t (line 445) | sa_family_t getFamilyFromSockAddr(const sockaddr *addr)
function sockaddrToString (line 456) | std::string sockaddrToString(const sockaddr *addr)
function protocolVersionString (line 491) | std::string protocolVersionString(ProtocolVersion p)
function distanceBetweenStrings (line 515) | unsigned int distanceBetweenStrings(const std::string &stringA, const st...
function ageFromTimePoint (line 592) | uint32_t ageFromTimePoint(const std::chrono::time_point<std::chrono::ste...
function timepointFromAge (line 599) | std::chrono::time_point<std::chrono::steady_clock> timepointFromAge(cons...
function ReasonCodes (line 606) | ReasonCodes authResultToReasonCode(AuthResult authResult)
function maskAllSignalsCurrentThread (line 628) | int maskAllSignalsCurrentThread()
function parseSubscriptionShare (line 637) | void parseSubscriptionShare(std::vector<std::string> &subtopics, std::st...
function timestampWithMillis (line 679) | std::string timestampWithMillis()
function exceptionOnNonMqtt (line 699) | void exceptionOnNonMqtt(const std::vector<char> &data)
function getFirstWildcardDepth (line 728) | uint16_t getFirstWildcardDepth(const std::vector<std::string> &subtopics)
function reasonCodeToString (line 747) | std::string reasonCodeToString(ReasonCodes code)
function packetTypeToString (line 848) | std::string packetTypeToString(PacketType ptype)
function propertyToString (line 894) | std::string propertyToString(Mqtt5Properties p)
function unlink_if_sock (line 963) | void unlink_if_sock(const std::string &path)
function fmq_ensure_fail (line 978) | void fmq_ensure_fail(const char *file, int line)
function get_pw_name (line 984) | std::optional<SysUserFields> get_pw_name(const std::string &user)
function get_gr_name (line 1007) | std::optional<SysGroupFields> get_gr_name(const std::string &group)
function try_stoul (line 1029) | std::optional<unsigned long> try_stoul(const std::string &s) noexcept
FILE: utils.h
type sockaddr (line 200) | struct sockaddr
type sockaddr (line 201) | struct sockaddr
type stat (line 214) | struct stat
type stat (line 215) | struct stat
type SysUserFields (line 398) | struct SysUserFields
type SysGroupFields (line 407) | struct SysGroupFields
function string (line 427) | string make_string(const T &input, const size_t offset, const size_t len)
FILE: variablebyteint.cpp
function VariableByteInt (line 28) | VariableByteInt &VariableByteInt::operator=(uint32_t x)
FILE: variablebyteint.h
function class (line 18) | class VariableByteInt
FILE: x509manager.cpp
function X509 (line 14) | X509 *X509Manager::get()
FILE: x509manager.h
function class (line 7) | class X509Manager
Condensed preview — 235 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,821K chars).
[
{
"path": ".build-ci.sh",
"chars": 590,
"preview": "#!/bin/bash\n\nset -e # If any step reports a problem consider the whole build a failure\nwget https://github.com/linuxdep"
},
{
"path": ".clang-format",
"chars": 6052,
"preview": "---\nLanguage: Cpp\n# BasedOnStyle: LLVM\nAccessModifierOffset: -4\nAlignAfterOpenBracket: BlockIndent\nAlignArrayOfS"
},
{
"path": ".clang-format.original",
"chars": 6031,
"preview": "---\nLanguage: Cpp\n# BasedOnStyle: LLVM\nAccessModifierOffset: -2\nAlignAfterOpenBracket: Align\nAlignArrayOfStructu"
},
{
"path": ".dockerignore",
"chars": 11,
"preview": "Dockerfile\n"
},
{
"path": ".get-os-codename-and-stamp.sh",
"chars": 988,
"preview": "#!/bin/bash\n\nmy_codename=\"\"\nmy_int_version=\"\"\n\nif [[ -e \"/etc/os-release\" ]]; then\n eval \"$(cat \"/etc/os-release\")\"\n m"
},
{
"path": ".github/workflows/building.yml",
"chars": 1236,
"preview": "name: Building\non: [push]\njobs:\n compilation:\n strategy:\n matrix:\n include:\n - os: ubuntu-24.04"
},
{
"path": ".github/workflows/codestyle.yml",
"chars": 572,
"preview": "name: Checking code style\non: [pull_request]\njobs:\n check_cpp_codestyle:\n runs-on: ubuntu-latest\n steps:\n - "
},
{
"path": ".github/workflows/docker.yml",
"chars": 247,
"preview": "name: Docker\non: [push]\njobs:\n docker:\n runs-on: ubuntu-latest\n steps:\n - name: Checkout\n uses: actio"
},
{
"path": ".github/workflows/linting.yml",
"chars": 641,
"preview": "name: Linting\non: [push]\njobs:\n shellcheck:\n runs-on: ubuntu-latest\n steps:\n - name: Checkout\n uses: "
},
{
"path": ".github/workflows/testing.yml",
"chars": 1054,
"preview": "name: Testing\non: [push]\njobs:\n compilation:\n strategy:\n matrix:\n include:\n - os: ubuntu-24.04\n"
},
{
"path": ".github/workflows/testing_non_sse.yml",
"chars": 1090,
"preview": "name: TestingNoSse\non: [push]\njobs:\n compilation:\n strategy:\n matrix:\n include:\n - os: ubuntu-2"
},
{
"path": ".gitignore",
"chars": 99,
"preview": "*.user\nbuild-*\nFlashMQBuild*\n*.swp\ncompile_commands.json\n.clangd/\n.cache/\nbuild\nFlashMQTests/build\n"
},
{
"path": "CMakeLists.shared",
"chars": 4738,
"preview": "# This file is shared between FlashMQ itself and FlashMQTests\n# It should not contain any definitions that isn't used by"
},
{
"path": "CMakeLists.txt",
"chars": 2290,
"preview": "cmake_minimum_required(VERSION 3.5)\ncmake_policy(SET CMP0048 NEW)\ninclude(CheckCXXCompilerFlag)\ninclude(CMakeLists.share"
},
{
"path": "Dockerfile",
"chars": 2442,
"preview": "# build target, used for building the binary, providing shared libraries and could be used as a development env\nFROM deb"
},
{
"path": "FlashMQTests/CMakeLists.txt",
"chars": 2586,
"preview": "cmake_minimum_required(VERSION 3.5)\ncmake_policy(SET CMP0048 NEW)\ninclude(CheckCXXCompilerFlag)\ninclude(../CMakeLists.sh"
},
{
"path": "FlashMQTests/bridgeprefixtests.cpp",
"chars": 27368,
"preview": "#include \"maintests.h\"\n#include \"conffiletemp.h\"\n#include \"flashmqtestclient.h\"\n#include \"mainappasfork.h\"\n#include \"tes"
},
{
"path": "FlashMQTests/conffiletemp.cpp",
"chars": 1188,
"preview": "#include \"conffiletemp.h\"\n\n#include <vector>\n#include <unistd.h>\n#include <stdexcept>\n\nConfFileTemp::ConfFileTemp()\n{\n "
},
{
"path": "FlashMQTests/conffiletemp.h",
"chars": 490,
"preview": "#ifndef CONFFILETEMP_H\n#define CONFFILETEMP_H\n\n#include <string>\n\nclass ConfFileTemp\n{\n int fd = -1;\n std::string "
},
{
"path": "FlashMQTests/configtests.cpp",
"chars": 7115,
"preview": "#include \"maintests.h\"\n#include \"testhelpers.h\"\n#include \"conffiletemp.h\"\n#include \"exceptions.h\"\n#include \"settings.h\"\n"
},
{
"path": "FlashMQTests/dnstests.cpp",
"chars": 4448,
"preview": "#include \"maintests.h\"\n#include \"testhelpers.h\"\n\n#include \"utils.h\"\n\nvoid MainTests::testDnsResolver()\n{\n try\n {\n "
},
{
"path": "FlashMQTests/filecloser.cpp",
"chars": 175,
"preview": "#include \"filecloser.h\"\n\n#include <unistd.h>\n\nFileCloser::FileCloser(int fd) :\n fd(fd)\n{\n\n}\n\nFileCloser::~FileCloser("
},
{
"path": "FlashMQTests/filecloser.h",
"chars": 159,
"preview": "#ifndef FILECLOSER_H\n#define FILECLOSER_H\n\n\nclass FileCloser\n{\n int fd = -1;\n\npublic:\n FileCloser(int fd);\n ~Fi"
},
{
"path": "FlashMQTests/flashmqtempdir.cpp",
"chars": 825,
"preview": "#include \"flashmqtempdir.h\"\n\n#include <sys/types.h>\n#include <unistd.h>\n#include <filesystem>\n\n#include \"utils.h\"\n\nFlash"
},
{
"path": "FlashMQTests/flashmqtempdir.h",
"chars": 320,
"preview": "#ifndef FLASHMQTEMPDIR_H\n#define FLASHMQTEMPDIR_H\n\n#include <filesystem>\n#include <stdlib.h>\n#include <string>\n#include "
},
{
"path": "FlashMQTests/main.cpp",
"chars": 1499,
"preview": "#include <iostream>\n#include <vector>\n\n#include \"maintests.h\"\n\nvoid printHelp(const std::string &arg0)\n{\n std::cout <"
},
{
"path": "FlashMQTests/mainappasfork.cpp",
"chars": 3307,
"preview": "#include \"mainappasfork.h\"\n#include <mainapp.h>\n#include \"signal.h\"\n#include \"fmqmain.h\"\n#include \"sys/wait.h\"\n\nstd::str"
},
{
"path": "FlashMQTests/mainappasfork.h",
"chars": 982,
"preview": "#ifndef MAINAPPASFORK_H\n#define MAINAPPASFORK_H\n\n#include <vector>\n#include <string>\n#include <unistd.h>\n#include \"conff"
},
{
"path": "FlashMQTests/mainappinthread.cpp",
"chars": 2426,
"preview": "#include \"mainappinthread.h\"\n\nMainAppInThread::MainAppInThread()\n{\n\n}\n\nMainAppInThread::MainAppInThread(const std::vecto"
},
{
"path": "FlashMQTests/mainappinthread.h",
"chars": 471,
"preview": "#ifndef MAINAPPINTHREAD_H\n#define MAINAPPINTHREAD_H\n\n#include <thread>\n\n#include \"mainapp.h\"\n#include \"mutexowned.h\"\n\ncl"
},
{
"path": "FlashMQTests/maintests.cpp",
"chars": 17085,
"preview": "#include <unistd.h>\n\n#include \"maintests.h\"\n#include \"testhelpers.h\"\n#include \"testinitializer.h\"\n\n#include \"threadgloba"
},
{
"path": "FlashMQTests/maintests.h",
"chars": 8779,
"preview": "#ifndef MAINTESTS_H\n#define MAINTESTS_H\n\n#include <memory>\n#include <unordered_map>\n#include <functional>\n\n#include \"mai"
},
{
"path": "FlashMQTests/plugins/curlfunctions.cpp",
"chars": 3686,
"preview": "#include \"curlfunctions.h\"\n#include <sys/epoll.h>\n#include \"../../flashmq_plugin.h\"\n#include \"test_plugin.h\"\n#include <c"
},
{
"path": "FlashMQTests/plugins/curlfunctions.h",
"chars": 506,
"preview": "#ifndef CURLFUNCTIONS_H\n#define CURLFUNCTIONS_H\n\n#include <curl/curl.h>\n#include \"test_plugin.h\"\n\nint socket_event_watch"
},
{
"path": "FlashMQTests/plugins/test_plugin.cpp",
"chars": 12021,
"preview": "#include <functional>\n#include <unistd.h>\n#include <cassert>\n#include <cstring>\n#include <stdexcept>\n#include <optional>"
},
{
"path": "FlashMQTests/plugins/test_plugin.h",
"chars": 922,
"preview": "#ifndef TESTPLUGIN_H\n#define TESTPLUGIN_H\n\n#include <memory>\n#include <vector>\n#include <thread>\n#include \"../../forward"
},
{
"path": "FlashMQTests/plugins/test_plugin.pro",
"chars": 195,
"preview": "QT -= gui\n\nCONFIG += c++17\n\nTARGET = test_plugin\nTEMPLATE = lib\n\nVERSION=0.0.1\n\nLIBS += -lcurl\n\nHEADERS += test_plugin.h"
},
{
"path": "FlashMQTests/plugintests.cpp",
"chars": 39430,
"preview": "#include \"maintests.h\"\n#include \"testhelpers.h\"\n#include \"conffiletemp.h\"\n#include \"flashmqtestclient.h\"\n\n#include <sys/"
},
{
"path": "FlashMQTests/retaintests.cpp",
"chars": 25000,
"preview": "#include \"maintests.h\"\n#include \"flashmqtestclient.h\"\n#include \"conffiletemp.h\"\n#include \"testhelpers.h\"\n\n#include \"util"
},
{
"path": "FlashMQTests/run-make-from-ci.sh",
"chars": 683,
"preview": "#!/bin/bash\n\nextra_configs=()\ncompiler=\"g++\"\n\nwhile [ -n \"$*\" ]; do\n flag=$1\n value=$2\n\n case \"$flag\" in\n \"--compi"
},
{
"path": "FlashMQTests/run-tests-from-ci.sh",
"chars": 394,
"preview": "#!/bin/bash\n\nSTDERR_LOG=$(mktemp)\n\ncd buildtests || exit 1\n\n# Using --abort-on-first-fail because the output can be hard"
},
{
"path": "FlashMQTests/sharedsubscriptionstests.cpp",
"chars": 10773,
"preview": "#include \"maintests.h\"\n#include \"testhelpers.h\"\n#include \"flashmqtestclient.h\"\n#include \"flashmqtempdir.h\"\n#include \"con"
},
{
"path": "FlashMQTests/subscriptionidtests.cpp",
"chars": 10946,
"preview": "#include \"maintests.h\"\n#include \"flashmqtestclient.h\"\n#include \"testhelpers.h\"\n\nvoid MainTests::testSubscriptionIdOnline"
},
{
"path": "FlashMQTests/testhelpers.cpp",
"chars": 1449,
"preview": "#include \"testhelpers.h\"\n\n#include <iostream>\n#include <cstring>\n\nint assert_count;\nint assert_fail_count;\nbool asserts_"
},
{
"path": "FlashMQTests/testhelpers.h",
"chars": 2759,
"preview": "#ifndef TESTHELPERS_H\n#define TESTHELPERS_H\n\n#include <sstream>\n\n#define RED \"\\033[01;31m\"\n#define GREEN \"\\033[01;32m\"\n#"
},
{
"path": "FlashMQTests/testinitializer.cpp",
"chars": 414,
"preview": "#include \"testinitializer.h\"\n\nTestInitializer::TestInitializer(MainTests *tests) :\n tests(tests)\n{\n\n}\n\nvoid TestIniti"
},
{
"path": "FlashMQTests/testinitializer.h",
"chars": 554,
"preview": "#ifndef TESTINITIALIZER_H\n#define TESTINITIALIZER_H\n\n#include \"maintests.h\"\n\n/**\n * @brief Simple RAII way to make sure "
},
{
"path": "FlashMQTests/tst_maintests.cpp",
"chars": 116588,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "FlashMQTests/utiltests.cpp",
"chars": 9764,
"preview": "#include <random>\n\n#include \"maintests.h\"\n#include \"testhelpers.h\"\n\n#include \"utils.h\"\n#include \"exceptions.h\"\n#include "
},
{
"path": "FlashMQTests/websockettests.cpp",
"chars": 27307,
"preview": "#include \"maintests.h\"\n#include \"testhelpers.h\"\n\n#include <fstream>\n#include <sys/types.h>\n#include <sys/socket.h>\n#incl"
},
{
"path": "FlashMQTests/willtests.cpp",
"chars": 14921,
"preview": "#include <stdlib.h>\n\n#include \"maintests.h\"\n#include \"testhelpers.h\"\n#include \"flashmqtestclient.h\"\n#include \"conffilete"
},
{
"path": "LICENSE",
"chars": 10248,
"preview": "This Open Software License (the “License”) applies to any original work of\nauthorship (the “Original Work”) whose owner "
},
{
"path": "README.md",
"chars": 2670,
"preview": "# FlashMQ\n\n\n\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "acksender.h",
"chars": 890,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "acltree.cpp",
"chars": 9160,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "acltree.h",
"chars": 2929,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "backgroundworker.cpp",
"chars": 2898,
"preview": "#include <string.h>\n#include <unistd.h>\n#include <poll.h>\n\n#include \"backgroundworker.h\"\n\n#include \"logger.h\"\n\n\nvoid Bac"
},
{
"path": "backgroundworker.h",
"chars": 621,
"preview": "#ifndef BACKGROUNDWORKER_H\n#define BACKGROUNDWORKER_H\n\n\n#include <thread>\n#include <functional>\n#include <list>\n#include"
},
{
"path": "bindaddr.cpp",
"chars": 4624,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "bindaddr.h",
"chars": 1631,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "bridgeconfig.cpp",
"chars": 13245,
"preview": "#include \"bridgeconfig.h\"\n\n#include <sstream>\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <cassert>\n\n#inc"
},
{
"path": "bridgeconfig.h",
"chars": 4481,
"preview": "#ifndef BRIDGECONFIG_H\n#define BRIDGECONFIG_H\n\n#include <memory>\n#include <string>\n#include <vector>\n#include <optional>"
},
{
"path": "bridgeinfodb.cpp",
"chars": 2375,
"preview": "#include \"bridgeinfodb.h\"\n\nusing std::unordered_map;\n\nBridgeInfoForSerializing::BridgeInfoForSerializing(const BridgeCon"
},
{
"path": "bridgeinfodb.h",
"chars": 1041,
"preview": "#ifndef BRIDGEINFODB_H\n#define BRIDGEINFODB_H\n\n#include <list>\n\n#include \"persistencefile.h\"\n#include \"bridgeconfig.h\"\n\n"
},
{
"path": "build.sh",
"chars": 2641,
"preview": "#!/bin/bash\n\nthisfile=$(readlink --canonicalize \"$0\")\nthisdir=$(dirname \"$thisfile\")\nscript_name=$(basename \"$0\")\n\nDEFAU"
},
{
"path": "checkedsharedptr.h",
"chars": 917,
"preview": "#ifndef CHECKEDSHAREDPTR_H\n#define CHECKEDSHAREDPTR_H\n\n#include <memory>\n#include <cassert>\n#include <stdexcept>\n\ntempla"
},
{
"path": "checkedweakptr.h",
"chars": 859,
"preview": "#ifndef CHECKEDWEAKPTR_H\n#define CHECKEDWEAKPTR_H\n\n#include <memory>\n\nclass __attribute__((visibility(\"default\"))) Point"
},
{
"path": "cirbuf.cpp",
"chars": 6971,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "cirbuf.h",
"chars": 2462,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "client.cpp",
"chars": 39502,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "client.h",
"chars": 11979,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "clientacceptqueue.cpp",
"chars": 1672,
"preview": "#include <unistd.h>\n#include \"utils.h\"\n#include \"logger.h\"\n#include \"clientacceptqueue.h\"\n\n\n\nvoid ClientAcceptQueue::wak"
},
{
"path": "clientacceptqueue.h",
"chars": 693,
"preview": "#ifndef CLIENTACCEPTQUEUE_H\n#define CLIENTACCEPTQUEUE_H\n\n#include <sys/eventfd.h>\n\n#include <vector>\n\n#include \"mutexown"
},
{
"path": "configfileparser.cpp",
"chars": 59752,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "configfileparser.h",
"chars": 3604,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "debian/conffiles",
"chars": 26,
"preview": "/etc/flashmq/flashmq.conf\n"
},
{
"path": "debian/flashmq.service",
"chars": 313,
"preview": "[Unit]\nDescription=FlashMQ MQTT server\nAfter=network.target\n\n[Service]\nType=notify\nUser=root\nGroup=root\nLimitNOFILE=infi"
},
{
"path": "debian/postinst",
"chars": 671,
"preview": "#!/bin/bash -e\n\nFRESH_INSTALL=true\n\nif [[ \"$1\" == \"configure\" && \"$2\" != \"\" ]]; then\n FRESH_INSTALL=false\nfi\n\nif \"$FRES"
},
{
"path": "debian/postrm",
"chars": 153,
"preview": "#!/bin/bash -e\n\nif [[ \"$1\" != \"upgrade\" ]]; then\n echo \"Disabling FlashMQ systemd service\"\n systemctl disable flashmq."
},
{
"path": "debian/preinst",
"chars": 321,
"preview": "#!/bin/bash -e\n\nif [[ ! -f /etc/lsb-release && ! -f /etc/debian_version ]]; then\n echo \"This is not a Debian or Ubuntu "
},
{
"path": "debian/prerm",
"chars": 269,
"preview": "#!/bin/bash -e\n\necho \"Stopping FlashMQ systemd service\"\n\nif systemctl is-active --quiet flashmq.service; then\n systemct"
},
{
"path": "derivablecounter.cpp",
"chars": 1274,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "derivablecounter.h",
"chars": 861,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "dnsresolver.cpp",
"chars": 2791,
"preview": "#include \"dnsresolver.h\"\n\n#include <unistd.h>\n#include <cstring>\n#include <signal.h>\n#include <stdexcept>\n#include <thre"
},
{
"path": "dnsresolver.h",
"chars": 1231,
"preview": "#ifndef DNSRESOLVER_H\n#define DNSRESOLVER_H\n\n#include <netdb.h>\n#include <arpa/inet.h>\n#include <string>\n#include <list>"
},
{
"path": "driftcounter.cpp",
"chars": 1498,
"preview": "#include \"driftcounter.h\"\n\n#include <algorithm>\n#include <numeric>\n\n#include \"settings.h\"\n\nDriftCounter::DriftCounter()\n"
},
{
"path": "driftcounter.h",
"chars": 835,
"preview": "#ifndef DRIFTCOUNTER_H\n#define DRIFTCOUNTER_H\n\n#include <chrono>\n#include <array>\n#include <optional>\n\n/**\n * @brief The"
},
{
"path": "enums.h",
"chars": 871,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "evpencodectxmanager.cpp",
"chars": 545,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "evpencodectxmanager.h",
"chars": 527,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "examples/plugin_libcurl/CMakeLists.txt",
"chars": 680,
"preview": "cmake_minimum_required(VERSION 3.5)\ncmake_policy(SET CMP0048 NEW)\n\nproject(plugin_libcurl VERSION 1.0.0 LANGUAGES CXX)\n\n"
},
{
"path": "examples/plugin_libcurl/LICENSE",
"chars": 1250,
"preview": "The FlashMQ example plugin 'plugin_libcurl' is free and unencumbered\nsoftware released into the public domain.\n\nAnyone i"
},
{
"path": "examples/plugin_libcurl/README.md",
"chars": 1650,
"preview": "# Async auth using libcurl\n\nThis example plugin demonstrates the use of the async authentication interface in the `flash"
},
{
"path": "examples/plugin_libcurl/build.sh",
"chars": 495,
"preview": "#!/bin/bash\n\nthisfile=$(readlink --canonicalize \"$0\")\nthisdir=$(dirname \"$thisfile\")\n\nBUILD_TYPE=\"Release\"\nif [[ \"$1\" =="
},
{
"path": "examples/plugin_libcurl/src/authenticatingclient.cpp",
"chars": 1943,
"preview": "/*\nThis file is part of FlashMQ example plugin 'plugin_libcurl'\nand is free and unencumbered software released into the "
},
{
"path": "examples/plugin_libcurl/src/authenticatingclient.h",
"chars": 1906,
"preview": "/*\nThis file is part of FlashMQ example plugin 'plugin_libcurl'\nand is free and unencumbered software released into the "
},
{
"path": "examples/plugin_libcurl/src/curl_functions.cpp",
"chars": 5175,
"preview": "/*\nThis file is part of FlashMQ example plugin 'plugin_libcurl'\nand is free and unencumbered software released into the "
},
{
"path": "examples/plugin_libcurl/src/curl_functions.h",
"chars": 1843,
"preview": "/*\nThis file is part of FlashMQ example plugin 'plugin_libcurl'\nand is free and unencumbered software released into the "
},
{
"path": "examples/plugin_libcurl/src/plugin_libcurl.cpp",
"chars": 6552,
"preview": "/*\nThis file is part of FlashMQ example plugin 'plugin_libcurl'\nand is free and unencumbered software released into the "
},
{
"path": "examples/plugin_libcurl/src/pluginstate.cpp",
"chars": 2729,
"preview": "/*\nThis file is part of FlashMQ example plugin 'plugin_libcurl'\nand is free and unencumbered software released into the "
},
{
"path": "examples/plugin_libcurl/src/pluginstate.h",
"chars": 2012,
"preview": "/*\nThis file is part of FlashMQ example plugin 'plugin_libcurl'\nand is free and unencumbered software released into the "
},
{
"path": "examples/plugin_libcurl/vendor/flashmq_plugin.h",
"chars": 14520,
"preview": "/*\n * This file is part of FlashMQ (https://www.flashmq.org). It defines the\n * plugin interface.\n *\n * - flashmq_plugin"
},
{
"path": "examples/plugin_libcurl/vendor/flashmq_public.h",
"chars": 14071,
"preview": "/*\n * This file is part of FlashMQ (https://www.flashmq.org). It describes\n * public FlashMQ functions available to plug"
},
{
"path": "exceptions.cpp",
"chars": 290,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "exceptions.h",
"chars": 2501,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "fdmanaged.cpp",
"chars": 262,
"preview": "#include \"fdmanaged.h\"\n\n#include <unistd.h>\n\nFdManaged::FdManaged(int fd) :\n fd(fd)\n{\n\n}\n\nFdManaged::~FdManaged()\n{\n "
},
{
"path": "fdmanaged.h",
"chars": 307,
"preview": "#ifndef FDMANAGED_H\n#define FDMANAGED_H\n\n\nclass FdManaged\n{\n int fd = -1;\n\npublic:\n FdManaged() = default;\n FdM"
},
{
"path": "flags.h",
"chars": 801,
"preview": "#ifndef FLAGS_H\n#define FLAGS_H\n\n#include <stdint.h>\n#include <limits>\n\ntemplate<typename FlagType>\nclass Flags\n{\n ui"
},
{
"path": "flashmq.conf",
"chars": 124,
"preview": "# https://www.flashmq.org/documentation/config-file/\n\nlog_file /var/log/flashmq/flashmq.log\nstorage_dir /var/lib/flas"
},
{
"path": "flashmq_plugin.cpp",
"chars": 10506,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "flashmq_plugin.h",
"chars": 14520,
"preview": "/*\n * This file is part of FlashMQ (https://www.flashmq.org). It defines the\n * plugin interface.\n *\n * - flashmq_plugin"
},
{
"path": "flashmq_plugin_deprecated.h",
"chars": 893,
"preview": "#ifndef FLASHMQ_PLUGIN_DEPRECATED_H\n#define FLASHMQ_PLUGIN_DEPRECATED_H\n\n#include <string>\n\n#include \"flashmq_public.h\"\n"
},
{
"path": "flashmq_public.h",
"chars": 14071,
"preview": "/*\n * This file is part of FlashMQ (https://www.flashmq.org). It describes\n * public FlashMQ functions available to plug"
},
{
"path": "flashmqtestclient.cpp",
"chars": 12998,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "flashmqtestclient.h",
"chars": 2826,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "fmqmain.cpp",
"chars": 3072,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "fmqmain.h",
"chars": 101,
"preview": "void signal_handler(int signal);\nint register_signal_handers();\nint fmqmain(int argc, char *argv[]);\n"
},
{
"path": "fmqsockaddr.cpp",
"chars": 3442,
"preview": "#include \"fmqsockaddr.h\"\n\n#include <cstring>\n#include <stdexcept>\n#include \"utils.h\"\n\nFMQSockaddr::FMQSockaddr(const soc"
},
{
"path": "fmqsockaddr.h",
"chars": 855,
"preview": "#ifndef FMQSOCKADDR_H\n#define FMQSOCKADDR_H\n\n#include <string>\n#include <arpa/inet.h>\n#include <sys/un.h>\n#include <vect"
},
{
"path": "fmqssl.cpp",
"chars": 784,
"preview": "#include <openssl/ssl.h>\n#include \"fmqssl.h\"\n\nFmqSsl::~FmqSsl()\n{\n if (!d) return;\n\n /*\n * We write the shutdo"
},
{
"path": "fmqssl.h",
"chars": 497,
"preview": "#ifndef FMQSSL_H\n#define FMQSSL_H\n\n#include <memory>\n#include <openssl/ssl.h>\n\n#include \"sslctxmanager.h\"\n\nclass FmqSsl\n"
},
{
"path": "forward_declarations.h",
"chars": 548,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "fuzz-helper.sh",
"chars": 1610,
"preview": "#!/bin/bash\n#\n# Quick 'n dirty Script to build and run FlashMQ with American Fuzzy Lop.\n\nthisfile=$(readlink --canonical"
},
{
"path": "globals.cpp",
"chars": 1017,
"preview": "#include \"globals.h\"\n#include \"threadglobals.h\"\n\nGlobals globals;\n\n/**\n * Normally there's 'ThreadGlobals::getThreadData"
},
{
"path": "globals.h",
"chars": 1274,
"preview": "#ifndef GLOBALS_H\n#define GLOBALS_H\n\n#include <memory>\n#include <pthread.h>\n\n#include \"subscriptionstore.h\"\n#include \"gl"
},
{
"path": "globalstats.cpp",
"chars": 686,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "globalstats.h",
"chars": 746,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "globber.cpp",
"chars": 596,
"preview": "#include \"globber.h\"\n\n#include <cstring>\n#include <stdexcept>\n\nGlobT::GlobT()\n{\n\n}\n\nGlobT::~GlobT()\n{\n globfree(&resu"
},
{
"path": "globber.h",
"chars": 310,
"preview": "#ifndef GLOBBER_H\n#define GLOBBER_H\n\n#include <glob.h>\n#include <string>\n#include <vector>\n\nclass GlobT\n{\n friend cla"
},
{
"path": "haproxy.cpp",
"chars": 5148,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "haproxy.h",
"chars": 2377,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "http.cpp",
"chars": 6151,
"preview": "#include \"http.h\"\n\n#include <sstream>\n\n#include <openssl/evp.h>\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n\n#incl"
},
{
"path": "http.h",
"chars": 1220,
"preview": "#ifndef HTTP_H\n#define HTTP_H\n\n#include <string>\n\n#include \"cirbuf.h\"\n#include \"types.h\"\n#include \"exceptions.h\"\n\nclass "
},
{
"path": "iowrapper.cpp",
"chars": 40416,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "iowrapper.h",
"chars": 4805,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "listener.cpp",
"chars": 7611,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "listener.h",
"chars": 2266,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "lockedsharedptr.h",
"chars": 577,
"preview": "#ifndef LOCKEDSHAREDPTR_H\n#define LOCKEDSHAREDPTR_H\n\n#include <mutex>\n#include <memory>\n\ntemplate<typename T>\nclass Lock"
},
{
"path": "lockedweakptr.cpp",
"chars": 28,
"preview": "#include \"lockedweakptr.h\"\n\n"
},
{
"path": "lockedweakptr.h",
"chars": 688,
"preview": "#ifndef LOCKEDWEAKPTR_H\n#define LOCKEDWEAKPTR_H\n\n#include <mutex>\n#include <memory>\n\ntemplate<typename T>\nclass LockedWe"
},
{
"path": "logger.cpp",
"chars": 9304,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "logger.h",
"chars": 2446,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "main.cpp",
"chars": 92,
"preview": "#include \"fmqmain.h\"\n\nint main(int argc, char *argv[])\n{\n return fmqmain(argc, argv);\n}\n\n"
},
{
"path": "mainapp.cpp",
"chars": 43440,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "mainapp.h",
"chars": 3677,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "man/.gitignore",
"chars": 0,
"preview": ""
},
{
"path": "man/Makefile",
"chars": 427,
"preview": "GROFF_TARGETS := flashmq.1 flashmq.conf.5\nHTML5_TARGETS := flashmq.1.html flashmq.conf.5.html\n\nall: $(GROFF_TARGETS) $(H"
},
{
"path": "man/README.md",
"chars": 188,
"preview": "To render a man page:\n\n```\nman -l flashmq.1\nman -l flashmq.conf.5\n```\n\nTo enable color on Debian derivates:\n\n```\nenv GRO"
},
{
"path": "man/docbook5-refentry-xslt/docbook5-refentry-to-html5.xsl",
"chars": 19985,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet version=\"1.0\"\n xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n"
},
{
"path": "man/docbook5-refentry-xslt/docbook5-refentry-to-manpage.xsl",
"chars": 22872,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet version=\"1.0\"\n xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n"
},
{
"path": "man/flashmq-docbook5-refentry-to-html5.xsl",
"chars": 10695,
"preview": "<xsl:stylesheet version=\"1.0\"\n xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n xmlns:exsl=\"http://exslt.org/common\"\n"
},
{
"path": "man/flashmq-docbook5-refentry-to-manpage.xsl",
"chars": 4488,
"preview": "<xsl:stylesheet version=\"1.0\"\n xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n xmlns:dbk=\"http://docbook.org/ns/docb"
},
{
"path": "man/flashmq.1",
"chars": 3556,
"preview": ".if \\n(.g .ds T< \\\\FC\n.if \\n(.g .ds T> \\\\F[\\n[.fam]]\n.color\n.de URL\n\\\\$2 \\(la\\\\$1\\(ra\\\\$3\n..\n.if \\n(.g .mso www.tmac\n.TH"
},
{
"path": "man/flashmq.1.dbk5",
"chars": 6871,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<refentry\n xml:id=\"flashmq.1\"\n xml:lang=\"en\"\n xmlns=\"http://docbook.org/"
},
{
"path": "man/flashmq.1.html",
"chars": 17686,
"preview": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" data-color-scheme=\"light\" data-fallback-color-scheme=\"light\" "
},
{
"path": "man/flashmq.conf.5",
"chars": 56035,
"preview": ".if \\n(.g .ds T< \\\\FC\n.if \\n(.g .ds T> \\\\F[\\n[.fam]]\n.color\n.de URL\n\\\\$2 \\(la\\\\$1\\(ra\\\\$3\n..\n.if \\n(.g .mso www.tmac\n.TH"
},
{
"path": "man/flashmq.conf.5.dbk5",
"chars": 87711,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<refentry\n xml:id=\"flashmq.conf.5\"\n xml:lang=\"en\"\n xmlns=\"http://docbook"
},
{
"path": "man/flashmq.conf.5.html",
"chars": 111472,
"preview": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" data-color-scheme=\"light\" data-fallback-color-scheme=\"light\" "
},
{
"path": "man/refentry.colophon.dbk5",
"chars": 887,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<refsect1 xml:id=\"colophon\"\n xml:lang=\"en\"\n xmlns=\"http://docbook.org/ns/docboo"
},
{
"path": "man/reference.dbk5",
"chars": 270,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<reference\n xml:lang=\"en\"\n xmlns=\"http://docbook.org/ns/docbook\"\n versio"
},
{
"path": "mosquittoauthoptcompatwrap.cpp",
"chars": 1527,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "mosquittoauthoptcompatwrap.h",
"chars": 1710,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "mqtt5properties.cpp",
"chars": 7050,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "mqtt5properties.h",
"chars": 2343,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "mqttpacket.cpp",
"chars": 106741,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "mqttpacket.h",
"chars": 7927,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "mutexowned.h",
"chars": 2264,
"preview": "#ifndef MUTEXOWNED_H\n#define MUTEXOWNED_H\n\n#include <mutex>\n\nclass __attribute__((visibility(\"default\"))) MutexOwnedObje"
},
{
"path": "network.cpp",
"chars": 4529,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "network.h",
"chars": 826,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "nocopy.cpp",
"chars": 21,
"preview": "#include \"nocopy.h\"\n\n"
},
{
"path": "nocopy.h",
"chars": 635,
"preview": "#ifndef NOCOPY_H\n#define NOCOPY_H\n\n#include <optional>\n\ntemplate<typename T>\nclass NoCopy\n{\n std::optional<T> data;\n\n"
},
{
"path": "oneinstancelock.cpp",
"chars": 1456,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "oneinstancelock.h",
"chars": 653,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "packetdatatypes.cpp",
"chars": 892,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "packetdatatypes.h",
"chars": 2726,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "persistencefile.cpp",
"chars": 13232,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "persistencefile.h",
"chars": 2619,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "persistencefunctions.cpp",
"chars": 4133,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2025 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "persistencefunctions.h",
"chars": 728,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2025 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "plugin.cpp",
"chars": 37048,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "plugin.h",
"chars": 15405,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "pluginloader.cpp",
"chars": 3858,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "pluginloader.h",
"chars": 1542,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "publishcopyfactory.cpp",
"chars": 6950,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "publishcopyfactory.h",
"chars": 2609,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "qospacketqueue.cpp",
"chars": 5999,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "qospacketqueue.h",
"chars": 3001,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "queuedtasks.cpp",
"chars": 3032,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "queuedtasks.h",
"chars": 1291,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "release.sh",
"chars": 2423,
"preview": "#!/bin/bash\n\nSEMVER_REGEXP=\"^([0-9]+)\\.([0-9]+)\\.([0-9]+)$\"\n\nSCRIPT_NAME=$(basename \"$0\")\nSCRIPT_DIR=\"$(dirname \"$(realp"
},
{
"path": "retainedmessage.cpp",
"chars": 1645,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "retainedmessage.h",
"chars": 1185,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "retainedmessagesdb.cpp",
"chars": 7525,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "retainedmessagesdb.h",
"chars": 1765,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "rwlockguard.cpp",
"chars": 2726,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "rwlockguard.h",
"chars": 727,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "scopedsocket.cpp",
"chars": 2192,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "scopedsocket.h",
"chars": 1140,
"preview": "/*\nThis file is part of FlashMQ (https://www.flashmq.org)\nCopyright (C) 2021-2023 Wiebe Cazemier\n\nFlashMQ is free softwa"
},
{
"path": "sdnotify.cpp",
"chars": 3226,
"preview": "#include \"sdnotify.h\"\n\n/* SPDX-License-Identifier: MIT-0 */\n\n/* Implement the systemd notify protocol without external d"
}
]
// ... and 35 more files (download for full content)
About this extraction
This page contains the full source code of the halfgaar/FlashMQ GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 235 files (1.7 MB), approximately 434.5k tokens, and a symbol index with 461 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.