Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8cdcfc09d | ||
|
|
209b1950b6 | ||
|
|
7ea1a2b146 | ||
|
|
279530ade2 | ||
|
|
5a0fd95fc7 | ||
|
|
ec72ab6baf | ||
|
|
cd6c7d2da9 | ||
|
|
39371797d2 | ||
|
|
f672f4550e | ||
|
|
04c89d1fc1 | ||
|
|
97540bdd1b | ||
|
|
8514d808b3 | ||
|
|
e8e3d91dbc | ||
|
|
b64316c67d | ||
|
|
650ffa14e2 | ||
|
|
9fa33159c8 | ||
|
|
35794a777b | ||
|
|
bf60ed2d7d | ||
|
|
0d09aad24f | ||
|
|
8d31e031bc | ||
|
|
9af2bbf3d0 | ||
|
|
d391ba386c | ||
|
|
b2ef3152db | ||
|
|
05fe5bd766 | ||
|
|
48c9b10365 | ||
|
|
30cd56aae2 | ||
|
|
6fbecfb0fb | ||
|
|
d2cb8427bb | ||
|
|
826bad2d66 | ||
|
|
aaccec00e9 | ||
|
|
3c1c5ce6d3 | ||
|
|
b0fe1636a7 | ||
|
|
014cd3844d | ||
|
|
6cc00950f0 | ||
|
|
2918c06a01 | ||
|
|
c3b9b9fccf | ||
|
|
8c88d5a13f | ||
|
|
967526f5ec | ||
|
|
109078abca | ||
|
|
79a58e99aa | ||
|
|
a394444c86 | ||
|
|
dece1d7a1c | ||
|
|
2b09a7bf84 | ||
|
|
200a6ebd04 | ||
|
|
54db1f3b06 | ||
|
|
3b90d145a4 | ||
|
|
e16a116fe4 | ||
|
|
7bd0937069 | ||
|
|
c085057032 | ||
|
|
eac6648fc0 | ||
|
|
300f9aa0e6 | ||
|
|
dd40b72ec4 | ||
|
|
556e6ad2a0 | ||
|
|
473ae32f57 | ||
|
|
d57ae2ed58 | ||
|
|
1a6907efd3 | ||
|
|
1624c470d6 | ||
|
|
5ea6eefe67 | ||
|
|
8aebb2537a | ||
|
|
250b0ccebe | ||
|
|
91669c634f | ||
|
|
bf51cb9829 | ||
|
|
baadcac2c6 | ||
|
|
6c69a5ac96 | ||
|
|
eb19ed3301 | ||
|
|
5be7576bb8 | ||
|
|
86d552cd82 | ||
|
|
391f7345c0 | ||
|
|
92fa40f1fd | ||
|
|
3b3c05260c | ||
|
|
4e9cf86302 | ||
|
|
cbb89bbfb5 | ||
|
|
537221b0b4 | ||
|
|
9d23f9c996 | ||
|
|
6a25a83857 | ||
|
|
d0e533ad21 | ||
|
|
cd0e07f32d | ||
|
|
a6869c9072 | ||
|
|
03b3bb2093 | ||
|
|
304192e303 | ||
|
|
9490226757 | ||
|
|
c44062855b | ||
|
|
9d96fe927f | ||
|
|
ef21bd6db3 | ||
|
|
dc0b718083 | ||
|
|
c0703332d6 | ||
|
|
def048836f | ||
|
|
806cd440e3 | ||
|
|
a3f2623d7e | ||
|
|
4bc5fd6bb9 | ||
|
|
be2979c9a9 | ||
|
|
b7a30e1d14 | ||
|
|
beb591fdc5 | ||
|
|
d090244be9 | ||
|
|
22708caaaa | ||
|
|
2b24fb9f44 | ||
|
|
b8ccab5dde | ||
|
|
9d29357ffa | ||
|
|
dabb92571a | ||
|
|
acb4cf81de | ||
|
|
ace676d215 | ||
|
|
1170c4209b | ||
|
|
57e8814d9c | ||
|
|
e3d9e0d183 | ||
|
|
d6f5614f49 | ||
|
|
9ed4ab7e08 | ||
|
|
4da3d3421d | ||
|
|
40ef9abd35 | ||
|
|
6f5b021c89 | ||
|
|
f8f225d741 | ||
|
|
546110f6d3 | ||
|
|
c7dbf45af6 |
@@ -15,3 +15,6 @@
|
|||||||
|
|
||||||
#### demo对比结果
|
#### demo对比结果
|
||||||
请用demo对比测试,demo上是什么状态。
|
请用demo对比测试,demo上是什么状态。
|
||||||
|
|
||||||
|
#### 检索已有问题
|
||||||
|
除了可以检索已有issue外,还可以去野火论坛查找已有问题,论坛地址为 https://bbs.wildfirechat.cn
|
||||||
|
|||||||
@@ -1,54 +1,70 @@
|
|||||||
## 野火IM解决方案
|
# 野火IM解决方案
|
||||||
|
野火IM是专业级的即时通讯和实时音视频整体解决方案,由北京野火无限网络科技有限公司维护和支持。
|
||||||
|
|
||||||
野火IM是专业级即时通讯和实时音视频整体解决方案,由北京野火无限网络科技有限公司维护和支持。
|
## 功能特性
|
||||||
|
* 极致地硬件利用率,IM服务最低128M内存即可运行,上不封顶。
|
||||||
|
* 协议先进,采用MQTT+Protobuf组合,流量和性能极致优化。
|
||||||
|
* 性能强大,专业版IM服务支持百万在线和集群部署。
|
||||||
|
* 部署运维简单,依赖服务少,稍加配置一键启动。
|
||||||
|
* 安全加密。网络连接AES加密。客户端数据库SqlCipher加密。安全无小事。
|
||||||
|
* 全平台客户端,四端同时在线(移动端,pc端,web端和小程序端),数据和状态多端完美同步。
|
||||||
|
* 支持国产化。支持国产化操作系统、国产化芯片和国产操作系统。支持国密加密。
|
||||||
|
* 客户端使用微信[mars](https://github.com/tencent/mars)连接库,野火IM可能是最适应中国网络国情的即时通讯服务。
|
||||||
|
* 支持加速点加速,即可用于全球应用,也可用于政企内外双网复杂的网络环境。
|
||||||
|
* 支持阅读回执和在线状态功能,适用于办公环境。
|
||||||
|
* 音视频多种解决方案,丰俭由人,可自由选择。
|
||||||
|
* 高级音视频功能强大,支持9人以上的群组视频通话,支持1080P视频,支持会议模式,支持百人以上会议,支持服务器端录制。
|
||||||
|
* 全私有部署,可不依赖任何第三方服务,完全内网部署。
|
||||||
|
* 功能齐全,涵盖所有常见即时通讯功能。另外具有强大的可扩展能力。Demo成熟完善,基本可以做到开箱即用,也可把SDK嵌入其它应用。
|
||||||
|
* 拥有应用开放平台,可以开发和创建自建应用,扩展您的工作台。
|
||||||
|
* API丰富,方便与其它服务系统的对接。
|
||||||
|
* 拥有机器人和频道功能。
|
||||||
|
* 代码开源率高,二次开发简单。可以先试用3到6个月,二开搞定后再购买。
|
||||||
|
|
||||||
主要特性有:私有部署安全可靠,性能强大,功能齐全,全平台支持,开源率高,部署运维简单,二次开发友好,方便与第三方系统对接或者嵌入现有系统中。详细情况请参考[在线文档](https://docs.wildfirechat.cn)。
|
## 野火开源项目
|
||||||
|
主要包括以下项目:
|
||||||
|
|
||||||
主要包括一下项目:
|
| [GitHub仓库地址](https://github.com/wildfirechat) | [码云仓库地址](https://gitee.com/wfchat) | 说明
|
||||||
|
| ------------------------------------------------------------ | ----------------------------------------------------- | --------------------------------------------------------------------------
|
||||||
|
| [im-server](https://github.com/wildfirechat/im-server) | [im-server](https://gitee.com/wfchat/im-server) | 野火社区版IM服务,野火IM的核心服务,处理所有IM相关业务。 |
|
||||||
|
| [app_server](https://github.com/wildfirechat/app_server) | [app_server](https://gitee.com/wfchat/app_server) | Demo应用服务,模拟客户的应用服登陆处理逻辑及部分二次开发示例。 |
|
||||||
|
| [robot_server](https://github.com/wildfirechat/robot_server) | [robot_server](https://gitee.com/wfchat/robot_server) | 机器人服务,演示野火机器人对接其它系统的方法。 |
|
||||||
|
| [push_server](https://github.com/wildfirechat/push_server) | [push_server](https://gitee.com/wfchat/push_server) | 推送服务器,可以对接所有的系统厂商推送服务或者第三方推送服务。 |
|
||||||
|
| [wf-minio](https://github.com/wildfirechat/WF-minio) | [wf-minio](https://gitee.com/wfchat/WF-minio) | 私有对象存储服务,用来支持野火IM专业版的文件存储。 |
|
||||||
|
| [wf-janus](https://github.com/wildfirechat/wf-janus ) | [wf-janus](https://gitee.com/wfchat/wf-janus ) | 高级音视频媒体服务。 |
|
||||||
|
| [open-platform](https://github.com/wildfirechat/open-platform) | [open-platform](https://gitee.com/wfchat/open-platform) | 野火开放平台服务。 |
|
||||||
|
| [daily-report](https://github.com/wildfirechat/daily-report) | [daily-report](https://github.com/wildfirechat/daily-report) | 野火开放平台日报 demo服务。 |
|
||||||
|
| [channel-platform](https://github.com/wildfirechat/channel-platform) | [channel-platform](https://gitee.com/wfchat/channel-platform) | 野火频道(公众号)平台服务。 |
|
||||||
|
| [android-chat](https://github.com/wildfirechat/android-chat) | [android-chat](https://gitee.com/wfchat/android-chat) | 野火IM Android SDK源码和App源码。 |
|
||||||
|
| [ios-chat](https://github.com/wildfirechat/ios-chat) | [ios-chat](https://gitee.com/wfchat/ios-chat) | 野火IM iOS SDK源码和App源码。|
|
||||||
|
| [pc-chat](https://github.com/wildfirechat/vue-pc-chat) | [pc-chat](https://gitee.com/wfchat/vue-pc-chat) | 基于[Electron](https://electronjs.org/)的PC 端,支持Windows、Mac、Linux(包括国产化linux系统和CPU)。 |
|
||||||
|
| [web-chat](https://github.com/wildfirechat/vue-chat) | [web-chat](https://gitee.com/wfchat/vue-chat) | 野火IM Web 端, [体验地址](https://web.wildfirechat.cn)。 |
|
||||||
|
| [wx-chat](https://github.com/wildfirechat/wx-chat) | [wx-chat](https://gitee.com/wfchat/wx-chat) | 小程序平台的Demo(支持微信、百度、阿里、字节、QQ 等小程序平台)。 |
|
||||||
|
| [uni-chat](https://github.com/wildfirechat/uni-chat) | [uni-chat](https://gitee.com/wfchat/uni-chat) | UniApp平台移动端应用(使用原生插件)。 |
|
||||||
|
| [uni-wfc-client](https://github.com/wildfirechat/uni-wfc-client) | [uni-wfc-client](https://gitee.com/wfchat/uni-wfc-client) | UniApp平台原生插件。 |
|
||||||
|
| [flutter_imclient](https://github.com/wildfirechat/flutter_imclient) | [flutter_imclient](https://gitee.com/wfchat/flutter_imclient) | Flutter平台原生插件。 |
|
||||||
|
| [docs](https://github.com/wildfirechat/docs) | [docs](https://gitee.com/wfchat/docs) | 野火IM相关文档,包含设计、概念、开发、使用说明,[在线查看](https://docs.wildfirechat.cn/)。 |
|
||||||
|
|
||||||
| [GitHub仓库地址(主站)](https://github.com/wildfirechat) | [码云仓库地址(镜像)](https://gitee.com/wfchat) | 说明 | 备注 |
|
## 野火开发文档
|
||||||
| ------------------------------------------------------------ | ----------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------- |
|
[在线文档](https://docs.wildfirechat.cn/)
|
||||||
| [android-chat](https://github.com/wildfirechat/android-chat) | [android-chat](https://gitee.com/wfchat/android-chat) | 野火IM Android SDK源码和App源码 | 可以很方便地进行二次开发,或集成到现有应用当中 |
|
|
||||||
| [ios-chat](https://github.com/wildfirechat/ios-chat) | [ios-chat](https://gitee.com/wfchat/ios-chat) | 野火IM iOS SDK源码和App源码 | 可以很方便地进行二次开发,或集成到现有应用当中 |
|
|
||||||
| [pc-chat](https://github.com/wildfirechat/pc-chat) | [pc-chat](https://gitee.com/wfchat/pc-chat) | 基于[Electron](https://electronjs.org/)开发的PC平台应用 | |
|
|
||||||
| [web-chat](https://github.com/wildfirechat/web-chat) | [web-chat](https://gitee.com/wfchat/web-chat) | Web平台的Demo, [体验地址](http://web.wildfirechat.cn) | |
|
|
||||||
| [wx-chat](https://github.com/wildfirechat/wx-chat) | [wx-chat](https://gitee.com/wfchat/wx-chat) | 微信小程序平台的Demo | |
|
|
||||||
| [server](https://github.com/wildfirechat/server) | [server](https://gitee.com/wfchat/server) | IM server | |
|
|
||||||
| [app server](https://github.com/wildfirechat/app_server) | [app server](https://gitee.com/wfchat/app_server) | 应用服务端 | |
|
|
||||||
| [robot_server](https://github.com/wildfirechat/robot_server) | [robot_server](https://gitee.com/wfchat/robot_server) | 机器人服务端 | |
|
|
||||||
| [push_server](https://github.com/wildfirechat/push_server) | [push_server](https://gitee.com/wfchat/push_server) | 推送服务器 | |
|
|
||||||
| [docs](https://github.com/wildfirechat/docs) | [docs](https://gitee.com/wfchat/docs) | 野火IM相关文档,包含设计、概念、开发、使用说明,[在线查看](https://docs.wildfirechat.cn/) | | |
|
|
||||||
|
|
||||||
|
## 野火Demo
|
||||||
# server
|
请使用微信扫码下载安装体验野火IM移动客户端
|
||||||
本工程为野火IM 社区版IM服务软件。野火IM作为一个通用的即时通讯解决方案,既可以直接使用,也可以集成到各种应用中。
|
|
||||||
|
|
||||||
|
|
||||||
开发一套IM系统真的很艰辛,请路过的朋友们给点个star,支持我们坚持下去🙏🙏🙏🙏🙏
|
|
||||||
|
|
||||||
### 联系我们
|
|
||||||
|
|
||||||
> 商务合作请优先采用邮箱和我们联系。技术问题请到[野火IM论坛](http://bbs.wildfirechat.cn/)发帖交流
|
|
||||||
|
|
||||||
1. heavyrain.lee 邮箱: heavyrain.lee@wildfirechat.cn 微信:wildfirechat
|
|
||||||
2. imndx 邮箱: imndx@wildfirechat.cn 微信:wfchat
|
|
||||||
|
|
||||||
### 问题交流
|
|
||||||
|
|
||||||
1. 如果大家发现bug,请在GitHub提issue
|
|
||||||
2. 其他问题,请到[野火IM论坛](http://bbs.wildfirechat.cn/)进行交流学习
|
|
||||||
3. 微信公众号
|
|
||||||
|
|
||||||
<img src="http://static.wildfirechat.cn/wx_wfc_qrcode.jpg" width = 50% height = 50% />
|
|
||||||
|
|
||||||
> 强烈建议关注我们的公众号。我们有新版本发布或者有重大更新会通过公众号通知大家,另外我们也会不定期的发布一些关于野火IM的技术介绍。
|
|
||||||
|
|
||||||
#### 体验Demo
|
|
||||||
我们提供了体验demo,请使用微信扫码下载安装体验
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### 应用截图
|
Web客户端点击[这里](https://web.wildfirechat.cn)
|
||||||
|
|
||||||
|
PC客户端点[这里](https://github.com/wildfirechat/vue-pc-chat/releases)下载安装。
|
||||||
|
|
||||||
|
小程序客户端请用微信扫码
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 快速使用
|
||||||
|
野火提供免费的社区版IM服务和移动端,包括完整的移动端即时通讯和实时音视频功能,可以无条件商用。您可以直接从开源项目里下载到完整的后端和移动端产品,包括社区版IM服务、应用服务、开放平台服务、频道(公众号)服务、机器人服务、推送服务以及Android和iOS端源码。可以按照[快速开始](https://docs.wildfirechat.cn/quick_start/)来部署应用服务和IM服务,然后打包移动端,以最小化运行。之后可以详细阅读文档部署其他支撑服务。
|
||||||
|
|
||||||
|
## 应用截图
|
||||||

|

|
||||||
|
|
||||||
<img src="http://static.wildfirechat.cn/ios-message-view.png" width = 50% height = 50% />
|
<img src="http://static.wildfirechat.cn/ios-message-view.png" width = 50% height = 50% />
|
||||||
@@ -71,16 +87,49 @@
|
|||||||
|
|
||||||
<img src="http://static.wildfirechat.cn/ios-voip-view.png" width = 50% height = 50% />
|
<img src="http://static.wildfirechat.cn/ios-voip-view.png" width = 50% height = 50% />
|
||||||
|
|
||||||
|
|
||||||
## 编译
|
## 编译
|
||||||
在安装JDK1.8以上及maven的前提下,在命令行中执行```mvn clean compile package```,生成的目标文件在```./distribution/target/distribution-xxxx-bundle-tar.tar.gz```
|
在安装JDK1.8以上及maven的前提下,在命令行中执行```mvn clean package```,生成的目标文件在```./distribution/target/distribution-xxxx-bundle-tar.tar.gz```
|
||||||
> 由于使用了一个git的maven插件,如果本地没有git信息就会编译出错,请使用```git clone```的方法下载代码,或者下载压缩包解压后在根目录创建```.git```的空目录。建议用```git clone```的方式下载代码。
|
> 由于使用了一个git的maven插件,如果本地没有git信息就会编译出错,请使用```git clone```的方法下载代码,或者下载压缩包解压后在根目录创建```.git```的空目录。建议用```git clone```的方式下载代码。
|
||||||
|
|
||||||
|
## 配置
|
||||||
|
解压```distribution-xxxx-bundle-tar.tar.gz```,修改解压出来的```config```目录下的```wildfirechat.conf```。修改如下部分:
|
||||||
|
1. 修改```server.ip```为您的服务器的公网IP,或者域名。如果用域名需要做好域名解析。
|
||||||
|
2. 可以阅读配置文件注释和文档,对其它配置项进行调整。
|
||||||
|
|
||||||
|
## 运行
|
||||||
|
在程序目录运行如下命令:
|
||||||
|
```
|
||||||
|
./bin/wildfirechat.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
1. 在浏览器中输入地址 ```http://${ip}/api/version```可以看到返回一个json文件。
|
||||||
|
2. 部署[应用服务](应用服务),配置和编译[Android客户端](https://github.com/wildfirechat/android-chat)和[iOS客户端](https://github.com/wildfirechat/ios-chat)进行验证。详情可参考[快速开始](https://docs.wildfirechat.cn/quick_start/)。
|
||||||
|
|
||||||
## 升级说明
|
## 升级说明
|
||||||
1. 从0.42 版本增加了群成员数限制,默认为2000。如果您从之前的版本升级到这个版本或以后,需要注意到群成员数的限制。升级之后超出限制的群不受影响,但不能继续加人,如果您想修改默认值,可以在升级版本之后,修改t_setting表,把默认的大小改为您期望的人数。另外修改t_group表,把已经存在的群组max_member_count改成您期望的,然后重启。
|
1. 从0.42 版本增加了群成员数限制,默认为2000。如果您从之前的版本升级到这个版本或以后,需要注意到群成员数的限制。升级之后超出限制的群不受影响,但不能继续加人,如果您想修改默认值,可以在升级版本之后,修改t_setting表,把默认的大小改为您期望的人数。另外修改t_group表,把已经存在的群组max_member_count改成您期望的,然后重启。
|
||||||
2. 0.46和0.47版本升级到0.48及以后版本时,可能会提示flyway migrate 38错误,请执行 [修复脚本](https://github.com/wildfirechat/server/blob/wildfirechat/flyway_repaire_migrate_38.sql) 进行修复。0.46和0.47版本之外的版本不会出现此问题。
|
2. 0.46和0.47版本升级到0.48及以后版本时,可能会提示flyway migrate 38错误,请执行 [修复脚本](https://github.com/wildfirechat/server/blob/wildfirechat/flyway_repaire_migrate_38.sql) 进行修复。0.46和0.47版本之外的版本不会出现此问题。
|
||||||
3. 从0.54之前版本升级到0.54及以后版本时,会提示flyway migrate错误。因为0.54版本删除了sql脚本中默认敏感词的内容,flyway checksum失败。请执行```update flyway_schema_history set checksum = 0 where script = 'V17__add_default_sensitive_word.sql';```来修复。
|
3. 从0.54之前版本升级到0.54及以后版本时,会提示flyway migrate错误。因为0.54版本删除了sql脚本中默认敏感词的内容,flyway checksum失败。请执行```update flyway_schema_history set checksum = 0 where script = 'V17__add_default_sensitive_word.sql';```来修复。
|
||||||
4. 从0.59之前的版本升级到之后的版本执行数据库升级时间比较长,请耐心等待提示运行成功,避免中途中断。
|
4. 从0.59之前的版本升级到之后的版本执行数据库升级时间比较长,请耐心等待提示运行成功,避免中途中断。
|
||||||
|
|
||||||
|
|
||||||
|
## 联系我们
|
||||||
|
|
||||||
|
商务合作请使用如下邮箱和微信联系:
|
||||||
|
|
||||||
|
邮箱: support@wildfirechat.cn 微信1:wfchat 微信2:wildfirechat
|
||||||
|
|
||||||
|
## 问题交流
|
||||||
|
|
||||||
|
1. 如果大家发现bug,请在GitHub提issue;如果有需求也请给我们提issue。
|
||||||
|
2. 其他问题,请到[野火IM论坛](http://bbs.wildfirechat.cn/)进行交流学习
|
||||||
|
3. 关注我们的公众号。我们有新版本发布或者有重大更新会通过公众号通知大家,另外我们也会不定期的发布一些关于野火IM的技术介绍。
|
||||||
|
|
||||||
|
<img src="http://static.wildfirechat.cn/wx_wfc_qrcode.jpg" width = 50% height = 50% />
|
||||||
|
|
||||||
|
> 我们有研发工程师轮流值班处理issue和论坛,一般简单问题几个小时就会回复一遍,疑难Bug的修改和新需求的开发我们也会尽快解决。
|
||||||
|
|
||||||
## 特别感谢
|
## 特别感谢
|
||||||
1. [moquette](https://github.com/moquette-io/moquette) 本项目是基于此项目二次开发而来,处理MQTT相关业务。
|
1. [moquette](https://github.com/moquette-io/moquette) 本项目是基于此项目二次开发而来,处理MQTT相关业务。
|
||||||
2. [loServer](https://github.com/looly/loServer) 本项目使用loServer处理HTTP相关业务。
|
2. [loServer](https://github.com/looly/loServer) 本项目使用loServer处理HTTP相关业务。
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ http.admin.port 18080
|
|||||||
##节点ID,当集群部署时,一定不能有重复。
|
##节点ID,当集群部署时,一定不能有重复。
|
||||||
node_id 1
|
node_id 1
|
||||||
|
|
||||||
##绑定IP,保持不变即可
|
##绑定IP,不要打开,除非有特殊需求才可以打开
|
||||||
host 0.0.0.0
|
#host 0.0.0.0
|
||||||
|
|
||||||
##本地绑定端口
|
##本地绑定端口
|
||||||
local_port 80
|
local_port 80
|
||||||
@@ -38,6 +38,7 @@ embed.db 1
|
|||||||
|
|
||||||
## 是否自动清理历史消息记录,当为true时,IM服务会定时扫描t_messages_x和t_user_messages_x表,清除掉3年前的历史消息。
|
## 是否自动清理历史消息记录,当为true时,IM服务会定时扫描t_messages_x和t_user_messages_x表,清除掉3年前的历史消息。
|
||||||
## 如果不开启自动清理,需要手动清理t_messages_x和t_user_messages_x表内的历史数据,保存数据不能超过3年。
|
## 如果不开启自动清理,需要手动清理t_messages_x和t_user_messages_x表内的历史数据,保存数据不能超过3年。
|
||||||
|
## 如果需要消息保存更长时间,可以把临近销毁的消息转储到别的数据库,客户端在拉取不到服务器消息时,再去转储的数据库拉取。
|
||||||
## 使用mongodb时次开关无效(因为mongodb设置了消息过期时间,会自动清理,仅专业版支持mongodb)。
|
## 使用mongodb时次开关无效(因为mongodb设置了消息过期时间,会自动清理,仅专业版支持mongodb)。
|
||||||
db.auto_clean_history_messages true
|
db.auto_clean_history_messages true
|
||||||
|
|
||||||
@@ -56,10 +57,13 @@ token.key testim
|
|||||||
##token.expire_time 2592000000
|
##token.expire_time 2592000000
|
||||||
|
|
||||||
##首次登录,是否接收之前的历史消息。
|
##首次登录,是否接收之前的历史消息。
|
||||||
##0 不接收历史消息,只接收5分钟以内的消息,由于服务器没有保存已经收取记录,所以如果有超过5分钟之前未收取的消息也不会收取下拉;
|
##0 不接收历史消息,只接收${message.compensate_time_limit}毫秒以内的消息,由于服务器没有保存已经收取记录,所以如果有超过${message.compensate_time_limit}毫秒之前未收取的消息也不会收取下拉;
|
||||||
##1接收,会接收 message.max_queue 配置的条数的历史消息
|
##1接收,会接收 message.max_queue 配置的条数的历史消息
|
||||||
message.roaming 0
|
message.roaming 0
|
||||||
|
|
||||||
|
## 消息补偿时限,当${message.roaming}为0时,会同步时限以内且小于${message.max_queue}的消息,默认时限为5分钟。当${message.roaming}为1时,会接收 message.max_queue 配置的条数的历史消息。
|
||||||
|
message.compensate_time_limit 300000
|
||||||
|
|
||||||
##是否开启拉取远程历史消息。如果为1,客户端在会话内如果本地消息读取完了,可以下拉继续加载在服务器上的该会话的消息;如果为0则不能。
|
##是否开启拉取远程历史消息。如果为1,客户端在会话内如果本地消息读取完了,可以下拉继续加载在服务器上的该会话的消息;如果为0则不能。
|
||||||
message.remote_history_message 0
|
message.remote_history_message 0
|
||||||
|
|
||||||
@@ -108,6 +112,14 @@ message.global_mute_exception_types 401,402,403,404,405,407,408,410,411
|
|||||||
## 当为true时,不转发server api发送的消息;当为false时,转发server api发送的消息。
|
## 当为true时,不转发server api发送的消息;当为false时,转发server api发送的消息。
|
||||||
message.no_forward_admin_message false
|
message.no_forward_admin_message false
|
||||||
|
|
||||||
|
## 消息转发功能开启时,转发信息是否带上用户clientId和平台类型。
|
||||||
|
message.forward_with_client_info false
|
||||||
|
|
||||||
|
## 机器人回调信息是否带上用户clientId和平台类型。
|
||||||
|
robot.callback_with_client_info false
|
||||||
|
|
||||||
|
## 频道回调信息是否带上用户clientId和平台类型。
|
||||||
|
channel.callback_with_client_info false
|
||||||
|
|
||||||
##禁止搜索用户
|
##禁止搜索用户
|
||||||
friend.disable_search false
|
friend.disable_search false
|
||||||
@@ -118,13 +130,13 @@ friend.disable_nick_name_search false
|
|||||||
##禁止发送好友邀请,通过server api添加好友不受此限制(force参数需要为true)
|
##禁止发送好友邀请,通过server api添加好友不受此限制(force参数需要为true)
|
||||||
friend.disable_friend_request false
|
friend.disable_friend_request false
|
||||||
|
|
||||||
##好友请求重复发送的间隔,单位是毫秒,默认是7天,0为不限制
|
##好友请求重复发送的间隔,单位是毫秒,默认是7天,0为无限长期限。
|
||||||
friend.repeat_request_duration 604800000
|
friend.repeat_request_duration 604800000
|
||||||
|
|
||||||
##好友请求被拒绝后再次发送的间隔,单位是毫秒,默认是30天,0为不限制
|
##好友请求被拒绝后再次发送的间隔,单位是毫秒,默认是30天,0为不限制。不能小于friend.repeat_request_duration。
|
||||||
friend.reject_request_duration 2592000000
|
friend.reject_request_duration 2592000000
|
||||||
|
|
||||||
##好友请求过期时间,单位是毫秒,默认是7天,0为不限制
|
##好友请求过期时间,单位是毫秒,默认是7天,0为无限长期限。
|
||||||
friend.request_expiration_duration 604800000
|
friend.request_expiration_duration 604800000
|
||||||
|
|
||||||
##添加好友后会发送欢迎消息,比如"你们已经是好友了,可以聊天了"。这个开关控制是用旧的方式还是用新的方式。
|
##添加好友后会发送欢迎消息,比如"你们已经是好友了,可以聊天了"。这个开关控制是用旧的方式还是用新的方式。
|
||||||
@@ -157,6 +169,9 @@ group.visible_quit_or_kickoff_notification 0
|
|||||||
## 注意,修改本配置不会对已经同步的信息产生影响。
|
## 注意,修改本配置不会对已经同步的信息产生影响。
|
||||||
#user.hide_properties 3,4,5
|
#user.hide_properties 3,4,5
|
||||||
|
|
||||||
|
## 销毁用户时,用户信息会被清空,deleted字段设置为1,这个开关控制是否保留用户昵称,默认为true。
|
||||||
|
user.keep_display_name_when_destroy true
|
||||||
|
|
||||||
# 好友/好友请求/用户设置 这三类数据同步的最大条目数。之前是客户第一次登录时,会把所有的对应数据一次下载到客户端,但如果有特别多的信息,会导致无法同步到客户端,
|
# 好友/好友请求/用户设置 这三类数据同步的最大条目数。之前是客户第一次登录时,会把所有的对应数据一次下载到客户端,但如果有特别多的信息,会导致无法同步到客户端,
|
||||||
# 因此需要分段多次同步,此开关定义了每次同步的数据的条数。此需要客户端支持,客户端SDK在2021.5.25日后支持此功能。如果确认所有客户端都在这个日志之后才可以打开此设置。
|
# 因此需要分段多次同步,此开关定义了每次同步的数据的条数。此需要客户端支持,客户端SDK在2021.5.25日后支持此功能。如果确认所有客户端都在这个日志之后才可以打开此设置。
|
||||||
#sync.data_part_size 1000
|
#sync.data_part_size 1000
|
||||||
@@ -235,6 +250,9 @@ local.media.storage.root media
|
|||||||
# 建议使用false
|
# 建议使用false
|
||||||
server.multi_endpoint false
|
server.multi_endpoint false
|
||||||
|
|
||||||
|
## 是否支持PC多端登陆,当server.multi_endpoint为true时,此开关无意义,当为false时,可以单独打开PC端多端登录。
|
||||||
|
server.multi_pc_endpoint false
|
||||||
|
|
||||||
# 多平台连接状态通知(仅当multi_endpoint为false时有效),true时移动端可以收到pc或web端登录的通知。
|
# 多平台连接状态通知(仅当multi_endpoint为false时有效),true时移动端可以收到pc或web端登录的通知。
|
||||||
server.multi_platform_notification true
|
server.multi_platform_notification true
|
||||||
|
|
||||||
@@ -286,6 +304,10 @@ push.ios.server.address http://localhost:8085/ios/push
|
|||||||
##需要转发的消息类型,当有多个时以英文逗号分割。如果转发所有消息,请注释掉配置或者设置为空
|
##需要转发的消息类型,当有多个时以英文逗号分割。如果转发所有消息,请注释掉配置或者设置为空
|
||||||
#message.forward.types 1,2,3
|
#message.forward.types 1,2,3
|
||||||
|
|
||||||
|
##敏感消息转发地址
|
||||||
|
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
|
||||||
|
#message.sensitive.forward.url http://localhost:8888/im_event/message
|
||||||
|
|
||||||
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。此转发不受${message.forward.types}限制。
|
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。此转发不受${message.forward.types}限制。
|
||||||
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
|
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
|
||||||
#message.mentionmsg.forward.url http://localhost:8888/im_event/message
|
#message.mentionmsg.forward.url http://localhost:8888/im_event/message
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
DROP TABLE IF EXISTS `t_secret_chat`;
|
||||||
|
CREATE TABLE `t_secret_chat` (
|
||||||
|
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
`_uid` varchar(64) NOT NULL,
|
||||||
|
`_from` varchar(64) DEFAULT '',
|
||||||
|
`_from_cid` varchar(64) DEFAULT '',
|
||||||
|
`_to` varchar(64) DEFAULT '',
|
||||||
|
`_to_cid` varchar(64) DEFAULT '',
|
||||||
|
`_state` tinyint NOT NULL DEFAULT 0,
|
||||||
|
`_dt` bigint(20) NOT NULL,
|
||||||
|
UNIQUE INDEX `secret_chat_uid_index` (`_uid` DESC)
|
||||||
|
);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table `t_messages` add INDEX `messages_conv_index` ( `_type`, `_target`, `_line`);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table `t_channel_listener` add INDEX `channel_mid_index` ( `_mid`);
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
alter table `t_channel` add column `_menu` BLOB DEFAULT NULL;
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
DROP TABLE IF EXISTS `t_secret_chat`;
|
||||||
|
CREATE TABLE `t_secret_chat` (
|
||||||
|
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
`_uid` varchar(64) NOT NULL,
|
||||||
|
`_from` varchar(64) DEFAULT '',
|
||||||
|
`_from_cid` varchar(64) DEFAULT '',
|
||||||
|
`_to` varchar(64) DEFAULT '',
|
||||||
|
`_to_cid` varchar(64) DEFAULT '',
|
||||||
|
`_state` tinyint NOT NULL DEFAULT 0,
|
||||||
|
`_dt` bigint(20) NOT NULL,
|
||||||
|
UNIQUE INDEX `secret_chat_uid_index` (`_uid` DESC)
|
||||||
|
)
|
||||||
|
ENGINE = InnoDB
|
||||||
|
DEFAULT CHARACTER SET = utf8mb4
|
||||||
|
COLLATE = utf8mb4_bin;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
alter table `t_messages` add INDEX `messages_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_0` add INDEX `messages0_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_1` add INDEX `messages1_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_2` add INDEX `messages2_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_3` add INDEX `messages3_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_4` add INDEX `messages4_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_5` add INDEX `messages5_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_6` add INDEX `messages6_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_7` add INDEX `messages7_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_8` add INDEX `messages8_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_9` add INDEX `messages9_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_10` add INDEX `messages10_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_11` add INDEX `messages11_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_12` add INDEX `messages12_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_13` add INDEX `messages13_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_14` add INDEX `messages14_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_15` add INDEX `messages15_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_16` add INDEX `messages16_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_17` add INDEX `messages17_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_18` add INDEX `messages18_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_19` add INDEX `messages19_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_20` add INDEX `messages20_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_21` add INDEX `messages21_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_22` add INDEX `messages22_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_23` add INDEX `messages23_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_24` add INDEX `messages24_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_25` add INDEX `messages25_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_26` add INDEX `messages26_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_27` add INDEX `messages27_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_28` add INDEX `messages28_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_29` add INDEX `messages29_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_30` add INDEX `messages30_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_31` add INDEX `messages31_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_32` add INDEX `messages32_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_33` add INDEX `messages33_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_34` add INDEX `messages34_conv_index` (`_type`, `_target`, `_line`);
|
||||||
|
alter table `t_messages_35` add INDEX `messages35_conv_index` (`_type`, `_target`, `_line`);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table `t_channel_listener` add INDEX `channel_mid_index` (`_mid`);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table `t_channel` add column `_menu` BLOB DEFAULT NULL;
|
||||||
+10
-5
@@ -5,7 +5,7 @@
|
|||||||
<relativePath>../</relativePath>
|
<relativePath>../</relativePath>
|
||||||
<artifactId>wildfirechat-parent</artifactId>
|
<artifactId>wildfirechat-parent</artifactId>
|
||||||
<groupId>cn.wildfirechat</groupId>
|
<groupId>cn.wildfirechat</groupId>
|
||||||
<version>0.80</version>
|
<version>0.95</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>moquette-broker</artifactId>
|
<artifactId>moquette-broker</artifactId>
|
||||||
@@ -27,18 +27,18 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-core</artifactId>
|
<artifactId>log4j-core</artifactId>
|
||||||
<version>2.17.1</version>
|
<version>2.17.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-api</artifactId>
|
<artifactId>log4j-api</artifactId>
|
||||||
<version>2.17.1</version>
|
<version>2.17.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!--用于与slf4j保持桥接-->
|
<!--用于与slf4j保持桥接-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-slf4j-impl</artifactId>
|
<artifactId>log4j-slf4j-impl</artifactId>
|
||||||
<version>2.17.1</version>
|
<version>2.17.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- slf4j核心包-->
|
<!-- slf4j核心包-->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>mysql</groupId>
|
<groupId>mysql</groupId>
|
||||||
<artifactId>mysql-connector-java</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
<version>8.0.19</version>
|
<version>8.0.28</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -234,6 +234,11 @@
|
|||||||
<version>3.4.1</version>
|
<version>3.4.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
<version>2.4.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ package cn.wildfirechat.push;
|
|||||||
import cn.wildfirechat.common.IMExceptionEvent;
|
import cn.wildfirechat.common.IMExceptionEvent;
|
||||||
import cn.wildfirechat.proto.ProtoConstants;
|
import cn.wildfirechat.proto.ProtoConstants;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import io.moquette.persistence.MemorySessionStore;
|
import io.moquette.persistence.MemorySessionStore;
|
||||||
import io.moquette.server.config.IConfig;
|
import io.moquette.server.config.IConfig;
|
||||||
import io.moquette.spi.ISessionsStore;
|
import io.moquette.spi.ISessionsStore;
|
||||||
@@ -29,7 +30,7 @@ import static win.liyufan.im.HttpUtils.HttpPostType.POST_TYPE_Push;
|
|||||||
|
|
||||||
public class PushServer {
|
public class PushServer {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(PushServer.class);
|
private static final Logger LOG = LoggerFactory.getLogger(PushServer.class);
|
||||||
|
protected static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||||
public interface PushMessageType {
|
public interface PushMessageType {
|
||||||
int PUSH_MESSAGE_TYPE_NORMAL = 0;
|
int PUSH_MESSAGE_TYPE_NORMAL = 0;
|
||||||
int PUSH_MESSAGE_TYPE_VOIP_INVITE = 1;
|
int PUSH_MESSAGE_TYPE_VOIP_INVITE = 1;
|
||||||
@@ -94,17 +95,13 @@ public class PushServer {
|
|||||||
pushMessage.deviceToken = session.getDeviceToken();
|
pushMessage.deviceToken = session.getDeviceToken();
|
||||||
pushMessage.unReceivedMsg = badge;
|
pushMessage.unReceivedMsg = badge;
|
||||||
pushMessage.userId = session.getUsername();
|
pushMessage.userId = session.getUsername();
|
||||||
if (session.getPlatform() == ProtoConstants.Platform.Platform_iOS
|
if (session.getPlatform() == ProtoConstants.Platform.Platform_iOS || session.getPlatform() == ProtoConstants.Platform.Platform_Android) {
|
||||||
|| session.getPlatform() == ProtoConstants.Platform.Platform_Android
|
|
||||||
|| session.getPlatform() == ProtoConstants.Platform.Platform_iPad
|
|
||||||
|| session.getPlatform() == ProtoConstants.Platform.Platform_APad) {
|
|
||||||
String url = androidPushServerUrl;
|
String url = androidPushServerUrl;
|
||||||
if (session.getPlatform() == ProtoConstants.Platform.Platform_iOS
|
if (session.getPlatform() == ProtoConstants.Platform.Platform_iOS) {
|
||||||
|| session.getPlatform() == ProtoConstants.Platform.Platform_iPad) {
|
|
||||||
url = iOSPushServerUrl;
|
url = iOSPushServerUrl;
|
||||||
pushMessage.voipDeviceToken = session.getVoipDeviceToken();
|
pushMessage.voipDeviceToken = session.getVoipDeviceToken();
|
||||||
}
|
}
|
||||||
HttpUtils.httpJsonPost(url, new Gson().toJson(pushMessage, pushMessage.getClass()), POST_TYPE_Push);
|
HttpUtils.httpJsonPost(url, gson.toJson(pushMessage, pushMessage.getClass()), POST_TYPE_Push);
|
||||||
} else {
|
} else {
|
||||||
LOG.info("Not mobile platform {}", session.getPlatform());
|
LOG.info("Not mobile platform {}", session.getPlatform());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
package com.xiaoleilu.loServer.action;
|
package com.xiaoleilu.loServer.action;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import com.xiaoleilu.loServer.RestResult;
|
import com.xiaoleilu.loServer.RestResult;
|
||||||
import com.xiaoleilu.loServer.annotation.RequireAuthentication;
|
import com.xiaoleilu.loServer.annotation.RequireAuthentication;
|
||||||
import com.xiaoleilu.loServer.handler.Request;
|
import com.xiaoleilu.loServer.handler.Request;
|
||||||
@@ -39,7 +40,7 @@ import static io.moquette.BrokerConstants.*;
|
|||||||
abstract public class Action {
|
abstract public class Action {
|
||||||
public static IMessagesStore messagesStore = null;
|
public static IMessagesStore messagesStore = null;
|
||||||
public static ISessionsStore sessionsStore = null;
|
public static ISessionsStore sessionsStore = null;
|
||||||
|
protected static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||||
public ChannelHandlerContext ctx;
|
public ChannelHandlerContext ctx;
|
||||||
|
|
||||||
protected static RateLimiter adminLimiter = null;
|
protected static RateLimiter adminLimiter = null;
|
||||||
@@ -129,7 +130,7 @@ abstract public class Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), RestResult.resultOf(errorCode));
|
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), RestResult.resultOf(errorCode));
|
||||||
response.setContent(new Gson().toJson(result));
|
response.setContent(gson.toJson(result));
|
||||||
response.send();
|
response.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +146,7 @@ abstract public class Action {
|
|||||||
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
|
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
|
||||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||||
String content = new String(bytes, StandardCharsets.UTF_8);//contribute by JiaRG from github
|
String content = new String(bytes, StandardCharsets.UTF_8);//contribute by JiaRG from github
|
||||||
Gson gson = new Gson();
|
|
||||||
T t = gson.fromJson(content, cls);
|
T t = gson.fromJson(content, cls);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
@@ -154,6 +155,6 @@ abstract public class Action {
|
|||||||
|
|
||||||
protected void setResponseContent(RestResult result, Response response) {
|
protected void setResponseContent(RestResult result, Response response) {
|
||||||
response.setStatus(HttpResponseStatus.OK);
|
response.setStatus(HttpResponseStatus.OK);
|
||||||
response.setContent(new Gson().toJson(result));
|
response.setContent(gson.toJson(result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ public class UploadFileAction extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readHttpDataChunkByChunk(response, decoder, requestId, HttpHeaders.isKeepAlive(request));
|
readHttpDataChunkByChunk(response, decoder, requestId, HttpHeaders.isKeepAlive(request));
|
||||||
|
decoder.destroy();
|
||||||
|
|
||||||
if (chunk instanceof LastHttpContent) {
|
if (chunk instanceof LastHttpContent) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class VersionAction extends Action {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
response.setContent(Utility.formatJson(new Gson().toJson(GitRepositoryState.getGitRepositoryState())));
|
response.setContent(Utility.formatJson(gson.toJson(GitRepositoryState.getGitRepositoryState())));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
response.setContent("{\"version\":\"unknown\"}");
|
response.setContent("{\"version\":\"unknown\"}");
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ abstract public class AdminAction extends Action {
|
|||||||
SECRET_KEY = secretKey;
|
SECRET_KEY = secretKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getSecretKey() {
|
||||||
|
return SECRET_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setNoCheckTime(String noCheckTime) {
|
public static void setNoCheckTime(String noCheckTime) {
|
||||||
try {
|
try {
|
||||||
NO_CHECK_TIME = Boolean.parseBoolean(noCheckTime);
|
NO_CHECK_TIME = Boolean.parseBoolean(noCheckTime);
|
||||||
@@ -95,7 +99,7 @@ abstract public class AdminAction extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
||||||
response.setContent(new Gson().toJson(result));
|
response.setContent(gson.toJson(result));
|
||||||
response.send();
|
response.send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +112,7 @@ abstract public class AdminAction extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void sendApiMessage(Response response, String fromUser, String clientId, String topic, byte[] message, ApiCallback callback, boolean noAdmin) {
|
protected void sendApiMessage(Response response, String fromUser, String clientId, String topic, byte[] message, ApiCallback callback, boolean noAdmin) {
|
||||||
ServerAPIHelper.sendRequest(fromUser, clientId, topic, message, new ServerAPIHelper.Callback() {
|
ServerAPIHelper.sendRequest(fromUser, clientId, topic, message, callback == null ? null : new ServerAPIHelper.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(byte[] result) {
|
public void onSuccess(byte[] result) {
|
||||||
if(callback != null) {
|
if(callback != null) {
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package com.xiaoleilu.loServer.action.admin;
|
||||||
|
|
||||||
|
import cn.wildfirechat.common.APIPath;
|
||||||
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import cn.wildfirechat.pojos.InputApplicationGetUserInfo;
|
||||||
|
import cn.wildfirechat.pojos.OutputApplicationUserInfo;
|
||||||
|
import cn.wildfirechat.proto.ProtoConstants;
|
||||||
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.hazelcast.util.StringUtil;
|
||||||
|
import com.xiaoleilu.loServer.RestResult;
|
||||||
|
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||||
|
import com.xiaoleilu.loServer.annotation.Route;
|
||||||
|
import com.xiaoleilu.loServer.handler.Request;
|
||||||
|
import com.xiaoleilu.loServer.handler.Response;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
|
||||||
|
@Route(APIPath.User_Application_Get_UserInfo)
|
||||||
|
@HttpMethod("POST")
|
||||||
|
public class ApplicationGetUserInfoAction extends AdminAction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTransactionAction() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Request request, Response response) {
|
||||||
|
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||||
|
InputApplicationGetUserInfo inputUserToken = getRequestBody(request.getNettyRequest(), InputApplicationGetUserInfo.class);
|
||||||
|
|
||||||
|
RestResult result;
|
||||||
|
if (inputUserToken == null || StringUtil.isNullOrEmpty(inputUserToken.getAuthCode())) {
|
||||||
|
result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||||
|
} else {
|
||||||
|
String userId = messagesStore.verifyApplicationAuthCode(inputUserToken.getAuthCode(), "admin", ProtoConstants.ApplicationType.ApplicationType_Admin);
|
||||||
|
if(userId != null) {
|
||||||
|
OutputApplicationUserInfo outputVerifyApplicationUser = new OutputApplicationUserInfo();
|
||||||
|
outputVerifyApplicationUser.setUserId(userId);
|
||||||
|
WFCMessage.User user = messagesStore.getUserInfo(userId);
|
||||||
|
if(user != null) {
|
||||||
|
outputVerifyApplicationUser.setDisplayName(user.getDisplayName());
|
||||||
|
outputVerifyApplicationUser.setPortraitUrl(user.getPortrait());
|
||||||
|
}
|
||||||
|
result = RestResult.ok(outputVerifyApplicationUser);
|
||||||
|
} else {
|
||||||
|
result = RestResult.resultOf(ErrorCode.ERROR_CODE_TOKEN_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setStatus(HttpResponseStatus.OK);
|
||||||
|
response.setContent(gson.toJson(result));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ public class BlockUserAction extends AdminAction {
|
|||||||
response.setStatus(HttpResponseStatus.OK);
|
response.setStatus(HttpResponseStatus.OK);
|
||||||
RestResult result;
|
RestResult result;
|
||||||
result = RestResult.resultOf(errorCode);
|
result = RestResult.resultOf(errorCode);
|
||||||
response.setContent(new Gson().toJson(result));
|
response.setContent(gson.toJson(result));
|
||||||
|
|
||||||
if (inputUserBlock.getStatus() == 2) {
|
if (inputUserBlock.getStatus() == 2) {
|
||||||
sendApiMessage(null, null, ServerAPIHelper.KICKOFF_USER_REQUEST, inputUserBlock.getUserId().getBytes(), null);
|
sendApiMessage(null, null, ServerAPIHelper.KICKOFF_USER_REQUEST, inputUserBlock.getUserId().getBytes(), null);
|
||||||
|
|||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Wildfire Chat package.
|
||||||
|
* (c) Heavyrain2012 <[email protected]>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.xiaoleilu.loServer.action.admin;
|
||||||
|
|
||||||
|
import cn.wildfirechat.common.APIPath;
|
||||||
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import cn.wildfirechat.pojos.InputChannelSubscribe;
|
||||||
|
import cn.wildfirechat.pojos.InputCreateChannel;
|
||||||
|
import cn.wildfirechat.pojos.InputSubscribeChannel;
|
||||||
|
import cn.wildfirechat.pojos.OutputCreateChannel;
|
||||||
|
import cn.wildfirechat.proto.ProtoConstants;
|
||||||
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.xiaoleilu.loServer.RestResult;
|
||||||
|
import com.xiaoleilu.loServer.action.channel.ChannelAction;
|
||||||
|
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||||
|
import com.xiaoleilu.loServer.annotation.Route;
|
||||||
|
import com.xiaoleilu.loServer.handler.Request;
|
||||||
|
import com.xiaoleilu.loServer.handler.Response;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
import win.liyufan.im.IMTopic;
|
||||||
|
|
||||||
|
@Route(APIPath.Subscribe_Channel)
|
||||||
|
@HttpMethod("POST")
|
||||||
|
public class ChannelSubscriberAction extends AdminAction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Request request, Response response) {
|
||||||
|
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||||
|
InputSubscribeChannel inputSubscribeChannel = getRequestBody(request.getNettyRequest(), InputSubscribeChannel.class);
|
||||||
|
if (inputSubscribeChannel != null
|
||||||
|
&& !io.netty.util.internal.StringUtil.isNullOrEmpty(inputSubscribeChannel.getChannelId())
|
||||||
|
&& !io.netty.util.internal.StringUtil.isNullOrEmpty(inputSubscribeChannel.getUserId())) {
|
||||||
|
|
||||||
|
WFCMessage.ListenChannel.Builder builder = WFCMessage.ListenChannel.newBuilder().setChannelId(inputSubscribeChannel.getChannelId()).setListen(inputSubscribeChannel.getSubscribe());
|
||||||
|
sendApiMessage(response, inputSubscribeChannel.getUserId(), IMTopic.ChannelListenTopic, builder.build().toByteArray(), result -> {
|
||||||
|
ErrorCode errorCode = ErrorCode.fromCode(result[0]);
|
||||||
|
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||||
|
return new Result(ErrorCode.ERROR_CODE_SUCCESS);
|
||||||
|
} else {
|
||||||
|
return new Result(errorCode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ public class CheckUserOnlineAction extends AdminAction {
|
|||||||
InputGetUserInfo inputUserId = getRequestBody(request.getNettyRequest(), InputGetUserInfo.class);
|
InputGetUserInfo inputUserId = getRequestBody(request.getNettyRequest(), InputGetUserInfo.class);
|
||||||
if (inputUserId == null || !StringUtil.isNullOrEmpty(inputUserId.getUserId())) {
|
if (inputUserId == null || !StringUtil.isNullOrEmpty(inputUserId.getUserId())) {
|
||||||
sendApiMessage(response, inputUserId.getUserId(), ServerAPIHelper.CHECK_USER_ONLINE_REQUEST, inputUserId.getUserId().getBytes(), res -> {
|
sendApiMessage(response, inputUserId.getUserId(), ServerAPIHelper.CHECK_USER_ONLINE_REQUEST, inputUserId.getUserId().getBytes(), res -> {
|
||||||
OutputCheckUserOnline out = new Gson().fromJson(new String(res), OutputCheckUserOnline.class);
|
OutputCheckUserOnline out = gson.fromJson(new String(res), OutputCheckUserOnline.class);
|
||||||
return new Result(ErrorCode.ERROR_CODE_SUCCESS, out);
|
return new Result(ErrorCode.ERROR_CODE_SUCCESS, out);
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Executable
+52
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Wildfire Chat package.
|
||||||
|
* (c) Heavyrain2012 <[email protected]>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.xiaoleilu.loServer.action.admin;
|
||||||
|
|
||||||
|
import cn.wildfirechat.common.APIPath;
|
||||||
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import cn.wildfirechat.pojos.InputChannelId;
|
||||||
|
import cn.wildfirechat.pojos.InputSubscribeChannel;
|
||||||
|
import cn.wildfirechat.pojos.OutputBooleanValue;
|
||||||
|
import cn.wildfirechat.pojos.OutputGetChannelInfo;
|
||||||
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
|
import com.xiaoleilu.loServer.RestResult;
|
||||||
|
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||||
|
import com.xiaoleilu.loServer.annotation.Route;
|
||||||
|
import com.xiaoleilu.loServer.handler.Request;
|
||||||
|
import com.xiaoleilu.loServer.handler.Response;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
|
@Route(APIPath.Check_User_Subscribe_Channel)
|
||||||
|
@HttpMethod("POST")
|
||||||
|
public class CheckUserSubscribeChannelAction extends AdminAction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTransactionAction() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Request request, Response response) {
|
||||||
|
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||||
|
InputSubscribeChannel inputSubscribeChannel = getRequestBody(request.getNettyRequest(), InputSubscribeChannel.class);
|
||||||
|
if (inputSubscribeChannel != null
|
||||||
|
&& !StringUtil.isNullOrEmpty(inputSubscribeChannel.getChannelId())
|
||||||
|
&& !StringUtil.isNullOrEmpty(inputSubscribeChannel.getUserId())) {
|
||||||
|
boolean isInChannel = messagesStore.checkUserInChannel(inputSubscribeChannel.getUserId(), inputSubscribeChannel.getChannelId());
|
||||||
|
OutputBooleanValue outputBooleanValue = new OutputBooleanValue();
|
||||||
|
outputBooleanValue.value = isInChannel;
|
||||||
|
setResponseContent(RestResult.ok(outputBooleanValue), response);
|
||||||
|
} else {
|
||||||
|
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ public class CreateChatroomAction extends AdminAction {
|
|||||||
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
|
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
|
||||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||||
String content = new String(bytes);
|
String content = new String(bytes);
|
||||||
Gson gson = new Gson();
|
|
||||||
InputCreateChatroom inputCreateChatroom = gson.fromJson(content, InputCreateChatroom.class);
|
InputCreateChatroom inputCreateChatroom = gson.fromJson(content, InputCreateChatroom.class);
|
||||||
if (inputCreateChatroom != null
|
if (inputCreateChatroom != null
|
||||||
&& !StringUtil.isNullOrEmpty(inputCreateChatroom.getTitle())) {
|
&& !StringUtil.isNullOrEmpty(inputCreateChatroom.getTitle())) {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class CreateRobotAction extends AdminAction {
|
|||||||
Utility.printExecption(LOG, e, IMExceptionEvent.EventType.ADMIN_API_Exception);
|
Utility.printExecption(LOG, e, IMExceptionEvent.EventType.ADMIN_API_Exception);
|
||||||
response.setStatus(HttpResponseStatus.OK);
|
response.setStatus(HttpResponseStatus.OK);
|
||||||
RestResult result = RestResult.resultOf(ErrorCode.ERROR_CODE_SERVER_ERROR, e.getMessage());
|
RestResult result = RestResult.resultOf(ErrorCode.ERROR_CODE_SERVER_ERROR, e.getMessage());
|
||||||
response.setContent(new Gson().toJson(result));
|
response.setContent(gson.toJson(result));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class DestoryChatroomAction extends AdminAction {
|
|||||||
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
|
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
|
||||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||||
String content = new String(bytes);
|
String content = new String(bytes);
|
||||||
Gson gson = new Gson();
|
|
||||||
InputDestoryChatroom inputDestoryChatroom = gson.fromJson(content, InputDestoryChatroom.class);
|
InputDestoryChatroom inputDestoryChatroom = gson.fromJson(content, InputDestoryChatroom.class);
|
||||||
if (inputDestoryChatroom != null
|
if (inputDestoryChatroom != null
|
||||||
&& !StringUtil.isNullOrEmpty(inputDestoryChatroom.getChatroomId())) {
|
&& !StringUtil.isNullOrEmpty(inputDestoryChatroom.getChatroomId())) {
|
||||||
|
|||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Wildfire Chat package.
|
||||||
|
* (c) Heavyrain2012 <[email protected]>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.xiaoleilu.loServer.action.admin;
|
||||||
|
|
||||||
|
import cn.wildfirechat.common.APIPath;
|
||||||
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import cn.wildfirechat.pojos.InputMessageUid;
|
||||||
|
import cn.wildfirechat.pojos.OutputMessageData;
|
||||||
|
import cn.wildfirechat.pojos.RecallMessageData;
|
||||||
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
|
import com.xiaoleilu.loServer.RestResult;
|
||||||
|
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||||
|
import com.xiaoleilu.loServer.annotation.Route;
|
||||||
|
import com.xiaoleilu.loServer.handler.Request;
|
||||||
|
import com.xiaoleilu.loServer.handler.Response;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
import win.liyufan.im.IMTopic;
|
||||||
|
|
||||||
|
@Route(APIPath.Msg_GetOne)
|
||||||
|
@HttpMethod("POST")
|
||||||
|
public class GetMessageAction extends AdminAction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTransactionAction() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Request request, Response response) {
|
||||||
|
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||||
|
InputMessageUid inputMessageUid = getRequestBody(request.getNettyRequest(), InputMessageUid.class);
|
||||||
|
if (inputMessageUid != null && inputMessageUid.messageUid > 0) {
|
||||||
|
RestResult result;
|
||||||
|
WFCMessage.Message msg = messagesStore.getMessage(inputMessageUid.messageUid);
|
||||||
|
if(msg == null) {
|
||||||
|
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
|
||||||
|
} else {
|
||||||
|
result = RestResult.ok(OutputMessageData.fromProtoMessage(msg));
|
||||||
|
}
|
||||||
|
setResponseContent(result, response);
|
||||||
|
} else {
|
||||||
|
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Wildfire Chat package.
|
||||||
|
* (c) Heavyrain2012 <[email protected]>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.xiaoleilu.loServer.action.admin;
|
||||||
|
|
||||||
|
import cn.wildfirechat.common.APIPath;
|
||||||
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import cn.wildfirechat.pojos.InputGetUserInfo;
|
||||||
|
import cn.wildfirechat.pojos.InputOutputUserInfo;
|
||||||
|
import cn.wildfirechat.pojos.InputRobotId;
|
||||||
|
import cn.wildfirechat.pojos.OutputRobot;
|
||||||
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
|
import com.xiaoleilu.loServer.RestResult;
|
||||||
|
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||||
|
import com.xiaoleilu.loServer.annotation.Route;
|
||||||
|
import com.xiaoleilu.loServer.handler.Request;
|
||||||
|
import com.xiaoleilu.loServer.handler.Response;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
|
@Route(APIPath.User_Get_Robot_Info)
|
||||||
|
@HttpMethod("POST")
|
||||||
|
public class GetRobotAction extends AdminAction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTransactionAction() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Request request, Response response) {
|
||||||
|
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||||
|
InputRobotId inputUserId = getRequestBody(request.getNettyRequest(), InputRobotId.class);
|
||||||
|
if (inputUserId != null
|
||||||
|
&& (!StringUtil.isNullOrEmpty(inputUserId.getRobotId()))) {
|
||||||
|
|
||||||
|
WFCMessage.User user = messagesStore.getUserInfo(inputUserId.getRobotId());
|
||||||
|
WFCMessage.Robot robot = messagesStore.getRobot(inputUserId.getRobotId());
|
||||||
|
|
||||||
|
RestResult result;
|
||||||
|
if (user == null || robot == null) {
|
||||||
|
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
|
||||||
|
} else {
|
||||||
|
OutputRobot outputRobot = new OutputRobot();
|
||||||
|
outputRobot.fromUser(user);
|
||||||
|
outputRobot.fromRobot(robot, true);
|
||||||
|
result = RestResult.ok(outputRobot);
|
||||||
|
}
|
||||||
|
|
||||||
|
setResponseContent(result, response);
|
||||||
|
} else {
|
||||||
|
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -50,7 +50,7 @@ public class ModifyGroupMemberExtraAction extends AdminAction {
|
|||||||
} else {
|
} else {
|
||||||
response.setStatus(HttpResponseStatus.OK);
|
response.setStatus(HttpResponseStatus.OK);
|
||||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||||
response.setContent(new Gson().toJson(result));
|
response.setContent(gson.toJson(result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Executable
+59
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Wildfire Chat package.
|
||||||
|
* (c) Heavyrain2012 <[email protected]>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.xiaoleilu.loServer.action.channel;
|
||||||
|
|
||||||
|
import cn.wildfirechat.common.APIPath;
|
||||||
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import cn.wildfirechat.pojos.InputApplicationGetUserInfo;
|
||||||
|
import cn.wildfirechat.pojos.OutputApplicationUserInfo;
|
||||||
|
import cn.wildfirechat.proto.ProtoConstants;
|
||||||
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.xiaoleilu.loServer.RestResult;
|
||||||
|
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||||
|
import com.xiaoleilu.loServer.annotation.Route;
|
||||||
|
import com.xiaoleilu.loServer.handler.Request;
|
||||||
|
import com.xiaoleilu.loServer.handler.Response;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
|
@Route(APIPath.Channel_Application_Get_UserInfo)
|
||||||
|
@HttpMethod("POST")
|
||||||
|
public class ApplicationGetUserInfoAction extends ChannelAction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Request request, Response response) {
|
||||||
|
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||||
|
InputApplicationGetUserInfo inputUserToken = getRequestBody(request.getNettyRequest(), InputApplicationGetUserInfo.class);
|
||||||
|
RestResult result;
|
||||||
|
if (inputUserToken == null || StringUtil.isNullOrEmpty(inputUserToken.getAuthCode())) {
|
||||||
|
result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||||
|
} else {
|
||||||
|
String userId = messagesStore.verifyApplicationAuthCode(inputUserToken.getAuthCode(), channelInfo.getTargetId(), ProtoConstants.ApplicationType.ApplicationType_Channel);
|
||||||
|
if(userId != null) {
|
||||||
|
OutputApplicationUserInfo outputVerifyApplicationUser = new OutputApplicationUserInfo();
|
||||||
|
outputVerifyApplicationUser.setUserId(userId);
|
||||||
|
WFCMessage.User user = messagesStore.getUserInfo(userId);
|
||||||
|
if(user != null) {
|
||||||
|
outputVerifyApplicationUser.setDisplayName(user.getDisplayName());
|
||||||
|
outputVerifyApplicationUser.setPortraitUrl(user.getPortrait());
|
||||||
|
}
|
||||||
|
result = RestResult.ok(outputVerifyApplicationUser);
|
||||||
|
} else {
|
||||||
|
result = RestResult.resultOf(ErrorCode.ERROR_CODE_APPLICATION_TOKEN_ERROR_OR_TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setStatus(HttpResponseStatus.OK);
|
||||||
|
response.setContent(gson.toJson(result));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,7 +101,7 @@ abstract public class ChannelAction extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
||||||
response.setContent(new Gson().toJson(result));
|
response.setContent(gson.toJson(result));
|
||||||
response.send();
|
response.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ abstract public class ChannelAction extends Action {
|
|||||||
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
|
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
|
||||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||||
String content = new String(bytes, StandardCharsets.UTF_8);
|
String content = new String(bytes, StandardCharsets.UTF_8);
|
||||||
Gson gson = new Gson();
|
|
||||||
T t = gson.fromJson(content, cls);
|
T t = gson.fromJson(content, cls);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ public class SubscriberChannelAction extends ChannelAction {
|
|||||||
if(input.getSubscribe() > 0 && (channelInfo.getStatus() & ProtoConstants.ChannelState.Channel_State_Mask_Active_Subscribe) == 0) {
|
if(input.getSubscribe() > 0 && (channelInfo.getStatus() & ProtoConstants.ChannelState.Channel_State_Mask_Active_Subscribe) == 0) {
|
||||||
response.setStatus(HttpResponseStatus.OK);
|
response.setStatus(HttpResponseStatus.OK);
|
||||||
RestResult result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_RIGHT);
|
RestResult result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_RIGHT);
|
||||||
response.setContent(new Gson().toJson(result));
|
response.setContent(gson.toJson(result));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
WFCMessage.ListenChannel listenChannel = WFCMessage.ListenChannel.newBuilder().setChannelId(channelInfo.getTargetId()).setListen(input.getSubscribe()).build();
|
WFCMessage.ListenChannel listenChannel = WFCMessage.ListenChannel.newBuilder().setChannelId(channelInfo.getTargetId()).setListen(input.getSubscribe()).build();
|
||||||
|
|||||||
Executable
+59
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Wildfire Chat package.
|
||||||
|
* (c) Heavyrain2012 <[email protected]>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.xiaoleilu.loServer.action.robot;
|
||||||
|
|
||||||
|
import cn.wildfirechat.common.APIPath;
|
||||||
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import cn.wildfirechat.pojos.InputApplicationGetUserInfo;
|
||||||
|
import cn.wildfirechat.pojos.OutputApplicationUserInfo;
|
||||||
|
import cn.wildfirechat.proto.ProtoConstants;
|
||||||
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.xiaoleilu.loServer.RestResult;
|
||||||
|
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||||
|
import com.xiaoleilu.loServer.annotation.Route;
|
||||||
|
import com.xiaoleilu.loServer.handler.Request;
|
||||||
|
import com.xiaoleilu.loServer.handler.Response;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
|
@Route(APIPath.Robot_Application_Get_UserInfo)
|
||||||
|
@HttpMethod("POST")
|
||||||
|
public class ApplicationGetUserInfoAction extends RobotAction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Request request, Response response) {
|
||||||
|
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||||
|
InputApplicationGetUserInfo inputUserToken = getRequestBody(request.getNettyRequest(), InputApplicationGetUserInfo.class);
|
||||||
|
RestResult result;
|
||||||
|
if (inputUserToken == null || StringUtil.isNullOrEmpty(inputUserToken.getAuthCode())) {
|
||||||
|
result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||||
|
} else {
|
||||||
|
String userId = messagesStore.verifyApplicationAuthCode(inputUserToken.getAuthCode(), robot.getUid(), ProtoConstants.ApplicationType.ApplicationType_Robot);
|
||||||
|
if(userId != null) {
|
||||||
|
OutputApplicationUserInfo outputVerifyApplicationUser = new OutputApplicationUserInfo();
|
||||||
|
outputVerifyApplicationUser.setUserId(userId);
|
||||||
|
WFCMessage.User user = messagesStore.getUserInfo(userId);
|
||||||
|
if(user != null) {
|
||||||
|
outputVerifyApplicationUser.setDisplayName(user.getDisplayName());
|
||||||
|
outputVerifyApplicationUser.setPortraitUrl(user.getPortrait());
|
||||||
|
}
|
||||||
|
result = RestResult.ok(outputVerifyApplicationUser);
|
||||||
|
} else {
|
||||||
|
result = RestResult.resultOf(ErrorCode.ERROR_CODE_APPLICATION_TOKEN_ERROR_OR_TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setStatus(HttpResponseStatus.OK);
|
||||||
|
response.setContent(gson.toJson(result));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Wildfire Chat package.
|
||||||
|
* (c) Heavyrain2012 <[email protected]>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.xiaoleilu.loServer.action.robot;
|
||||||
|
|
||||||
|
import cn.wildfirechat.common.APIPath;
|
||||||
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import cn.wildfirechat.pojos.InputGetUserInfo;
|
||||||
|
import cn.wildfirechat.pojos.InputOutputUserInfo;
|
||||||
|
import cn.wildfirechat.pojos.OutputRobot;
|
||||||
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
|
import com.xiaoleilu.loServer.RestResult;
|
||||||
|
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||||
|
import com.xiaoleilu.loServer.annotation.Route;
|
||||||
|
import com.xiaoleilu.loServer.handler.Request;
|
||||||
|
import com.xiaoleilu.loServer.handler.Response;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
|
@Route(APIPath.Robot_Get_Profile)
|
||||||
|
@HttpMethod("POST")
|
||||||
|
public class GetProfileAction extends RobotAction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Request request, Response response) {
|
||||||
|
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||||
|
WFCMessage.User user = messagesStore.getUserInfo(robot.getUid());
|
||||||
|
RestResult result;
|
||||||
|
if (user == null) {
|
||||||
|
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
|
||||||
|
} else {
|
||||||
|
OutputRobot outputRobot = new OutputRobot();
|
||||||
|
outputRobot.fromUser(user);
|
||||||
|
outputRobot.fromRobot(robot, false);
|
||||||
|
result = RestResult.ok(outputRobot);
|
||||||
|
}
|
||||||
|
setResponseContent(result, response);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -53,7 +53,7 @@ public class ModifyGroupMemberExtraAction extends RobotAction {
|
|||||||
} else {
|
} else {
|
||||||
response.setStatus(HttpResponseStatus.OK);
|
response.setStatus(HttpResponseStatus.OK);
|
||||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||||
response.setContent(new Gson().toJson(result));
|
response.setContent(gson.toJson(result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ abstract public class RobotAction extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
||||||
response.setContent(new Gson().toJson(result));
|
response.setContent(gson.toJson(result));
|
||||||
response.send();
|
response.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ abstract public class RobotAction extends Action {
|
|||||||
String content = null;
|
String content = null;
|
||||||
try {
|
try {
|
||||||
content = new String(bytes, "UTF-8");
|
content = new String(bytes, "UTF-8");
|
||||||
Gson gson = new Gson();
|
|
||||||
T t = gson.fromJson(content, cls);
|
T t = gson.fromJson(content, cls);
|
||||||
return t;
|
return t;
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
|||||||
@@ -114,6 +114,13 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
|
|||||||
handleOptions(ctx, request, response);
|
handleOptions(ctx, request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("HEAD".equalsIgnoreCase(request.getMethod())) {
|
||||||
|
response.setStatus(HttpResponseStatus.OK);
|
||||||
|
response.send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Action action;
|
Action action;
|
||||||
if (isValidePath(request.getPath())) {
|
if (isValidePath(request.getPath())) {
|
||||||
action = ServerSetting.getAction(request.getPath(), request.getMethod().toUpperCase());
|
action = ServerSetting.getAction(request.getPath(), request.getMethod().toUpperCase());
|
||||||
|
|||||||
@@ -81,18 +81,6 @@ public class Request {
|
|||||||
|
|
||||||
// request URI parameters
|
// request URI parameters
|
||||||
this.putParams(new QueryStringDecoder(uri));
|
this.putParams(new QueryStringDecoder(uri));
|
||||||
if(nettyRequest.method() != HttpMethod.GET){
|
|
||||||
HttpPostRequestDecoder decoder = null;
|
|
||||||
try {
|
|
||||||
decoder = new HttpPostRequestDecoder(HTTP_DATA_FACTORY, nettyRequest);
|
|
||||||
this.putParams(decoder);
|
|
||||||
} finally {
|
|
||||||
if(null != decoder){
|
|
||||||
decoder.destroy();
|
|
||||||
decoder = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IP
|
// IP
|
||||||
this.putIp(ctx);
|
this.putIp(ctx);
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import io.netty.handler.codec.http.LastHttpContent;
|
|||||||
import io.netty.handler.codec.http.cookie.Cookie;
|
import io.netty.handler.codec.http.cookie.Cookie;
|
||||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||||
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
|
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
|
||||||
|
import org.apache.tika.Tika;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -469,7 +470,9 @@ public class Response {
|
|||||||
this.setContentLength(fileLength);
|
this.setContentLength(fileLength);
|
||||||
|
|
||||||
//文件类型
|
//文件类型
|
||||||
String contentType = HttpUtil.getMimeType(file.getName());
|
Tika tika = new Tika();
|
||||||
|
String contentType = tika.detect(file);
|
||||||
|
// String contentType = HttpUtil.getMimeType(file.getName());
|
||||||
if(StrUtil.isBlank(contentType)){
|
if(StrUtil.isBlank(contentType)){
|
||||||
//无法识别默认使用数据流
|
//无法识别默认使用数据流
|
||||||
contentType = "application/octet-stream";
|
contentType = "application/octet-stream";
|
||||||
|
|||||||
@@ -120,16 +120,19 @@ public final class BrokerConstants {
|
|||||||
public static final String SENSITIVE_Remote_Fail_When_Matched = "sensitive.remote_fail_when_matched";
|
public static final String SENSITIVE_Remote_Fail_When_Matched = "sensitive.remote_fail_when_matched";
|
||||||
|
|
||||||
public static final String MESSAGE_Forward_Url = "message.forward.url";
|
public static final String MESSAGE_Forward_Url = "message.forward.url";
|
||||||
|
public static final String MESSAGE_Sensitive_Forward_Url = "message.sensitive.forward.url";
|
||||||
public static final String MESSAGE_Forward_Types = "message.forward.types";
|
public static final String MESSAGE_Forward_Types = "message.forward.types";
|
||||||
public static final String MESSAGE_MentionMsg_Forward_Url = "message.mentionmsg.forward.url";
|
public static final String MESSAGE_MentionMsg_Forward_Url = "message.mentionmsg.forward.url";
|
||||||
|
|
||||||
|
|
||||||
public static final String SERVER_MULTI_ENDPOINT = "server.multi_endpoint";
|
public static final String SERVER_MULTI_ENDPOINT = "server.multi_endpoint";
|
||||||
|
public static final String SERVER_MULTI_PC_ENDPOINT = "server.multi_pc_endpoint";
|
||||||
public static final String SERVER_MULTI_PLATFROM_NOTIFICATION = "server.multi_platform_notification";
|
public static final String SERVER_MULTI_PLATFROM_NOTIFICATION = "server.multi_platform_notification";
|
||||||
public static final String SERVER_MOBILE_DEFAULT_SILENT_WHEN_PC_ONLINE = "server.mobile_default_silent_when_pc_online";
|
public static final String SERVER_MOBILE_DEFAULT_SILENT_WHEN_PC_ONLINE = "server.mobile_default_silent_when_pc_online";
|
||||||
public static final String SERVER_CLIENT_SUPPORT_KICKOFF_EVENT = "server.client_support_kickoff_event";
|
public static final String SERVER_CLIENT_SUPPORT_KICKOFF_EVENT = "server.client_support_kickoff_event";
|
||||||
|
|
||||||
public static final String MESSAGE_ROAMING = "message.roaming";
|
public static final String MESSAGE_ROAMING = "message.roaming";
|
||||||
|
public static final String MESSAGE_Compensate_Time_Limit = "message.compensate_time_limit";
|
||||||
public static final String MESSAGE_Remote_History_Message = "message.remote_history_message";
|
public static final String MESSAGE_Remote_History_Message = "message.remote_history_message";
|
||||||
public static final String MESSAGE_Remote_Chatroom_History_Message = "message.chatroom_remote_history_message";
|
public static final String MESSAGE_Remote_Chatroom_History_Message = "message.chatroom_remote_history_message";
|
||||||
|
|
||||||
@@ -141,6 +144,10 @@ public final class BrokerConstants {
|
|||||||
|
|
||||||
public static final String MESSAGE_NO_Forward_Admin_Message = "message.no_forward_admin_message";
|
public static final String MESSAGE_NO_Forward_Admin_Message = "message.no_forward_admin_message";
|
||||||
|
|
||||||
|
public static final String MESSAGE_Forward_With_Client_Info = "message.forward_with_client_info";
|
||||||
|
public static final String ROBOT_Callback_With_Client_Info = "robot.callback_with_client_info";
|
||||||
|
public static final String CHANNEL_Callback_With_Client_Info = "channel.callback_with_client_info";
|
||||||
|
|
||||||
public static final String FRIEND_Disable_Search = "friend.disable_search";
|
public static final String FRIEND_Disable_Search = "friend.disable_search";
|
||||||
public static final String FRIEND_Disable_NickName_Search = "friend.disable_nick_name_search";
|
public static final String FRIEND_Disable_NickName_Search = "friend.disable_nick_name_search";
|
||||||
public static final String FRIEND_Disable_Friend_Request = "friend.disable_friend_request";
|
public static final String FRIEND_Disable_Friend_Request = "friend.disable_friend_request";
|
||||||
@@ -160,6 +167,8 @@ public final class BrokerConstants {
|
|||||||
public static final String GROUP_Visible_Quit_Kickoff_Notification = "group.visible_quit_or_kickoff_notification";
|
public static final String GROUP_Visible_Quit_Kickoff_Notification = "group.visible_quit_or_kickoff_notification";
|
||||||
|
|
||||||
public static final String USER_HIDE_PROPERTIES = "user.hide_properties";
|
public static final String USER_HIDE_PROPERTIES = "user.hide_properties";
|
||||||
|
public static final String USER_KEEP_DISPLAY_NAME_WHEN_DESTROY = "user.keep_display_name_when_destroy";
|
||||||
|
|
||||||
public static final String SYNC_Data_Part_Size = "sync.data_part_size";
|
public static final String SYNC_Data_Part_Size = "sync.data_part_size";
|
||||||
|
|
||||||
public static final String MESSAGES_FORBIDDEN_CLIENT_SEND_TYPES = "message.forbidden_client_send_types";
|
public static final String MESSAGES_FORBIDDEN_CLIENT_SEND_TYPES = "message.forbidden_client_send_types";
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ public class ChannelListenMember extends IMHandler<WFCMessage.ListenChannel> {
|
|||||||
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||||
WFCMessage.ModifyUserSettingReq modifyUserSettingReq = WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingListenedChannels).setKey(request.getChannelId()).setValue(request.getListen() > 0 ? "1" : "0").build();
|
WFCMessage.ModifyUserSettingReq modifyUserSettingReq = WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingListenedChannels).setKey(request.getChannelId()).setValue(request.getListen() > 0 ? "1" : "0").build();
|
||||||
mServer.onApiMessage(fromUser, null, modifyUserSettingReq.toByteArray(), 0, fromUser, PutUserSettingTopic, requestSourceType);
|
mServer.onApiMessage(fromUser, null, modifyUserSettingReq.toByteArray(), 0, fromUser, PutUserSettingTopic, requestSourceType);
|
||||||
|
|
||||||
publisher.notifyChannelListenStatusChanged(m_messagesStore.getChannelInfo(request.getChannelId()), fromUser, request.getListen() > 0);
|
|
||||||
}
|
}
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Wildfire Chat package.
|
||||||
|
* (c) Heavyrain2012 <[email protected]>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.moquette.imhandler;
|
||||||
|
|
||||||
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import cn.wildfirechat.proto.ProtoConstants;
|
||||||
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
|
import io.moquette.spi.impl.Qos1PublishHandler;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import win.liyufan.im.IMTopic;
|
||||||
|
|
||||||
|
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
|
|
||||||
|
@Handler(IMTopic.ApplicationConfigRequestTopic)
|
||||||
|
public class ConfigApplicationHandler extends IMHandler<WFCMessage.ApplicationConfigRequest> {
|
||||||
|
@Override
|
||||||
|
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.ApplicationConfigRequest request, Qos1PublishHandler.IMCallback callback) {
|
||||||
|
ErrorCode errorCode = m_messagesStore.configApplication(request.getAppId(), request.getAppType(), request.getTimestamp(), request.getNonce(), request.getSignature());
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -21,6 +21,9 @@ import win.liyufan.im.IMTopic;
|
|||||||
public class CreateGroupHandler extends GroupHandler<WFCMessage.CreateGroupRequest> {
|
public class CreateGroupHandler extends GroupHandler<WFCMessage.CreateGroupRequest> {
|
||||||
@Override
|
@Override
|
||||||
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.CreateGroupRequest request, Qos1PublishHandler.IMCallback callback) {
|
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.CreateGroupRequest request, Qos1PublishHandler.IMCallback callback) {
|
||||||
|
if(request.getGroup().getGroupInfo().getType() < 0 || request.getGroup().getGroupInfo().getType() > 2) {
|
||||||
|
return ErrorCode.ERROR_CODE_INVALID_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
if (!StringUtil.isNullOrEmpty(request.getGroup().getGroupInfo().getTargetId())) {
|
if (!StringUtil.isNullOrEmpty(request.getGroup().getGroupInfo().getTargetId())) {
|
||||||
WFCMessage.GroupInfo existGroupInfo = m_messagesStore.getGroupInfo(request.getGroup().getGroupInfo().getTargetId());
|
WFCMessage.GroupInfo existGroupInfo = m_messagesStore.getGroupInfo(request.getGroup().getGroupInfo().getTargetId());
|
||||||
|
|||||||
+2
-2
@@ -19,11 +19,11 @@ import static win.liyufan.im.IMTopic.PutUserSettingTopic;
|
|||||||
import static win.liyufan.im.UserSettingScope.kUserSettingMyChannels;
|
import static win.liyufan.im.UserSettingScope.kUserSettingMyChannels;
|
||||||
|
|
||||||
@Handler(value = IMTopic.DestroyChannelInfoTopic)
|
@Handler(value = IMTopic.DestroyChannelInfoTopic)
|
||||||
public class DistroyChannelHandler extends GroupHandler<WFCMessage.IDBuf> {
|
public class DestroyChannelHandler extends GroupHandler<WFCMessage.IDBuf> {
|
||||||
@Override
|
@Override
|
||||||
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.IDBuf request, Qos1PublishHandler.IMCallback callback) {
|
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.IDBuf request, Qos1PublishHandler.IMCallback callback) {
|
||||||
boolean isAdmin = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin;
|
boolean isAdmin = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin;
|
||||||
ErrorCode errorCode = m_messagesStore.distoryChannel(fromUser, request.getId(), isAdmin);
|
ErrorCode errorCode = m_messagesStore.destroyChannel(fromUser, request.getId(), isAdmin);
|
||||||
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||||
WFCMessage.ModifyUserSettingReq modifyUserSettingReq = WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingMyChannels).setKey(request.getId()).setValue("0").build();
|
WFCMessage.ModifyUserSettingReq modifyUserSettingReq = WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingMyChannels).setKey(request.getId()).setValue("0").build();
|
||||||
mServer.onApiMessage(fromUser, null, modifyUserSettingReq.toByteArray(), 0, fromUser, PutUserSettingTopic, requestSourceType);
|
mServer.onApiMessage(fromUser, null, modifyUserSettingReq.toByteArray(), 0, fromUser, PutUserSettingTopic, requestSourceType);
|
||||||
@@ -11,9 +11,7 @@ package io.moquette.imhandler;
|
|||||||
import cn.wildfirechat.common.ErrorCode;
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
import cn.wildfirechat.proto.ProtoConstants;
|
import cn.wildfirechat.proto.ProtoConstants;
|
||||||
import cn.wildfirechat.proto.WFCMessage;
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
import io.moquette.persistence.MemorySessionStore;
|
|
||||||
import io.moquette.spi.impl.Qos1PublishHandler;
|
import io.moquette.spi.impl.Qos1PublishHandler;
|
||||||
import io.moquette.spi.impl.security.TokenAuthenticator;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import win.liyufan.im.IMTopic;
|
import win.liyufan.im.IMTopic;
|
||||||
|
|
||||||
@@ -30,7 +28,7 @@ public class DestroyUserHandler extends IMHandler<WFCMessage.IDBuf> {
|
|||||||
m_messagesStore.clearUserFriend(fromUser);
|
m_messagesStore.clearUserFriend(fromUser);
|
||||||
m_messagesStore.clearUserGroups(fromUser);
|
m_messagesStore.clearUserGroups(fromUser);
|
||||||
m_messagesStore.clearUserChannels(fromUser);
|
m_messagesStore.clearUserChannels(fromUser);
|
||||||
m_messagesStore.destoryUser(fromUser);
|
m_messagesStore.destroyUser(fromUser);
|
||||||
|
|
||||||
m_messagesStore.destroyRobot(fromUser);
|
m_messagesStore.destroyRobot(fromUser);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Wildfire Chat package.
|
||||||
|
* (c) Heavyrain2012 <[email protected]>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.moquette.imhandler;
|
||||||
|
|
||||||
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import cn.wildfirechat.proto.ProtoConstants;
|
||||||
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
|
import io.moquette.spi.impl.Qos1PublishHandler;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import win.liyufan.im.IMTopic;
|
||||||
|
|
||||||
|
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
|
|
||||||
|
@Handler(IMTopic.GetApplicationTokenRequestTopic)
|
||||||
|
public class GetApplicationTokenHandler extends IMHandler<WFCMessage.AuthCodeRequest> {
|
||||||
|
@Override
|
||||||
|
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.AuthCodeRequest request, Qos1PublishHandler.IMCallback callback) {
|
||||||
|
String authCode = m_messagesStore.getApplicationAuthCode(fromUser, request.getTargetId(), request.getType(), request.getHost());
|
||||||
|
if(authCode != null) {
|
||||||
|
byte[] data = WFCMessage.IDBuf.newBuilder().setId(authCode).build().toByteArray();
|
||||||
|
ackPayload.ensureWritable(data.length).writeBytes(data);
|
||||||
|
return ERROR_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
return ErrorCode.INVALID_PARAMETER;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ public class GetQiniuUploadTokenHandler extends IMHandler<Byte> {
|
|||||||
String token;
|
String token;
|
||||||
|
|
||||||
WFCMessage.GetUploadTokenResult.Builder resultBuilder = WFCMessage.GetUploadTokenResult.newBuilder();
|
WFCMessage.GetUploadTokenResult.Builder resultBuilder = WFCMessage.GetUploadTokenResult.newBuilder();
|
||||||
|
|
||||||
if (MediaServerConfig.USER_QINIU) {
|
if (MediaServerConfig.USER_QINIU) {
|
||||||
Auth auth = Auth.create(MediaServerConfig.QINIU_ACCESS_KEY, MediaServerConfig.QINIU_SECRET_KEY);
|
Auth auth = Auth.create(MediaServerConfig.QINIU_ACCESS_KEY, MediaServerConfig.QINIU_SECRET_KEY);
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class HandleFriendRequestHandler extends IMHandler<WFCMessage.HandleFrien
|
|||||||
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.HandleFriendRequest request, Qos1PublishHandler.IMCallback callback) {
|
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.HandleFriendRequest request, Qos1PublishHandler.IMCallback callback) {
|
||||||
WFCMessage.Message.Builder builder = WFCMessage.Message.newBuilder();
|
WFCMessage.Message.Builder builder = WFCMessage.Message.newBuilder();
|
||||||
builder.setFromUser(request.getTargetUid());
|
builder.setFromUser(request.getTargetUid());
|
||||||
long[] heads = new long[2];
|
long[] heads = new long[4];
|
||||||
boolean isAdmin = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin;
|
boolean isAdmin = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin;
|
||||||
ErrorCode errorCode = m_messagesStore.handleFriendRequest(fromUser, request, builder, heads, isAdmin);
|
ErrorCode errorCode = m_messagesStore.handleFriendRequest(fromUser, request, builder, heads, isAdmin);
|
||||||
|
|
||||||
@@ -84,6 +84,14 @@ public class HandleFriendRequestHandler extends IMHandler<WFCMessage.HandleFrien
|
|||||||
publisher.publishNotification(IMTopic.NotifyFriendTopic, request.getTargetUid(), heads[0]);
|
publisher.publishNotification(IMTopic.NotifyFriendTopic, request.getTargetUid(), heads[0]);
|
||||||
publisher.publishNotification(IMTopic.NotifyFriendTopic, fromUser, heads[1]);
|
publisher.publishNotification(IMTopic.NotifyFriendTopic, fromUser, heads[1]);
|
||||||
}
|
}
|
||||||
|
if(!isAdmin) {
|
||||||
|
if(heads[2] > 0) {
|
||||||
|
publisher.publishNotification(IMTopic.NotifyFriendRequestTopic, request.getTargetUid(), heads[2], fromUser, null);
|
||||||
|
}
|
||||||
|
if(heads[3] > 0) {
|
||||||
|
publisher.publishNotification(IMTopic.NotifyFriendRequestTopic, fromUser, heads[3], fromUser, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(errorCode == ERROR_CODE_ALREADY_FRIENDS) {
|
if(errorCode == ERROR_CODE_ALREADY_FRIENDS) {
|
||||||
errorCode = ERROR_CODE_SUCCESS;
|
errorCode = ERROR_CODE_SUCCESS;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import io.netty.util.internal.StringUtil;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import cn.wildfirechat.common.ErrorCode;
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import win.liyufan.im.GsonUtil;
|
||||||
import win.liyufan.im.RateLimiter;
|
import win.liyufan.im.RateLimiter;
|
||||||
import win.liyufan.im.Utility;
|
import win.liyufan.im.Utility;
|
||||||
|
|
||||||
@@ -39,6 +40,8 @@ import java.util.*;
|
|||||||
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
||||||
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
|
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
import static io.moquette.BrokerConstants.CLIENT_REQUEST_RATE_LIMIT;
|
import static io.moquette.BrokerConstants.CLIENT_REQUEST_RATE_LIMIT;
|
||||||
|
import static win.liyufan.im.IMTopic.GetUserSettingTopic;
|
||||||
|
import static win.liyufan.im.IMTopic.PullMessageTopic;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求处理接口<br>
|
* 请求处理接口<br>
|
||||||
@@ -139,7 +142,7 @@ abstract public class IMHandler<T> {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
//json ?
|
//json ?
|
||||||
return (T)(new Gson().fromJson(new String(bytes), dataCls));
|
return (T)(GsonUtil.gson.fromJson(new String(bytes), dataCls));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init(IMessagesStore ms, ISessionsStore ss, MessagesPublisher p, ThreadPoolExecutorWrapper businessExecutor, Server server) {
|
public static void init(IMessagesStore ms, ISessionsStore ss, MessagesPublisher p, ThreadPoolExecutorWrapper businessExecutor, Server server) {
|
||||||
@@ -162,18 +165,17 @@ abstract public class IMHandler<T> {
|
|||||||
mLimitCounter = new RateLimiter(5, clientRateLimit);
|
mLimitCounter = new RateLimiter(5, clientRateLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isNotLimitTopic(String topic) {
|
||||||
|
if (PullMessageTopic.equals(topic)
|
||||||
|
|| GetUserSettingTopic.equals(topic)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public ErrorCode preAction(String clientID, String fromUser, String topic, Qos1PublishHandler.IMCallback callback, ProtoConstants.RequestSourceType requestSourceType) {
|
public ErrorCode preAction(String clientID, String fromUser, String topic, Qos1PublishHandler.IMCallback callback, ProtoConstants.RequestSourceType requestSourceType) {
|
||||||
LOG.info("imHandler fromUser={}, clientId={}, topic={}", fromUser, clientID, topic);
|
LOG.info("imHandler fromUser={}, clientId={}, topic={}", fromUser, clientID, topic);
|
||||||
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User && !mLimitCounter.isGranted(clientID + fromUser + topic)) {
|
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User && !isNotLimitTopic(topic) && !mLimitCounter.isGranted(clientID + fromUser + topic)) {
|
||||||
ByteBuf ackPayload = Unpooled.buffer();
|
|
||||||
ackPayload.ensureWritable(1).writeByte(ERROR_CODE_OVER_FREQUENCY.getCode());
|
|
||||||
try {
|
|
||||||
callback.onIMHandled(ERROR_CODE_OVER_FREQUENCY, ackPayload);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Utility.printExecption(LOG, e);
|
|
||||||
}
|
|
||||||
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
||||||
}
|
}
|
||||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class RecallMessageHandler extends IMHandler<WFCMessage.INT64Buf> {
|
|||||||
return ErrorCode.ERROR_CODE_NOT_EXIST;
|
return ErrorCode.ERROR_CODE_NOT_EXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
publish(fromUser, clientID, message, requestSourceType);
|
publish(message.getFromUser(), clientID, message, requestSourceType);
|
||||||
|
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,19 +9,18 @@
|
|||||||
package io.moquette.imhandler;
|
package io.moquette.imhandler;
|
||||||
|
|
||||||
import cn.wildfirechat.pojos.MessagePayload;
|
import cn.wildfirechat.pojos.MessagePayload;
|
||||||
|
import cn.wildfirechat.pojos.OutputClient;
|
||||||
import cn.wildfirechat.proto.ProtoConstants;
|
import cn.wildfirechat.proto.ProtoConstants;
|
||||||
import cn.wildfirechat.proto.WFCMessage;
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.hazelcast.util.StringUtil;
|
import com.hazelcast.util.StringUtil;
|
||||||
import io.moquette.BrokerConstants;
|
import io.moquette.BrokerConstants;
|
||||||
|
import io.moquette.persistence.MemorySessionStore;
|
||||||
import io.moquette.spi.impl.Qos1PublishHandler;
|
import io.moquette.spi.impl.Qos1PublishHandler;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import cn.wildfirechat.common.ErrorCode;
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import win.liyufan.im.HttpUtils;
|
import win.liyufan.im.*;
|
||||||
import win.liyufan.im.IMTopic;
|
|
||||||
import win.liyufan.im.MessageShardingUtil;
|
|
||||||
import win.liyufan.im.Utility;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -32,6 +31,7 @@ import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
|
|||||||
public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
|
public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
|
||||||
private int mSensitiveType = 0; //命中敏感词时,0 失败,1 吞掉, 2 敏感词替换成*。
|
private int mSensitiveType = 0; //命中敏感词时,0 失败,1 吞掉, 2 敏感词替换成*。
|
||||||
private String mForwardUrl = null;
|
private String mForwardUrl = null;
|
||||||
|
private String mSensitiveMessageForwardUrl = null;
|
||||||
private Set<Integer> mForwardMessageTypes = new HashSet<>();
|
private Set<Integer> mForwardMessageTypes = new HashSet<>();
|
||||||
private String mMentionForwardUrl = null;
|
private String mMentionForwardUrl = null;
|
||||||
private int mBlacklistStrategy = 0; //黑名单中时,0失败,1吞掉。
|
private int mBlacklistStrategy = 0; //黑名单中时,0失败,1吞掉。
|
||||||
@@ -54,7 +54,7 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mSensitiveMessageForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Sensitive_Forward_Url);
|
||||||
mMentionForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_MentionMsg_Forward_Url);
|
mMentionForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_MentionMsg_Forward_Url);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -171,12 +171,20 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
|
|||||||
}
|
}
|
||||||
message = message.toBuilder().setFromUser(fromUser).setMessageId(messageId).setServerTimestamp(timestamp).build();
|
message = message.toBuilder().setFromUser(fromUser).setMessageId(messageId).setServerTimestamp(timestamp).build();
|
||||||
|
|
||||||
|
OutputClient outputClient = null;
|
||||||
|
if(m_messagesStore.isForwardMessageWithClientInfo() && requestSourceType == ProtoConstants.RequestSourceType.Request_From_User && !StringUtil.isNullOrEmpty(clientID)) {
|
||||||
|
MemorySessionStore.Session session = m_sessionsStore.getSession(clientID);
|
||||||
|
if(session != null && session.getUsername().equals(fromUser)) {
|
||||||
|
outputClient = new OutputClient(session.getPlatform(), clientID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!StringUtil.isNullOrEmpty(mForwardUrl) && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !(isAdmin && mNoForwardAdminMessage)) {
|
if (!StringUtil.isNullOrEmpty(mForwardUrl) && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !(isAdmin && mNoForwardAdminMessage)) {
|
||||||
publisher.forwardMessage(message, mForwardUrl);
|
publisher.forwardMessage(message, mForwardUrl, outputClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!StringUtil.isNullOrEmpty(mMentionForwardUrl) && message.hasContent() && message.getContent().getMentionedType() != 0 && !(isAdmin && mNoForwardAdminMessage)) {
|
if(!StringUtil.isNullOrEmpty(mMentionForwardUrl) && message.hasContent() && message.getContent().getMentionedType() != 0 && !(isAdmin && mNoForwardAdminMessage)) {
|
||||||
publisher.forwardMessage(message, mMentionForwardUrl);
|
publisher.forwardMessage(message, mMentionForwardUrl, outputClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isAdmin) {
|
if (!isAdmin) {
|
||||||
@@ -184,6 +192,9 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
|
|||||||
Set<String> matched = m_messagesStore.handleSensitiveWord(message.getContent().getSearchableContent());
|
Set<String> matched = m_messagesStore.handleSensitiveWord(message.getContent().getSearchableContent());
|
||||||
if (matched != null && !matched.isEmpty()) {
|
if (matched != null && !matched.isEmpty()) {
|
||||||
m_messagesStore.storeSensitiveMessage(message);
|
m_messagesStore.storeSensitiveMessage(message);
|
||||||
|
if (!StringUtil.isNullOrEmpty(mSensitiveMessageForwardUrl)) {
|
||||||
|
publisher.forwardMessage(message, mSensitiveMessageForwardUrl, outputClient);
|
||||||
|
}
|
||||||
if (mSensitiveType == 0) {
|
if (mSensitiveType == 0) {
|
||||||
errorCode = ErrorCode.ERROR_CODE_SENSITIVE_MATCHED;
|
errorCode = ErrorCode.ERROR_CODE_SENSITIVE_MATCHED;
|
||||||
} else if (mSensitiveType == 1) {
|
} else if (mSensitiveType == 1) {
|
||||||
@@ -205,7 +216,7 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
|
|||||||
if(StringUtil.isNullOrEmpty(content)) {
|
if(StringUtil.isNullOrEmpty(content)) {
|
||||||
saveAndPublish(fromUser, clientID, finalMsg, requestSourceType);
|
saveAndPublish(fromUser, clientID, finalMsg, requestSourceType);
|
||||||
} else {
|
} else {
|
||||||
MessagePayload payload = new Gson().fromJson(content, MessagePayload.class);
|
MessagePayload payload = GsonUtil.gson.fromJson(content, MessagePayload.class);
|
||||||
if (payload != null && payload.getType() > 0) {
|
if (payload != null && payload.getType() > 0) {
|
||||||
WFCMessage.Message newMsg = finalMsg.toBuilder().setContent(payload.toProtoMessageContent()).build();
|
WFCMessage.Message newMsg = finalMsg.toBuilder().setContent(payload.toProtoMessageContent()).build();
|
||||||
saveAndPublish(fromUser, clientID, newMsg, requestSourceType);
|
saveAndPublish(fromUser, clientID, newMsg, requestSourceType);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class UploadDeviceTokenHandler extends IMHandler<WFCMessage.UploadDeviceT
|
|||||||
MemorySessionStore.Session session = m_sessionsStore.getSession(clientID);
|
MemorySessionStore.Session session = m_sessionsStore.getSession(clientID);
|
||||||
session.setPlatform(request.getPlatform());
|
session.setPlatform(request.getPlatform());
|
||||||
session.setAppName(request.getAppName());
|
session.setAppName(request.getAppName());
|
||||||
if ((request.getPlatform() == ProtoConstants.Platform.Platform_iOS || request.getPlatform() == ProtoConstants.Platform.Platform_iPad) && request.getPushType() == 2) {
|
if (request.getPlatform() == ProtoConstants.Platform.Platform_iOS && request.getPushType() == 2) {
|
||||||
session.setVoipDeviceToken(request.getDeviceToken());
|
session.setVoipDeviceToken(request.getDeviceToken());
|
||||||
m_sessionsStore.updateSessionToken(session, true);
|
m_sessionsStore.updateSessionToken(session, true);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public class InterceptConnectMessage extends InterceptAbstractMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getPassword() {
|
public byte[] getPassword() {
|
||||||
return msg.payload().password();
|
return msg.payload().passwordInBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getWillTopic() {
|
public String getWillTopic() {
|
||||||
|
|||||||
@@ -553,7 +553,7 @@ public class DatabaseStore {
|
|||||||
statement.setInt(index++, message.getContent().getType());
|
statement.setInt(index++, message.getContent().getType());
|
||||||
String to = message.getToUser();
|
String to = message.getToUser();
|
||||||
if (StringUtil.isNullOrEmpty(message.getToUser())) {
|
if (StringUtil.isNullOrEmpty(message.getToUser())) {
|
||||||
if (message.getToList()!= null && message.getToList().size() == 1) {
|
if (message.getToList().size() > 0) {
|
||||||
to = message.getToList().get(0);
|
to = message.getToList().get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -914,20 +914,24 @@ public class DatabaseStore {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void persistUserMessage(final String userId, final long messageId, final long messageSeq, final int messageContentType) {
|
void persistUserMessage(final String userId, final long messageId, final long messageSeq, int type, String target, int line, boolean directing, final int messageContentType) {
|
||||||
mScheduler.execute(()->{
|
mScheduler.execute(()->{
|
||||||
Connection connection = null;
|
Connection connection = null;
|
||||||
PreparedStatement statement = null;
|
PreparedStatement statement = null;
|
||||||
try {
|
try {
|
||||||
connection = DBUtil.getConnection();
|
connection = DBUtil.getConnection();
|
||||||
|
|
||||||
String sql = "insert into " + getUserMessageTable(userId) + " (`_mid`, `_uid`, `_seq`, `_cont_type`) values(?, ?, ?, ?)";
|
String sql = "insert into " + getUserMessageTable(userId) + " (`_mid`, `_uid`, `_seq`, `_type`, `_target`, `_line`, `_directing`, `_cont_type`) values(?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
|
||||||
statement = connection.prepareStatement(sql);
|
statement = connection.prepareStatement(sql);
|
||||||
int index = 1;
|
int index = 1;
|
||||||
statement.setLong(index++, messageId);
|
statement.setLong(index++, messageId);
|
||||||
statement.setString(index++, userId);
|
statement.setString(index++, userId);
|
||||||
statement.setLong(index++, messageSeq);
|
statement.setLong(index++, messageSeq);
|
||||||
|
statement.setInt(index++, type);
|
||||||
|
statement.setString(index++, target);
|
||||||
|
statement.setInt(index++, line);
|
||||||
|
statement.setInt(index++, directing ? 1 :0);
|
||||||
statement.setInt(index++, messageContentType);
|
statement.setInt(index++, messageContentType);
|
||||||
int count = statement.executeUpdate();
|
int count = statement.executeUpdate();
|
||||||
LOG.info("Update rows {}", count);
|
LOG.info("Update rows {}", count);
|
||||||
@@ -990,7 +994,7 @@ public class DatabaseStore {
|
|||||||
}
|
}
|
||||||
sb.append(")");
|
sb.append(")");
|
||||||
|
|
||||||
sb.append(" and _scope = 6 and _key = ?");
|
sb.append(" and _scope in (5,6,26) and _key = ?");
|
||||||
|
|
||||||
statement = connection.prepareStatement(sb.toString());
|
statement = connection.prepareStatement(sb.toString());
|
||||||
int index = 1;
|
int index = 1;
|
||||||
@@ -1138,23 +1142,21 @@ public class DatabaseStore {
|
|||||||
try {
|
try {
|
||||||
connection = DBUtil.getConnection();
|
connection = DBUtil.getConnection();
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder("delete from t_user_setting where _scope in (1,3,5,7) and _uid in (");
|
StringBuilder sb = new StringBuilder("delete from t_user_setting where _scope in (1,3,5,6,7,19) and _uid in (");
|
||||||
for (int i = 0; i < users.size(); i++) {
|
for (int i = 0; i < users.size(); i++) {
|
||||||
sb.append("?");
|
sb.append("?");
|
||||||
if (i != users.size() - 1) {
|
if (i != users.size() - 1) {
|
||||||
sb.append(",");
|
sb.append(",");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.append(") and _key like '1-_-");
|
sb.append(") and _key like ?");
|
||||||
sb.append(groupId);
|
|
||||||
sb.append("'");
|
|
||||||
|
|
||||||
|
|
||||||
statement = connection.prepareStatement(sb.toString());
|
statement = connection.prepareStatement(sb.toString());
|
||||||
int index = 1;
|
int index = 1;
|
||||||
for (String userId:users) {
|
for (String userId:users) {
|
||||||
statement.setString(index++, userId);
|
statement.setString(index++, userId);
|
||||||
}
|
}
|
||||||
|
statement.setString(index++, "1-_-" + groupId);
|
||||||
|
|
||||||
int count = statement.executeUpdate();
|
int count = statement.executeUpdate();
|
||||||
LOG.info("Update rows {}", count);
|
LOG.info("Update rows {}", count);
|
||||||
@@ -1573,8 +1575,6 @@ public class DatabaseStore {
|
|||||||
sql = "update t_user_session set `_deleted` = ?, `_token` = ?, `_voip_token` = ?, `_dt` = ? where `_uid`=? and (`_platform` = ? or `_platform` = ? or `_platform` = ?) and `_cid` <> ? and `_deleted` = 0";
|
sql = "update t_user_session set `_deleted` = ?, `_token` = ?, `_voip_token` = ?, `_dt` = ? where `_uid`=? and (`_platform` = ? or `_platform` = ? or `_platform` = ?) and `_cid` <> ? and `_deleted` = 0";
|
||||||
} else if(platform == ProtoConstants.Platform.Platform_iOS || platform == ProtoConstants.Platform.Platform_Android) {
|
} else if(platform == ProtoConstants.Platform.Platform_iOS || platform == ProtoConstants.Platform.Platform_Android) {
|
||||||
sql = "update t_user_session set `_deleted` = ?, `_token` = ?, `_voip_token` = ?, `_dt` = ? where `_uid`=? and (`_platform` = ? or `_platform` = ?) and `_cid` <> ? and `_deleted` = 0";
|
sql = "update t_user_session set `_deleted` = ?, `_token` = ?, `_voip_token` = ?, `_dt` = ? where `_uid`=? and (`_platform` = ? or `_platform` = ?) and `_cid` <> ? and `_deleted` = 0";
|
||||||
} else if(platform == ProtoConstants.Platform.Platform_iPad || platform == ProtoConstants.Platform.Platform_APad) {
|
|
||||||
sql = "update t_user_session set `_deleted` = ?, `_token` = ?, `_voip_token` = ?, `_dt` = ? where `_uid`=? and (`_platform` = ? or `_platform` = ?) and `_cid` <> ? and `_deleted` = 0";
|
|
||||||
} else {
|
} else {
|
||||||
sql = "update t_user_session set `_deleted` = ?, `_token` = ?, `_voip_token` = ?, `_dt` = ? where `_uid`=? and `_platform` = ? and `_cid` <> ? and `_deleted` = 0";
|
sql = "update t_user_session set `_deleted` = ?, `_token` = ?, `_voip_token` = ?, `_dt` = ? where `_uid`=? and `_platform` = ? and `_cid` <> ? and `_deleted` = 0";
|
||||||
}
|
}
|
||||||
@@ -1596,9 +1596,6 @@ public class DatabaseStore {
|
|||||||
} else if(platform == ProtoConstants.Platform.Platform_iOS || platform == ProtoConstants.Platform.Platform_Android) {
|
} else if(platform == ProtoConstants.Platform.Platform_iOS || platform == ProtoConstants.Platform.Platform_Android) {
|
||||||
statement.setInt(index++, ProtoConstants.Platform.Platform_iOS);
|
statement.setInt(index++, ProtoConstants.Platform.Platform_iOS);
|
||||||
statement.setInt(index++, ProtoConstants.Platform.Platform_Android);
|
statement.setInt(index++, ProtoConstants.Platform.Platform_Android);
|
||||||
} else if(platform == ProtoConstants.Platform.Platform_iPad || platform == ProtoConstants.Platform.Platform_APad) {
|
|
||||||
statement.setInt(index++, ProtoConstants.Platform.Platform_iPad);
|
|
||||||
statement.setInt(index++, ProtoConstants.Platform.Platform_APad);
|
|
||||||
} else {
|
} else {
|
||||||
statement.setInt(index++, platform);
|
statement.setInt(index++, platform);
|
||||||
}
|
}
|
||||||
@@ -2975,7 +2972,8 @@ public class DatabaseStore {
|
|||||||
", `_secret`" +
|
", `_secret`" +
|
||||||
", `_callback`" +
|
", `_callback`" +
|
||||||
", `_automatic`" +
|
", `_automatic`" +
|
||||||
", `_dt`) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" +
|
", `_menu`" +
|
||||||
|
", `_dt`) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" +
|
||||||
" ON DUPLICATE KEY UPDATE `_name`=?" +
|
" ON DUPLICATE KEY UPDATE `_name`=?" +
|
||||||
", `_portrait`=?" +
|
", `_portrait`=?" +
|
||||||
", `_owner`=?" +
|
", `_owner`=?" +
|
||||||
@@ -2985,12 +2983,20 @@ public class DatabaseStore {
|
|||||||
", `_secret`=?" +
|
", `_secret`=?" +
|
||||||
", `_callback`=?" +
|
", `_callback`=?" +
|
||||||
", `_automatic`=?" +
|
", `_automatic`=?" +
|
||||||
|
", `_menu`=?" +
|
||||||
", `_dt`=?";
|
", `_dt`=?";
|
||||||
|
|
||||||
statement = connection.prepareStatement(sql);
|
statement = connection.prepareStatement(sql);
|
||||||
|
|
||||||
int index = 1;
|
WFCMessage.ChannelMenuList.Builder builder = WFCMessage.ChannelMenuList.newBuilder();
|
||||||
|
if (!channelInfo.getMenuList().isEmpty()) {
|
||||||
|
for (WFCMessage.ChannelMenu menuBtn:channelInfo.getMenuList()) {
|
||||||
|
builder.addMenu(menuBtn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] menuBytes = builder.build().toByteArray();
|
||||||
|
|
||||||
|
int index = 1;
|
||||||
statement.setString(index++, channelInfo.getTargetId());
|
statement.setString(index++, channelInfo.getTargetId());
|
||||||
statement.setString(index++, channelInfo.getName());
|
statement.setString(index++, channelInfo.getName());
|
||||||
statement.setString(index++, channelInfo.getPortrait());
|
statement.setString(index++, channelInfo.getPortrait());
|
||||||
@@ -3001,6 +3007,7 @@ public class DatabaseStore {
|
|||||||
statement.setString(index++, channelInfo.getSecret());
|
statement.setString(index++, channelInfo.getSecret());
|
||||||
statement.setString(index++, channelInfo.getCallback());
|
statement.setString(index++, channelInfo.getCallback());
|
||||||
statement.setInt(index++, channelInfo.getAutomatic());
|
statement.setInt(index++, channelInfo.getAutomatic());
|
||||||
|
statement.setBytes(index++, menuBytes);
|
||||||
statement.setLong(index++, channelInfo.getUpdateDt() == 0 ? System.currentTimeMillis() : channelInfo.getUpdateDt());
|
statement.setLong(index++, channelInfo.getUpdateDt() == 0 ? System.currentTimeMillis() : channelInfo.getUpdateDt());
|
||||||
|
|
||||||
statement.setString(index++, channelInfo.getName());
|
statement.setString(index++, channelInfo.getName());
|
||||||
@@ -3012,6 +3019,7 @@ public class DatabaseStore {
|
|||||||
statement.setString(index++, channelInfo.getSecret());
|
statement.setString(index++, channelInfo.getSecret());
|
||||||
statement.setString(index++, channelInfo.getCallback());
|
statement.setString(index++, channelInfo.getCallback());
|
||||||
statement.setInt(index++, channelInfo.getAutomatic());
|
statement.setInt(index++, channelInfo.getAutomatic());
|
||||||
|
statement.setBytes(index++, menuBytes);
|
||||||
statement.setLong(index++, channelInfo.getUpdateDt() == 0 ? System.currentTimeMillis() : channelInfo.getUpdateDt());
|
statement.setLong(index++, channelInfo.getUpdateDt() == 0 ? System.currentTimeMillis() : channelInfo.getUpdateDt());
|
||||||
|
|
||||||
int count = statement.executeUpdate();
|
int count = statement.executeUpdate();
|
||||||
@@ -3064,6 +3072,7 @@ public class DatabaseStore {
|
|||||||
", `_secret`" +
|
", `_secret`" +
|
||||||
", `_callback`" +
|
", `_callback`" +
|
||||||
", `_automatic`" +
|
", `_automatic`" +
|
||||||
|
", `_menu`" +
|
||||||
", `_dt` from t_channel where `_cid` = ?";
|
", `_dt` from t_channel where `_cid` = ?";
|
||||||
|
|
||||||
statement = connection.prepareStatement(sql);
|
statement = connection.prepareStatement(sql);
|
||||||
@@ -3114,6 +3123,21 @@ public class DatabaseStore {
|
|||||||
intValue = rs.getInt(index++);
|
intValue = rs.getInt(index++);
|
||||||
builder.setAutomatic(intValue);
|
builder.setAutomatic(intValue);
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] bytes = null;
|
||||||
|
Blob blob = rs.getBlob(index++);
|
||||||
|
if (blob != null) {
|
||||||
|
bytes = toByteArray(blob.getBinaryStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
WFCMessage.ChannelMenuList menuButtonList = WFCMessage.ChannelMenuList.parseFrom(bytes);
|
||||||
|
if (menuButtonList.getMenuCount() > 0) {
|
||||||
|
builder.addAllMenu(menuButtonList.getMenuList());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
long longValue = rs.getLong(index++);
|
long longValue = rs.getLong(index++);
|
||||||
builder.setUpdateDt(longValue);
|
builder.setUpdateDt(longValue);
|
||||||
|
|
||||||
@@ -3335,6 +3359,57 @@ public class DatabaseStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> getUserChannels(String userId) {
|
||||||
|
List<String> out = new ArrayList<>();
|
||||||
|
|
||||||
|
Connection connection = null;
|
||||||
|
PreparedStatement statement = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
try {
|
||||||
|
connection = DBUtil.getConnection();
|
||||||
|
String sql = "select _cid from t_channel_listener where _mid = ?";
|
||||||
|
statement = connection.prepareStatement(sql);
|
||||||
|
|
||||||
|
int index = 1;
|
||||||
|
statement.setString(index++, userId);
|
||||||
|
|
||||||
|
rs = statement.executeQuery();
|
||||||
|
while (rs.next()) {
|
||||||
|
String value = rs.getString(1);
|
||||||
|
if (!StringUtil.isNullOrEmpty(value)) {
|
||||||
|
out.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
Utility.printExecption(LOG, e, RDBS_Exception);
|
||||||
|
} finally {
|
||||||
|
DBUtil.closeDB(connection, statement, rs);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearChannelListener(final String channelId) {
|
||||||
|
LOG.info("Database remove channel {}", channelId);
|
||||||
|
Connection connection = null;
|
||||||
|
PreparedStatement statement = null;
|
||||||
|
try {
|
||||||
|
connection = DBUtil.getConnection();
|
||||||
|
String sql = "delete from t_channel_listener where _cid=?";
|
||||||
|
statement = connection.prepareStatement(sql);
|
||||||
|
statement.setString(1, channelId);
|
||||||
|
int count = statement.executeUpdate();
|
||||||
|
LOG.info("Update rows {}", count);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
Utility.printExecption(LOG, e, RDBS_Exception);
|
||||||
|
} finally {
|
||||||
|
DBUtil.closeDB(connection, statement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Set<String> getSensitiveWord() {
|
public Set<String> getSensitiveWord() {
|
||||||
Connection connection = null;
|
Connection connection = null;
|
||||||
PreparedStatement statement = null;
|
PreparedStatement statement = null;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.google.gson.Gson;
|
|||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.hazelcast.core.*;
|
import com.hazelcast.core.*;
|
||||||
import com.hazelcast.util.StringUtil;
|
import com.hazelcast.util.StringUtil;
|
||||||
|
import com.xiaoleilu.loServer.action.admin.AdminAction;
|
||||||
import com.xiaoleilu.loServer.model.FriendData;
|
import com.xiaoleilu.loServer.model.FriendData;
|
||||||
import cn.wildfirechat.common.ErrorCode;
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
import io.moquette.BrokerConstants;
|
import io.moquette.BrokerConstants;
|
||||||
@@ -31,14 +32,18 @@ import io.moquette.server.Constants;
|
|||||||
import io.moquette.server.Server;
|
import io.moquette.server.Server;
|
||||||
import io.moquette.spi.IMatchingCondition;
|
import io.moquette.spi.IMatchingCondition;
|
||||||
import io.moquette.spi.IMessagesStore;
|
import io.moquette.spi.IMessagesStore;
|
||||||
|
import io.moquette.spi.impl.security.AES;
|
||||||
import io.moquette.spi.security.Tokenor;
|
import io.moquette.spi.security.Tokenor;
|
||||||
import io.moquette.spi.impl.subscriptions.Topic;
|
import io.moquette.spi.impl.subscriptions.Topic;
|
||||||
import io.netty.handler.codec.mqtt.MqttVersion;
|
import io.netty.handler.codec.mqtt.MqttVersion;
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
import org.json.simple.JSONObject;
|
import org.json.simple.JSONObject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import win.liyufan.im.*;
|
import win.liyufan.im.*;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -91,6 +96,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
private static final String CHANNEL_LISTENERS = "channel_listeners";
|
private static final String CHANNEL_LISTENERS = "channel_listeners";
|
||||||
|
|
||||||
private static boolean IS_MESSAGE_ROAMING = true;
|
private static boolean IS_MESSAGE_ROAMING = true;
|
||||||
|
private static long msgCompensateTimeLimit = 300000;
|
||||||
|
|
||||||
private static boolean IS_MESSAGE_REMOTE_HISTORY_MESSAGE = true;
|
private static boolean IS_MESSAGE_REMOTE_HISTORY_MESSAGE = true;
|
||||||
private static boolean IS_CHATROOM_MESSAGE_REMOTE_HISTORY_MESSAGE = true;
|
private static boolean IS_CHATROOM_MESSAGE_REMOTE_HISTORY_MESSAGE = true;
|
||||||
@@ -160,6 +166,11 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
private boolean mGroupAllowRobotCustomOperationNotification;
|
private boolean mGroupAllowRobotCustomOperationNotification;
|
||||||
private int mGroupVisibleQuitKickoffNotification;
|
private int mGroupVisibleQuitKickoffNotification;
|
||||||
private int mSyncDataPartSize = 0;
|
private int mSyncDataPartSize = 0;
|
||||||
|
private boolean keepDisplayNameWhenDestroyUser = true;
|
||||||
|
|
||||||
|
private boolean mForwardMessageWithClientInfo = false;
|
||||||
|
private boolean mRobotCallbackWithClientInfo = false;
|
||||||
|
private boolean mChannelCallbackWithClientInfo = false;
|
||||||
|
|
||||||
private Set<Integer> mUserHideProperties = new HashSet<>();
|
private Set<Integer> mUserHideProperties = new HashSet<>();
|
||||||
|
|
||||||
@@ -168,6 +179,11 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
this.databaseStore = databaseStore;
|
this.databaseStore = databaseStore;
|
||||||
|
|
||||||
IS_MESSAGE_ROAMING = "1".equals(m_Server.getConfig().getProperty(MESSAGE_ROAMING));
|
IS_MESSAGE_ROAMING = "1".equals(m_Server.getConfig().getProperty(MESSAGE_ROAMING));
|
||||||
|
try {
|
||||||
|
msgCompensateTimeLimit = Long.parseLong(m_Server.getConfig().getProperty(MESSAGE_Compensate_Time_Limit, "300000"));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
|
||||||
|
}
|
||||||
IS_MESSAGE_REMOTE_HISTORY_MESSAGE = "1".equals(m_Server.getConfig().getProperty(MESSAGE_Remote_History_Message));
|
IS_MESSAGE_REMOTE_HISTORY_MESSAGE = "1".equals(m_Server.getConfig().getProperty(MESSAGE_Remote_History_Message));
|
||||||
IS_CHATROOM_MESSAGE_REMOTE_HISTORY_MESSAGE = "1".equals(m_Server.getConfig().getProperty(MESSAGE_Remote_Chatroom_History_Message, "1"));
|
IS_CHATROOM_MESSAGE_REMOTE_HISTORY_MESSAGE = "1".equals(m_Server.getConfig().getProperty(MESSAGE_Remote_Chatroom_History_Message, "1"));
|
||||||
Constants.MAX_MESSAGE_QUEUE = Integer.parseInt(m_Server.getConfig().getProperty(MESSAGE_Max_Queue));
|
Constants.MAX_MESSAGE_QUEUE = Integer.parseInt(m_Server.getConfig().getProperty(MESSAGE_Max_Queue));
|
||||||
@@ -418,6 +434,11 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
mSyncDataPartSize = Integer.parseInt(server.getConfig().getProperty(SYNC_Data_Part_Size, "0"));
|
mSyncDataPartSize = Integer.parseInt(server.getConfig().getProperty(SYNC_Data_Part_Size, "0"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
keepDisplayNameWhenDestroyUser = Boolean.parseBoolean(server.getConfig().getProperty(USER_KEEP_DISPLAY_NAME_WHEN_DESTROY, "true"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -436,6 +457,21 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
mForwardMessageWithClientInfo = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_With_Client_Info, "false"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mRobotCallbackWithClientInfo = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.ROBOT_Callback_With_Client_Info, "false"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mChannelCallbackWithClientInfo = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.CHANNEL_Callback_With_Client_Info, "false"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,8 +594,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
if (channelInfo != null) {
|
if (channelInfo != null) {
|
||||||
if (channelInfo.getOwner().equals(fromUser)) {
|
if (channelInfo.getOwner().equals(fromUser)) {
|
||||||
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_Channel
|
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_Channel
|
||||||
&& !StringUtil.isNullOrEmpty(channelInfo.getCallback())
|
&& channelInfo.getAutomatic() != 0) {
|
||||||
&& channelInfo.getAutomatic() == 1) {
|
|
||||||
LOG.info("Channel api message not send to the owner when automatic");
|
LOG.info("Channel api message not send to the owner when automatic");
|
||||||
} else {
|
} else {
|
||||||
notifyReceivers.add(fromUser);
|
notifyReceivers.add(fromUser);
|
||||||
@@ -589,8 +624,10 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
notifyReceivers.add(fromUser);
|
notifyReceivers.add(fromUser);
|
||||||
if (StringUtil.isNullOrEmpty(channelInfo.getCallback()) || channelInfo.getAutomatic() == 0) {
|
if (channelInfo.getAutomatic() == 0) {
|
||||||
notifyReceivers.add(channelInfo.getOwner());
|
notifyReceivers.add(channelInfo.getOwner());
|
||||||
|
} else {
|
||||||
|
LOG.info("Channel api message not send to the owner when automatic");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -708,7 +745,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noRoaming && (System.currentTimeMillis() - bundle.getMessage().getServerTimestamp() > 5 * 60 * 1000)) {
|
if (noRoaming && (System.currentTimeMillis() - bundle.getMessage().getServerTimestamp() > msgCompensateTimeLimit)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -866,7 +903,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long insertUserMessages(String sender, int conversationType, String target, int line, int messageContentType, String user, long messageId) {
|
public long insertUserMessages(String sender, int conversationType, String target, int line, int messageContentType, String user, long messageId, boolean directing) {
|
||||||
// messageId是全局的,messageSeq是跟个人相关的,理论上messageId的增长数度远远大于seq。
|
// messageId是全局的,messageSeq是跟个人相关的,理论上messageId的增长数度远远大于seq。
|
||||||
// 考虑到一种情况,当服务器发生变化,用户发生迁移后,messageSeq还需要保持有序。 要么把Seq持久化,要么在迁移后Seq取一个肯定比以前更大的数字(这个数字就是messageId)
|
// 考虑到一种情况,当服务器发生变化,用户发生迁移后,messageSeq还需要保持有序。 要么把Seq持久化,要么在迁移后Seq取一个肯定比以前更大的数字(这个数字就是messageId)
|
||||||
// 这里选择使用后面一种情况
|
// 这里选择使用后面一种情况
|
||||||
@@ -906,7 +943,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
mWriteLock.unlock();
|
mWriteLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
databaseStore.persistUserMessage(user, messageId, messageSeq, messageContentType);
|
databaseStore.persistUserMessage(user, messageId, messageSeq, conversationType, target, line, directing, messageContentType);
|
||||||
return messageSeq;
|
return messageSeq;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1106,7 +1143,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
event.type = type;
|
event.type = type;
|
||||||
m_Server.getCallbackScheduler().execute(() -> {
|
m_Server.getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(mGroupInfoUpdateCallback, new Gson().toJson(event), HttpUtils.HttpPostType.POST_TYPE_Grout_Event_Callback);
|
HttpUtils.httpJsonPost(mGroupInfoUpdateCallback, GsonUtil.gson.toJson(event), HttpUtils.HttpPostType.POST_TYPE_Grout_Event_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -1125,7 +1162,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
event.value = value;
|
event.value = value;
|
||||||
m_Server.getCallbackScheduler().execute(() -> {
|
m_Server.getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(mGroupMemberUpdateCallback, new Gson().toJson(event), HttpUtils.HttpPostType.POST_TYPE_Grout_Member_Event_Callback);
|
HttpUtils.httpJsonPost(mGroupMemberUpdateCallback, GsonUtil.gson.toJson(event), HttpUtils.HttpPostType.POST_TYPE_Grout_Member_Event_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -1143,7 +1180,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
event.value = value;
|
event.value = value;
|
||||||
m_Server.getCallbackScheduler().execute(() -> {
|
m_Server.getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(mRelationUpdateCallback, new Gson().toJson(event), HttpUtils.HttpPostType.POST_TYPE_User_Relation_Event_Callback);
|
HttpUtils.httpJsonPost(mRelationUpdateCallback, GsonUtil.gson.toJson(event), HttpUtils.HttpPostType.POST_TYPE_User_Relation_Event_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -1156,7 +1193,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
if (!StringUtil.isNullOrEmpty(mUserInfoUpdateCallback)) {
|
if (!StringUtil.isNullOrEmpty(mUserInfoUpdateCallback)) {
|
||||||
m_Server.getCallbackScheduler().execute(() -> {
|
m_Server.getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(mUserInfoUpdateCallback, new Gson().toJson(InputOutputUserInfo.fromPbUser(user)), HttpUtils.HttpPostType.POST_TYPE_User_Info_Event_Callback);
|
HttpUtils.httpJsonPost(mUserInfoUpdateCallback, GsonUtil.gson.toJson(InputOutputUserInfo.fromPbUser(user)), HttpUtils.HttpPostType.POST_TYPE_User_Info_Event_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -1173,7 +1210,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
event.type = type;
|
event.type = type;
|
||||||
m_Server.getCallbackScheduler().execute(() -> {
|
m_Server.getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(mChannelInfoUpdateCallback, new Gson().toJson(event), HttpUtils.HttpPostType.POST_TYPE_Channel_Info_Event_Callback);
|
HttpUtils.httpJsonPost(mChannelInfoUpdateCallback, GsonUtil.gson.toJson(event), HttpUtils.HttpPostType.POST_TYPE_Channel_Info_Event_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -1189,7 +1226,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
event.type = type;
|
event.type = type;
|
||||||
m_Server.getCallbackScheduler().execute(() -> {
|
m_Server.getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(mChatroomInfoUpdateCallback, new Gson().toJson(event), HttpUtils.HttpPostType.POST_TYPE_Chatroom_Info_Event_Callback);
|
HttpUtils.httpJsonPost(mChatroomInfoUpdateCallback, GsonUtil.gson.toJson(event), HttpUtils.HttpPostType.POST_TYPE_Chatroom_Info_Event_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -1207,7 +1244,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
event.type = type;
|
event.type = type;
|
||||||
m_Server.getCallbackScheduler().execute(() -> {
|
m_Server.getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(mChatroomMemberUpdateCallback, new Gson().toJson(event), HttpUtils.HttpPostType.POST_TYPE_Chatroom_Member_Event_Callback);
|
HttpUtils.httpJsonPost(mChatroomMemberUpdateCallback, GsonUtil.gson.toJson(event), HttpUtils.HttpPostType.POST_TYPE_Chatroom_Member_Event_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -1269,11 +1306,31 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
|
||||||
|
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
|
||||||
|
if (members == null || members.size() == 0) {
|
||||||
|
members = loadGroupMemberFromDB(hzInstance, groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<String> existMemberIds = new ArrayList<>();
|
||||||
|
for (WFCMessage.GroupMember member : members) {
|
||||||
|
if (member.getType() != GroupMemberType_Removed) {
|
||||||
|
existMemberIds.add(member.getMemberId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
long updateDt = System.currentTimeMillis();
|
long updateDt = System.currentTimeMillis();
|
||||||
|
|
||||||
List<WFCMessage.GroupMember> tmp = new ArrayList<>();
|
List<WFCMessage.GroupMember> tmp = new ArrayList<>();
|
||||||
ArrayList<String> newInviteUsers = new ArrayList<>();
|
int newMemberCount = 0;
|
||||||
for (WFCMessage.GroupMember member : memberList) {
|
for (WFCMessage.GroupMember member : memberList) {
|
||||||
|
if (existMemberIds.contains(member.getMemberId()) && member.getType() != GroupMemberType_Removed) {
|
||||||
|
if(!isAdmin)
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
newMemberCount++;
|
||||||
|
}
|
||||||
|
|
||||||
WFCMessage.GroupMember.Builder builder = member.toBuilder();
|
WFCMessage.GroupMember.Builder builder = member.toBuilder();
|
||||||
builder.setUpdateDt(updateDt).setCreateDt(updateDt);
|
builder.setUpdateDt(updateDt).setCreateDt(updateDt);
|
||||||
if (member.getMemberId().equals(groupInfo.getOwner())) {
|
if (member.getMemberId().equals(groupInfo.getOwner())) {
|
||||||
@@ -1293,29 +1350,23 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
}
|
}
|
||||||
member = builder.build();
|
member = builder.build();
|
||||||
tmp.add(member);
|
tmp.add(member);
|
||||||
newInviteUsers.add(member.getMemberId());
|
|
||||||
updateDt++;
|
updateDt++;
|
||||||
}
|
}
|
||||||
memberList = tmp;
|
memberList = tmp;
|
||||||
|
|
||||||
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
|
if (memberList.size() == 0) {
|
||||||
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
|
if (!isAdmin) {
|
||||||
if (members == null || members.size() == 0) {
|
return ErrorCode.ERROR_CODE_ALREADY_IN_GROUP;
|
||||||
members = loadGroupMemberFromDB(hzInstance, groupId);
|
} else {
|
||||||
}
|
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
|
|
||||||
if (maxCount != Integer.MAX_VALUE) {
|
|
||||||
int existCount = 0;
|
|
||||||
for (WFCMessage.GroupMember member : members) {
|
|
||||||
if (member.getType() != GroupMemberType_Removed) {
|
|
||||||
existCount++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (existCount + newInviteUsers.size() > maxCount) {
|
} else {
|
||||||
|
if (existMemberIds.size() + newMemberCount > maxCount) {
|
||||||
return ErrorCode.ERROR_CODE_GROUP_EXCEED_MAX_MEMBER_COUNT;
|
return ErrorCode.ERROR_CODE_GROUP_EXCEED_MAX_MEMBER_COUNT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
databaseStore.persistGroupMember(groupId, memberList, true);
|
databaseStore.persistGroupMember(groupId, memberList, true);
|
||||||
databaseStore.updateGroupMemberCountDt(groupId);
|
databaseStore.updateGroupMemberCountDt(groupId);
|
||||||
|
|
||||||
@@ -2077,6 +2128,10 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
if (!canRecall) {
|
if (!canRecall) {
|
||||||
return ErrorCode.ERROR_CODE_NOT_RIGHT;
|
return ErrorCode.ERROR_CODE_NOT_RIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(message.getContent().getType() == 80) {
|
||||||
|
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
JSONObject json = new JSONObject();
|
||||||
json.put("s", message.getFromUser());
|
json.put("s", message.getFromUser());
|
||||||
@@ -2313,6 +2368,24 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void forceCleanOnlineStatus(String userId, String clientId) {
|
||||||
|
if (m_Server.isShutdowning()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!mMultiPlatformNotification || m_Server.getStore().sessionsStore().isMultiEndpointSupported()) {
|
||||||
|
WFCMessage.UserSettingEntry pcentry = getUserSetting(userId, kUserSettingPCOnline, "PC");
|
||||||
|
if (pcentry != null && !StringUtil.isNullOrEmpty(pcentry.getValue()) && pcentry.getValue().contains(clientId)) {
|
||||||
|
updateUserSettings(userId, WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingPCOnline).setKey("PC").setValue("").build(), clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
WFCMessage.UserSettingEntry padentry = getUserSetting(userId, kUserSettingPCOnline, "Pad");
|
||||||
|
if (padentry != null && !StringUtil.isNullOrEmpty(padentry.getValue()) && padentry.getValue().contains(clientId)) {
|
||||||
|
updateUserSettings(userId, WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingPCOnline).setKey("Pad").setValue("").build(), clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateUserOnlineSetting(MemorySessionStore.Session session, boolean online) {
|
public void updateUserOnlineSetting(MemorySessionStore.Session session, boolean online) {
|
||||||
if(!mMultiPlatformNotification) {
|
if(!mMultiPlatformNotification) {
|
||||||
@@ -2327,7 +2400,6 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String pcValue = null;
|
String pcValue = null;
|
||||||
String padValue = null;
|
|
||||||
for (MemorySessionStore.Session s : m_Server.getStore().sessionsStore().sessionForUser(session.username)) {
|
for (MemorySessionStore.Session s : m_Server.getStore().sessionsStore().sessionForUser(session.username)) {
|
||||||
if (s.getDeleted() != 0 || !m_Server.getConnectionsManager().isConnected(s.getClientID())) {
|
if (s.getDeleted() != 0 || !m_Server.getConnectionsManager().isConnected(s.getClientID())) {
|
||||||
continue;
|
continue;
|
||||||
@@ -2339,10 +2411,6 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
case Platform_OSX:
|
case Platform_OSX:
|
||||||
pcValue = System.currentTimeMillis() + "|" + s.getPlatform() + "|" + s.getClientID() + "|" + s.getPhoneName();
|
pcValue = System.currentTimeMillis() + "|" + s.getPlatform() + "|" + s.getClientID() + "|" + s.getPhoneName();
|
||||||
break;
|
break;
|
||||||
case Platform_iPad:
|
|
||||||
case Platform_APad:
|
|
||||||
padValue = System.currentTimeMillis() + "|" + s.getPlatform() + "|" + s.getClientID() + "|" + s.getPhoneName();
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2358,17 +2426,6 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
updateUserSettings(session.username, WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingPCOnline).setKey("PC").setValue("").build(), session.clientID);
|
updateUserSettings(session.username, WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingPCOnline).setKey("PC").setValue("").build(), session.clientID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WFCMessage.UserSettingEntry padentry = getUserSetting(session.getUsername(), kUserSettingPCOnline, "Pad");
|
|
||||||
if (padValue != null) {
|
|
||||||
if (padentry == null || StringUtil.isNullOrEmpty(padentry.getValue())) {
|
|
||||||
updateUserSettings(session.username, WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingPCOnline).setKey("Pad").setValue(padValue).build(), session.clientID);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (padentry != null && !StringUtil.isNullOrEmpty(padentry.getValue())) {
|
|
||||||
updateUserSettings(session.username, WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingPCOnline).setKey("Pad").setValue("").build(), session.clientID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2481,12 +2538,42 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destoryUser(String userId) {
|
public void destroyUser(String userId) {
|
||||||
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
|
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
|
||||||
IMap<String, WFCMessage.User> mUserMap = hzInstance.getMap(USERS);
|
IMap<String, WFCMessage.User> mUserMap = hzInstance.getMap(USERS);
|
||||||
WFCMessage.User us = mUserMap.get(userId);
|
WFCMessage.User us = mUserMap.get(userId);
|
||||||
if (us != null) {
|
if (us != null) {
|
||||||
us = WFCMessage.User.newBuilder().setUid(userId).setUpdateDt(System.currentTimeMillis()).setDisplayName("").setName(userId).setDeleted(1).build();
|
if (keepDisplayNameWhenDestroyUser) {
|
||||||
|
us = WFCMessage.User.newBuilder()
|
||||||
|
.setUid(userId)
|
||||||
|
.setUpdateDt(System.currentTimeMillis())
|
||||||
|
.setName(userId)
|
||||||
|
.setDeleted(1)
|
||||||
|
.clearAddress()
|
||||||
|
.clearCompany()
|
||||||
|
.clearEmail()
|
||||||
|
.clearExtra()
|
||||||
|
.clearMobile()
|
||||||
|
.clearPortrait()
|
||||||
|
.clearSocial()
|
||||||
|
.build();
|
||||||
|
} else {
|
||||||
|
us = WFCMessage.User.newBuilder()
|
||||||
|
.setUid(userId)
|
||||||
|
.setUpdateDt(System.currentTimeMillis())
|
||||||
|
.setName(userId)
|
||||||
|
.setDeleted(1)
|
||||||
|
.clearDisplayName()
|
||||||
|
.clearAddress()
|
||||||
|
.clearCompany()
|
||||||
|
.clearEmail()
|
||||||
|
.clearExtra()
|
||||||
|
.clearMobile()
|
||||||
|
.clearPortrait()
|
||||||
|
.clearSocial()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
databaseStore.updateUser(us);
|
databaseStore.updateUser(us);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -3017,6 +3104,8 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
return ErrorCode.ERROR_CODE_FRIEND_REQUEST_EXPIRED;
|
return ErrorCode.ERROR_CODE_FRIEND_REQUEST_EXPIRED;
|
||||||
} else {
|
} else {
|
||||||
existRequest = existRequest.toBuilder().setStatus(request.getStatus()).setUpdateDt(System.currentTimeMillis()).build();
|
existRequest = existRequest.toBuilder().setStatus(request.getStatus()).setUpdateDt(System.currentTimeMillis()).build();
|
||||||
|
heads[2] = existRequest.getUpdateDt();
|
||||||
|
heads[3] = existRequest.getUpdateDt();
|
||||||
databaseStore.persistOrUpdateFriendRequest(existRequest);
|
databaseStore.persistOrUpdateFriendRequest(existRequest);
|
||||||
|
|
||||||
if (request.getStatus() == ProtoConstants.FriendRequestStatus.RequestStatus_Accepted) {
|
if (request.getStatus() == ProtoConstants.FriendRequestStatus.RequestStatus_Accepted) {
|
||||||
@@ -3257,6 +3346,156 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String ensureSecretLength(String secret) {
|
||||||
|
while ( secret.length() < 16) {
|
||||||
|
secret += "w";
|
||||||
|
secret += secret;
|
||||||
|
}
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApplicationAuthCode(String fromUser, String applicationId, int type, String host) {
|
||||||
|
String secret = null;
|
||||||
|
if(type == ProtoConstants.ApplicationType.ApplicationType_Robot) {
|
||||||
|
WFCMessage.Robot robotData = getRobot(applicationId);
|
||||||
|
if(robotData != null && !StringUtil.isNullOrEmpty(robotData.getCallback()) && !StringUtil.isNullOrEmpty(robotData.getSecret())) {
|
||||||
|
try {
|
||||||
|
URL url = new URL(robotData.getCallback());
|
||||||
|
if(url.getHost().equals(host)) {
|
||||||
|
secret = robotData.getSecret();
|
||||||
|
} else {
|
||||||
|
LOG.warn("get application auth code error, request host is not the same host with callback host");
|
||||||
|
}
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String errorReason;
|
||||||
|
if(robotData == null) {
|
||||||
|
errorReason = "Robot not exist.";
|
||||||
|
} else if(StringUtil.isNullOrEmpty(robotData.getCallback())) {
|
||||||
|
errorReason = "Robot no callback address";
|
||||||
|
} else {
|
||||||
|
errorReason = "Robot no secret";
|
||||||
|
}
|
||||||
|
LOG.warn("get application auth code error, reason {}", errorReason);
|
||||||
|
}
|
||||||
|
} else if(type == ProtoConstants.ApplicationType.ApplicationType_Channel) {
|
||||||
|
WFCMessage.ChannelInfo channelData = getChannelInfo(applicationId);
|
||||||
|
if(channelData != null && !StringUtil.isNullOrEmpty(channelData.getCallback()) && !StringUtil.isNullOrEmpty(channelData.getSecret())) {
|
||||||
|
try {
|
||||||
|
URL url = new URL(channelData.getCallback());
|
||||||
|
if(url.getHost().equals(host)) {
|
||||||
|
secret = channelData.getSecret();
|
||||||
|
} else {
|
||||||
|
LOG.warn("get application auth code error, request host is not the same host with callback host");
|
||||||
|
}
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String errorReason;
|
||||||
|
if(channelData == null) {
|
||||||
|
errorReason = "Channel not exist.";
|
||||||
|
} else if(StringUtil.isNullOrEmpty(channelData.getCallback())) {
|
||||||
|
errorReason = "Channel no callback address";
|
||||||
|
} else {
|
||||||
|
errorReason = "Channel no secret";
|
||||||
|
}
|
||||||
|
LOG.warn("get application auth code error, reason {}", errorReason);
|
||||||
|
}
|
||||||
|
} else if(type == ProtoConstants.ApplicationType.ApplicationType_Admin) {
|
||||||
|
secret = AdminAction.getSecretKey();
|
||||||
|
applicationId = "wfadmin";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!StringUtil.isNullOrEmpty(secret)) {
|
||||||
|
secret = ensureSecretLength(secret);
|
||||||
|
byte[] token = AES.AESEncrypt(fromUser + "?|?" + System.currentTimeMillis() + "?|?" + applicationId + "?|?" + type, secret);
|
||||||
|
return new String(Base64.getEncoder().encode(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String verifyApplicationAuthCode(String authCode, String applicationId, int type) {
|
||||||
|
String secret = null;
|
||||||
|
if(type == ProtoConstants.ApplicationType.ApplicationType_Robot) {
|
||||||
|
WFCMessage.Robot robotData = getRobot(applicationId);
|
||||||
|
if(robotData != null && !StringUtil.isNullOrEmpty(robotData.getCallback()) && !StringUtil.isNullOrEmpty(robotData.getSecret())) {
|
||||||
|
secret = robotData.getSecret();
|
||||||
|
}
|
||||||
|
} else if(type == ProtoConstants.ApplicationType.ApplicationType_Channel) {
|
||||||
|
WFCMessage.ChannelInfo channelData = getChannelInfo(applicationId);
|
||||||
|
if(channelData != null && !StringUtil.isNullOrEmpty(channelData.getCallback()) && !StringUtil.isNullOrEmpty(channelData.getSecret())) {
|
||||||
|
secret = channelData.getSecret();
|
||||||
|
}
|
||||||
|
} else if(type == ProtoConstants.ApplicationType.ApplicationType_Admin) {
|
||||||
|
secret = AdminAction.getSecretKey();
|
||||||
|
applicationId = "wfadmin";
|
||||||
|
}
|
||||||
|
|
||||||
|
secret = ensureSecretLength(secret);
|
||||||
|
|
||||||
|
byte[] data = Base64.getDecoder().decode(authCode);
|
||||||
|
data = AES.AESDecrypt(data, secret, true);
|
||||||
|
|
||||||
|
if (data == null || data.length == 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
String str = new String(data);
|
||||||
|
String[] strArr = str.split("\\?\\|\\?");
|
||||||
|
if (strArr.length == 4) {
|
||||||
|
if (applicationId.equals(strArr[2])) {
|
||||||
|
long timestamp= Long.parseLong(strArr[1]);
|
||||||
|
if(System.currentTimeMillis() - timestamp > 60*1000) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return strArr[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ErrorCode configApplication(String appId, int appType, long timestamp, String nonce, String signature) {
|
||||||
|
String secret;
|
||||||
|
if(System.currentTimeMillis()/1000 - timestamp > 300) {
|
||||||
|
return ErrorCode.ERROR_CODE_SIGN_EXPIRED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(appType == ProtoConstants.ApplicationType.ApplicationType_Robot) {
|
||||||
|
WFCMessage.Robot robotData = getRobot(appId);
|
||||||
|
if(robotData != null) {
|
||||||
|
secret = robotData.getSecret();
|
||||||
|
} else {
|
||||||
|
return ErrorCode.ERROR_CODE_NOT_EXIST;
|
||||||
|
}
|
||||||
|
} else if(appType == ProtoConstants.ApplicationType.ApplicationType_Channel) {
|
||||||
|
WFCMessage.ChannelInfo channelData = getChannelInfo(appId);
|
||||||
|
if(channelData != null) {
|
||||||
|
secret = channelData.getSecret();
|
||||||
|
} else {
|
||||||
|
return ErrorCode.ERROR_CODE_NOT_EXIST;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.error("Config application type only support 0 or 1");
|
||||||
|
return ErrorCode.INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
String str = nonce + "|" + appId + "|" + timestamp + "|" + secret;
|
||||||
|
String sign = DigestUtils.sha1Hex(str);
|
||||||
|
if(sign.equals(signature)) {
|
||||||
|
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode.ERROR_CODE_AUTH_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNewFriendWelcomeMessage() {
|
public boolean isNewFriendWelcomeMessage() {
|
||||||
return mFriendNewWelcomeMessage;
|
return mFriendNewWelcomeMessage;
|
||||||
@@ -3607,7 +3846,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearUserChannels(String userId) {
|
public void clearUserChannels(String userId) {
|
||||||
|
databaseStore.getUserChannels(userId).forEach(s -> listenChannel(userId, s, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3649,9 +3888,15 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e);
|
Utility.printExecption(LOG, e);
|
||||||
}
|
}
|
||||||
|
} else if(modifyType == Modify_Channel_Menu) {
|
||||||
|
OutputGetChannelInfo.OutputMenuList outputMenuButtons = GsonUtil.gson.fromJson(value, OutputGetChannelInfo.OutputMenuList.class);
|
||||||
|
if (!outputMenuButtons.isEmpty()) {
|
||||||
|
for (OutputGetChannelInfo.OutputMenu outputMenuButton : outputMenuButtons) {
|
||||||
|
newInfoBuilder.addMenu(outputMenuButton.toPbInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
newInfoBuilder.setUpdateDt(System.currentTimeMillis());
|
newInfoBuilder.setUpdateDt(System.currentTimeMillis());
|
||||||
mIMap.put(channelId, newInfoBuilder.build());
|
mIMap.put(channelId, newInfoBuilder.build());
|
||||||
callbackChannelInfoUpdateEvent(operator, channelId, Channel_Event_Update);
|
callbackChannelInfoUpdateEvent(operator, channelId, Channel_Event_Update);
|
||||||
@@ -3687,7 +3932,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ErrorCode distoryChannel(String operator, String channelId, boolean isAdmin) {
|
public ErrorCode destroyChannel(String operator, String channelId, boolean isAdmin) {
|
||||||
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
|
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
|
||||||
IMap<String, WFCMessage.ChannelInfo> mIMap = hzInstance.getMap(CHANNELS);
|
IMap<String, WFCMessage.ChannelInfo> mIMap = hzInstance.getMap(CHANNELS);
|
||||||
|
|
||||||
@@ -3707,6 +3952,8 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
newInfoBuilder.setStatus(oldInfo.getStatus() | Channel_State_Mask_Deleted);
|
newInfoBuilder.setStatus(oldInfo.getStatus() | Channel_State_Mask_Deleted);
|
||||||
newInfoBuilder.setUpdateDt(System.currentTimeMillis());
|
newInfoBuilder.setUpdateDt(System.currentTimeMillis());
|
||||||
mIMap.put(channelId, newInfoBuilder.build());
|
mIMap.put(channelId, newInfoBuilder.build());
|
||||||
|
databaseStore.clearChannelListener(channelId);
|
||||||
|
hzInstance.getMultiMap(CHANNEL_LISTENERS).remove(channelId);
|
||||||
|
|
||||||
callbackChannelInfoUpdateEvent(operator, channelId, Channel_Event_Destroy);
|
callbackChannelInfoUpdateEvent(operator, channelId, Channel_Event_Destroy);
|
||||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
@@ -3724,18 +3971,18 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
IMap<String, WFCMessage.ChannelInfo> mIMap = hzInstance.getMap(CHANNELS);
|
IMap<String, WFCMessage.ChannelInfo> mIMap = hzInstance.getMap(CHANNELS);
|
||||||
WFCMessage.ChannelInfo channelInfo = mIMap.get(channelId);
|
WFCMessage.ChannelInfo channelInfo = mIMap.get(channelId);
|
||||||
if(channelInfo != null && (channelInfo.getStatus() & ProtoConstants.ChannelState.Channel_State_Mask_Private) > 0) {
|
if(channelInfo != null && (channelInfo.getStatus() & ProtoConstants.ChannelState.Channel_State_Mask_Private) > 0) {
|
||||||
return ErrorCode.ERROR_CODE_NOT_RIGHT;
|
return ErrorCode.ERROR_CODE_NOT_EXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
getChannelListener(channelId);
|
try {
|
||||||
|
mWriteLock.lock();
|
||||||
if (listen) {
|
databaseStore.updateChannelListener(channelId, operator, listen);
|
||||||
listeners.put(channelId, operator);
|
listeners.remove(channelId);
|
||||||
} else {
|
} finally {
|
||||||
listeners.remove(channelId, operator);
|
mWriteLock.unlock();
|
||||||
}
|
}
|
||||||
databaseStore.updateChannelListener(channelId, operator, listen);
|
|
||||||
|
IMHandler.getPublisher().notifyChannelListenStatusChanged(channelInfo, operator, listen);
|
||||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3744,9 +3991,14 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
MultiMap<String, String> listeners = hzInstance.getMultiMap(CHANNEL_LISTENERS);
|
MultiMap<String, String> listeners = hzInstance.getMultiMap(CHANNEL_LISTENERS);
|
||||||
Collection<String> result = listeners.get(channelId);
|
Collection<String> result = listeners.get(channelId);
|
||||||
if (result.isEmpty()) {
|
if (result.isEmpty()) {
|
||||||
result = databaseStore.getChannelListener(channelId);
|
try {
|
||||||
for (String userId : result) {
|
mWriteLock.lock();
|
||||||
listeners.put(channelId, userId);
|
result = databaseStore.getChannelListener(channelId);
|
||||||
|
for (String userId : result) {
|
||||||
|
listeners.put(channelId, userId);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
mWriteLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -3775,12 +4027,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Collection<String> channelMembers = getChannelListener(channelId);
|
Collection<String> channelMembers = getChannelListener(channelId);
|
||||||
if (channelMembers == null) {
|
if (channelMembers == null || !channelMembers.contains(user)) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<String> chatroomMembers = getChannelListener(channelId);
|
|
||||||
if (chatroomMembers == null || !chatroomMembers.contains(user)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3806,12 +4053,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> getChannelSubscriber(String channelId) {
|
public Collection<String> getChannelSubscriber(String channelId) {
|
||||||
MultiMap<String, String> channelMembers = m_Server.getHazelcastInstance().getMultiMap(CHANNEL_LISTENERS);
|
return getChannelListener(channelId);
|
||||||
if (channelMembers == null) {
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return channelMembers.get(channelId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -3871,6 +4113,20 @@ public class MemoryMessagesStore implements IMessagesStore {
|
|||||||
return mGroupVisibleQuitKickoffNotification;
|
return mGroupVisibleQuitKickoffNotification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isForwardMessageWithClientInfo() {
|
||||||
|
return mForwardMessageWithClientInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRobotCallbackWithClientInfo() {
|
||||||
|
return mRobotCallbackWithClientInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChannelCallbackWithClientInfo() {
|
||||||
|
return mChannelCallbackWithClientInfo;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Integer> getClientForbiddenSendTypes() {
|
public List<Integer> getClientForbiddenSendTypes() {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public class MemorySessionStore implements ISessionsStore {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(MemorySessionStore.class);
|
private static final Logger LOG = LoggerFactory.getLogger(MemorySessionStore.class);
|
||||||
|
|
||||||
private boolean supportMultiEndpoint = false;
|
private boolean supportMultiEndpoint = false;
|
||||||
|
private boolean supportMultiPCEndpoint = false;
|
||||||
private boolean clientSupportKickoff = false;
|
private boolean clientSupportKickoff = false;
|
||||||
|
|
||||||
public static class Session implements Comparable<Session>{
|
public static class Session implements Comparable<Session>{
|
||||||
@@ -287,6 +288,11 @@ public class MemorySessionStore implements ISessionsStore {
|
|||||||
Utility.printExecption(LOG, e);
|
Utility.printExecption(LOG, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
supportMultiPCEndpoint = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.SERVER_MULTI_PC_ENDPOINT, "false"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
clientSupportKickoff = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.SERVER_CLIENT_SUPPORT_KICKOFF_EVENT, "false"));
|
clientSupportKickoff = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.SERVER_CLIENT_SUPPORT_KICKOFF_EVENT, "false"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -385,7 +391,12 @@ public class MemorySessionStore implements ISessionsStore {
|
|||||||
}
|
}
|
||||||
databaseStore.clearMultiUser(username, clientID);
|
databaseStore.clearMultiUser(username, clientID);
|
||||||
|
|
||||||
if (!supportMultiEndpoint && platform > 0) {
|
if (!supportMultiEndpoint
|
||||||
|
&& platform > 0
|
||||||
|
&& !(platform == ProtoConstants.Platform.Platform_Windows && supportMultiPCEndpoint)
|
||||||
|
&& !(platform == ProtoConstants.Platform.Platform_LINUX && supportMultiPCEndpoint)
|
||||||
|
&& !(platform == ProtoConstants.Platform.Platform_OSX && supportMultiPCEndpoint)
|
||||||
|
) {
|
||||||
databaseStore.clearMultiEndpoint(username, clientID, platform);
|
databaseStore.clearMultiEndpoint(username, clientID, platform);
|
||||||
if (userSessions.get(username) != null) {
|
if (userSessions.get(username) != null) {
|
||||||
Iterator<String> it = userSessions.get(username).iterator();
|
Iterator<String> it = userSessions.get(username).iterator();
|
||||||
@@ -407,10 +418,6 @@ public class MemorySessionStore implements ISessionsStore {
|
|||||||
if (s.getPlatform() == ProtoConstants.Platform.Platform_OSX || s.getPlatform() == ProtoConstants.Platform.Platform_Windows || platform == ProtoConstants.Platform.Platform_LINUX) {
|
if (s.getPlatform() == ProtoConstants.Platform.Platform_OSX || s.getPlatform() == ProtoConstants.Platform.Platform_Windows || platform == ProtoConstants.Platform.Platform_LINUX) {
|
||||||
remove = true;
|
remove = true;
|
||||||
}
|
}
|
||||||
} else if(platform == ProtoConstants.Platform.Platform_iPad || platform == ProtoConstants.Platform.Platform_APad) {
|
|
||||||
if (s.getPlatform() == ProtoConstants.Platform.Platform_iPad || s.getPlatform() == ProtoConstants.Platform.Platform_APad) {
|
|
||||||
remove = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (s.getPlatform() ==platform) {
|
if (s.getPlatform() ==platform) {
|
||||||
remove = true;
|
remove = true;
|
||||||
@@ -729,14 +736,13 @@ public class MemorySessionStore implements ISessionsStore {
|
|||||||
if (session != null) {
|
if (session != null) {
|
||||||
if(!operator.equals(session.getUsername())) {
|
if(!operator.equals(session.getUsername())) {
|
||||||
LOG.error("kickoffPCClient failure, user {} don't have client {}", operator, pcClientId);
|
LOG.error("kickoffPCClient failure, user {} don't have client {}", operator, pcClientId);
|
||||||
return ErrorCode.ERROR_CODE_NOT_RIGHT;
|
mServer.getProcessor().getMessagesStore().forceCleanOnlineStatus(operator, pcClientId);
|
||||||
|
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
if (session.getPlatform() == ProtoConstants.Platform.Platform_LINUX
|
if (session.getPlatform() == ProtoConstants.Platform.Platform_LINUX
|
||||||
|
|| session.getPlatform() == ProtoConstants.Platform.Platform_WEB
|
||||||
|| session.getPlatform() == ProtoConstants.Platform.Platform_Windows
|
|| session.getPlatform() == ProtoConstants.Platform.Platform_Windows
|
||||||
|| session.getPlatform() == ProtoConstants.Platform.Platform_OSX
|
|| session.getPlatform() == ProtoConstants.Platform.Platform_OSX) {
|
||||||
|| session.getPlatform() == ProtoConstants.Platform.Platform_iPad
|
|
||||||
|| session.getPlatform() == ProtoConstants.Platform.Platform_APad
|
|
||||||
) {
|
|
||||||
databaseStore.updateSessionDeleted(operator, pcClientId, 1);
|
databaseStore.updateSessionDeleted(operator, pcClientId, 1);
|
||||||
sessions.remove(pcClientId);
|
sessions.remove(pcClientId);
|
||||||
mServer.getProcessor().kickoffSession(session);
|
mServer.getProcessor().kickoffSession(session);
|
||||||
@@ -746,6 +752,7 @@ public class MemorySessionStore implements ISessionsStore {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.error("Can't find the session for client <{}>", pcClientId);
|
LOG.error("Can't find the session for client <{}>", pcClientId);
|
||||||
|
mServer.getProcessor().getMessagesStore().forceCleanOnlineStatus(operator, pcClientId);
|
||||||
}
|
}
|
||||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -755,10 +762,12 @@ public class MemorySessionStore implements ISessionsStore {
|
|||||||
public void disableSession(String userId, String clientId) {
|
public void disableSession(String userId, String clientId) {
|
||||||
Session session = sessions.get(clientId);
|
Session session = sessions.get(clientId);
|
||||||
if (session != null && session.getDeleted() == 0 && (userId == null || session.getUsername().equals(userId))) {
|
if (session != null && session.getDeleted() == 0 && (userId == null || session.getUsername().equals(userId))) {
|
||||||
databaseStore.updateSessionDeleted(session.getUsername(), clientId, 1);
|
mServer.getProcessor().processOffline(session, true, () -> {
|
||||||
ConcurrentSkipListSet<String> sessionSet = getUserSessionSet(session.username);
|
databaseStore.updateSessionDeleted(session.getUsername(), clientId, 1);
|
||||||
sessionSet.remove(clientId);
|
ConcurrentSkipListSet<String> sessionSet = getUserSessionSet(session.username);
|
||||||
sessions.remove(clientId);
|
sessionSet.remove(clientId);
|
||||||
|
sessions.remove(clientId);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,23 +785,25 @@ public class MemorySessionStore implements ISessionsStore {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConcurrentSkipListSet<String> sessionSet = getUserSessionSet(session.username);
|
mServer.getProcessor().processOffline(session, true, () -> {
|
||||||
sessionSet.remove(clientID);
|
ConcurrentSkipListSet<String> sessionSet = getUserSessionSet(session.username);
|
||||||
|
sessionSet.remove(clientID);
|
||||||
|
|
||||||
// remove also the messages stored of type QoS1/2
|
// remove also the messages stored of type QoS1/2
|
||||||
LOG.info("Removing stored messages with QoS 1 and 2. ClientId={}", clientID);
|
LOG.info("Removing stored messages with QoS 1 and 2. ClientId={}", clientID);
|
||||||
|
|
||||||
session.secondPhaseStore.clear();
|
session.secondPhaseStore.clear();
|
||||||
session.outboundFlightMessages.clear();
|
session.outboundFlightMessages.clear();
|
||||||
session.inboundFlightMessages.clear();
|
session.inboundFlightMessages.clear();
|
||||||
|
|
||||||
LOG.info("Wiping existing subscriptions. ClientId={}", clientID);
|
LOG.info("Wiping existing subscriptions. ClientId={}", clientID);
|
||||||
|
|
||||||
//remove also the enqueued messages
|
//remove also the enqueued messages
|
||||||
dropQueue(clientID);
|
dropQueue(clientID);
|
||||||
|
|
||||||
// TODO this missing last step breaks the junit test
|
// TODO this missing last step breaks the junit test
|
||||||
sessions.remove(clientID);
|
sessions.remove(clientID);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean isMultiEndpointSupported() {
|
public boolean isMultiEndpointSupported() {
|
||||||
|
|||||||
@@ -369,9 +369,6 @@ public class Server {
|
|||||||
private String getServerIp(IConfig config) {
|
private String getServerIp(IConfig config) {
|
||||||
String serverIp = config.getProperty(BrokerConstants.SERVER_IP_PROPERTY_NAME);
|
String serverIp = config.getProperty(BrokerConstants.SERVER_IP_PROPERTY_NAME);
|
||||||
if (serverIp == null || serverIp.equals("0.0.0.0")) {
|
if (serverIp == null || serverIp.equals("0.0.0.0")) {
|
||||||
String warning = "警告:无效配置,server.ip不能为空,需要配置服务的公网IP地址或者域名才能在公网使用!!!\nIM服务将使用本地地址,只能在局域网内使用!";
|
|
||||||
System.out.println(warning);
|
|
||||||
LOG.error(warning);
|
|
||||||
serverIp = Utility.getLocalAddress().getHostAddress();
|
serverIp = Utility.getLocalAddress().getHostAddress();
|
||||||
}
|
}
|
||||||
return serverIp;
|
return serverIp;
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ public class NettyAcceptor implements ServerAcceptor {
|
|||||||
IConfig props) throws IOException {
|
IConfig props) throws IOException {
|
||||||
LOG.info("Configuring TCP MQTT transport");
|
LOG.info("Configuring TCP MQTT transport");
|
||||||
final MoquetteIdleTimeoutHandler timeoutHandler = new MoquetteIdleTimeoutHandler();
|
final MoquetteIdleTimeoutHandler timeoutHandler = new MoquetteIdleTimeoutHandler();
|
||||||
String host = props.getProperty(BrokerConstants.HOST_PROPERTY_NAME);
|
String host = props.getProperty(BrokerConstants.HOST_PROPERTY_NAME, "0.0.0.0");
|
||||||
String tcpPortProp = props.getProperty(PORT_PROPERTY_NAME, DISABLED_PORT_BIND);
|
String tcpPortProp = props.getProperty(PORT_PROPERTY_NAME, DISABLED_PORT_BIND);
|
||||||
if (DISABLED_PORT_BIND.equals(tcpPortProp)) {
|
if (DISABLED_PORT_BIND.equals(tcpPortProp)) {
|
||||||
LOG.info("Property {} has been set to {}. TCP MQTT will be disabled", BrokerConstants.PORT_PROPERTY_NAME,
|
LOG.info("Property {} has been set to {}. TCP MQTT will be disabled", BrokerConstants.PORT_PROPERTY_NAME,
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ public interface IMessagesStore {
|
|||||||
Set<String> getAllEnds();
|
Set<String> getAllEnds();
|
||||||
WFCMessage.PullMessageResult fetchMessage(String user, String exceptClientId, long fromMessageId, int pullType);
|
WFCMessage.PullMessageResult fetchMessage(String user, String exceptClientId, long fromMessageId, int pullType);
|
||||||
WFCMessage.PullMessageResult loadRemoteMessages(String user, WFCMessage.Conversation conversation, long beforeUid, int count, Collection<Integer> contentTypes);
|
WFCMessage.PullMessageResult loadRemoteMessages(String user, WFCMessage.Conversation conversation, long beforeUid, int count, Collection<Integer> contentTypes);
|
||||||
long insertUserMessages(String sender, int conversationType, String target, int line, int messageContentType, String userId, long messageId);
|
long insertUserMessages(String sender, int conversationType, String target, int line, int messageContentType, String userId, long messageId, boolean directing);
|
||||||
WFCMessage.GroupInfo createGroup(String operator, WFCMessage.GroupInfo groupInfo, List<WFCMessage.GroupMember> memberList, String memberExtra, boolean isAdmin);
|
WFCMessage.GroupInfo createGroup(String operator, WFCMessage.GroupInfo groupInfo, List<WFCMessage.GroupMember> memberList, String memberExtra, boolean isAdmin);
|
||||||
ErrorCode addGroupMembers(String operator, boolean isAdmin, String groupId, List<WFCMessage.GroupMember> memberList, String extra);
|
ErrorCode addGroupMembers(String operator, boolean isAdmin, String groupId, List<WFCMessage.GroupMember> memberList, String extra);
|
||||||
ErrorCode kickoffGroupMembers(String operator, boolean isAdmin, String groupId, List<String> memberList);
|
ErrorCode kickoffGroupMembers(String operator, boolean isAdmin, String groupId, List<String> memberList);
|
||||||
@@ -140,6 +140,8 @@ public interface IMessagesStore {
|
|||||||
|
|
||||||
ErrorCode modifyUserInfo(String userId, WFCMessage.ModifyMyInfoRequest request) throws Exception;
|
ErrorCode modifyUserInfo(String userId, WFCMessage.ModifyMyInfoRequest request) throws Exception;
|
||||||
|
|
||||||
|
void forceCleanOnlineStatus(String userId, String clientId);
|
||||||
|
|
||||||
void updateUserOnlineSetting(MemorySessionStore.Session session, boolean online);
|
void updateUserOnlineSetting(MemorySessionStore.Session session, boolean online);
|
||||||
|
|
||||||
ErrorCode modifyUserStatus(String userId, int status);
|
ErrorCode modifyUserStatus(String userId, int status);
|
||||||
@@ -148,7 +150,7 @@ public interface IMessagesStore {
|
|||||||
|
|
||||||
ErrorCode updateUserInfo(InputOutputUserInfo userInfo, int flag);
|
ErrorCode updateUserInfo(InputOutputUserInfo userInfo, int flag);
|
||||||
void addUserInfo(WFCMessage.User user, String password) throws Exception;
|
void addUserInfo(WFCMessage.User user, String password) throws Exception;
|
||||||
void destoryUser(String userId);
|
void destroyUser(String userId);
|
||||||
void updateUserInfo(WFCMessage.User user) throws Exception;
|
void updateUserInfo(WFCMessage.User user) throws Exception;
|
||||||
WFCMessage.User getUserInfo(String userId);
|
WFCMessage.User getUserInfo(String userId);
|
||||||
WFCMessage.User getUserInfoByName(String name);
|
WFCMessage.User getUserInfoByName(String name);
|
||||||
@@ -191,6 +193,11 @@ public interface IMessagesStore {
|
|||||||
|
|
||||||
boolean checkChatroomParticipantIdelTime(MemorySessionStore.Session session);
|
boolean checkChatroomParticipantIdelTime(MemorySessionStore.Session session);
|
||||||
|
|
||||||
|
|
||||||
|
String getApplicationAuthCode(String fromUser, String applicationId, int appType, String host);
|
||||||
|
String verifyApplicationAuthCode(String token, String applicationId, int type);
|
||||||
|
ErrorCode configApplication(String appId, int appType, long timestamp, String nonce, String signature);
|
||||||
|
|
||||||
ErrorCode getUserSettings(String userId, long version, WFCMessage.GetUserSettingResult.Builder builder);
|
ErrorCode getUserSettings(String userId, long version, WFCMessage.GetUserSettingResult.Builder builder);
|
||||||
WFCMessage.UserSettingEntry getUserSetting(String userId, int scope, String key);
|
WFCMessage.UserSettingEntry getUserSetting(String userId, int scope, String key);
|
||||||
List<WFCMessage.UserSettingEntry> getUserSetting(String userId, int scope);
|
List<WFCMessage.UserSettingEntry> getUserSetting(String userId, int scope);
|
||||||
@@ -207,7 +214,7 @@ public interface IMessagesStore {
|
|||||||
void clearUserChannels(String userId);
|
void clearUserChannels(String userId);
|
||||||
ErrorCode modifyChannelInfo(String operator, String channelId, int modifyType, String value);
|
ErrorCode modifyChannelInfo(String operator, String channelId, int modifyType, String value);
|
||||||
ErrorCode transferChannel(String operator, String channelId, String newOwner);
|
ErrorCode transferChannel(String operator, String channelId, String newOwner);
|
||||||
ErrorCode distoryChannel(String operator, String channelId, boolean isAdmin);
|
ErrorCode destroyChannel(String operator, String channelId, boolean isAdmin);
|
||||||
List<WFCMessage.ChannelInfo> searchChannel(String keyword, boolean buzzy, int page);
|
List<WFCMessage.ChannelInfo> searchChannel(String keyword, boolean buzzy, int page);
|
||||||
ErrorCode listenChannel(String operator, String channelId, boolean listen);
|
ErrorCode listenChannel(String operator, String channelId, boolean listen);
|
||||||
WFCMessage.ChannelInfo getChannelInfo(String channelId);
|
WFCMessage.ChannelInfo getChannelInfo(String channelId);
|
||||||
@@ -226,6 +233,10 @@ public interface IMessagesStore {
|
|||||||
boolean isAllowRobotCustomGroupNotification();
|
boolean isAllowRobotCustomGroupNotification();
|
||||||
int getVisibleQuitKickoffNotification();
|
int getVisibleQuitKickoffNotification();
|
||||||
|
|
||||||
|
boolean isForwardMessageWithClientInfo();
|
||||||
|
boolean isRobotCallbackWithClientInfo();
|
||||||
|
boolean isChannelCallbackWithClientInfo();
|
||||||
|
|
||||||
List<Integer> getClientForbiddenSendTypes();
|
List<Integer> getClientForbiddenSendTypes();
|
||||||
List<Integer> getBlackListExceptionTypes();
|
List<Integer> getBlackListExceptionTypes();
|
||||||
List<Integer> getGroupMuteExceptionTypes();
|
List<Integer> getGroupMuteExceptionTypes();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package io.moquette.spi.impl;
|
package io.moquette.spi.impl;
|
||||||
|
|
||||||
|
import cn.wildfirechat.pojos.OutputClient;
|
||||||
import cn.wildfirechat.pojos.OutputMessageData;
|
import cn.wildfirechat.pojos.OutputMessageData;
|
||||||
import cn.wildfirechat.proto.ProtoConstants;
|
import cn.wildfirechat.proto.ProtoConstants;
|
||||||
import cn.wildfirechat.proto.WFCMessage;
|
import cn.wildfirechat.proto.WFCMessage;
|
||||||
@@ -24,7 +25,6 @@ import com.google.gson.Gson;
|
|||||||
import com.hazelcast.core.HazelcastInstance;
|
import com.hazelcast.core.HazelcastInstance;
|
||||||
import com.hazelcast.util.StringUtil;
|
import com.hazelcast.util.StringUtil;
|
||||||
import cn.wildfirechat.pojos.OutputNotifyChannelSubscribeStatus;
|
import cn.wildfirechat.pojos.OutputNotifyChannelSubscribeStatus;
|
||||||
import cn.wildfirechat.pojos.SendMessageData;
|
|
||||||
import com.xiaoleilu.loServer.model.FriendData;
|
import com.xiaoleilu.loServer.model.FriendData;
|
||||||
import io.moquette.persistence.*;
|
import io.moquette.persistence.*;
|
||||||
import io.moquette.persistence.MemorySessionStore.Session;
|
import io.moquette.persistence.MemorySessionStore.Session;
|
||||||
@@ -35,6 +35,7 @@ import io.moquette.spi.ISessionsStore;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.handler.codec.mqtt.*;
|
import io.netty.handler.codec.mqtt.*;
|
||||||
|
import win.liyufan.im.GsonUtil;
|
||||||
import win.liyufan.im.HttpUtils;
|
import win.liyufan.im.HttpUtils;
|
||||||
import win.liyufan.im.IMTopic;
|
import win.liyufan.im.IMTopic;
|
||||||
|
|
||||||
@@ -160,7 +161,7 @@ public class MessagesPublisher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publish2Receivers(String sender, int conversationType, String target, int line, long messageId, Collection<String> receivers, String pushContent, String pushData, String exceptClientId, int pullType, int messageContentType, long serverTime, int mentionType, List<String> mentionTargets, int persistFlag) {
|
private void publish2Receivers(String sender, int conversationType, String target, int line, long messageId, Collection<String> receivers, String pushContent, String pushData, String exceptClientId, int pullType, int messageContentType, long serverTime, int mentionType, List<String> mentionTargets, int persistFlag, boolean directing) {
|
||||||
if (persistFlag == Transparent) {
|
if (persistFlag == Transparent) {
|
||||||
publishTransparentMessage2Receivers(messageId, receivers, pullType, exceptClientId);
|
publishTransparentMessage2Receivers(messageId, receivers, pullType, exceptClientId);
|
||||||
return;
|
return;
|
||||||
@@ -176,10 +177,19 @@ public class MessagesPublisher {
|
|||||||
if (message == null) {
|
if (message == null) {
|
||||||
message = m_messagesStore.getMessage(messageId);
|
message = m_messagesStore.getMessage(messageId);
|
||||||
}
|
}
|
||||||
|
OutputClient outputClient = null;
|
||||||
|
if(m_messagesStore.isRobotCallbackWithClientInfo() && !StringUtil.isNullOrEmpty(exceptClientId)) {
|
||||||
|
Session session = m_sessionsStore.getSession(exceptClientId);
|
||||||
|
if(session != null && session.getUsername().equals(sender)) {
|
||||||
|
outputClient = new OutputClient(session.getPlatform(), exceptClientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final WFCMessage.Message finalMsg = message;
|
final WFCMessage.Message finalMsg = message;
|
||||||
|
OutputClient finalOutputClient = outputClient;
|
||||||
Server.getServer().getCallbackScheduler().execute(() -> {
|
Server.getServer().getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(robot.getCallback(), new Gson().toJson(SendMessageData.fromProtoMessage(finalMsg), SendMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Robot_Message_Callback);
|
HttpUtils.httpJsonPost(robot.getCallback(), GsonUtil.gson.toJson(OutputMessageData.fromProtoMessage(finalMsg, finalOutputClient), OutputMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Robot_Message_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -191,7 +201,7 @@ public class MessagesPublisher {
|
|||||||
}
|
}
|
||||||
long messageSeq;
|
long messageSeq;
|
||||||
if (pullType != ProtoConstants.PullType.Pull_ChatRoom) {
|
if (pullType != ProtoConstants.PullType.Pull_ChatRoom) {
|
||||||
messageSeq = m_messagesStore.insertUserMessages(sender, conversationType, target, line, messageContentType, user, messageId);
|
messageSeq = m_messagesStore.insertUserMessages(sender, conversationType, target, line, messageContentType, user, messageId, directing);
|
||||||
} else {
|
} else {
|
||||||
messageSeq = m_messagesStore.insertChatroomMessages(user, line, messageId);
|
messageSeq = m_messagesStore.insertChatroomMessages(user, line, messageId);
|
||||||
}
|
}
|
||||||
@@ -213,9 +223,7 @@ public class MessagesPublisher {
|
|||||||
if (targetSession.getPlatform() == ProtoConstants.Platform.Platform_WEB
|
if (targetSession.getPlatform() == ProtoConstants.Platform.Platform_WEB
|
||||||
|| targetSession.getPlatform() == ProtoConstants.Platform.Platform_Windows
|
|| targetSession.getPlatform() == ProtoConstants.Platform.Platform_Windows
|
||||||
|| targetSession.getPlatform() == ProtoConstants.Platform.Platform_LINUX
|
|| targetSession.getPlatform() == ProtoConstants.Platform.Platform_LINUX
|
||||||
|| targetSession.getPlatform() == ProtoConstants.Platform.Platform_OSX
|
|| targetSession.getPlatform() == ProtoConstants.Platform.Platform_OSX) {
|
||||||
|| targetSession.getPlatform() == ProtoConstants.Platform.Platform_iPad
|
|
||||||
|| targetSession.getPlatform() == ProtoConstants.Platform.Platform_APad) {
|
|
||||||
boolean targetIsActive = this.connectionDescriptors.isConnected(targetSession.getClientSession().clientID);
|
boolean targetIsActive = this.connectionDescriptors.isConnected(targetSession.getClientSession().clientID);
|
||||||
if (targetIsActive) {
|
if (targetIsActive) {
|
||||||
isPcOnline = true;
|
isPcOnline = true;
|
||||||
@@ -597,10 +605,19 @@ public class MessagesPublisher {
|
|||||||
public void publish2Receivers(WFCMessage.Message message, Set<String> receivers, String exceptClientId, int pullType) {
|
public void publish2Receivers(WFCMessage.Message message, Set<String> receivers, String exceptClientId, int pullType) {
|
||||||
if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Channel) {
|
if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Channel) {
|
||||||
WFCMessage.ChannelInfo channelInfo = m_messagesStore.getChannelInfo(message.getConversation().getTarget());
|
WFCMessage.ChannelInfo channelInfo = m_messagesStore.getChannelInfo(message.getConversation().getTarget());
|
||||||
if (channelInfo != null && !StringUtil.isNullOrEmpty(channelInfo.getCallback())) {
|
if (channelInfo != null && !StringUtil.isNullOrEmpty(channelInfo.getCallback()) && channelInfo.getAutomatic() == 1 && !message.getFromUser().equals(channelInfo.getOwner())) {
|
||||||
|
OutputClient outputClient = null;
|
||||||
|
if(m_messagesStore.isChannelCallbackWithClientInfo() && !StringUtil.isNullOrEmpty(exceptClientId)) {
|
||||||
|
Session session = m_sessionsStore.getSession(exceptClientId);
|
||||||
|
if(session != null && session.getUsername().equals(message.getFromUser())) {
|
||||||
|
outputClient = new OutputClient(session.getPlatform(), exceptClientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputClient finalOutputClient = outputClient;
|
||||||
Server.getServer().getCallbackScheduler().execute(() -> {
|
Server.getServer().getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(channelInfo.getCallback() + "/message", new Gson().toJson(SendMessageData.fromProtoMessage(message), SendMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Channel_Message_Callback);
|
HttpUtils.httpJsonPost(channelInfo.getCallback() + "/message", GsonUtil.gson.toJson(OutputMessageData.fromProtoMessage(message, finalOutputClient), OutputMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Channel_Message_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -629,8 +646,8 @@ public class MessagesPublisher {
|
|||||||
pushContent = "[语音]";
|
pushContent = "[语音]";
|
||||||
} else if(type == ProtoConstants.ContentType.Video) {
|
} else if(type == ProtoConstants.ContentType.Video) {
|
||||||
pushContent = "[视频]";
|
pushContent = "[视频]";
|
||||||
} else if(type == ProtoConstants.ContentType.RichMedia) {
|
} else if(type == ProtoConstants.ContentType.Link) {
|
||||||
pushContent = "[图文]";
|
pushContent = "[链接]";
|
||||||
} else if(type == ProtoConstants.ContentType.File) {
|
} else if(type == ProtoConstants.ContentType.File) {
|
||||||
pushContent = "[文件]";
|
pushContent = "[文件]";
|
||||||
} else if(type == ProtoConstants.ContentType.Sticker) {
|
} else if(type == ProtoConstants.ContentType.Sticker) {
|
||||||
@@ -646,14 +663,14 @@ public class MessagesPublisher {
|
|||||||
message.getConversation().getType(), message.getConversation().getTarget(), message.getConversation().getLine(),
|
message.getConversation().getType(), message.getConversation().getTarget(), message.getConversation().getLine(),
|
||||||
messageId,
|
messageId,
|
||||||
receivers,
|
receivers,
|
||||||
pushContent, message.getContent().getPushData(), exceptClientId, pullType, message.getContent().getType(), message.getServerTimestamp(), message.getContent().getMentionedType(), message.getContent().getMentionedTargetList(), message.getContent().getPersistFlag());
|
pushContent, message.getContent().getPushData(), exceptClientId, pullType, message.getContent().getType(), message.getServerTimestamp(), message.getContent().getMentionedType(), message.getContent().getMentionedTargetList(), message.getContent().getPersistFlag(), message.hasToUser() || !message.getToList().isEmpty());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forwardMessage(final WFCMessage.Message message, String forwardUrl) {
|
public void forwardMessage(final WFCMessage.Message message, String forwardUrl, OutputClient outputClient) {
|
||||||
Server.getServer().getCallbackScheduler().execute(() -> {
|
Server.getServer().getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(forwardUrl, new Gson().toJson(OutputMessageData.fromProtoMessage(message), OutputMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Forward_Message_Callback);
|
HttpUtils.httpJsonPost(forwardUrl, GsonUtil.gson.toJson(OutputMessageData.fromProtoMessage(message, outputClient), OutputMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Forward_Message_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -663,7 +680,7 @@ public class MessagesPublisher {
|
|||||||
|
|
||||||
public void forwardMessageWithCallback(final WFCMessage.Message message, String forwardUrl, HttpUtils.HttpCallback callback) {
|
public void forwardMessageWithCallback(final WFCMessage.Message message, String forwardUrl, HttpUtils.HttpCallback callback) {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(forwardUrl, new Gson().toJson(OutputMessageData.fromProtoMessage(message), OutputMessageData.class), new HttpUtils.HttpCallback() {
|
HttpUtils.httpJsonPost(forwardUrl, GsonUtil.gson.toJson(OutputMessageData.fromProtoMessage(message), OutputMessageData.class), new HttpUtils.HttpCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(String content) {
|
public void onSuccess(String content) {
|
||||||
Server.getServer().getImBusinessScheduler().execute(()->{
|
Server.getServer().getImBusinessScheduler().execute(()->{
|
||||||
@@ -685,13 +702,13 @@ public class MessagesPublisher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void notifyChannelListenStatusChanged(WFCMessage.ChannelInfo channelInfo, String user, boolean listen) {
|
public void notifyChannelListenStatusChanged(WFCMessage.ChannelInfo channelInfo, String user, boolean listen) {
|
||||||
if (channelInfo == null || StringUtil.isNullOrEmpty(channelInfo.getCallback())) {
|
if (channelInfo == null || StringUtil.isNullOrEmpty(channelInfo.getCallback()) || channelInfo.getAutomatic() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Server.getServer().getCallbackScheduler().execute(() -> {
|
Server.getServer().getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(channelInfo.getCallback() + "/subscribe", new Gson().toJson(new OutputNotifyChannelSubscribeStatus(user, channelInfo.getTargetId(), listen), OutputNotifyChannelSubscribeStatus.class), HttpUtils.HttpPostType.POST_TYPE_Channel_Subscriber_Event_Callback);
|
HttpUtils.httpJsonPost(channelInfo.getCallback() + "/subscribe", GsonUtil.gson.toJson(new OutputNotifyChannelSubscribeStatus(user, channelInfo.getTargetId(), listen), OutputNotifyChannelSubscribeStatus.class), HttpUtils.HttpPostType.POST_TYPE_Channel_Subscriber_Event_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import io.netty.handler.codec.mqtt.*;
|
|||||||
import io.netty.handler.timeout.IdleStateHandler;
|
import io.netty.handler.timeout.IdleStateHandler;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import win.liyufan.im.GsonUtil;
|
||||||
import win.liyufan.im.HttpUtils;
|
import win.liyufan.im.HttpUtils;
|
||||||
import win.liyufan.im.IMTopic;
|
import win.liyufan.im.IMTopic;
|
||||||
import win.liyufan.im.Utility;
|
import win.liyufan.im.Utility;
|
||||||
@@ -264,7 +265,7 @@ public class ProtocolProcessor {
|
|||||||
if (!StringUtil.isNullOrEmpty(mUserOnlineStatusCallback)) {
|
if (!StringUtil.isNullOrEmpty(mUserOnlineStatusCallback)) {
|
||||||
mServer.getCallbackScheduler().execute(() -> {
|
mServer.getCallbackScheduler().execute(() -> {
|
||||||
try {
|
try {
|
||||||
HttpUtils.httpJsonPost(mUserOnlineStatusCallback, new Gson().toJson(new UserOnlineStatus(userId, clientId, platform, status, packageName)), HttpUtils.HttpPostType.POST_TYPE_User_Online_Event_Callback);
|
HttpUtils.httpJsonPost(mUserOnlineStatusCallback, GsonUtil.gson.toJson(new UserOnlineStatus(userId, clientId, platform, status, packageName)), HttpUtils.HttpPostType.POST_TYPE_User_Online_Event_Callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
|
||||||
@@ -307,7 +308,7 @@ public class ProtocolProcessor {
|
|||||||
}
|
}
|
||||||
byte[] pwd = null;
|
byte[] pwd = null;
|
||||||
if (msg.variableHeader().hasPassword()) {
|
if (msg.variableHeader().hasPassword()) {
|
||||||
pwd = msg.payload().password();
|
pwd = msg.payload().passwordInBytes();
|
||||||
|
|
||||||
MemorySessionStore.Session session = m_sessionsStore.getSession(clientId);
|
MemorySessionStore.Session session = m_sessionsStore.getSession(clientId);
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
@@ -620,15 +621,27 @@ public class ProtocolProcessor {
|
|||||||
String username = NettyUtils.userName(channel);
|
String username = NettyUtils.userName(channel);
|
||||||
MemorySessionStore.Session session = m_sessionsStore.getSession(clientID);
|
MemorySessionStore.Session session = m_sessionsStore.getSession(clientID);
|
||||||
if(session != null) {
|
if(session != null) {
|
||||||
session.refreshLastActiveTime();
|
processOffline(session, clearSession, () -> {
|
||||||
forwardOnlineStatusEvent(username, clientID, session.getPlatform(), clearSession ? UserOnlineStatus.LOGOUT : UserOnlineStatus.OFFLINE, session.getAppName());
|
ConnectionDescriptor oldConnDescr = new ConnectionDescriptor(clientID, channel);
|
||||||
|
if(connectionDescriptors.removeConnection(oldConnDescr)) {
|
||||||
|
m_interceptor.notifyClientConnectionLost(clientID, username);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ConnectionDescriptor oldConnDescr = new ConnectionDescriptor(clientID, channel);
|
||||||
|
if(connectionDescriptors.removeConnection(oldConnDescr)) {
|
||||||
|
m_interceptor.notifyClientConnectionLost(clientID, username);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ConnectionDescriptor oldConnDescr = new ConnectionDescriptor(clientID, channel);
|
public void processOffline(MemorySessionStore.Session session, boolean logout, Runnable runnable) {
|
||||||
if(connectionDescriptors.removeConnection(oldConnDescr)) {
|
|
||||||
m_interceptor.notifyClientConnectionLost(clientID, username);
|
|
||||||
}
|
|
||||||
if(session != null) {
|
if(session != null) {
|
||||||
|
session.refreshLastActiveTime();
|
||||||
|
forwardOnlineStatusEvent(session.getUsername(), session.getClientID(), session.getPlatform(), logout ? UserOnlineStatus.LOGOUT : UserOnlineStatus.OFFLINE, session.getAppName());
|
||||||
|
if(runnable != null) {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
m_messagesStore.updateUserOnlineSetting(session, false);
|
m_messagesStore.updateUserOnlineSetting(session, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import io.netty.handler.codec.mqtt.MqttFixedHeader;
|
|||||||
import io.netty.handler.codec.mqtt.MqttMessageType;
|
import io.netty.handler.codec.mqtt.MqttMessageType;
|
||||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||||
import cn.wildfirechat.common.ErrorCode;
|
import cn.wildfirechat.common.ErrorCode;
|
||||||
|
import win.liyufan.im.GsonUtil;
|
||||||
import win.liyufan.im.IMTopic;
|
import win.liyufan.im.IMTopic;
|
||||||
import win.liyufan.im.RateLimiter;
|
import win.liyufan.im.RateLimiter;
|
||||||
import win.liyufan.im.Utility;
|
import win.liyufan.im.Utility;
|
||||||
@@ -142,7 +143,7 @@ public class Qos1PublishHandler extends QosPublishHandler {
|
|||||||
out.addSession(userId, session.getClientID(), session.getPlatform(), status, session.getLastActiveTime());
|
out.addSession(userId, session.getClientID(), session.getPlatform(), status, session.getLastActiveTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
callback.onRouteHandled(new Gson().toJson(out).getBytes());
|
callback.onRouteHandled(GsonUtil.gson.toJson(out).getBytes());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,16 +164,19 @@ public class Qos1PublishHandler extends QosPublishHandler {
|
|||||||
} else {
|
} else {
|
||||||
MemorySessionStore.Session session = m_sessionStore.getSession(clientID);
|
MemorySessionStore.Session session = m_sessionStore.getSession(clientID);
|
||||||
if (session != null && session.getUsername().equals(fromUser)) {
|
if (session != null && session.getUsername().equals(fromUser)) {
|
||||||
if (data.length > 7*1024 && session.getMqttVersion().protocolLevel() >= MqttVersion.Wildfire_1.protocolLevel()) {
|
if (data.length > 7*1024 && session.getMqttVersion().protocolLevel() >= MqttVersion.MQTT_5.protocolLevel()) {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
GZIPOutputStream gzip;
|
GZIPOutputStream gzip = null;
|
||||||
try {
|
try {
|
||||||
gzip = new GZIPOutputStream(out);
|
gzip = new GZIPOutputStream(out);
|
||||||
gzip.write(data);
|
gzip.write(data);
|
||||||
gzip.close();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Utility.printExecption(LOG, e);
|
Utility.printExecption(LOG, e);
|
||||||
|
} finally {
|
||||||
|
if(gzip != null) {
|
||||||
|
gzip.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data = out.toByteArray();
|
data = out.toByteArray();
|
||||||
code = (byte)ErrorCode.ERROR_CODE_SUCCESS_GZIPED.code;
|
code = (byte)ErrorCode.ERROR_CODE_SUCCESS_GZIPED.code;
|
||||||
|
|||||||
Executable → Regular
+32
-9
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -16,13 +16,30 @@
|
|||||||
|
|
||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.DecoderException;
|
import io.netty.handler.codec.DecoderException;
|
||||||
|
import io.netty.util.Attribute;
|
||||||
|
import io.netty.util.AttributeKey;
|
||||||
|
|
||||||
final class MqttCodecUtil {
|
final class MqttCodecUtil {
|
||||||
|
|
||||||
private static final char[] TOPIC_WILDCARDS = {'#', '+'};
|
private static final char[] TOPIC_WILDCARDS = {'#', '+'};
|
||||||
private static final int MIN_CLIENT_ID_LENGTH = 1;
|
|
||||||
private static final int MAX_CLIENT_ID_LENGTH = 23;
|
static final AttributeKey<MqttVersion> MQTT_VERSION_KEY = AttributeKey.valueOf("NETTY_CODEC_MQTT_VERSION");
|
||||||
|
|
||||||
|
static MqttVersion getMqttVersion(ChannelHandlerContext ctx) {
|
||||||
|
Attribute<MqttVersion> attr = ctx.channel().attr(MQTT_VERSION_KEY);
|
||||||
|
MqttVersion version = attr.get();
|
||||||
|
if (version == null) {
|
||||||
|
return MqttVersion.MQTT_3_1_1;
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setMqttVersion(ChannelHandlerContext ctx, MqttVersion version) {
|
||||||
|
Attribute<MqttVersion> attr = ctx.channel().attr(MQTT_VERSION_KEY);
|
||||||
|
attr.set(version);
|
||||||
|
}
|
||||||
|
|
||||||
static boolean isValidPublishTopicName(String topicName) {
|
static boolean isValidPublishTopicName(String topicName) {
|
||||||
// publish topic name must not contain any wildcard
|
// publish topic name must not contain any wildcard
|
||||||
@@ -38,20 +55,20 @@ final class MqttCodecUtil {
|
|||||||
return messageId != 0;
|
return messageId != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isValidClientId(MqttVersion mqttVersion, String clientId) {
|
static boolean isValidClientId(MqttVersion mqttVersion, int maxClientIdLength, String clientId) {
|
||||||
if (mqttVersion == MqttVersion.MQTT_3_1) {
|
if (mqttVersion == MqttVersion.MQTT_3_1) {
|
||||||
return clientId != null && clientId.length() >= MIN_CLIENT_ID_LENGTH &&
|
return clientId != null && clientId.length() >= MqttConstant.MIN_CLIENT_ID_LENGTH &&
|
||||||
clientId.length() <= MAX_CLIENT_ID_LENGTH;
|
clientId.length() <= maxClientIdLength;
|
||||||
}
|
}
|
||||||
if (mqttVersion.protocolLevel() >= MqttVersion.MQTT_3_1_1.protocolLevel()) {
|
if (mqttVersion.protocolLevel() >= MqttVersion.MQTT_3_1_1.protocolLevel()) {
|
||||||
// In 3.1.3.1 Client Identifier of MQTT 3.1.1 specification, The Server MAY allow ClientId’s
|
// In 3.1.3.1 Client Identifier of MQTT 3.1.1 and 5.0 specifications, The Server MAY allow ClientId’s
|
||||||
// that contain more than 23 encoded bytes. And, The Server MAY allow zero-length ClientId.
|
// that contain more than 23 encoded bytes. And, The Server MAY allow zero-length ClientId.
|
||||||
return clientId != null;
|
return clientId != null;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException(mqttVersion + " is unknown mqtt version");
|
throw new IllegalArgumentException(mqttVersion + " is unknown mqtt version");
|
||||||
}
|
}
|
||||||
|
|
||||||
static MqttFixedHeader validateFixedHeader(MqttFixedHeader mqttFixedHeader) {
|
static MqttFixedHeader validateFixedHeader(ChannelHandlerContext ctx, MqttFixedHeader mqttFixedHeader) {
|
||||||
switch (mqttFixedHeader.messageType()) {
|
switch (mqttFixedHeader.messageType()) {
|
||||||
case PUBREL:
|
case PUBREL:
|
||||||
case SUBSCRIBE:
|
case SUBSCRIBE:
|
||||||
@@ -59,6 +76,12 @@ final class MqttCodecUtil {
|
|||||||
if (mqttFixedHeader.qosLevel() != MqttQoS.AT_LEAST_ONCE) {
|
if (mqttFixedHeader.qosLevel() != MqttQoS.AT_LEAST_ONCE) {
|
||||||
throw new DecoderException(mqttFixedHeader.messageType().name() + " message must have QoS 1");
|
throw new DecoderException(mqttFixedHeader.messageType().name() + " message must have QoS 1");
|
||||||
}
|
}
|
||||||
|
return mqttFixedHeader;
|
||||||
|
case AUTH:
|
||||||
|
if (MqttCodecUtil.getMqttVersion(ctx) != MqttVersion.MQTT_5) {
|
||||||
|
throw new DecoderException("AUTH message requires at least MQTT 5");
|
||||||
|
}
|
||||||
|
return mqttFixedHeader;
|
||||||
default:
|
default:
|
||||||
return mqttFixedHeader;
|
return mqttFixedHeader;
|
||||||
}
|
}
|
||||||
@@ -75,7 +98,7 @@ final class MqttCodecUtil {
|
|||||||
case UNSUBACK:
|
case UNSUBACK:
|
||||||
case PINGREQ:
|
case PINGREQ:
|
||||||
case PINGRESP:
|
case PINGRESP:
|
||||||
//case DISCONNECT:
|
case DISCONNECT:
|
||||||
if (mqttFixedHeader.isDup() ||
|
if (mqttFixedHeader.isDup() ||
|
||||||
mqttFixedHeader.qosLevel() != MqttQoS.AT_MOST_ONCE ||
|
mqttFixedHeader.qosLevel() != MqttQoS.AT_MOST_ONCE ||
|
||||||
mqttFixedHeader.isRetain()) {
|
mqttFixedHeader.isRetain()) {
|
||||||
|
|||||||
Executable → Regular
+3
-3
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connack">MQTTV3.1/connack</a>
|
* See <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connack">MQTTV3.1/connack</a>
|
||||||
*/
|
*/
|
||||||
public final class MqttConnAckMessage extends MqttMessage {
|
public final class MqttConnAckMessage extends MqttMessage {
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ public final class MqttConnAckMessage extends MqttMessage {
|
|||||||
public MqttConnAckMessage(MqttFixedHeader mqttFixedHeader, MqttConnAckVariableHeader variableHeader, MqttConnectAckPayload payload) {
|
public MqttConnAckMessage(MqttFixedHeader mqttFixedHeader, MqttConnAckVariableHeader variableHeader, MqttConnectAckPayload payload) {
|
||||||
super(mqttFixedHeader, variableHeader, payload);
|
super(mqttFixedHeader, variableHeader, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MqttConnAckVariableHeader variableHeader() {
|
public MqttConnAckVariableHeader variableHeader() {
|
||||||
return (MqttConnAckVariableHeader) super.variableHeader();
|
return (MqttConnAckVariableHeader) super.variableHeader();
|
||||||
|
|||||||
Executable → Regular
+13
-1
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -27,9 +27,17 @@ public final class MqttConnAckVariableHeader {
|
|||||||
|
|
||||||
private final boolean sessionPresent;
|
private final boolean sessionPresent;
|
||||||
|
|
||||||
|
private final MqttProperties properties;
|
||||||
|
|
||||||
public MqttConnAckVariableHeader(MqttConnectReturnCode connectReturnCode, boolean sessionPresent) {
|
public MqttConnAckVariableHeader(MqttConnectReturnCode connectReturnCode, boolean sessionPresent) {
|
||||||
|
this(connectReturnCode, sessionPresent, MqttProperties.NO_PROPERTIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttConnAckVariableHeader(MqttConnectReturnCode connectReturnCode, boolean sessionPresent,
|
||||||
|
MqttProperties properties) {
|
||||||
this.connectReturnCode = connectReturnCode;
|
this.connectReturnCode = connectReturnCode;
|
||||||
this.sessionPresent = sessionPresent;
|
this.sessionPresent = sessionPresent;
|
||||||
|
this.properties = MqttProperties.withEmptyDefaults(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttConnectReturnCode connectReturnCode() {
|
public MqttConnectReturnCode connectReturnCode() {
|
||||||
@@ -40,6 +48,10 @@ public final class MqttConnAckVariableHeader {
|
|||||||
return sessionPresent;
|
return sessionPresent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttProperties properties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder(StringUtil.simpleClassName(this))
|
return new StringBuilder(StringUtil.simpleClassName(this))
|
||||||
|
|||||||
Executable → Regular
+2
-2
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect">MQTTV3.1/connect</a>
|
* See <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect">MQTTV3.1/connect</a>
|
||||||
*/
|
*/
|
||||||
public final class MqttConnectMessage extends MqttMessage {
|
public final class MqttConnectMessage extends MqttMessage {
|
||||||
|
|
||||||
|
|||||||
Executable → Regular
+65
-5
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,18 +27,55 @@ import io.netty.util.internal.StringUtil;
|
|||||||
public final class MqttConnectPayload {
|
public final class MqttConnectPayload {
|
||||||
|
|
||||||
private final String clientIdentifier;
|
private final String clientIdentifier;
|
||||||
|
private final MqttProperties willProperties;
|
||||||
private final String willTopic;
|
private final String willTopic;
|
||||||
private final String willMessage;
|
private final byte[] willMessage;
|
||||||
private final String userName;
|
private final String userName;
|
||||||
private final byte[] password;
|
private final byte[] password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link MqttConnectPayload#MqttConnectPayload(String,
|
||||||
|
* MqttProperties, String, byte[], String, byte[])} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public MqttConnectPayload(
|
public MqttConnectPayload(
|
||||||
String clientIdentifier,
|
String clientIdentifier,
|
||||||
String willTopic,
|
String willTopic,
|
||||||
String willMessage,
|
String willMessage,
|
||||||
String userName,
|
String userName,
|
||||||
|
String password) {
|
||||||
|
this(
|
||||||
|
clientIdentifier,
|
||||||
|
MqttProperties.NO_PROPERTIES,
|
||||||
|
willTopic,
|
||||||
|
willMessage.getBytes(CharsetUtil.UTF_8),
|
||||||
|
userName,
|
||||||
|
password.getBytes(CharsetUtil.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttConnectPayload(
|
||||||
|
String clientIdentifier,
|
||||||
|
String willTopic,
|
||||||
|
byte[] willMessage,
|
||||||
|
String userName,
|
||||||
|
byte[] password) {
|
||||||
|
this(clientIdentifier,
|
||||||
|
MqttProperties.NO_PROPERTIES,
|
||||||
|
willTopic,
|
||||||
|
willMessage,
|
||||||
|
userName,
|
||||||
|
password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttConnectPayload(
|
||||||
|
String clientIdentifier,
|
||||||
|
MqttProperties willProperties,
|
||||||
|
String willTopic,
|
||||||
|
byte[] willMessage,
|
||||||
|
String userName,
|
||||||
byte[] password) {
|
byte[] password) {
|
||||||
this.clientIdentifier = clientIdentifier;
|
this.clientIdentifier = clientIdentifier;
|
||||||
|
this.willProperties = MqttProperties.withEmptyDefaults(willProperties);
|
||||||
this.willTopic = willTopic;
|
this.willTopic = willTopic;
|
||||||
this.willMessage = willMessage;
|
this.willMessage = willMessage;
|
||||||
this.userName = userName;
|
this.userName = userName;
|
||||||
@@ -46,11 +86,23 @@ public final class MqttConnectPayload {
|
|||||||
return clientIdentifier;
|
return clientIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttProperties willProperties() {
|
||||||
|
return willProperties;
|
||||||
|
}
|
||||||
|
|
||||||
public String willTopic() {
|
public String willTopic() {
|
||||||
return willTopic;
|
return willTopic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link MqttConnectPayload#willMessageInBytes()} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public String willMessage() {
|
public String willMessage() {
|
||||||
|
return willMessage == null ? null : new String(willMessage, CharsetUtil.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] willMessageInBytes() {
|
||||||
return willMessage;
|
return willMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +110,15 @@ public final class MqttConnectPayload {
|
|||||||
return userName;
|
return userName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] password() {
|
/**
|
||||||
|
* @deprecated use {@link MqttConnectPayload#passwordInBytes()} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public String password() {
|
||||||
|
return password == null ? null : new String(password, CharsetUtil.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] passwordInBytes() {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,9 +128,9 @@ public final class MqttConnectPayload {
|
|||||||
.append('[')
|
.append('[')
|
||||||
.append("clientIdentifier=").append(clientIdentifier)
|
.append("clientIdentifier=").append(clientIdentifier)
|
||||||
.append(", willTopic=").append(willTopic)
|
.append(", willTopic=").append(willTopic)
|
||||||
.append(", willMessage=").append(willMessage)
|
.append(", willMessage=").append(Arrays.toString(willMessage))
|
||||||
.append(", userName=").append(userName)
|
.append(", userName=").append(userName)
|
||||||
.append(", password=").append(password)
|
.append(", password=").append(Arrays.toString(password))
|
||||||
.append(']')
|
.append(']')
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+42
-14
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -16,31 +16,52 @@
|
|||||||
|
|
||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return Code of {@link MqttConnAckMessage}
|
* Return Code of {@link MqttConnAckMessage}
|
||||||
*/
|
*/
|
||||||
public enum MqttConnectReturnCode {
|
public enum MqttConnectReturnCode {
|
||||||
CONNECTION_ACCEPTED((byte) 0x00),
|
CONNECTION_ACCEPTED((byte) 0x00),
|
||||||
|
//MQTT 3 codes
|
||||||
CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION((byte) 0X01),
|
CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION((byte) 0X01),
|
||||||
CONNECTION_REFUSED_IDENTIFIER_REJECTED((byte) 0x02),
|
CONNECTION_REFUSED_IDENTIFIER_REJECTED((byte) 0x02),
|
||||||
CONNECTION_REFUSED_SERVER_UNAVAILABLE((byte) 0x03),
|
CONNECTION_REFUSED_SERVER_UNAVAILABLE((byte) 0x03),
|
||||||
CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD((byte) 0x04),
|
CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD((byte) 0x04),
|
||||||
CONNECTION_REFUSED_NOT_AUTHORIZED((byte) 0x05),
|
CONNECTION_REFUSED_NOT_AUTHORIZED((byte) 0x05),
|
||||||
CONNECTION_REFUSED_UNEXPECT_NODE((byte) 0X06),
|
CONNECTION_REFUSED_UNEXPECT_NODE((byte) 0X06),
|
||||||
CONNECTION_REFUSED_SESSION_NOT_EXIST((byte) 0x07);
|
CONNECTION_REFUSED_SESSION_NOT_EXIST((byte) 0x07),
|
||||||
|
//MQTT 5 codes
|
||||||
|
CONNECTION_REFUSED_UNSPECIFIED_ERROR((byte) 0x80),
|
||||||
|
CONNECTION_REFUSED_MALFORMED_PACKET((byte) 0x81),
|
||||||
|
CONNECTION_REFUSED_PROTOCOL_ERROR((byte) 0x82),
|
||||||
|
CONNECTION_REFUSED_IMPLEMENTATION_SPECIFIC((byte) 0x83),
|
||||||
|
CONNECTION_REFUSED_UNSUPPORTED_PROTOCOL_VERSION((byte) 0x84),
|
||||||
|
CONNECTION_REFUSED_CLIENT_IDENTIFIER_NOT_VALID((byte) 0x85),
|
||||||
|
CONNECTION_REFUSED_BAD_USERNAME_OR_PASSWORD((byte) 0x86),
|
||||||
|
CONNECTION_REFUSED_NOT_AUTHORIZED_5((byte) 0x87),
|
||||||
|
CONNECTION_REFUSED_SERVER_UNAVAILABLE_5((byte) 0x88),
|
||||||
|
CONNECTION_REFUSED_SERVER_BUSY((byte) 0x89),
|
||||||
|
CONNECTION_REFUSED_BANNED((byte) 0x8A),
|
||||||
|
CONNECTION_REFUSED_BAD_AUTHENTICATION_METHOD((byte) 0x8C),
|
||||||
|
CONNECTION_REFUSED_TOPIC_NAME_INVALID((byte) 0x90),
|
||||||
|
CONNECTION_REFUSED_PACKET_TOO_LARGE((byte) 0x95),
|
||||||
|
CONNECTION_REFUSED_QUOTA_EXCEEDED((byte) 0x97),
|
||||||
|
CONNECTION_REFUSED_PAYLOAD_FORMAT_INVALID((byte) 0x99),
|
||||||
|
CONNECTION_REFUSED_RETAIN_NOT_SUPPORTED((byte) 0x9A),
|
||||||
|
CONNECTION_REFUSED_QOS_NOT_SUPPORTED((byte) 0x9B),
|
||||||
|
CONNECTION_REFUSED_USE_ANOTHER_SERVER((byte) 0x9C),
|
||||||
|
CONNECTION_REFUSED_SERVER_MOVED((byte) 0x9D),
|
||||||
|
CONNECTION_REFUSED_CONNECTION_RATE_EXCEEDED((byte) 0x9F);
|
||||||
|
|
||||||
private static final Map<Byte, MqttConnectReturnCode> VALUE_TO_CODE_MAP;
|
private static final MqttConnectReturnCode[] VALUES;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
final Map<Byte, MqttConnectReturnCode> valueMap = new HashMap<Byte, MqttConnectReturnCode>();
|
MqttConnectReturnCode[] values = values();
|
||||||
for (MqttConnectReturnCode code: values()) {
|
VALUES = new MqttConnectReturnCode[160];
|
||||||
valueMap.put(code.byteValue, code);
|
for (MqttConnectReturnCode code : values) {
|
||||||
|
final int unsignedByte = code.byteValue & 0xFF;
|
||||||
|
// Suppress a warning about out of bounds access since the enum contains only correct values
|
||||||
|
VALUES[unsignedByte] = code; // lgtm [java/index-out-of-bounds]
|
||||||
}
|
}
|
||||||
VALUE_TO_CODE_MAP = Collections.unmodifiableMap(valueMap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final byte byteValue;
|
private final byte byteValue;
|
||||||
@@ -54,9 +75,16 @@ public enum MqttConnectReturnCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static MqttConnectReturnCode valueOf(byte b) {
|
public static MqttConnectReturnCode valueOf(byte b) {
|
||||||
if (VALUE_TO_CODE_MAP.containsKey(b)) {
|
final int unsignedByte = b & 0xFF;
|
||||||
return VALUE_TO_CODE_MAP.get(b);
|
MqttConnectReturnCode mqttConnectReturnCode = null;
|
||||||
|
try {
|
||||||
|
mqttConnectReturnCode = VALUES[unsignedByte];
|
||||||
|
} catch (ArrayIndexOutOfBoundsException ignored) {
|
||||||
|
// no op
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("unknown connect return code: " + (b & 0xFF));
|
if (mqttConnectReturnCode == null) {
|
||||||
|
throw new IllegalArgumentException("unknown connect return code: " + unsignedByte);
|
||||||
|
}
|
||||||
|
return mqttConnectReturnCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+30
-1
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -32,6 +32,7 @@ public final class MqttConnectVariableHeader {
|
|||||||
private final boolean isWillFlag;
|
private final boolean isWillFlag;
|
||||||
private final boolean isCleanSession;
|
private final boolean isCleanSession;
|
||||||
private final int keepAliveTimeSeconds;
|
private final int keepAliveTimeSeconds;
|
||||||
|
private final MqttProperties properties;
|
||||||
|
|
||||||
public MqttConnectVariableHeader(
|
public MqttConnectVariableHeader(
|
||||||
String name,
|
String name,
|
||||||
@@ -43,6 +44,29 @@ public final class MqttConnectVariableHeader {
|
|||||||
boolean isWillFlag,
|
boolean isWillFlag,
|
||||||
boolean isCleanSession,
|
boolean isCleanSession,
|
||||||
int keepAliveTimeSeconds) {
|
int keepAliveTimeSeconds) {
|
||||||
|
this(name,
|
||||||
|
version,
|
||||||
|
hasUserName,
|
||||||
|
hasPassword,
|
||||||
|
isWillRetain,
|
||||||
|
willQos,
|
||||||
|
isWillFlag,
|
||||||
|
isCleanSession,
|
||||||
|
keepAliveTimeSeconds,
|
||||||
|
MqttProperties.NO_PROPERTIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttConnectVariableHeader(
|
||||||
|
String name,
|
||||||
|
int version,
|
||||||
|
boolean hasUserName,
|
||||||
|
boolean hasPassword,
|
||||||
|
boolean isWillRetain,
|
||||||
|
int willQos,
|
||||||
|
boolean isWillFlag,
|
||||||
|
boolean isCleanSession,
|
||||||
|
int keepAliveTimeSeconds,
|
||||||
|
MqttProperties properties) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.hasUserName = hasUserName;
|
this.hasUserName = hasUserName;
|
||||||
@@ -52,6 +76,7 @@ public final class MqttConnectVariableHeader {
|
|||||||
this.isWillFlag = isWillFlag;
|
this.isWillFlag = isWillFlag;
|
||||||
this.isCleanSession = isCleanSession;
|
this.isCleanSession = isCleanSession;
|
||||||
this.keepAliveTimeSeconds = keepAliveTimeSeconds;
|
this.keepAliveTimeSeconds = keepAliveTimeSeconds;
|
||||||
|
this.properties = MqttProperties.withEmptyDefaults(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -90,6 +115,10 @@ public final class MqttConnectVariableHeader {
|
|||||||
return keepAliveTimeSeconds;
|
return keepAliveTimeSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttProperties properties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder(StringUtil.simpleClassName(this))
|
return new StringBuilder(StringUtil.simpleClassName(this))
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
public final class MqttConstant {
|
||||||
|
|
||||||
|
private MqttConstant() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default max bytes in message
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_MAX_BYTES_IN_MESSAGE = 262144; //256KB
|
||||||
|
|
||||||
|
/**
|
||||||
|
* min client id length
|
||||||
|
*/
|
||||||
|
public static final int MIN_CLIENT_ID_LENGTH = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default max client id length,In the mqtt3.1 protocol,
|
||||||
|
* the default maximum Client Identifier length is 23
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_MAX_CLIENT_ID_LENGTH = 23;
|
||||||
|
|
||||||
|
}
|
||||||
Executable → Regular
+436
-112
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -20,26 +20,22 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.DecoderException;
|
import io.netty.handler.codec.DecoderException;
|
||||||
import io.netty.handler.codec.ReplayingDecoder;
|
import io.netty.handler.codec.ReplayingDecoder;
|
||||||
import io.netty.handler.codec.mqtt.MqttDecoder.DecoderState;
|
import io.netty.handler.codec.TooLongFrameException;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
import io.netty.util.internal.ObjectUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static io.netty.handler.codec.mqtt.MqttCodecUtil.isValidClientId;
|
|
||||||
import static io.netty.handler.codec.mqtt.MqttCodecUtil.isValidMessageId;
|
|
||||||
import static io.netty.handler.codec.mqtt.MqttCodecUtil.isValidPublishTopicName;
|
|
||||||
import static io.netty.handler.codec.mqtt.MqttCodecUtil.resetUnusedFields;
|
|
||||||
import static io.netty.handler.codec.mqtt.MqttCodecUtil.validateFixedHeader;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes Mqtt messages from bytes, following
|
* Decodes Mqtt messages from bytes, following
|
||||||
* <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html">
|
* the MQTT protocol specification
|
||||||
* the MQTT protocol specification v3.1</a>
|
* <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html">v3.1</a>
|
||||||
|
* or
|
||||||
|
* <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html">v5.0</a>, depending on the
|
||||||
|
* version specified in the CONNECT message that first goes through the channel.
|
||||||
*/
|
*/
|
||||||
public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
public final class MqttDecoder extends ReplayingDecoder<MqttDecoder.DecoderState> {
|
||||||
|
|
||||||
private static final int DEFAULT_MAX_BYTES_IN_MESSAGE = 262144; //256K
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* States of the decoder.
|
* States of the decoder.
|
||||||
@@ -58,31 +54,42 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
private int bytesRemainingInVariablePart;
|
private int bytesRemainingInVariablePart;
|
||||||
|
|
||||||
private final int maxBytesInMessage;
|
private final int maxBytesInMessage;
|
||||||
|
private final int maxClientIdLength;
|
||||||
|
|
||||||
public MqttDecoder() {
|
public MqttDecoder() {
|
||||||
this(DEFAULT_MAX_BYTES_IN_MESSAGE);
|
this(MqttConstant.DEFAULT_MAX_BYTES_IN_MESSAGE, MqttConstant.DEFAULT_MAX_CLIENT_ID_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttDecoder(int maxBytesInMessage) {
|
public MqttDecoder(int maxBytesInMessage) {
|
||||||
|
this(maxBytesInMessage, MqttConstant.DEFAULT_MAX_CLIENT_ID_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttDecoder(int maxBytesInMessage, int maxClientIdLength) {
|
||||||
super(DecoderState.READ_FIXED_HEADER);
|
super(DecoderState.READ_FIXED_HEADER);
|
||||||
this.maxBytesInMessage = maxBytesInMessage;
|
this.maxBytesInMessage = ObjectUtil.checkPositive(maxBytesInMessage, "maxBytesInMessage");
|
||||||
|
this.maxClientIdLength = ObjectUtil.checkPositive(maxClientIdLength, "maxClientIdLength");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
|
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
|
||||||
switch (state()) {
|
switch (state()) {
|
||||||
case READ_FIXED_HEADER:
|
case READ_FIXED_HEADER: try {
|
||||||
mqttFixedHeader = decodeFixedHeader(buffer);
|
mqttFixedHeader = decodeFixedHeader(ctx, buffer);
|
||||||
bytesRemainingInVariablePart = mqttFixedHeader.remainingLength();
|
bytesRemainingInVariablePart = mqttFixedHeader.remainingLength();
|
||||||
checkpoint(DecoderState.READ_VARIABLE_HEADER);
|
checkpoint(DecoderState.READ_VARIABLE_HEADER);
|
||||||
// fall through
|
// fall through
|
||||||
|
} catch (Exception cause) {
|
||||||
|
out.add(invalidMessage(cause));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case READ_VARIABLE_HEADER: try {
|
case READ_VARIABLE_HEADER: try {
|
||||||
if (bytesRemainingInVariablePart > maxBytesInMessage) {
|
final Result<?> decodedVariableHeader = decodeVariableHeader(ctx, buffer, mqttFixedHeader);
|
||||||
throw new DecoderException("too large message: " + bytesRemainingInVariablePart + " bytes");
|
|
||||||
}
|
|
||||||
final Result<?> decodedVariableHeader = decodeVariableHeader(buffer, mqttFixedHeader);
|
|
||||||
variableHeader = decodedVariableHeader.value;
|
variableHeader = decodedVariableHeader.value;
|
||||||
|
if (bytesRemainingInVariablePart > maxBytesInMessage) {
|
||||||
|
buffer.skipBytes(actualReadableBytes());
|
||||||
|
throw new TooLongFrameException("too large message: " + bytesRemainingInVariablePart + " bytes");
|
||||||
|
}
|
||||||
bytesRemainingInVariablePart -= decodedVariableHeader.numberOfBytesConsumed;
|
bytesRemainingInVariablePart -= decodedVariableHeader.numberOfBytesConsumed;
|
||||||
checkpoint(DecoderState.READ_PAYLOAD);
|
checkpoint(DecoderState.READ_PAYLOAD);
|
||||||
// fall through
|
// fall through
|
||||||
@@ -94,9 +101,11 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
case READ_PAYLOAD: try {
|
case READ_PAYLOAD: try {
|
||||||
final Result<?> decodedPayload =
|
final Result<?> decodedPayload =
|
||||||
decodePayload(
|
decodePayload(
|
||||||
|
ctx,
|
||||||
buffer,
|
buffer,
|
||||||
mqttFixedHeader.messageType(),
|
mqttFixedHeader.messageType(),
|
||||||
bytesRemainingInVariablePart,
|
bytesRemainingInVariablePart,
|
||||||
|
maxClientIdLength,
|
||||||
variableHeader);
|
variableHeader);
|
||||||
bytesRemainingInVariablePart -= decodedPayload.numberOfBytesConsumed;
|
bytesRemainingInVariablePart -= decodedPayload.numberOfBytesConsumed;
|
||||||
if (bytesRemainingInVariablePart != 0) {
|
if (bytesRemainingInVariablePart != 0) {
|
||||||
@@ -129,16 +138,20 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
|
|
||||||
private MqttMessage invalidMessage(Throwable cause) {
|
private MqttMessage invalidMessage(Throwable cause) {
|
||||||
checkpoint(DecoderState.BAD_MESSAGE);
|
checkpoint(DecoderState.BAD_MESSAGE);
|
||||||
return MqttMessageFactory.newInvalidMessage(cause);
|
return MqttMessageFactory.newInvalidMessage(mqttFixedHeader, variableHeader, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes the fixed header. It's one byte for the flags and then variable bytes for the remaining length.
|
* Decodes the fixed header. It's one byte for the flags and then variable
|
||||||
|
* bytes for the remaining length.
|
||||||
|
*
|
||||||
|
* @see
|
||||||
|
* https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180841
|
||||||
*
|
*
|
||||||
* @param buffer the buffer to decode from
|
* @param buffer the buffer to decode from
|
||||||
* @return the fixed header
|
* @return the fixed header
|
||||||
*/
|
*/
|
||||||
private static MqttFixedHeader decodeFixedHeader(ByteBuf buffer) {
|
private static MqttFixedHeader decodeFixedHeader(ChannelHandlerContext ctx, ByteBuf buffer) {
|
||||||
short b1 = buffer.readUnsignedByte();
|
short b1 = buffer.readUnsignedByte();
|
||||||
|
|
||||||
MqttMessageType messageType = MqttMessageType.valueOf(b1 >> 4);
|
MqttMessageType messageType = MqttMessageType.valueOf(b1 >> 4);
|
||||||
@@ -146,6 +159,59 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
int qosLevel = (b1 & 0x06) >> 1;
|
int qosLevel = (b1 & 0x06) >> 1;
|
||||||
boolean retain = (b1 & 0x01) != 0;
|
boolean retain = (b1 & 0x01) != 0;
|
||||||
|
|
||||||
|
switch (messageType) {
|
||||||
|
case PUBLISH:
|
||||||
|
if (qosLevel == 3) {
|
||||||
|
throw new DecoderException("Illegal QOS Level in fixed header of PUBLISH message ("
|
||||||
|
+ qosLevel + ')');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PUBREL:
|
||||||
|
case SUBSCRIBE:
|
||||||
|
case UNSUBSCRIBE:
|
||||||
|
if (dupFlag) {
|
||||||
|
throw new DecoderException("Illegal BIT 3 in fixed header of " + messageType
|
||||||
|
+ " message, must be 0, found 1");
|
||||||
|
}
|
||||||
|
if (qosLevel != 1) {
|
||||||
|
throw new DecoderException("Illegal QOS Level in fixed header of " + messageType
|
||||||
|
+ " message, must be 1, found " + qosLevel);
|
||||||
|
}
|
||||||
|
if (retain) {
|
||||||
|
throw new DecoderException("Illegal BIT 0 in fixed header of " + messageType
|
||||||
|
+ " message, must be 0, found 1");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUTH:
|
||||||
|
case CONNACK:
|
||||||
|
case CONNECT:
|
||||||
|
case DISCONNECT:
|
||||||
|
case PINGREQ:
|
||||||
|
case PINGRESP:
|
||||||
|
case PUBACK:
|
||||||
|
case PUBCOMP:
|
||||||
|
case PUBREC:
|
||||||
|
case SUBACK:
|
||||||
|
case UNSUBACK:
|
||||||
|
if (dupFlag) {
|
||||||
|
throw new DecoderException("Illegal BIT 3 in fixed header of " + messageType
|
||||||
|
+ " message, must be 0, found 1");
|
||||||
|
}
|
||||||
|
if (qosLevel != 0) {
|
||||||
|
throw new DecoderException("Illegal BIT 2 or 1 in fixed header of " + messageType
|
||||||
|
+ " message, must be 0, found " + qosLevel);
|
||||||
|
}
|
||||||
|
if (retain) {
|
||||||
|
throw new DecoderException("Illegal BIT 0 in fixed header of " + messageType
|
||||||
|
+ " message, must be 0, found 1");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new DecoderException("Unknown message type, do not know how to validate fixed header");
|
||||||
|
}
|
||||||
|
|
||||||
int remainingLength = 0;
|
int remainingLength = 0;
|
||||||
int multiplier = 1;
|
int multiplier = 1;
|
||||||
short digit;
|
short digit;
|
||||||
@@ -163,7 +229,7 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
}
|
}
|
||||||
MqttFixedHeader decodedFixedHeader =
|
MqttFixedHeader decodedFixedHeader =
|
||||||
new MqttFixedHeader(messageType, dupFlag, MqttQoS.valueOf(qosLevel), retain, remainingLength);
|
new MqttFixedHeader(messageType, dupFlag, MqttQoS.valueOf(qosLevel), retain, remainingLength);
|
||||||
return validateFixedHeader(resetUnusedFields(decodedFixedHeader));
|
return MqttCodecUtil.validateFixedHeader(ctx, MqttCodecUtil.resetUnusedFields(decodedFixedHeader));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,50 +238,60 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
* @param mqttFixedHeader MqttFixedHeader of the same message
|
* @param mqttFixedHeader MqttFixedHeader of the same message
|
||||||
* @return the variable header
|
* @return the variable header
|
||||||
*/
|
*/
|
||||||
private static Result<?> decodeVariableHeader(ByteBuf buffer, MqttFixedHeader mqttFixedHeader) {
|
private Result<?> decodeVariableHeader(ChannelHandlerContext ctx, ByteBuf buffer, MqttFixedHeader mqttFixedHeader) {
|
||||||
switch (mqttFixedHeader.messageType()) {
|
switch (mqttFixedHeader.messageType()) {
|
||||||
case CONNECT:
|
case CONNECT:
|
||||||
return decodeConnectionVariableHeader(buffer);
|
return decodeConnectionVariableHeader(ctx, buffer);
|
||||||
|
|
||||||
case CONNACK:
|
case CONNACK:
|
||||||
return decodeConnAckVariableHeader(buffer);
|
return decodeConnAckVariableHeader(ctx, buffer);
|
||||||
|
|
||||||
case SUBSCRIBE:
|
|
||||||
case UNSUBSCRIBE:
|
case UNSUBSCRIBE:
|
||||||
|
case SUBSCRIBE:
|
||||||
case SUBACK:
|
case SUBACK:
|
||||||
case UNSUBACK:
|
case UNSUBACK:
|
||||||
|
return decodeMessageIdAndPropertiesVariableHeader(ctx, buffer);
|
||||||
|
|
||||||
case PUBACK:
|
case PUBACK:
|
||||||
case PUBREC:
|
case PUBREC:
|
||||||
case PUBCOMP:
|
case PUBCOMP:
|
||||||
case PUBREL:
|
case PUBREL:
|
||||||
return decodeMessageIdVariableHeader(buffer);
|
return decodePubReplyMessage(buffer);
|
||||||
|
|
||||||
case PUBLISH:
|
case PUBLISH:
|
||||||
return decodePublishVariableHeader(buffer, mqttFixedHeader);
|
return decodePublishVariableHeader(ctx, buffer, mqttFixedHeader);
|
||||||
|
|
||||||
|
case DISCONNECT:
|
||||||
|
case AUTH:
|
||||||
|
return decodeReasonCodeAndPropertiesVariableHeader(buffer);
|
||||||
|
|
||||||
case PINGREQ:
|
case PINGREQ:
|
||||||
case PINGRESP:
|
case PINGRESP:
|
||||||
case DISCONNECT:
|
|
||||||
// Empty variable header
|
// Empty variable header
|
||||||
return new Result<Object>(null, 0);
|
return new Result<Object>(null, 0);
|
||||||
|
default:
|
||||||
|
//shouldn't reach here
|
||||||
|
throw new DecoderException("Unknown message type: " + mqttFixedHeader.messageType());
|
||||||
}
|
}
|
||||||
return new Result<Object>(null, 0); //should never reach here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Result<MqttConnectVariableHeader> decodeConnectionVariableHeader(ByteBuf buffer) {
|
private static Result<MqttConnectVariableHeader> decodeConnectionVariableHeader(
|
||||||
|
ChannelHandlerContext ctx,
|
||||||
|
ByteBuf buffer) {
|
||||||
final Result<String> protoString = decodeString(buffer);
|
final Result<String> protoString = decodeString(buffer);
|
||||||
int numberOfBytesConsumed = protoString.numberOfBytesConsumed;
|
int numberOfBytesConsumed = protoString.numberOfBytesConsumed;
|
||||||
|
|
||||||
final byte protocolLevel = buffer.readByte();
|
final byte protocolLevel = buffer.readByte();
|
||||||
numberOfBytesConsumed += 1;
|
numberOfBytesConsumed += 1;
|
||||||
|
|
||||||
final MqttVersion mqttVersion = MqttVersion.fromProtocolNameAndLevel(protoString.value, protocolLevel);
|
MqttVersion version = MqttVersion.fromProtocolNameAndLevel(protoString.value, protocolLevel);
|
||||||
|
MqttCodecUtil.setMqttVersion(ctx, version);
|
||||||
|
|
||||||
final int b1 = buffer.readUnsignedByte();
|
final int b1 = buffer.readUnsignedByte();
|
||||||
numberOfBytesConsumed += 1;
|
numberOfBytesConsumed += 1;
|
||||||
|
|
||||||
final Result<Integer> keepAlive = decodeMsbLsb(buffer);
|
final int keepAlive = decodeMsbLsb(buffer);
|
||||||
numberOfBytesConsumed += keepAlive.numberOfBytesConsumed;
|
numberOfBytesConsumed += 2;
|
||||||
|
|
||||||
final boolean hasUserName = (b1 & 0x80) == 0x80;
|
final boolean hasUserName = (b1 & 0x80) == 0x80;
|
||||||
final boolean hasPassword = (b1 & 0x40) == 0x40;
|
final boolean hasPassword = (b1 & 0x40) == 0x40;
|
||||||
@@ -223,69 +299,178 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
final int willQos = (b1 & 0x18) >> 3;
|
final int willQos = (b1 & 0x18) >> 3;
|
||||||
final boolean willFlag = (b1 & 0x04) == 0x04;
|
final boolean willFlag = (b1 & 0x04) == 0x04;
|
||||||
final boolean cleanSession = (b1 & 0x02) == 0x02;
|
final boolean cleanSession = (b1 & 0x02) == 0x02;
|
||||||
if (mqttVersion.protocolLevel() >= MqttVersion.MQTT_3_1_1.protocolLevel()) {
|
if (version == MqttVersion.MQTT_3_1_1 || version == MqttVersion.MQTT_5) {
|
||||||
final boolean zeroReservedFlag = (b1 & 0x01) == 0x0;
|
final boolean zeroReservedFlag = (b1 & 0x01) == 0x0;
|
||||||
if (!zeroReservedFlag) {
|
if (!zeroReservedFlag) {
|
||||||
// MQTT v3.1.1: The Server MUST validate that the reserved flag in the CONNECT Control Packet is
|
// MQTT v3.1.1: The Server MUST validate that the reserved flag in the CONNECT Control Packet is
|
||||||
// set to zero and disconnect the Client if it is not zero.
|
// set to zero and disconnect the Client if it is not zero.
|
||||||
// See http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230
|
// See https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230
|
||||||
throw new DecoderException("non-zero reserved flag");
|
throw new DecoderException("non-zero reserved flag");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final MqttProperties properties;
|
||||||
|
if (version == MqttVersion.MQTT_5) {
|
||||||
|
final Result<MqttProperties> propertiesResult = decodeProperties(buffer);
|
||||||
|
properties = propertiesResult.value;
|
||||||
|
numberOfBytesConsumed += propertiesResult.numberOfBytesConsumed;
|
||||||
|
} else {
|
||||||
|
properties = MqttProperties.NO_PROPERTIES;
|
||||||
|
}
|
||||||
|
|
||||||
final MqttConnectVariableHeader mqttConnectVariableHeader = new MqttConnectVariableHeader(
|
final MqttConnectVariableHeader mqttConnectVariableHeader = new MqttConnectVariableHeader(
|
||||||
mqttVersion.protocolName(),
|
version.protocolName(),
|
||||||
mqttVersion.protocolLevel(),
|
version.protocolLevel(),
|
||||||
hasUserName,
|
hasUserName,
|
||||||
hasPassword,
|
hasPassword,
|
||||||
willRetain,
|
willRetain,
|
||||||
willQos,
|
willQos,
|
||||||
willFlag,
|
willFlag,
|
||||||
cleanSession,
|
cleanSession,
|
||||||
keepAlive.value);
|
keepAlive,
|
||||||
|
properties);
|
||||||
return new Result<MqttConnectVariableHeader>(mqttConnectVariableHeader, numberOfBytesConsumed);
|
return new Result<MqttConnectVariableHeader>(mqttConnectVariableHeader, numberOfBytesConsumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Result<MqttConnAckVariableHeader> decodeConnAckVariableHeader(ByteBuf buffer) {
|
private static Result<MqttConnAckVariableHeader> decodeConnAckVariableHeader(
|
||||||
|
ChannelHandlerContext ctx,
|
||||||
|
ByteBuf buffer) {
|
||||||
|
final MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
final boolean sessionPresent = (buffer.readUnsignedByte() & 0x01) == 0x01;
|
final boolean sessionPresent = (buffer.readUnsignedByte() & 0x01) == 0x01;
|
||||||
byte returnCode = buffer.readByte();
|
byte returnCode = buffer.readByte();
|
||||||
final int numberOfBytesConsumed = 2;
|
int numberOfBytesConsumed = 2;
|
||||||
|
|
||||||
|
final MqttProperties properties;
|
||||||
|
if (mqttVersion == MqttVersion.MQTT_5) {
|
||||||
|
final Result<MqttProperties> propertiesResult = decodeProperties(buffer);
|
||||||
|
properties = propertiesResult.value;
|
||||||
|
numberOfBytesConsumed += propertiesResult.numberOfBytesConsumed;
|
||||||
|
} else {
|
||||||
|
properties = MqttProperties.NO_PROPERTIES;
|
||||||
|
}
|
||||||
|
|
||||||
final MqttConnAckVariableHeader mqttConnAckVariableHeader =
|
final MqttConnAckVariableHeader mqttConnAckVariableHeader =
|
||||||
new MqttConnAckVariableHeader(MqttConnectReturnCode.valueOf(returnCode), sessionPresent);
|
new MqttConnAckVariableHeader(MqttConnectReturnCode.valueOf(returnCode), sessionPresent, properties);
|
||||||
return new Result<MqttConnAckVariableHeader>(mqttConnAckVariableHeader, numberOfBytesConsumed);
|
return new Result<MqttConnAckVariableHeader>(mqttConnAckVariableHeader, numberOfBytesConsumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Result<MqttMessageIdVariableHeader> decodeMessageIdVariableHeader(ByteBuf buffer) {
|
private static Result<MqttMessageIdAndPropertiesVariableHeader> decodeMessageIdAndPropertiesVariableHeader(
|
||||||
final Result<Integer> messageId = decodeMessageId(buffer);
|
ChannelHandlerContext ctx,
|
||||||
return new Result<MqttMessageIdVariableHeader>(
|
ByteBuf buffer) {
|
||||||
MqttMessageIdVariableHeader.from(messageId.value),
|
final MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
messageId.numberOfBytesConsumed);
|
final int packetId = decodeMessageId(buffer);
|
||||||
|
|
||||||
|
final MqttMessageIdAndPropertiesVariableHeader mqttVariableHeader;
|
||||||
|
final int mqtt5Consumed;
|
||||||
|
|
||||||
|
if (mqttVersion == MqttVersion.MQTT_5) {
|
||||||
|
final Result<MqttProperties> properties = decodeProperties(buffer);
|
||||||
|
mqttVariableHeader = new MqttMessageIdAndPropertiesVariableHeader(packetId, properties.value);
|
||||||
|
mqtt5Consumed = properties.numberOfBytesConsumed;
|
||||||
|
} else {
|
||||||
|
mqttVariableHeader = new MqttMessageIdAndPropertiesVariableHeader(packetId,
|
||||||
|
MqttProperties.NO_PROPERTIES);
|
||||||
|
mqtt5Consumed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Result<MqttMessageIdAndPropertiesVariableHeader>(mqttVariableHeader,
|
||||||
|
2 + mqtt5Consumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Result<MqttPublishVariableHeader> decodePublishVariableHeader(
|
private Result<MqttPubReplyMessageVariableHeader> decodePubReplyMessage(ByteBuf buffer) {
|
||||||
|
final int packetId = decodeMessageId(buffer);
|
||||||
|
|
||||||
|
final MqttPubReplyMessageVariableHeader mqttPubAckVariableHeader;
|
||||||
|
final int consumed;
|
||||||
|
final int packetIdNumberOfBytesConsumed = 2;
|
||||||
|
if (bytesRemainingInVariablePart > 3) {
|
||||||
|
final byte reasonCode = buffer.readByte();
|
||||||
|
final Result<MqttProperties> properties = decodeProperties(buffer);
|
||||||
|
mqttPubAckVariableHeader = new MqttPubReplyMessageVariableHeader(packetId,
|
||||||
|
reasonCode,
|
||||||
|
properties.value);
|
||||||
|
consumed = packetIdNumberOfBytesConsumed + 1 + properties.numberOfBytesConsumed;
|
||||||
|
} else if (bytesRemainingInVariablePart > 2) {
|
||||||
|
final byte reasonCode = buffer.readByte();
|
||||||
|
mqttPubAckVariableHeader = new MqttPubReplyMessageVariableHeader(packetId,
|
||||||
|
reasonCode,
|
||||||
|
MqttProperties.NO_PROPERTIES);
|
||||||
|
consumed = packetIdNumberOfBytesConsumed + 1;
|
||||||
|
} else {
|
||||||
|
mqttPubAckVariableHeader = new MqttPubReplyMessageVariableHeader(packetId,
|
||||||
|
(byte) 0,
|
||||||
|
MqttProperties.NO_PROPERTIES);
|
||||||
|
consumed = packetIdNumberOfBytesConsumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Result<MqttPubReplyMessageVariableHeader>(mqttPubAckVariableHeader, consumed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<MqttReasonCodeAndPropertiesVariableHeader> decodeReasonCodeAndPropertiesVariableHeader(
|
||||||
|
ByteBuf buffer) {
|
||||||
|
final byte reasonCode;
|
||||||
|
final MqttProperties properties;
|
||||||
|
final int consumed;
|
||||||
|
if (bytesRemainingInVariablePart > 1) {
|
||||||
|
reasonCode = buffer.readByte();
|
||||||
|
final Result<MqttProperties> propertiesResult = decodeProperties(buffer);
|
||||||
|
properties = propertiesResult.value;
|
||||||
|
consumed = 1 + propertiesResult.numberOfBytesConsumed;
|
||||||
|
} else if (bytesRemainingInVariablePart > 0) {
|
||||||
|
reasonCode = buffer.readByte();
|
||||||
|
properties = MqttProperties.NO_PROPERTIES;
|
||||||
|
consumed = 1;
|
||||||
|
} else {
|
||||||
|
reasonCode = 0;
|
||||||
|
properties = MqttProperties.NO_PROPERTIES;
|
||||||
|
consumed = 0;
|
||||||
|
}
|
||||||
|
final MqttReasonCodeAndPropertiesVariableHeader mqttReasonAndPropsVariableHeader =
|
||||||
|
new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, properties);
|
||||||
|
|
||||||
|
return new Result<MqttReasonCodeAndPropertiesVariableHeader>(
|
||||||
|
mqttReasonAndPropsVariableHeader,
|
||||||
|
consumed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<MqttPublishVariableHeader> decodePublishVariableHeader(
|
||||||
|
ChannelHandlerContext ctx,
|
||||||
ByteBuf buffer,
|
ByteBuf buffer,
|
||||||
MqttFixedHeader mqttFixedHeader) {
|
MqttFixedHeader mqttFixedHeader) {
|
||||||
|
final MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
final Result<String> decodedTopic = decodeString(buffer);
|
final Result<String> decodedTopic = decodeString(buffer);
|
||||||
if (!isValidPublishTopicName(decodedTopic.value)) {
|
if (!MqttCodecUtil.isValidPublishTopicName(decodedTopic.value)) {
|
||||||
throw new DecoderException("invalid publish topic name: " + decodedTopic.value + " (contains wildcards)");
|
throw new DecoderException("invalid publish topic name: " + decodedTopic.value + " (contains wildcards)");
|
||||||
}
|
}
|
||||||
int numberOfBytesConsumed = decodedTopic.numberOfBytesConsumed;
|
int numberOfBytesConsumed = decodedTopic.numberOfBytesConsumed;
|
||||||
|
|
||||||
int messageId = -1;
|
int messageId = -1;
|
||||||
if (mqttFixedHeader.qosLevel().value() > 0) {
|
if (mqttFixedHeader.qosLevel().value() > 0) {
|
||||||
final Result<Integer> decodedMessageId = decodeMessageId(buffer);
|
messageId = decodeMessageId(buffer);
|
||||||
messageId = decodedMessageId.value;
|
numberOfBytesConsumed += 2;
|
||||||
numberOfBytesConsumed += decodedMessageId.numberOfBytesConsumed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final MqttProperties properties;
|
||||||
|
if (mqttVersion == MqttVersion.MQTT_5) {
|
||||||
|
final Result<MqttProperties> propertiesResult = decodeProperties(buffer);
|
||||||
|
properties = propertiesResult.value;
|
||||||
|
numberOfBytesConsumed += propertiesResult.numberOfBytesConsumed;
|
||||||
|
} else {
|
||||||
|
properties = MqttProperties.NO_PROPERTIES;
|
||||||
|
}
|
||||||
|
|
||||||
final MqttPublishVariableHeader mqttPublishVariableHeader =
|
final MqttPublishVariableHeader mqttPublishVariableHeader =
|
||||||
new MqttPublishVariableHeader(decodedTopic.value, messageId);
|
new MqttPublishVariableHeader(decodedTopic.value, messageId, properties);
|
||||||
return new Result<MqttPublishVariableHeader>(mqttPublishVariableHeader, numberOfBytesConsumed);
|
return new Result<MqttPublishVariableHeader>(mqttPublishVariableHeader, numberOfBytesConsumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Result<Integer> decodeMessageId(ByteBuf buffer) {
|
/**
|
||||||
final Result<Integer> messageId = decodeMsbLsb(buffer);
|
* @return messageId with numberOfBytesConsumed is 2
|
||||||
if (!isValidMessageId(messageId.value)) {
|
*/
|
||||||
throw new DecoderException("invalid messageId: " + messageId.value);
|
private static int decodeMessageId(ByteBuf buffer) {
|
||||||
|
final int messageId = decodeMsbLsb(buffer);
|
||||||
|
if (!MqttCodecUtil.isValidMessageId(messageId)) {
|
||||||
|
throw new DecoderException("invalid messageId: " + messageId);
|
||||||
}
|
}
|
||||||
return messageId;
|
return messageId;
|
||||||
}
|
}
|
||||||
@@ -300,13 +485,15 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
* @return the payload
|
* @return the payload
|
||||||
*/
|
*/
|
||||||
private static Result<?> decodePayload(
|
private static Result<?> decodePayload(
|
||||||
|
ChannelHandlerContext ctx,
|
||||||
ByteBuf buffer,
|
ByteBuf buffer,
|
||||||
MqttMessageType messageType,
|
MqttMessageType messageType,
|
||||||
int bytesRemainingInVariablePart,
|
int bytesRemainingInVariablePart,
|
||||||
|
int maxClientIdLength,
|
||||||
Object variableHeader) {
|
Object variableHeader) {
|
||||||
switch (messageType) {
|
switch (messageType) {
|
||||||
case CONNECT:
|
case CONNECT:
|
||||||
return decodeConnectionPayload(buffer, (MqttConnectVariableHeader) variableHeader);
|
return decodeConnectionPayload(buffer, maxClientIdLength, (MqttConnectVariableHeader) variableHeader);
|
||||||
|
|
||||||
case SUBSCRIBE:
|
case SUBSCRIBE:
|
||||||
return decodeSubscribePayload(buffer, bytesRemainingInVariablePart);
|
return decodeSubscribePayload(buffer, bytesRemainingInVariablePart);
|
||||||
@@ -317,6 +504,9 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
case UNSUBSCRIBE:
|
case UNSUBSCRIBE:
|
||||||
return decodeUnsubscribePayload(buffer, bytesRemainingInVariablePart);
|
return decodeUnsubscribePayload(buffer, bytesRemainingInVariablePart);
|
||||||
|
|
||||||
|
case UNSUBACK:
|
||||||
|
return decodeUnsubAckPayload(ctx, buffer, bytesRemainingInVariablePart);
|
||||||
|
|
||||||
case PUBLISH:
|
case PUBLISH:
|
||||||
return decodePublishPayload(buffer, bytesRemainingInVariablePart);
|
return decodePublishPayload(buffer, bytesRemainingInVariablePart);
|
||||||
|
|
||||||
@@ -328,42 +518,55 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
|
|
||||||
private static Result<MqttConnectPayload> decodeConnectionPayload(
|
private static Result<MqttConnectPayload> decodeConnectionPayload(
|
||||||
ByteBuf buffer,
|
ByteBuf buffer,
|
||||||
|
int maxClientIdLength,
|
||||||
MqttConnectVariableHeader mqttConnectVariableHeader) {
|
MqttConnectVariableHeader mqttConnectVariableHeader) {
|
||||||
final Result<String> decodedClientId = decodeString(buffer);
|
final Result<String> decodedClientId = decodeString(buffer);
|
||||||
final String decodedClientIdValue = decodedClientId.value;
|
final String decodedClientIdValue = decodedClientId.value;
|
||||||
final MqttVersion mqttVersion = MqttVersion.fromProtocolNameAndLevel(mqttConnectVariableHeader.name(),
|
final MqttVersion mqttVersion = MqttVersion.fromProtocolNameAndLevel(mqttConnectVariableHeader.name(),
|
||||||
(byte) mqttConnectVariableHeader.version());
|
(byte) mqttConnectVariableHeader.version());
|
||||||
if (!isValidClientId(mqttVersion, decodedClientIdValue)) {
|
if (!MqttCodecUtil.isValidClientId(mqttVersion, maxClientIdLength, decodedClientIdValue)) {
|
||||||
throw new MqttIdentifierRejectedException("invalid clientIdentifier: " + decodedClientIdValue);
|
throw new MqttIdentifierRejectedException("invalid clientIdentifier: " + decodedClientIdValue);
|
||||||
}
|
}
|
||||||
int numberOfBytesConsumed = decodedClientId.numberOfBytesConsumed;
|
int numberOfBytesConsumed = decodedClientId.numberOfBytesConsumed;
|
||||||
|
|
||||||
Result<String> decodedWillTopic = null;
|
Result<String> decodedWillTopic = null;
|
||||||
Result<String> decodedWillMessage = null;
|
byte[] decodedWillMessage = null;
|
||||||
|
|
||||||
|
final MqttProperties willProperties;
|
||||||
if (mqttConnectVariableHeader.isWillFlag()) {
|
if (mqttConnectVariableHeader.isWillFlag()) {
|
||||||
|
if (mqttVersion == MqttVersion.MQTT_5) {
|
||||||
|
final Result<MqttProperties> propertiesResult = decodeProperties(buffer);
|
||||||
|
willProperties = propertiesResult.value;
|
||||||
|
numberOfBytesConsumed += propertiesResult.numberOfBytesConsumed;
|
||||||
|
} else {
|
||||||
|
willProperties = MqttProperties.NO_PROPERTIES;
|
||||||
|
}
|
||||||
decodedWillTopic = decodeString(buffer, 0, 32767);
|
decodedWillTopic = decodeString(buffer, 0, 32767);
|
||||||
numberOfBytesConsumed += decodedWillTopic.numberOfBytesConsumed;
|
numberOfBytesConsumed += decodedWillTopic.numberOfBytesConsumed;
|
||||||
decodedWillMessage = decodeAsciiString(buffer);
|
decodedWillMessage = decodeByteArray(buffer);
|
||||||
numberOfBytesConsumed += decodedWillMessage.numberOfBytesConsumed;
|
numberOfBytesConsumed += decodedWillMessage.length + 2;
|
||||||
|
} else {
|
||||||
|
willProperties = MqttProperties.NO_PROPERTIES;
|
||||||
}
|
}
|
||||||
Result<String> decodedUserName = null;
|
Result<String> decodedUserName = null;
|
||||||
Result<byte[]> decodedPassword = null;
|
byte[] decodedPassword = null;
|
||||||
if (mqttConnectVariableHeader.hasUserName()) {
|
if (mqttConnectVariableHeader.hasUserName()) {
|
||||||
decodedUserName = decodeString(buffer);
|
decodedUserName = decodeString(buffer);
|
||||||
numberOfBytesConsumed += decodedUserName.numberOfBytesConsumed;
|
numberOfBytesConsumed += decodedUserName.numberOfBytesConsumed;
|
||||||
}
|
}
|
||||||
if (mqttConnectVariableHeader.hasPassword()) {
|
if (mqttConnectVariableHeader.hasPassword()) {
|
||||||
decodedPassword = decodeByte(buffer);
|
decodedPassword = decodeByteArray(buffer);
|
||||||
numberOfBytesConsumed += decodedPassword.numberOfBytesConsumed;
|
numberOfBytesConsumed += decodedPassword.length + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
final MqttConnectPayload mqttConnectPayload =
|
final MqttConnectPayload mqttConnectPayload =
|
||||||
new MqttConnectPayload(
|
new MqttConnectPayload(
|
||||||
decodedClientId.value,
|
decodedClientId.value,
|
||||||
|
willProperties,
|
||||||
decodedWillTopic != null ? decodedWillTopic.value : null,
|
decodedWillTopic != null ? decodedWillTopic.value : null,
|
||||||
decodedWillMessage != null ? decodedWillMessage.value : null,
|
decodedWillMessage,
|
||||||
decodedUserName != null ? decodedUserName.value : null,
|
decodedUserName != null ? decodedUserName.value : null,
|
||||||
decodedPassword != null ? decodedPassword.value : null);
|
decodedPassword);
|
||||||
return new Result<MqttConnectPayload>(mqttConnectPayload, numberOfBytesConsumed);
|
return new Result<MqttConnectPayload>(mqttConnectPayload, numberOfBytesConsumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,9 +578,21 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
while (numberOfBytesConsumed < bytesRemainingInVariablePart) {
|
while (numberOfBytesConsumed < bytesRemainingInVariablePart) {
|
||||||
final Result<String> decodedTopicName = decodeString(buffer);
|
final Result<String> decodedTopicName = decodeString(buffer);
|
||||||
numberOfBytesConsumed += decodedTopicName.numberOfBytesConsumed;
|
numberOfBytesConsumed += decodedTopicName.numberOfBytesConsumed;
|
||||||
int qos = buffer.readUnsignedByte() & 0x03;
|
//See 3.8.3.1 Subscription Options of MQTT 5.0 specification for optionByte details
|
||||||
|
final short optionByte = buffer.readUnsignedByte();
|
||||||
|
|
||||||
|
MqttQoS qos = MqttQoS.valueOf(optionByte & 0x03);
|
||||||
|
boolean noLocal = ((optionByte & 0x04) >> 2) == 1;
|
||||||
|
boolean retainAsPublished = ((optionByte & 0x08) >> 3) == 1;
|
||||||
|
MqttSubscriptionOption.RetainedHandlingPolicy retainHandling = MqttSubscriptionOption.RetainedHandlingPolicy.valueOf((optionByte & 0x30) >> 4);
|
||||||
|
|
||||||
|
final MqttSubscriptionOption subscriptionOption = new MqttSubscriptionOption(qos,
|
||||||
|
noLocal,
|
||||||
|
retainAsPublished,
|
||||||
|
retainHandling);
|
||||||
|
|
||||||
numberOfBytesConsumed++;
|
numberOfBytesConsumed++;
|
||||||
subscribeTopics.add(new MqttTopicSubscription(decodedTopicName.value, MqttQoS.valueOf(qos)));
|
subscribeTopics.add(new MqttTopicSubscription(decodedTopicName.value, subscriptionOption));
|
||||||
}
|
}
|
||||||
return new Result<MqttSubscribePayload>(new MqttSubscribePayload(subscribeTopics), numberOfBytesConsumed);
|
return new Result<MqttSubscribePayload>(new MqttSubscribePayload(subscribeTopics), numberOfBytesConsumed);
|
||||||
}
|
}
|
||||||
@@ -385,16 +600,30 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
private static Result<MqttSubAckPayload> decodeSubackPayload(
|
private static Result<MqttSubAckPayload> decodeSubackPayload(
|
||||||
ByteBuf buffer,
|
ByteBuf buffer,
|
||||||
int bytesRemainingInVariablePart) {
|
int bytesRemainingInVariablePart) {
|
||||||
final List<Integer> grantedQos = new ArrayList<Integer>();
|
final List<Integer> grantedQos = new ArrayList<Integer>(bytesRemainingInVariablePart);
|
||||||
int numberOfBytesConsumed = 0;
|
int numberOfBytesConsumed = 0;
|
||||||
while (numberOfBytesConsumed < bytesRemainingInVariablePart) {
|
while (numberOfBytesConsumed < bytesRemainingInVariablePart) {
|
||||||
int qos = buffer.readUnsignedByte() & 0x03;
|
int reasonCode = buffer.readUnsignedByte();
|
||||||
numberOfBytesConsumed++;
|
numberOfBytesConsumed++;
|
||||||
grantedQos.add(qos);
|
grantedQos.add(reasonCode);
|
||||||
}
|
}
|
||||||
return new Result<MqttSubAckPayload>(new MqttSubAckPayload(grantedQos), numberOfBytesConsumed);
|
return new Result<MqttSubAckPayload>(new MqttSubAckPayload(grantedQos), numberOfBytesConsumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Result<MqttUnsubAckPayload> decodeUnsubAckPayload(
|
||||||
|
ChannelHandlerContext ctx,
|
||||||
|
ByteBuf buffer,
|
||||||
|
int bytesRemainingInVariablePart) {
|
||||||
|
final List<Short> reasonCodes = new ArrayList<Short>(bytesRemainingInVariablePart);
|
||||||
|
int numberOfBytesConsumed = 0;
|
||||||
|
while (numberOfBytesConsumed < bytesRemainingInVariablePart) {
|
||||||
|
short reasonCode = buffer.readUnsignedByte();
|
||||||
|
numberOfBytesConsumed++;
|
||||||
|
reasonCodes.add(reasonCode);
|
||||||
|
}
|
||||||
|
return new Result<MqttUnsubAckPayload>(new MqttUnsubAckPayload(reasonCodes), numberOfBytesConsumed);
|
||||||
|
}
|
||||||
|
|
||||||
private static Result<MqttUnsubscribePayload> decodeUnsubscribePayload(
|
private static Result<MqttUnsubscribePayload> decodeUnsubscribePayload(
|
||||||
ByteBuf buffer,
|
ByteBuf buffer,
|
||||||
int bytesRemainingInVariablePart) {
|
int bytesRemainingInVariablePart) {
|
||||||
@@ -419,25 +648,9 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
return decodeString(buffer, 0, Integer.MAX_VALUE);
|
return decodeString(buffer, 0, Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Result<String> decodeAsciiString(ByteBuf buffer) {
|
|
||||||
Result<String> result = decodeString(buffer, 0, Integer.MAX_VALUE);
|
|
||||||
final String s = result.value;
|
|
||||||
for (int i = 0; i < s.length(); i++) {
|
|
||||||
if (s.charAt(i) > 127) {
|
|
||||||
return new Result<String>(null, result.numberOfBytesConsumed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Result<String>(s, result.numberOfBytesConsumed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Result<byte[]> decodeByte(ByteBuf buffer) {
|
|
||||||
return decodeByte(buffer, 0, Integer.MAX_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Result<String> decodeString(ByteBuf buffer, int minBytes, int maxBytes) {
|
private static Result<String> decodeString(ByteBuf buffer, int minBytes, int maxBytes) {
|
||||||
final Result<Integer> decodedSize = decodeMsbLsb(buffer);
|
int size = decodeMsbLsb(buffer);
|
||||||
int size = decodedSize.value;
|
int numberOfBytesConsumed = 2;
|
||||||
int numberOfBytesConsumed = decodedSize.numberOfBytesConsumed;
|
|
||||||
if (size < minBytes || size > maxBytes) {
|
if (size < minBytes || size > maxBytes) {
|
||||||
buffer.skipBytes(size);
|
buffer.skipBytes(size);
|
||||||
numberOfBytesConsumed += size;
|
numberOfBytesConsumed += size;
|
||||||
@@ -449,35 +662,68 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
return new Result<String>(s, numberOfBytesConsumed);
|
return new Result<String>(s, numberOfBytesConsumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Result<byte[]> decodeByte(ByteBuf buffer, int minBytes, int maxBytes) {
|
/**
|
||||||
final Result<Integer> decodedSize = decodeMsbLsb(buffer);
|
*
|
||||||
int size = decodedSize.value;
|
* @return the decoded byte[], numberOfBytesConsumed = byte[].length + 2
|
||||||
int numberOfBytesConsumed = decodedSize.numberOfBytesConsumed;
|
*/
|
||||||
if (size < minBytes || size > maxBytes) {
|
private static byte[] decodeByteArray(ByteBuf buffer) {
|
||||||
buffer.skipBytes(size);
|
int size = decodeMsbLsb(buffer);
|
||||||
numberOfBytesConsumed += size;
|
byte[] bytes = new byte[size];
|
||||||
return new Result<>(null, numberOfBytesConsumed);
|
buffer.readBytes(bytes);
|
||||||
}
|
return bytes;
|
||||||
byte[] s = new byte[size];
|
|
||||||
buffer.getBytes(buffer.readerIndex(), s, 0, size);
|
|
||||||
buffer.skipBytes(size);
|
|
||||||
numberOfBytesConsumed += size;
|
|
||||||
return new Result<>(s, numberOfBytesConsumed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Result<Integer> decodeMsbLsb(ByteBuf buffer) {
|
// packing utils to reduce the amount of garbage while decoding ints
|
||||||
return decodeMsbLsb(buffer, 0, 65535);
|
private static long packInts(int a, int b) {
|
||||||
|
return (((long) a) << 32) | (b & 0xFFFFFFFFL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Result<Integer> decodeMsbLsb(ByteBuf buffer, int min, int max) {
|
private static int unpackA(long ints) {
|
||||||
|
return (int) (ints >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int unpackB(long ints) {
|
||||||
|
return (int) ints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* numberOfBytesConsumed = 2. return decoded result.
|
||||||
|
*/
|
||||||
|
private static int decodeMsbLsb(ByteBuf buffer) {
|
||||||
|
int min = 0;
|
||||||
|
int max = 65535;
|
||||||
short msbSize = buffer.readUnsignedByte();
|
short msbSize = buffer.readUnsignedByte();
|
||||||
short lsbSize = buffer.readUnsignedByte();
|
short lsbSize = buffer.readUnsignedByte();
|
||||||
final int numberOfBytesConsumed = 2;
|
|
||||||
int result = msbSize << 8 | lsbSize;
|
int result = msbSize << 8 | lsbSize;
|
||||||
if (result < min || result > max) {
|
if (result < min || result > max) {
|
||||||
result = -1;
|
result = -1;
|
||||||
}
|
}
|
||||||
return new Result<Integer>(result, numberOfBytesConsumed);
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See 1.5.5 Variable Byte Integer section of MQTT 5.0 specification for encoding/decoding rules
|
||||||
|
*
|
||||||
|
* @param buffer the buffer to decode from
|
||||||
|
* @return result pack with a = decoded integer, b = numberOfBytesConsumed. Need to unpack to read them.
|
||||||
|
* @throws DecoderException if bad MQTT protocol limits Remaining Length
|
||||||
|
*/
|
||||||
|
private static long decodeVariableByteInteger(ByteBuf buffer) {
|
||||||
|
int remainingLength = 0;
|
||||||
|
int multiplier = 1;
|
||||||
|
short digit;
|
||||||
|
int loops = 0;
|
||||||
|
do {
|
||||||
|
digit = buffer.readUnsignedByte();
|
||||||
|
remainingLength += (digit & 127) * multiplier;
|
||||||
|
multiplier *= 128;
|
||||||
|
loops++;
|
||||||
|
} while ((digit & 128) != 0 && loops < 4);
|
||||||
|
|
||||||
|
if (loops == 4 && (digit & 128) != 0) {
|
||||||
|
throw new DecoderException("MQTT protocol limits Remaining Length to 4 bytes");
|
||||||
|
}
|
||||||
|
return packInts(remainingLength, loops);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class Result<T> {
|
private static final class Result<T> {
|
||||||
@@ -490,4 +736,82 @@ public final class MqttDecoder extends ReplayingDecoder<DecoderState> {
|
|||||||
this.numberOfBytesConsumed = numberOfBytesConsumed;
|
this.numberOfBytesConsumed = numberOfBytesConsumed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Result<MqttProperties> decodeProperties(ByteBuf buffer) {
|
||||||
|
final long propertiesLength = decodeVariableByteInteger(buffer);
|
||||||
|
int totalPropertiesLength = unpackA(propertiesLength);
|
||||||
|
int numberOfBytesConsumed = unpackB(propertiesLength);
|
||||||
|
|
||||||
|
MqttProperties decodedProperties = new MqttProperties();
|
||||||
|
while (numberOfBytesConsumed < totalPropertiesLength) {
|
||||||
|
long propertyId = decodeVariableByteInteger(buffer);
|
||||||
|
final int propertyIdValue = unpackA(propertyId);
|
||||||
|
numberOfBytesConsumed += unpackB(propertyId);
|
||||||
|
MqttProperties.MqttPropertyType propertyType = MqttProperties.MqttPropertyType.valueOf(propertyIdValue);
|
||||||
|
switch (propertyType) {
|
||||||
|
case PAYLOAD_FORMAT_INDICATOR:
|
||||||
|
case REQUEST_PROBLEM_INFORMATION:
|
||||||
|
case REQUEST_RESPONSE_INFORMATION:
|
||||||
|
case MAXIMUM_QOS:
|
||||||
|
case RETAIN_AVAILABLE:
|
||||||
|
case WILDCARD_SUBSCRIPTION_AVAILABLE:
|
||||||
|
case SUBSCRIPTION_IDENTIFIER_AVAILABLE:
|
||||||
|
case SHARED_SUBSCRIPTION_AVAILABLE:
|
||||||
|
final int b1 = buffer.readUnsignedByte();
|
||||||
|
numberOfBytesConsumed++;
|
||||||
|
decodedProperties.add(new MqttProperties.IntegerProperty(propertyIdValue, b1));
|
||||||
|
break;
|
||||||
|
case SERVER_KEEP_ALIVE:
|
||||||
|
case RECEIVE_MAXIMUM:
|
||||||
|
case TOPIC_ALIAS_MAXIMUM:
|
||||||
|
case TOPIC_ALIAS:
|
||||||
|
final int int2BytesResult = decodeMsbLsb(buffer);
|
||||||
|
numberOfBytesConsumed += 2;
|
||||||
|
decodedProperties.add(new MqttProperties.IntegerProperty(propertyIdValue, int2BytesResult));
|
||||||
|
break;
|
||||||
|
case PUBLICATION_EXPIRY_INTERVAL:
|
||||||
|
case SESSION_EXPIRY_INTERVAL:
|
||||||
|
case WILL_DELAY_INTERVAL:
|
||||||
|
case MAXIMUM_PACKET_SIZE:
|
||||||
|
final int maxPacketSize = buffer.readInt();
|
||||||
|
numberOfBytesConsumed += 4;
|
||||||
|
decodedProperties.add(new MqttProperties.IntegerProperty(propertyIdValue, maxPacketSize));
|
||||||
|
break;
|
||||||
|
case SUBSCRIPTION_IDENTIFIER:
|
||||||
|
long vbIntegerResult = decodeVariableByteInteger(buffer);
|
||||||
|
numberOfBytesConsumed += unpackB(vbIntegerResult);
|
||||||
|
decodedProperties.add(new MqttProperties.IntegerProperty(propertyIdValue, unpackA(vbIntegerResult)));
|
||||||
|
break;
|
||||||
|
case CONTENT_TYPE:
|
||||||
|
case RESPONSE_TOPIC:
|
||||||
|
case ASSIGNED_CLIENT_IDENTIFIER:
|
||||||
|
case AUTHENTICATION_METHOD:
|
||||||
|
case RESPONSE_INFORMATION:
|
||||||
|
case SERVER_REFERENCE:
|
||||||
|
case REASON_STRING:
|
||||||
|
final Result<String> stringResult = decodeString(buffer);
|
||||||
|
numberOfBytesConsumed += stringResult.numberOfBytesConsumed;
|
||||||
|
decodedProperties.add(new MqttProperties.StringProperty(propertyIdValue, stringResult.value));
|
||||||
|
break;
|
||||||
|
case USER_PROPERTY:
|
||||||
|
final Result<String> keyResult = decodeString(buffer);
|
||||||
|
final Result<String> valueResult = decodeString(buffer);
|
||||||
|
numberOfBytesConsumed += keyResult.numberOfBytesConsumed;
|
||||||
|
numberOfBytesConsumed += valueResult.numberOfBytesConsumed;
|
||||||
|
decodedProperties.add(new MqttProperties.UserProperty(keyResult.value, valueResult.value));
|
||||||
|
break;
|
||||||
|
case CORRELATION_DATA:
|
||||||
|
case AUTHENTICATION_DATA:
|
||||||
|
final byte[] binaryDataResult = decodeByteArray(buffer);
|
||||||
|
numberOfBytesConsumed += binaryDataResult.length + 2;
|
||||||
|
decodedProperties.add(new MqttProperties.BinaryProperty(propertyIdValue, binaryDataResult));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//shouldn't reach here
|
||||||
|
throw new DecoderException("Unknown property type: " + propertyType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Result<MqttProperties>(decodedProperties, numberOfBytesConsumed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+517
-172
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -18,19 +18,22 @@ package io.netty.handler.codec.mqtt;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.EncoderException;
|
||||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||||
import io.netty.util.CharsetUtil;
|
|
||||||
import io.netty.util.internal.EmptyArrays;
|
import io.netty.util.internal.EmptyArrays;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static io.netty.handler.codec.mqtt.MqttCodecUtil.*;
|
import static io.netty.buffer.ByteBufUtil.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes Mqtt messages into bytes following the protocol specification v3.1
|
* Encodes Mqtt messages into bytes following the protocol specification v3.1
|
||||||
* as described here <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html">MQTTV3.1</a>
|
* as described here <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html">MQTTV3.1</a>
|
||||||
|
* or v5.0 as described here <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html">MQTTv5.0</a> -
|
||||||
|
* depending on the version specified in the first CONNECT message that goes through the channel.
|
||||||
*/
|
*/
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
||||||
@@ -41,49 +44,57 @@ public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encode(ChannelHandlerContext ctx, MqttMessage msg, List<Object> out) throws Exception {
|
protected void encode(ChannelHandlerContext ctx, MqttMessage msg, List<Object> out) throws Exception {
|
||||||
out.add(doEncode(ctx.alloc(), msg));
|
out.add(doEncode(ctx, msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main encoding method.
|
* This is the main encoding method.
|
||||||
* It's only visible for testing.
|
* It's only visible for testing.
|
||||||
*
|
*
|
||||||
* @param byteBufAllocator Allocates ByteBuf
|
|
||||||
* @param message MQTT message to encode
|
* @param message MQTT message to encode
|
||||||
* @return ByteBuf with encoded bytes
|
* @return ByteBuf with encoded bytes
|
||||||
*/
|
*/
|
||||||
static ByteBuf doEncode(ByteBufAllocator byteBufAllocator, MqttMessage message) {
|
static ByteBuf doEncode(ChannelHandlerContext ctx,
|
||||||
|
MqttMessage message) {
|
||||||
|
|
||||||
switch (message.fixedHeader().messageType()) {
|
switch (message.fixedHeader().messageType()) {
|
||||||
case CONNECT:
|
case CONNECT:
|
||||||
return encodeConnectMessage(byteBufAllocator, (MqttConnectMessage) message);
|
return encodeConnectMessage(ctx, (MqttConnectMessage) message);
|
||||||
|
|
||||||
case CONNACK:
|
case CONNACK:
|
||||||
return encodeConnAckMessage(byteBufAllocator, (MqttConnAckMessage) message);
|
return encodeConnAckMessage(ctx, (MqttConnAckMessage) message);
|
||||||
|
|
||||||
case PUBLISH:
|
case PUBLISH:
|
||||||
return encodePublishMessage(byteBufAllocator, (MqttPublishMessage) message);
|
return encodePublishMessage(ctx, (MqttPublishMessage) message);
|
||||||
|
|
||||||
case SUBSCRIBE:
|
case SUBSCRIBE:
|
||||||
return encodeSubscribeMessage(byteBufAllocator, (MqttSubscribeMessage) message);
|
return encodeSubscribeMessage(ctx, (MqttSubscribeMessage) message);
|
||||||
|
|
||||||
case UNSUBSCRIBE:
|
case UNSUBSCRIBE:
|
||||||
return encodeUnsubscribeMessage(byteBufAllocator, (MqttUnsubscribeMessage) message);
|
return encodeUnsubscribeMessage(ctx, (MqttUnsubscribeMessage) message);
|
||||||
|
|
||||||
case SUBACK:
|
case SUBACK:
|
||||||
return encodeSubAckMessage(byteBufAllocator, (MqttSubAckMessage) message);
|
return encodeSubAckMessage(ctx, (MqttSubAckMessage) message);
|
||||||
|
|
||||||
case UNSUBACK:
|
case UNSUBACK:
|
||||||
|
if (message instanceof MqttUnsubAckMessage) {
|
||||||
|
return encodeUnsubAckMessage(ctx, (MqttUnsubAckMessage) message);
|
||||||
|
}
|
||||||
|
return encodeMessageWithOnlySingleByteFixedHeaderAndMessageId(ctx.alloc(), message);
|
||||||
|
|
||||||
case PUBACK:
|
case PUBACK:
|
||||||
case PUBREC:
|
case PUBREC:
|
||||||
case PUBREL:
|
case PUBREL:
|
||||||
case PUBCOMP:
|
case PUBCOMP:
|
||||||
return encodeMessageWithOnlySingleByteFixedHeaderAndMessageId(byteBufAllocator, message);
|
return encodePubReplyMessage(ctx, message);
|
||||||
|
|
||||||
|
case DISCONNECT:
|
||||||
|
case AUTH:
|
||||||
|
return encodeReasonCodePlusPropertiesMessage(ctx, message);
|
||||||
|
|
||||||
case PINGREQ:
|
case PINGREQ:
|
||||||
case PINGRESP:
|
case PINGRESP:
|
||||||
case DISCONNECT:
|
return encodeMessageWithOnlySingleByteFixedHeader(ctx.alloc(), message);
|
||||||
return encodeMessageWithOnlySingleByteFixedHeader(byteBufAllocator, message);
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
@@ -92,7 +103,7 @@ public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf encodeConnectMessage(
|
private static ByteBuf encodeConnectMessage(
|
||||||
ByteBufAllocator byteBufAllocator,
|
ChannelHandlerContext ctx,
|
||||||
MqttConnectMessage message) {
|
MqttConnectMessage message) {
|
||||||
int payloadBufferSize = 0;
|
int payloadBufferSize = 0;
|
||||||
|
|
||||||
@@ -101,70 +112,96 @@ public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
|||||||
MqttConnectPayload payload = message.payload();
|
MqttConnectPayload payload = message.payload();
|
||||||
MqttVersion mqttVersion = MqttVersion.fromProtocolNameAndLevel(variableHeader.name(),
|
MqttVersion mqttVersion = MqttVersion.fromProtocolNameAndLevel(variableHeader.name(),
|
||||||
(byte) variableHeader.version());
|
(byte) variableHeader.version());
|
||||||
|
MqttCodecUtil.setMqttVersion(ctx, mqttVersion);
|
||||||
|
|
||||||
|
// as MQTT 3.1 & 3.1.1 spec, If the User Name Flag is set to 0, the Password Flag MUST be set to 0
|
||||||
|
if (!variableHeader.hasUserName() && variableHeader.hasPassword()) {
|
||||||
|
throw new EncoderException("Without a username, the password MUST be not set");
|
||||||
|
}
|
||||||
|
|
||||||
// Client id
|
// Client id
|
||||||
String clientIdentifier = payload.clientIdentifier();
|
String clientIdentifier = payload.clientIdentifier();
|
||||||
if (!isValidClientId(mqttVersion, clientIdentifier)) {
|
if (!MqttCodecUtil.isValidClientId(mqttVersion, MqttConstant.DEFAULT_MAX_CLIENT_ID_LENGTH, clientIdentifier)) {
|
||||||
throw new MqttIdentifierRejectedException("invalid clientIdentifier: " + clientIdentifier);
|
throw new MqttIdentifierRejectedException("invalid clientIdentifier: " + clientIdentifier);
|
||||||
}
|
}
|
||||||
byte[] clientIdentifierBytes = encodeStringUtf8(clientIdentifier);
|
int clientIdentifierBytes = utf8Bytes(clientIdentifier);
|
||||||
payloadBufferSize += 2 + clientIdentifierBytes.length;
|
payloadBufferSize += 2 + clientIdentifierBytes;
|
||||||
|
|
||||||
// Will topic and message
|
// Will topic and message
|
||||||
String willTopic = payload.willTopic();
|
String willTopic = payload.willTopic();
|
||||||
byte[] willTopicBytes = willTopic != null ? encodeStringUtf8(willTopic) : EmptyArrays.EMPTY_BYTES;
|
int willTopicBytes = nullableUtf8Bytes(willTopic);
|
||||||
String willMessage = payload.willMessage();
|
byte[] willMessage = payload.willMessageInBytes();
|
||||||
byte[] willMessageBytes = willMessage != null ? encodeStringUtf8(willMessage) : EmptyArrays.EMPTY_BYTES;
|
byte[] willMessageBytes = willMessage != null ? willMessage : EmptyArrays.EMPTY_BYTES;
|
||||||
if (variableHeader.isWillFlag()) {
|
if (variableHeader.isWillFlag()) {
|
||||||
payloadBufferSize += 2 + willTopicBytes.length;
|
payloadBufferSize += 2 + willTopicBytes;
|
||||||
payloadBufferSize += 2 + willMessageBytes.length;
|
payloadBufferSize += 2 + willMessageBytes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
String userName = payload.userName();
|
String userName = payload.userName();
|
||||||
byte[] userNameBytes = userName != null ? encodeStringUtf8(userName) : EmptyArrays.EMPTY_BYTES;
|
int userNameBytes = nullableUtf8Bytes(userName);
|
||||||
if (variableHeader.hasUserName()) {
|
if (variableHeader.hasUserName()) {
|
||||||
payloadBufferSize += 2 + userNameBytes.length;
|
payloadBufferSize += 2 + userNameBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] passwordBytes = payload.password();
|
byte[] password = payload.passwordInBytes();
|
||||||
|
byte[] passwordBytes = password != null ? password : EmptyArrays.EMPTY_BYTES;
|
||||||
if (variableHeader.hasPassword()) {
|
if (variableHeader.hasPassword()) {
|
||||||
payloadBufferSize += 2 + passwordBytes.length;
|
payloadBufferSize += 2 + passwordBytes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fixed header
|
// Fixed and variable header
|
||||||
byte[] protocolNameBytes = mqttVersion.protocolNameBytes();
|
byte[] protocolNameBytes = mqttVersion.protocolNameBytes();
|
||||||
int variableHeaderBufferSize = 2 + protocolNameBytes.length + 4;
|
ByteBuf propertiesBuf = encodePropertiesIfNeeded(
|
||||||
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
mqttVersion,
|
||||||
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
ctx.alloc(),
|
||||||
ByteBuf buf = byteBufAllocator.buffer(fixedHeaderBufferSize + variablePartSize);
|
message.variableHeader().properties());
|
||||||
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
try {
|
||||||
writeVariableLengthInt(buf, variablePartSize);
|
final ByteBuf willPropertiesBuf;
|
||||||
|
if (variableHeader.isWillFlag()) {
|
||||||
|
willPropertiesBuf = encodePropertiesIfNeeded(mqttVersion, ctx.alloc(), payload.willProperties());
|
||||||
|
payloadBufferSize += willPropertiesBuf.readableBytes();
|
||||||
|
} else {
|
||||||
|
willPropertiesBuf = Unpooled.EMPTY_BUFFER;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int variableHeaderBufferSize = 2 + protocolNameBytes.length + 4 + propertiesBuf.readableBytes();
|
||||||
|
|
||||||
buf.writeShort(protocolNameBytes.length);
|
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
||||||
buf.writeBytes(protocolNameBytes);
|
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
||||||
|
ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize);
|
||||||
|
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
||||||
|
writeVariableLengthInt(buf, variablePartSize);
|
||||||
|
|
||||||
buf.writeByte(variableHeader.version());
|
buf.writeShort(protocolNameBytes.length);
|
||||||
buf.writeByte(getConnVariableHeaderFlag(variableHeader));
|
buf.writeBytes(protocolNameBytes);
|
||||||
buf.writeShort(variableHeader.keepAliveTimeSeconds());
|
|
||||||
|
|
||||||
// Payload
|
buf.writeByte(variableHeader.version());
|
||||||
buf.writeShort(clientIdentifierBytes.length);
|
buf.writeByte(getConnVariableHeaderFlag(variableHeader));
|
||||||
buf.writeBytes(clientIdentifierBytes, 0, clientIdentifierBytes.length);
|
buf.writeShort(variableHeader.keepAliveTimeSeconds());
|
||||||
if (variableHeader.isWillFlag()) {
|
buf.writeBytes(propertiesBuf);
|
||||||
buf.writeShort(willTopicBytes.length);
|
|
||||||
buf.writeBytes(willTopicBytes, 0, willTopicBytes.length);
|
// Payload
|
||||||
buf.writeShort(willMessageBytes.length);
|
writeExactUTF8String(buf, clientIdentifier, clientIdentifierBytes);
|
||||||
buf.writeBytes(willMessageBytes, 0, willMessageBytes.length);
|
if (variableHeader.isWillFlag()) {
|
||||||
|
buf.writeBytes(willPropertiesBuf);
|
||||||
|
writeExactUTF8String(buf, willTopic, willTopicBytes);
|
||||||
|
buf.writeShort(willMessageBytes.length);
|
||||||
|
buf.writeBytes(willMessageBytes, 0, willMessageBytes.length);
|
||||||
|
}
|
||||||
|
if (variableHeader.hasUserName()) {
|
||||||
|
writeExactUTF8String(buf, userName, userNameBytes);
|
||||||
|
}
|
||||||
|
if (variableHeader.hasPassword()) {
|
||||||
|
buf.writeShort(passwordBytes.length);
|
||||||
|
buf.writeBytes(passwordBytes, 0, passwordBytes.length);
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
} finally {
|
||||||
|
willPropertiesBuf.release();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
propertiesBuf.release();
|
||||||
}
|
}
|
||||||
if (variableHeader.hasUserName()) {
|
|
||||||
buf.writeShort(userNameBytes.length);
|
|
||||||
buf.writeBytes(userNameBytes, 0, userNameBytes.length);
|
|
||||||
}
|
|
||||||
if (variableHeader.hasPassword()) {
|
|
||||||
buf.writeShort(passwordBytes.length);
|
|
||||||
buf.writeBytes(passwordBytes, 0, passwordBytes.length);
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getConnVariableHeaderFlag(MqttConnectVariableHeader variableHeader) {
|
private static int getConnVariableHeaderFlag(MqttConnectVariableHeader variableHeader) {
|
||||||
@@ -189,149 +226,285 @@ public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf encodeConnAckMessage(
|
private static ByteBuf encodeConnAckMessage(
|
||||||
ByteBufAllocator byteBufAllocator,
|
ChannelHandlerContext ctx,
|
||||||
MqttConnAckMessage message) {
|
MqttConnAckMessage message) {
|
||||||
int length = 4;
|
final MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
MqttConnectAckPayload payload = null;
|
ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion,
|
||||||
if (message.payload() != null) {
|
ctx.alloc(),
|
||||||
payload = (MqttConnectAckPayload)message.payload();
|
message.variableHeader().properties());
|
||||||
if (payload.getData() != null && payload.getData().length > 0) {
|
|
||||||
length += payload.getData().length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ByteBuf buf = byteBufAllocator.buffer(length);
|
|
||||||
buf.writeByte(getFixedHeaderByte1(message.fixedHeader()));
|
|
||||||
buf.writeByte(length - 2);
|
|
||||||
buf.writeByte(message.variableHeader().isSessionPresent() ? 0x01 : 0x00);
|
|
||||||
buf.writeByte(message.variableHeader().connectReturnCode().byteValue());
|
|
||||||
if (length > 4) {
|
|
||||||
buf.writeBytes(payload.getData());
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf;
|
try {
|
||||||
|
int length = 4;
|
||||||
|
MqttConnectAckPayload payload = null;
|
||||||
|
if (message.payload() != null) {
|
||||||
|
payload = (MqttConnectAckPayload)message.payload();
|
||||||
|
if (payload.getData() != null && payload.getData().length > 0) {
|
||||||
|
length += payload.getData().length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ByteBuf buf = ctx.alloc().buffer(length + propertiesBuf.readableBytes());
|
||||||
|
buf.writeByte(getFixedHeaderByte1(message.fixedHeader()));
|
||||||
|
writeVariableLengthInt(buf, length - 2 + propertiesBuf.readableBytes());
|
||||||
|
buf.writeByte(message.variableHeader().isSessionPresent() ? 0x01 : 0x00);
|
||||||
|
buf.writeByte(message.variableHeader().connectReturnCode().byteValue());
|
||||||
|
if (length > 4) {
|
||||||
|
buf.writeBytes(payload.getData());
|
||||||
|
}
|
||||||
|
buf.writeBytes(propertiesBuf);
|
||||||
|
return buf;
|
||||||
|
} finally {
|
||||||
|
propertiesBuf.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf encodeSubscribeMessage(
|
private static ByteBuf encodeSubscribeMessage(
|
||||||
ByteBufAllocator byteBufAllocator,
|
ChannelHandlerContext ctx,
|
||||||
MqttSubscribeMessage message) {
|
MqttSubscribeMessage message) {
|
||||||
int variableHeaderBufferSize = 2;
|
MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
int payloadBufferSize = 0;
|
ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion,
|
||||||
|
ctx.alloc(),
|
||||||
|
message.idAndPropertiesVariableHeader().properties());
|
||||||
|
|
||||||
MqttFixedHeader mqttFixedHeader = message.fixedHeader();
|
try {
|
||||||
MqttMessageIdVariableHeader variableHeader = message.variableHeader();
|
final int variableHeaderBufferSize = 2 + propertiesBuf.readableBytes();
|
||||||
MqttSubscribePayload payload = message.payload();
|
int payloadBufferSize = 0;
|
||||||
|
|
||||||
for (MqttTopicSubscription topic : payload.topicSubscriptions()) {
|
MqttFixedHeader mqttFixedHeader = message.fixedHeader();
|
||||||
String topicName = topic.topicName();
|
MqttMessageIdVariableHeader variableHeader = message.variableHeader();
|
||||||
byte[] topicNameBytes = encodeStringUtf8(topicName);
|
MqttSubscribePayload payload = message.payload();
|
||||||
payloadBufferSize += 2 + topicNameBytes.length;
|
|
||||||
payloadBufferSize += 1;
|
for (MqttTopicSubscription topic : payload.topicSubscriptions()) {
|
||||||
|
String topicName = topic.topicName();
|
||||||
|
int topicNameBytes = utf8Bytes(topicName);
|
||||||
|
payloadBufferSize += 2 + topicNameBytes;
|
||||||
|
payloadBufferSize += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
||||||
|
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
||||||
|
|
||||||
|
ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize);
|
||||||
|
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
||||||
|
writeVariableLengthInt(buf, variablePartSize);
|
||||||
|
|
||||||
|
// Variable Header
|
||||||
|
int messageId = variableHeader.messageId();
|
||||||
|
buf.writeShort(messageId);
|
||||||
|
buf.writeBytes(propertiesBuf);
|
||||||
|
|
||||||
|
// Payload
|
||||||
|
for (MqttTopicSubscription topic : payload.topicSubscriptions()) {
|
||||||
|
writeUnsafeUTF8String(buf, topic.topicName());
|
||||||
|
if (mqttVersion.protocolLevel() >= MqttVersion.MQTT_3_1_1.protocolLevel()) {
|
||||||
|
buf.writeByte(topic.qualityOfService().value());
|
||||||
|
} else {
|
||||||
|
final MqttSubscriptionOption option = topic.option();
|
||||||
|
|
||||||
|
int optionEncoded = option.retainHandling().value() << 4;
|
||||||
|
if (option.isRetainAsPublished()) {
|
||||||
|
optionEncoded |= 0x08;
|
||||||
|
}
|
||||||
|
if (option.isNoLocal()) {
|
||||||
|
optionEncoded |= 0x04;
|
||||||
|
}
|
||||||
|
optionEncoded |= option.qos().value();
|
||||||
|
|
||||||
|
buf.writeByte(optionEncoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
} finally {
|
||||||
|
propertiesBuf.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
|
||||||
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
|
||||||
|
|
||||||
ByteBuf buf = byteBufAllocator.buffer(fixedHeaderBufferSize + variablePartSize);
|
|
||||||
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
|
||||||
writeVariableLengthInt(buf, variablePartSize);
|
|
||||||
|
|
||||||
// Variable Header
|
|
||||||
int messageId = variableHeader.messageId();
|
|
||||||
buf.writeShort(messageId);
|
|
||||||
|
|
||||||
// Payload
|
|
||||||
for (MqttTopicSubscription topic : payload.topicSubscriptions()) {
|
|
||||||
String topicName = topic.topicName();
|
|
||||||
byte[] topicNameBytes = encodeStringUtf8(topicName);
|
|
||||||
buf.writeShort(topicNameBytes.length);
|
|
||||||
buf.writeBytes(topicNameBytes, 0, topicNameBytes.length);
|
|
||||||
buf.writeByte(topic.qualityOfService().value());
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf encodeUnsubscribeMessage(
|
private static ByteBuf encodeUnsubscribeMessage(
|
||||||
ByteBufAllocator byteBufAllocator,
|
ChannelHandlerContext ctx,
|
||||||
MqttUnsubscribeMessage message) {
|
MqttUnsubscribeMessage message) {
|
||||||
int variableHeaderBufferSize = 2;
|
MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
int payloadBufferSize = 0;
|
ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion,
|
||||||
|
ctx.alloc(),
|
||||||
|
message.idAndPropertiesVariableHeader().properties());
|
||||||
|
|
||||||
MqttFixedHeader mqttFixedHeader = message.fixedHeader();
|
try {
|
||||||
MqttMessageIdVariableHeader variableHeader = message.variableHeader();
|
final int variableHeaderBufferSize = 2 + propertiesBuf.readableBytes();
|
||||||
MqttUnsubscribePayload payload = message.payload();
|
int payloadBufferSize = 0;
|
||||||
|
|
||||||
for (String topicName : payload.topics()) {
|
MqttFixedHeader mqttFixedHeader = message.fixedHeader();
|
||||||
byte[] topicNameBytes = encodeStringUtf8(topicName);
|
MqttMessageIdVariableHeader variableHeader = message.variableHeader();
|
||||||
payloadBufferSize += 2 + topicNameBytes.length;
|
MqttUnsubscribePayload payload = message.payload();
|
||||||
|
|
||||||
|
for (String topicName : payload.topics()) {
|
||||||
|
int topicNameBytes = utf8Bytes(topicName);
|
||||||
|
payloadBufferSize += 2 + topicNameBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
||||||
|
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
||||||
|
|
||||||
|
ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize);
|
||||||
|
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
||||||
|
writeVariableLengthInt(buf, variablePartSize);
|
||||||
|
|
||||||
|
// Variable Header
|
||||||
|
int messageId = variableHeader.messageId();
|
||||||
|
buf.writeShort(messageId);
|
||||||
|
buf.writeBytes(propertiesBuf);
|
||||||
|
|
||||||
|
// Payload
|
||||||
|
for (String topicName : payload.topics()) {
|
||||||
|
writeUnsafeUTF8String(buf, topicName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
} finally {
|
||||||
|
propertiesBuf.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
|
||||||
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
|
||||||
|
|
||||||
ByteBuf buf = byteBufAllocator.buffer(fixedHeaderBufferSize + variablePartSize);
|
|
||||||
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
|
||||||
writeVariableLengthInt(buf, variablePartSize);
|
|
||||||
|
|
||||||
// Variable Header
|
|
||||||
int messageId = variableHeader.messageId();
|
|
||||||
buf.writeShort(messageId);
|
|
||||||
|
|
||||||
// Payload
|
|
||||||
for (String topicName : payload.topics()) {
|
|
||||||
byte[] topicNameBytes = encodeStringUtf8(topicName);
|
|
||||||
buf.writeShort(topicNameBytes.length);
|
|
||||||
buf.writeBytes(topicNameBytes, 0, topicNameBytes.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf encodeSubAckMessage(
|
private static ByteBuf encodeSubAckMessage(
|
||||||
ByteBufAllocator byteBufAllocator,
|
ChannelHandlerContext ctx,
|
||||||
MqttSubAckMessage message) {
|
MqttSubAckMessage message) {
|
||||||
int variableHeaderBufferSize = 2;
|
MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
int payloadBufferSize = message.payload().grantedQoSLevels().size();
|
ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion,
|
||||||
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
ctx.alloc(),
|
||||||
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
message.idAndPropertiesVariableHeader().properties());
|
||||||
ByteBuf buf = byteBufAllocator.buffer(fixedHeaderBufferSize + variablePartSize);
|
try {
|
||||||
buf.writeByte(getFixedHeaderByte1(message.fixedHeader()));
|
int variableHeaderBufferSize = 2 + propertiesBuf.readableBytes();
|
||||||
writeVariableLengthInt(buf, variablePartSize);
|
int payloadBufferSize = message.payload().grantedQoSLevels().size();
|
||||||
buf.writeShort(message.variableHeader().messageId());
|
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
||||||
for (int qos : message.payload().grantedQoSLevels()) {
|
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
||||||
buf.writeByte(qos);
|
ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize);
|
||||||
}
|
buf.writeByte(getFixedHeaderByte1(message.fixedHeader()));
|
||||||
|
writeVariableLengthInt(buf, variablePartSize);
|
||||||
|
buf.writeShort(message.variableHeader().messageId());
|
||||||
|
buf.writeBytes(propertiesBuf);
|
||||||
|
for (int code: message.payload().reasonCodes()) {
|
||||||
|
buf.writeByte(code);
|
||||||
|
}
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
|
} finally {
|
||||||
|
propertiesBuf.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteBuf encodeUnsubAckMessage(
|
||||||
|
ChannelHandlerContext ctx,
|
||||||
|
MqttUnsubAckMessage message) {
|
||||||
|
if (message.variableHeader() instanceof MqttMessageIdAndPropertiesVariableHeader) {
|
||||||
|
MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
|
ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion,
|
||||||
|
ctx.alloc(),
|
||||||
|
message.idAndPropertiesVariableHeader().properties());
|
||||||
|
try {
|
||||||
|
int variableHeaderBufferSize = 2 + propertiesBuf.readableBytes();
|
||||||
|
MqttUnsubAckPayload payload = message.payload();
|
||||||
|
int payloadBufferSize = payload == null ? 0 : payload.unsubscribeReasonCodes().size();
|
||||||
|
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
||||||
|
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
||||||
|
ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize);
|
||||||
|
buf.writeByte(getFixedHeaderByte1(message.fixedHeader()));
|
||||||
|
writeVariableLengthInt(buf, variablePartSize);
|
||||||
|
buf.writeShort(message.variableHeader().messageId());
|
||||||
|
buf.writeBytes(propertiesBuf);
|
||||||
|
|
||||||
|
if (payload != null) {
|
||||||
|
for (Short reasonCode : payload.unsubscribeReasonCodes()) {
|
||||||
|
buf.writeByte(reasonCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
} finally {
|
||||||
|
propertiesBuf.release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return encodeMessageWithOnlySingleByteFixedHeaderAndMessageId(ctx.alloc(), message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf encodePublishMessage(
|
private static ByteBuf encodePublishMessage(
|
||||||
ByteBufAllocator byteBufAllocator,
|
ChannelHandlerContext ctx,
|
||||||
MqttPublishMessage message) {
|
MqttPublishMessage message) {
|
||||||
|
MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
MqttFixedHeader mqttFixedHeader = message.fixedHeader();
|
MqttFixedHeader mqttFixedHeader = message.fixedHeader();
|
||||||
MqttPublishVariableHeader variableHeader = message.variableHeader();
|
MqttPublishVariableHeader variableHeader = message.variableHeader();
|
||||||
ByteBuf payload = message.payload().duplicate();
|
ByteBuf payload = message.payload().duplicate();
|
||||||
|
|
||||||
String topicName = variableHeader.topicName();
|
String topicName = variableHeader.topicName();
|
||||||
byte[] topicNameBytes = encodeStringUtf8(topicName);
|
int topicNameBytes = utf8Bytes(topicName);
|
||||||
|
|
||||||
int variableHeaderBufferSize = 2 + topicNameBytes.length +
|
ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion,
|
||||||
(mqttFixedHeader.qosLevel().value() > 0 ? 2 : 0);
|
ctx.alloc(),
|
||||||
int payloadBufferSize = payload.readableBytes();
|
message.variableHeader().properties());
|
||||||
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
|
||||||
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
|
||||||
|
|
||||||
ByteBuf buf = byteBufAllocator.buffer(fixedHeaderBufferSize + variablePartSize);
|
try {
|
||||||
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
int variableHeaderBufferSize = 2 + topicNameBytes +
|
||||||
writeVariableLengthInt(buf, variablePartSize);
|
(mqttFixedHeader.qosLevel().value() > 0 ? 2 : 0) + propertiesBuf.readableBytes();
|
||||||
buf.writeShort(topicNameBytes.length);
|
int payloadBufferSize = payload.readableBytes();
|
||||||
buf.writeBytes(topicNameBytes);
|
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
|
||||||
if (mqttFixedHeader.qosLevel().value() > 0) {
|
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize);
|
||||||
buf.writeShort(variableHeader.messageId());
|
|
||||||
|
ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize);
|
||||||
|
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
||||||
|
writeVariableLengthInt(buf, variablePartSize);
|
||||||
|
writeExactUTF8String(buf, topicName, topicNameBytes);
|
||||||
|
if (mqttFixedHeader.qosLevel().value() > 0) {
|
||||||
|
buf.writeShort(variableHeader.packetId());
|
||||||
|
}
|
||||||
|
buf.writeBytes(propertiesBuf);
|
||||||
|
buf.writeBytes(payload);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
} finally {
|
||||||
|
propertiesBuf.release();
|
||||||
}
|
}
|
||||||
buf.writeBytes(payload);
|
}
|
||||||
|
|
||||||
return buf;
|
private static ByteBuf encodePubReplyMessage(ChannelHandlerContext ctx,
|
||||||
|
MqttMessage message) {
|
||||||
|
if (message.variableHeader() instanceof MqttPubReplyMessageVariableHeader) {
|
||||||
|
MqttFixedHeader mqttFixedHeader = message.fixedHeader();
|
||||||
|
MqttPubReplyMessageVariableHeader variableHeader =
|
||||||
|
(MqttPubReplyMessageVariableHeader) message.variableHeader();
|
||||||
|
int msgId = variableHeader.messageId();
|
||||||
|
|
||||||
|
final ByteBuf propertiesBuf;
|
||||||
|
final boolean includeReasonCode;
|
||||||
|
final int variableHeaderBufferSize;
|
||||||
|
final MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
|
if (mqttVersion == MqttVersion.MQTT_5 &&
|
||||||
|
(variableHeader.reasonCode() != MqttPubReplyMessageVariableHeader.REASON_CODE_OK ||
|
||||||
|
!variableHeader.properties().isEmpty())) {
|
||||||
|
propertiesBuf = encodeProperties(ctx.alloc(), variableHeader.properties());
|
||||||
|
includeReasonCode = true;
|
||||||
|
variableHeaderBufferSize = 3 + propertiesBuf.readableBytes();
|
||||||
|
} else {
|
||||||
|
propertiesBuf = Unpooled.EMPTY_BUFFER;
|
||||||
|
includeReasonCode = false;
|
||||||
|
variableHeaderBufferSize = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final int fixedHeaderBufferSize = 1 + getVariableLengthInt(variableHeaderBufferSize);
|
||||||
|
ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variableHeaderBufferSize);
|
||||||
|
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
||||||
|
writeVariableLengthInt(buf, variableHeaderBufferSize);
|
||||||
|
buf.writeShort(msgId);
|
||||||
|
if (includeReasonCode) {
|
||||||
|
buf.writeByte(variableHeader.reasonCode());
|
||||||
|
}
|
||||||
|
buf.writeBytes(propertiesBuf);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
} finally {
|
||||||
|
propertiesBuf.release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return encodeMessageWithOnlySingleByteFixedHeaderAndMessageId(ctx.alloc(), message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf encodeMessageWithOnlySingleByteFixedHeaderAndMessageId(
|
private static ByteBuf encodeMessageWithOnlySingleByteFixedHeaderAndMessageId(
|
||||||
@@ -342,12 +515,11 @@ public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
|||||||
int msgId = variableHeader.messageId();
|
int msgId = variableHeader.messageId();
|
||||||
|
|
||||||
int variableHeaderBufferSize = 2; // variable part only has a message id
|
int variableHeaderBufferSize = 2; // variable part only has a message id
|
||||||
|
|
||||||
ByteBuf payload = null;
|
ByteBuf payload = null;
|
||||||
if (message.payload() != null) {
|
if (message.payload() != null) {
|
||||||
payload = ((ByteBuf)message.payload()).duplicate();
|
payload = ((ByteBuf)message.payload()).duplicate();
|
||||||
variableHeaderBufferSize += payload.readableBytes();
|
variableHeaderBufferSize += payload.readableBytes();
|
||||||
}
|
}
|
||||||
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variableHeaderBufferSize);
|
int fixedHeaderBufferSize = 1 + getVariableLengthInt(variableHeaderBufferSize);
|
||||||
ByteBuf buf = byteBufAllocator.buffer(fixedHeaderBufferSize + variableHeaderBufferSize);
|
ByteBuf buf = byteBufAllocator.buffer(fixedHeaderBufferSize + variableHeaderBufferSize);
|
||||||
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
||||||
@@ -355,11 +527,53 @@ public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
|||||||
buf.writeShort(msgId);
|
buf.writeShort(msgId);
|
||||||
if (payload != null) {
|
if (payload != null) {
|
||||||
buf.writeBytes(payload);
|
buf.writeBytes(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ByteBuf encodeReasonCodePlusPropertiesMessage(
|
||||||
|
ChannelHandlerContext ctx,
|
||||||
|
MqttMessage message) {
|
||||||
|
if (message.variableHeader() instanceof MqttReasonCodeAndPropertiesVariableHeader) {
|
||||||
|
MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx);
|
||||||
|
MqttFixedHeader mqttFixedHeader = message.fixedHeader();
|
||||||
|
MqttReasonCodeAndPropertiesVariableHeader variableHeader =
|
||||||
|
(MqttReasonCodeAndPropertiesVariableHeader) message.variableHeader();
|
||||||
|
|
||||||
|
final ByteBuf propertiesBuf;
|
||||||
|
final boolean includeReasonCode;
|
||||||
|
final int variableHeaderBufferSize;
|
||||||
|
if (mqttVersion == MqttVersion.MQTT_5 &&
|
||||||
|
(variableHeader.reasonCode() != MqttReasonCodeAndPropertiesVariableHeader.REASON_CODE_OK ||
|
||||||
|
!variableHeader.properties().isEmpty())) {
|
||||||
|
propertiesBuf = encodeProperties(ctx.alloc(), variableHeader.properties());
|
||||||
|
includeReasonCode = true;
|
||||||
|
variableHeaderBufferSize = 1 + propertiesBuf.readableBytes();
|
||||||
|
} else {
|
||||||
|
propertiesBuf = Unpooled.EMPTY_BUFFER;
|
||||||
|
includeReasonCode = false;
|
||||||
|
variableHeaderBufferSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final int fixedHeaderBufferSize = 1 + getVariableLengthInt(variableHeaderBufferSize);
|
||||||
|
ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variableHeaderBufferSize);
|
||||||
|
buf.writeByte(getFixedHeaderByte1(mqttFixedHeader));
|
||||||
|
writeVariableLengthInt(buf, variableHeaderBufferSize);
|
||||||
|
if (includeReasonCode) {
|
||||||
|
buf.writeByte(variableHeader.reasonCode());
|
||||||
|
}
|
||||||
|
buf.writeBytes(propertiesBuf);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
} finally {
|
||||||
|
propertiesBuf.release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return encodeMessageWithOnlySingleByteFixedHeader(ctx.alloc(), message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static ByteBuf encodeMessageWithOnlySingleByteFixedHeader(
|
private static ByteBuf encodeMessageWithOnlySingleByteFixedHeader(
|
||||||
ByteBufAllocator byteBufAllocator,
|
ByteBufAllocator byteBufAllocator,
|
||||||
MqttMessage message) {
|
MqttMessage message) {
|
||||||
@@ -371,6 +585,104 @@ public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ByteBuf encodePropertiesIfNeeded(MqttVersion mqttVersion,
|
||||||
|
ByteBufAllocator byteBufAllocator,
|
||||||
|
MqttProperties mqttProperties) {
|
||||||
|
if (mqttVersion == MqttVersion.MQTT_5) {
|
||||||
|
return encodeProperties(byteBufAllocator, mqttProperties);
|
||||||
|
}
|
||||||
|
return Unpooled.EMPTY_BUFFER;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteBuf encodeProperties(ByteBufAllocator byteBufAllocator,
|
||||||
|
MqttProperties mqttProperties) {
|
||||||
|
ByteBuf propertiesHeaderBuf = byteBufAllocator.buffer();
|
||||||
|
// encode also the Properties part
|
||||||
|
try {
|
||||||
|
ByteBuf propertiesBuf = byteBufAllocator.buffer();
|
||||||
|
try {
|
||||||
|
for (MqttProperties.MqttProperty property : mqttProperties.listAll()) {
|
||||||
|
MqttProperties.MqttPropertyType propertyType =
|
||||||
|
MqttProperties.MqttPropertyType.valueOf(property.propertyId);
|
||||||
|
switch (propertyType) {
|
||||||
|
case PAYLOAD_FORMAT_INDICATOR:
|
||||||
|
case REQUEST_PROBLEM_INFORMATION:
|
||||||
|
case REQUEST_RESPONSE_INFORMATION:
|
||||||
|
case MAXIMUM_QOS:
|
||||||
|
case RETAIN_AVAILABLE:
|
||||||
|
case WILDCARD_SUBSCRIPTION_AVAILABLE:
|
||||||
|
case SUBSCRIPTION_IDENTIFIER_AVAILABLE:
|
||||||
|
case SHARED_SUBSCRIPTION_AVAILABLE:
|
||||||
|
writeVariableLengthInt(propertiesBuf, property.propertyId);
|
||||||
|
final byte bytePropValue = ((MqttProperties.IntegerProperty) property).value.byteValue();
|
||||||
|
propertiesBuf.writeByte(bytePropValue);
|
||||||
|
break;
|
||||||
|
case SERVER_KEEP_ALIVE:
|
||||||
|
case RECEIVE_MAXIMUM:
|
||||||
|
case TOPIC_ALIAS_MAXIMUM:
|
||||||
|
case TOPIC_ALIAS:
|
||||||
|
writeVariableLengthInt(propertiesBuf, property.propertyId);
|
||||||
|
final short twoBytesInPropValue =
|
||||||
|
((MqttProperties.IntegerProperty) property).value.shortValue();
|
||||||
|
propertiesBuf.writeShort(twoBytesInPropValue);
|
||||||
|
break;
|
||||||
|
case PUBLICATION_EXPIRY_INTERVAL:
|
||||||
|
case SESSION_EXPIRY_INTERVAL:
|
||||||
|
case WILL_DELAY_INTERVAL:
|
||||||
|
case MAXIMUM_PACKET_SIZE:
|
||||||
|
writeVariableLengthInt(propertiesBuf, property.propertyId);
|
||||||
|
final int fourBytesIntPropValue = ((MqttProperties.IntegerProperty) property).value;
|
||||||
|
propertiesBuf.writeInt(fourBytesIntPropValue);
|
||||||
|
break;
|
||||||
|
case SUBSCRIPTION_IDENTIFIER:
|
||||||
|
writeVariableLengthInt(propertiesBuf, property.propertyId);
|
||||||
|
final int vbi = ((MqttProperties.IntegerProperty) property).value;
|
||||||
|
writeVariableLengthInt(propertiesBuf, vbi);
|
||||||
|
break;
|
||||||
|
case CONTENT_TYPE:
|
||||||
|
case RESPONSE_TOPIC:
|
||||||
|
case ASSIGNED_CLIENT_IDENTIFIER:
|
||||||
|
case AUTHENTICATION_METHOD:
|
||||||
|
case RESPONSE_INFORMATION:
|
||||||
|
case SERVER_REFERENCE:
|
||||||
|
case REASON_STRING:
|
||||||
|
writeVariableLengthInt(propertiesBuf, property.propertyId);
|
||||||
|
writeEagerUTF8String(propertiesBuf, ((MqttProperties.StringProperty) property).value);
|
||||||
|
break;
|
||||||
|
case USER_PROPERTY:
|
||||||
|
final List<MqttProperties.StringPair> pairs =
|
||||||
|
((MqttProperties.UserProperties) property).value;
|
||||||
|
for (MqttProperties.StringPair pair : pairs) {
|
||||||
|
writeVariableLengthInt(propertiesBuf, property.propertyId);
|
||||||
|
writeEagerUTF8String(propertiesBuf, pair.key);
|
||||||
|
writeEagerUTF8String(propertiesBuf, pair.value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CORRELATION_DATA:
|
||||||
|
case AUTHENTICATION_DATA:
|
||||||
|
writeVariableLengthInt(propertiesBuf, property.propertyId);
|
||||||
|
final byte[] binaryPropValue = ((MqttProperties.BinaryProperty) property).value;
|
||||||
|
propertiesBuf.writeShort(binaryPropValue.length);
|
||||||
|
propertiesBuf.writeBytes(binaryPropValue, 0, binaryPropValue.length);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//shouldn't reach here
|
||||||
|
throw new EncoderException("Unknown property type: " + propertyType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeVariableLengthInt(propertiesHeaderBuf, propertiesBuf.readableBytes());
|
||||||
|
propertiesHeaderBuf.writeBytes(propertiesBuf);
|
||||||
|
|
||||||
|
return propertiesHeaderBuf;
|
||||||
|
} finally {
|
||||||
|
propertiesBuf.release();
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
propertiesHeaderBuf.release();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static int getFixedHeaderByte1(MqttFixedHeader header) {
|
private static int getFixedHeaderByte1(MqttFixedHeader header) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
ret |= header.messageType().value() << 4;
|
ret |= header.messageType().value() << 4;
|
||||||
@@ -395,6 +707,42 @@ public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
|||||||
} while (num > 0);
|
} while (num > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int nullableUtf8Bytes(String s) {
|
||||||
|
return s == null? 0 : utf8Bytes(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int nullableMaxUtf8Bytes(String s) {
|
||||||
|
return s == null? 0 : utf8MaxBytes(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeExactUTF8String(ByteBuf buf, String s, int utf8Length) {
|
||||||
|
buf.ensureWritable(utf8Length + 2);
|
||||||
|
buf.writeShort(utf8Length);
|
||||||
|
if (utf8Length > 0) {
|
||||||
|
final int writtenUtf8Length = reserveAndWriteUtf8(buf, s, utf8Length);
|
||||||
|
assert writtenUtf8Length == utf8Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeEagerUTF8String(ByteBuf buf, String s) {
|
||||||
|
final int maxUtf8Length = nullableMaxUtf8Bytes(s);
|
||||||
|
buf.ensureWritable(maxUtf8Length + 2);
|
||||||
|
final int writerIndex = buf.writerIndex();
|
||||||
|
final int startUtf8String = writerIndex + 2;
|
||||||
|
buf.writerIndex(startUtf8String);
|
||||||
|
final int utf8Length = s != null? reserveAndWriteUtf8(buf, s, maxUtf8Length) : 0;
|
||||||
|
buf.setShort(writerIndex, utf8Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeUnsafeUTF8String(ByteBuf buf, String s) {
|
||||||
|
final int writerIndex = buf.writerIndex();
|
||||||
|
final int startUtf8String = writerIndex + 2;
|
||||||
|
// no need to reserve any capacity here, already done earlier: that's why is Unsafe
|
||||||
|
buf.writerIndex(startUtf8String);
|
||||||
|
final int utf8Length = s != null? reserveAndWriteUtf8(buf, s, 0) : 0;
|
||||||
|
buf.setShort(writerIndex, utf8Length);
|
||||||
|
}
|
||||||
|
|
||||||
private static int getVariableLengthInt(int num) {
|
private static int getVariableLengthInt(int num) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
do {
|
do {
|
||||||
@@ -404,7 +752,4 @@ public final class MqttEncoder extends MessageToMessageEncoder<MqttMessage> {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] encodeStringUtf8(String s) {
|
|
||||||
return s.getBytes(CharsetUtil.UTF_8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+2
-2
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -20,7 +20,7 @@ import io.netty.util.internal.ObjectUtil;
|
|||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#fixed-header">
|
* See <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#fixed-header">
|
||||||
* MQTTV3.1/fixed-header</a>
|
* MQTTV3.1/fixed-header</a>
|
||||||
*/
|
*/
|
||||||
public final class MqttFixedHeader {
|
public final class MqttFixedHeader {
|
||||||
|
|||||||
Executable → Regular
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
|||||||
Executable → Regular
+12
-1
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -29,6 +29,17 @@ public class MqttMessage {
|
|||||||
private final Object payload;
|
private final Object payload;
|
||||||
private final DecoderResult decoderResult;
|
private final DecoderResult decoderResult;
|
||||||
|
|
||||||
|
// Constants for fixed-header only message types with all flags set to 0 (see
|
||||||
|
// https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Table_2.2_-)
|
||||||
|
public static final MqttMessage PINGREQ = new MqttMessage(new MqttFixedHeader(MqttMessageType.PINGREQ, false,
|
||||||
|
MqttQoS.AT_MOST_ONCE, false, 0));
|
||||||
|
|
||||||
|
public static final MqttMessage PINGRESP = new MqttMessage(new MqttFixedHeader(MqttMessageType.PINGRESP, false,
|
||||||
|
MqttQoS.AT_MOST_ONCE, false, 0));
|
||||||
|
|
||||||
|
public static final MqttMessage DISCONNECT = new MqttMessage(new MqttFixedHeader(MqttMessageType.DISCONNECT, false,
|
||||||
|
MqttQoS.AT_MOST_ONCE, false, 0));
|
||||||
|
|
||||||
public MqttMessage(MqttFixedHeader mqttFixedHeader) {
|
public MqttMessage(MqttFixedHeader mqttFixedHeader) {
|
||||||
this(mqttFixedHeader, null, null);
|
this(mqttFixedHeader, null, null);
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+493
-14
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -15,10 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
import static io.netty.util.internal.ObjectUtil.checkPositive;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class MqttMessageBuilders {
|
public final class MqttMessageBuilders {
|
||||||
@@ -29,6 +33,7 @@ public final class MqttMessageBuilders {
|
|||||||
private MqttQoS qos;
|
private MqttQoS qos;
|
||||||
private ByteBuf payload;
|
private ByteBuf payload;
|
||||||
private int messageId;
|
private int messageId;
|
||||||
|
private MqttProperties mqttProperties;
|
||||||
|
|
||||||
PublishBuilder() {
|
PublishBuilder() {
|
||||||
}
|
}
|
||||||
@@ -58,9 +63,15 @@ public final class MqttMessageBuilders {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PublishBuilder properties(MqttProperties properties) {
|
||||||
|
this.mqttProperties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttPublishMessage build() {
|
public MqttPublishMessage build() {
|
||||||
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retained, 0);
|
MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retained, 0);
|
||||||
MqttPublishVariableHeader mqttVariableHeader = new MqttPublishVariableHeader(topic, messageId);
|
MqttPublishVariableHeader mqttVariableHeader =
|
||||||
|
new MqttPublishVariableHeader(topic, messageId, mqttProperties);
|
||||||
return new MqttPublishMessage(mqttFixedHeader, mqttVariableHeader, Unpooled.buffer().writeBytes(payload));
|
return new MqttPublishMessage(mqttFixedHeader, mqttVariableHeader, Unpooled.buffer().writeBytes(payload));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,13 +84,15 @@ public final class MqttMessageBuilders {
|
|||||||
private boolean hasUser;
|
private boolean hasUser;
|
||||||
private boolean hasPassword;
|
private boolean hasPassword;
|
||||||
private int keepAliveSecs;
|
private int keepAliveSecs;
|
||||||
|
private MqttProperties willProperties = MqttProperties.NO_PROPERTIES;
|
||||||
private boolean willFlag;
|
private boolean willFlag;
|
||||||
private boolean willRetain;
|
private boolean willRetain;
|
||||||
private MqttQoS willQos = MqttQoS.AT_MOST_ONCE;
|
private MqttQoS willQos = MqttQoS.AT_MOST_ONCE;
|
||||||
private String willTopic;
|
private String willTopic;
|
||||||
private String willMessage;
|
private byte[] willMessage;
|
||||||
private String username;
|
private String username;
|
||||||
private byte[] password;
|
private byte[] password;
|
||||||
|
private MqttProperties properties = MqttProperties.NO_PROPERTIES;
|
||||||
|
|
||||||
ConnectBuilder() {
|
ConnectBuilder() {
|
||||||
}
|
}
|
||||||
@@ -119,7 +132,16 @@ public final class MqttMessageBuilders {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link ConnectBuilder#willMessage(byte[])} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public ConnectBuilder willMessage(String willMessage) {
|
public ConnectBuilder willMessage(String willMessage) {
|
||||||
|
willMessage(willMessage == null ? null : willMessage.getBytes(CharsetUtil.UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectBuilder willMessage(byte[] willMessage) {
|
||||||
this.willMessage = willMessage;
|
this.willMessage = willMessage;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -129,6 +151,11 @@ public final class MqttMessageBuilders {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConnectBuilder willProperties(MqttProperties willProperties) {
|
||||||
|
this.willProperties = willProperties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ConnectBuilder hasUser(boolean value) {
|
public ConnectBuilder hasUser(boolean value) {
|
||||||
this.hasUser = value;
|
this.hasUser = value;
|
||||||
return this;
|
return this;
|
||||||
@@ -140,17 +167,31 @@ public final class MqttMessageBuilders {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ConnectBuilder username(String username) {
|
public ConnectBuilder username(String username) {
|
||||||
this.hasUser = true;
|
this.hasUser = username != null;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link ConnectBuilder#password(byte[])} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public ConnectBuilder password(String password) {
|
||||||
|
password(password == null ? null : password.getBytes(CharsetUtil.UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ConnectBuilder password(byte[] password) {
|
public ConnectBuilder password(byte[] password) {
|
||||||
this.hasPassword = true;
|
this.hasPassword = password != null;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConnectBuilder properties(MqttProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttConnectMessage build() {
|
public MqttConnectMessage build() {
|
||||||
MqttFixedHeader mqttFixedHeader =
|
MqttFixedHeader mqttFixedHeader =
|
||||||
new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||||
@@ -164,9 +205,10 @@ public final class MqttMessageBuilders {
|
|||||||
willQos.value(),
|
willQos.value(),
|
||||||
willFlag,
|
willFlag,
|
||||||
cleanSession,
|
cleanSession,
|
||||||
keepAliveSecs);
|
keepAliveSecs,
|
||||||
|
properties);
|
||||||
MqttConnectPayload mqttConnectPayload =
|
MqttConnectPayload mqttConnectPayload =
|
||||||
new MqttConnectPayload(clientId, willTopic, willMessage, username, password);
|
new MqttConnectPayload(clientId, willProperties, willTopic, willMessage, username, password);
|
||||||
return new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader, mqttConnectPayload);
|
return new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader, mqttConnectPayload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,36 +217,54 @@ public final class MqttMessageBuilders {
|
|||||||
|
|
||||||
private List<MqttTopicSubscription> subscriptions;
|
private List<MqttTopicSubscription> subscriptions;
|
||||||
private int messageId;
|
private int messageId;
|
||||||
|
private MqttProperties properties;
|
||||||
|
|
||||||
SubscribeBuilder() {
|
SubscribeBuilder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SubscribeBuilder addSubscription(MqttQoS qos, String topic) {
|
public SubscribeBuilder addSubscription(MqttQoS qos, String topic) {
|
||||||
if (subscriptions == null) {
|
ensureSubscriptionsExist();
|
||||||
subscriptions = new ArrayList<MqttTopicSubscription>(5);
|
|
||||||
}
|
|
||||||
subscriptions.add(new MqttTopicSubscription(topic, qos));
|
subscriptions.add(new MqttTopicSubscription(topic, qos));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SubscribeBuilder addSubscription(String topic, MqttSubscriptionOption option) {
|
||||||
|
ensureSubscriptionsExist();
|
||||||
|
subscriptions.add(new MqttTopicSubscription(topic, option));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SubscribeBuilder messageId(int messageId) {
|
public SubscribeBuilder messageId(int messageId) {
|
||||||
this.messageId = messageId;
|
this.messageId = messageId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SubscribeBuilder properties(MqttProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttSubscribeMessage build() {
|
public MqttSubscribeMessage build() {
|
||||||
MqttFixedHeader mqttFixedHeader =
|
MqttFixedHeader mqttFixedHeader =
|
||||||
new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
|
new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
|
||||||
MqttMessageIdVariableHeader mqttVariableHeader = MqttMessageIdVariableHeader.from(messageId);
|
MqttMessageIdAndPropertiesVariableHeader mqttVariableHeader =
|
||||||
|
new MqttMessageIdAndPropertiesVariableHeader(messageId, properties);
|
||||||
MqttSubscribePayload mqttSubscribePayload = new MqttSubscribePayload(subscriptions);
|
MqttSubscribePayload mqttSubscribePayload = new MqttSubscribePayload(subscriptions);
|
||||||
return new MqttSubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload);
|
return new MqttSubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureSubscriptionsExist() {
|
||||||
|
if (subscriptions == null) {
|
||||||
|
subscriptions = new ArrayList<MqttTopicSubscription>(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class UnsubscribeBuilder {
|
public static final class UnsubscribeBuilder {
|
||||||
|
|
||||||
private List<String> topicFilters;
|
private List<String> topicFilters;
|
||||||
private int messageId;
|
private int messageId;
|
||||||
|
private MqttProperties properties;
|
||||||
|
|
||||||
UnsubscribeBuilder() {
|
UnsubscribeBuilder() {
|
||||||
}
|
}
|
||||||
@@ -222,21 +282,33 @@ public final class MqttMessageBuilders {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UnsubscribeBuilder properties(MqttProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttUnsubscribeMessage build() {
|
public MqttUnsubscribeMessage build() {
|
||||||
MqttFixedHeader mqttFixedHeader =
|
MqttFixedHeader mqttFixedHeader =
|
||||||
new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
|
new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
|
||||||
MqttMessageIdVariableHeader mqttVariableHeader = MqttMessageIdVariableHeader.from(messageId);
|
MqttMessageIdAndPropertiesVariableHeader mqttVariableHeader =
|
||||||
|
new MqttMessageIdAndPropertiesVariableHeader(messageId, properties);
|
||||||
MqttUnsubscribePayload mqttSubscribePayload = new MqttUnsubscribePayload(topicFilters);
|
MqttUnsubscribePayload mqttSubscribePayload = new MqttUnsubscribePayload(topicFilters);
|
||||||
return new MqttUnsubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload);
|
return new MqttUnsubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface PropertiesInitializer<T> {
|
||||||
|
void apply(T builder);
|
||||||
|
}
|
||||||
|
|
||||||
public static final class ConnAckBuilder {
|
public static final class ConnAckBuilder {
|
||||||
|
|
||||||
private MqttConnectReturnCode returnCode;
|
private MqttConnectReturnCode returnCode;
|
||||||
private boolean sessionPresent;
|
private boolean sessionPresent;
|
||||||
|
private MqttProperties properties = MqttProperties.NO_PROPERTIES;
|
||||||
|
private ConnAckPropertiesBuilder propsBuilder;
|
||||||
|
|
||||||
ConnAckBuilder() {
|
private ConnAckBuilder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConnAckBuilder returnCode(MqttConnectReturnCode returnCode) {
|
public ConnAckBuilder returnCode(MqttConnectReturnCode returnCode) {
|
||||||
@@ -249,15 +321,402 @@ public final class MqttMessageBuilders {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConnAckBuilder properties(MqttProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckBuilder properties(PropertiesInitializer<ConnAckPropertiesBuilder> consumer) {
|
||||||
|
if (propsBuilder == null) {
|
||||||
|
propsBuilder = new ConnAckPropertiesBuilder();
|
||||||
|
}
|
||||||
|
consumer.apply(propsBuilder);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttConnAckMessage build() {
|
public MqttConnAckMessage build() {
|
||||||
|
if (propsBuilder != null) {
|
||||||
|
properties = propsBuilder.build();
|
||||||
|
}
|
||||||
MqttFixedHeader mqttFixedHeader =
|
MqttFixedHeader mqttFixedHeader =
|
||||||
new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||||
MqttConnAckVariableHeader mqttConnAckVariableHeader =
|
MqttConnAckVariableHeader mqttConnAckVariableHeader =
|
||||||
new MqttConnAckVariableHeader(returnCode, sessionPresent);
|
new MqttConnAckVariableHeader(returnCode, sessionPresent, properties);
|
||||||
return new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader);
|
return new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final class ConnAckPropertiesBuilder {
|
||||||
|
private String clientId;
|
||||||
|
private Long sessionExpiryInterval;
|
||||||
|
private int receiveMaximum;
|
||||||
|
private Byte maximumQos;
|
||||||
|
private boolean retain;
|
||||||
|
private Long maximumPacketSize;
|
||||||
|
private int topicAliasMaximum;
|
||||||
|
private String reasonString;
|
||||||
|
private final MqttProperties.UserProperties userProperties = new MqttProperties.UserProperties();
|
||||||
|
private Boolean wildcardSubscriptionAvailable;
|
||||||
|
private Boolean subscriptionIdentifiersAvailable;
|
||||||
|
private Boolean sharedSubscriptionAvailable;
|
||||||
|
private Integer serverKeepAlive;
|
||||||
|
private String responseInformation;
|
||||||
|
private String serverReference;
|
||||||
|
private String authenticationMethod;
|
||||||
|
private byte[] authenticationData;
|
||||||
|
|
||||||
|
public MqttProperties build() {
|
||||||
|
final MqttProperties props = new MqttProperties();
|
||||||
|
if (clientId != null) {
|
||||||
|
props.add(new MqttProperties.StringProperty(MqttProperties.MqttPropertyType.ASSIGNED_CLIENT_IDENTIFIER.value(),
|
||||||
|
clientId));
|
||||||
|
}
|
||||||
|
if (sessionExpiryInterval != null) {
|
||||||
|
props.add(new MqttProperties.IntegerProperty(
|
||||||
|
MqttProperties.MqttPropertyType.SESSION_EXPIRY_INTERVAL.value(), sessionExpiryInterval.intValue()));
|
||||||
|
}
|
||||||
|
if (receiveMaximum > 0) {
|
||||||
|
props.add(new MqttProperties.IntegerProperty(MqttProperties.MqttPropertyType.RECEIVE_MAXIMUM.value(), receiveMaximum));
|
||||||
|
}
|
||||||
|
if (maximumQos != null) {
|
||||||
|
props.add(new MqttProperties.IntegerProperty(MqttProperties.MqttPropertyType.MAXIMUM_QOS.value(), receiveMaximum));
|
||||||
|
}
|
||||||
|
props.add(new MqttProperties.IntegerProperty(MqttProperties.MqttPropertyType.RETAIN_AVAILABLE.value(), retain ? 1 : 0));
|
||||||
|
if (maximumPacketSize != null) {
|
||||||
|
props.add(new MqttProperties.IntegerProperty(MqttProperties.MqttPropertyType.MAXIMUM_PACKET_SIZE.value(),
|
||||||
|
maximumPacketSize.intValue()));
|
||||||
|
}
|
||||||
|
props.add(new MqttProperties.IntegerProperty(MqttProperties.MqttPropertyType.TOPIC_ALIAS_MAXIMUM.value(),
|
||||||
|
topicAliasMaximum));
|
||||||
|
if (reasonString != null) {
|
||||||
|
props.add(new MqttProperties.StringProperty(MqttProperties.MqttPropertyType.REASON_STRING.value(), reasonString));
|
||||||
|
}
|
||||||
|
props.add(userProperties);
|
||||||
|
if (wildcardSubscriptionAvailable != null) {
|
||||||
|
props.add(new MqttProperties.IntegerProperty(MqttProperties.MqttPropertyType.WILDCARD_SUBSCRIPTION_AVAILABLE.value(),
|
||||||
|
wildcardSubscriptionAvailable ? 1 : 0));
|
||||||
|
}
|
||||||
|
if (subscriptionIdentifiersAvailable != null) {
|
||||||
|
props.add(new MqttProperties.IntegerProperty(MqttProperties.MqttPropertyType.SUBSCRIPTION_IDENTIFIER_AVAILABLE.value(),
|
||||||
|
subscriptionIdentifiersAvailable ? 1 : 0));
|
||||||
|
}
|
||||||
|
if (sharedSubscriptionAvailable != null) {
|
||||||
|
props.add(new MqttProperties.IntegerProperty(MqttProperties.MqttPropertyType.SHARED_SUBSCRIPTION_AVAILABLE.value(),
|
||||||
|
sharedSubscriptionAvailable ? 1 : 0));
|
||||||
|
}
|
||||||
|
if (serverKeepAlive != null) {
|
||||||
|
props.add(new MqttProperties.IntegerProperty(MqttProperties.MqttPropertyType.SERVER_KEEP_ALIVE.value(),
|
||||||
|
serverKeepAlive));
|
||||||
|
}
|
||||||
|
if (responseInformation != null) {
|
||||||
|
props.add(new MqttProperties.StringProperty(MqttProperties.MqttPropertyType.RESPONSE_INFORMATION.value(),
|
||||||
|
responseInformation));
|
||||||
|
}
|
||||||
|
if (serverReference != null) {
|
||||||
|
props.add(new MqttProperties.StringProperty(MqttProperties.MqttPropertyType.SERVER_REFERENCE.value(),
|
||||||
|
serverReference));
|
||||||
|
}
|
||||||
|
if (authenticationMethod != null) {
|
||||||
|
props.add(new MqttProperties.StringProperty(MqttProperties.MqttPropertyType.AUTHENTICATION_METHOD.value(),
|
||||||
|
authenticationMethod));
|
||||||
|
}
|
||||||
|
if (authenticationData != null) {
|
||||||
|
props.add(new MqttProperties.BinaryProperty(MqttProperties.MqttPropertyType.AUTHENTICATION_DATA.value(),
|
||||||
|
authenticationData));
|
||||||
|
}
|
||||||
|
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder sessionExpiryInterval(long seconds) {
|
||||||
|
this.sessionExpiryInterval = seconds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder receiveMaximum(int value) {
|
||||||
|
this.receiveMaximum = checkPositive(value, "value");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder maximumQos(byte value) {
|
||||||
|
if (value != 0 && value != 1) {
|
||||||
|
throw new IllegalArgumentException("maximum QoS property could be 0 or 1");
|
||||||
|
}
|
||||||
|
this.maximumQos = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder retainAvailable(boolean retain) {
|
||||||
|
this.retain = retain;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder maximumPacketSize(long size) {
|
||||||
|
this.maximumPacketSize = checkPositive(size, "size");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder assignedClientId(String clientId) {
|
||||||
|
this.clientId = clientId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder topicAliasMaximum(int value) {
|
||||||
|
this.topicAliasMaximum = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder reasonString(String reason) {
|
||||||
|
this.reasonString = reason;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder userProperty(String name, String value) {
|
||||||
|
userProperties.add(name, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder wildcardSubscriptionAvailable(boolean value) {
|
||||||
|
this.wildcardSubscriptionAvailable = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder subscriptionIdentifiersAvailable(boolean value) {
|
||||||
|
this.subscriptionIdentifiersAvailable = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder sharedSubscriptionAvailable(boolean value) {
|
||||||
|
this.sharedSubscriptionAvailable = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder serverKeepAlive(int seconds) {
|
||||||
|
this.serverKeepAlive = seconds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder responseInformation(String value) {
|
||||||
|
this.responseInformation = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder serverReference(String host) {
|
||||||
|
this.serverReference = host;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder authenticationMethod(String methodName) {
|
||||||
|
this.authenticationMethod = methodName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnAckPropertiesBuilder authenticationData(byte[] rawData) {
|
||||||
|
this.authenticationData = rawData.clone();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class PubAckBuilder {
|
||||||
|
|
||||||
|
private int packetId;
|
||||||
|
private byte reasonCode;
|
||||||
|
private MqttProperties properties;
|
||||||
|
|
||||||
|
PubAckBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PubAckBuilder reasonCode(byte reasonCode) {
|
||||||
|
this.reasonCode = reasonCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PubAckBuilder packetId(int packetId) {
|
||||||
|
this.packetId = packetId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link PubAckBuilder#packetId(int)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public PubAckBuilder packetId(short packetId) {
|
||||||
|
return packetId(packetId & 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PubAckBuilder properties(MqttProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttMessage build() {
|
||||||
|
MqttFixedHeader mqttFixedHeader =
|
||||||
|
new MqttFixedHeader(MqttMessageType.PUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||||
|
MqttPubReplyMessageVariableHeader mqttPubAckVariableHeader =
|
||||||
|
new MqttPubReplyMessageVariableHeader(packetId, reasonCode, properties);
|
||||||
|
return new MqttMessage(mqttFixedHeader, mqttPubAckVariableHeader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class SubAckBuilder {
|
||||||
|
|
||||||
|
private int packetId;
|
||||||
|
private MqttProperties properties;
|
||||||
|
private final List<MqttQoS> grantedQoses = new ArrayList<MqttQoS>();
|
||||||
|
|
||||||
|
SubAckBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubAckBuilder packetId(int packetId) {
|
||||||
|
this.packetId = packetId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link SubAckBuilder#packetId(int)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public SubAckBuilder packetId(short packetId) {
|
||||||
|
return packetId(packetId & 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubAckBuilder properties(MqttProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubAckBuilder addGrantedQos(MqttQoS qos) {
|
||||||
|
this.grantedQoses.add(qos);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubAckBuilder addGrantedQoses(MqttQoS... qoses) {
|
||||||
|
this.grantedQoses.addAll(Arrays.asList(qoses));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttSubAckMessage build() {
|
||||||
|
MqttFixedHeader mqttFixedHeader =
|
||||||
|
new MqttFixedHeader(MqttMessageType.SUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||||
|
MqttMessageIdAndPropertiesVariableHeader mqttSubAckVariableHeader =
|
||||||
|
new MqttMessageIdAndPropertiesVariableHeader(packetId, properties);
|
||||||
|
|
||||||
|
//transform to primitive types
|
||||||
|
int[] grantedQoses = new int[this.grantedQoses.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (MqttQoS grantedQos : this.grantedQoses) {
|
||||||
|
grantedQoses[i++] = grantedQos.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttSubAckPayload subAckPayload = new MqttSubAckPayload(grantedQoses);
|
||||||
|
return new MqttSubAckMessage(mqttFixedHeader, mqttSubAckVariableHeader, subAckPayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class UnsubAckBuilder {
|
||||||
|
|
||||||
|
private int packetId;
|
||||||
|
private MqttProperties properties;
|
||||||
|
private final List<Short> reasonCodes = new ArrayList<Short>();
|
||||||
|
|
||||||
|
UnsubAckBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnsubAckBuilder packetId(int packetId) {
|
||||||
|
this.packetId = packetId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link UnsubAckBuilder#packetId(int)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public UnsubAckBuilder packetId(short packetId) {
|
||||||
|
return packetId(packetId & 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnsubAckBuilder properties(MqttProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnsubAckBuilder addReasonCode(short reasonCode) {
|
||||||
|
this.reasonCodes.add(reasonCode);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnsubAckBuilder addReasonCodes(Short... reasonCodes) {
|
||||||
|
this.reasonCodes.addAll(Arrays.asList(reasonCodes));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttUnsubAckMessage build() {
|
||||||
|
MqttFixedHeader mqttFixedHeader =
|
||||||
|
new MqttFixedHeader(MqttMessageType.UNSUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||||
|
MqttMessageIdAndPropertiesVariableHeader mqttSubAckVariableHeader =
|
||||||
|
new MqttMessageIdAndPropertiesVariableHeader(packetId, properties);
|
||||||
|
|
||||||
|
MqttUnsubAckPayload subAckPayload = new MqttUnsubAckPayload(reasonCodes);
|
||||||
|
return new MqttUnsubAckMessage(mqttFixedHeader, mqttSubAckVariableHeader, subAckPayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class DisconnectBuilder {
|
||||||
|
|
||||||
|
private MqttProperties properties;
|
||||||
|
private byte reasonCode;
|
||||||
|
|
||||||
|
DisconnectBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisconnectBuilder properties(MqttProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisconnectBuilder reasonCode(byte reasonCode) {
|
||||||
|
this.reasonCode = reasonCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttMessage build() {
|
||||||
|
MqttFixedHeader mqttFixedHeader =
|
||||||
|
new MqttFixedHeader(MqttMessageType.DISCONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||||
|
MqttReasonCodeAndPropertiesVariableHeader mqttDisconnectVariableHeader =
|
||||||
|
new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, properties);
|
||||||
|
|
||||||
|
return new MqttMessage(mqttFixedHeader, mqttDisconnectVariableHeader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class AuthBuilder {
|
||||||
|
|
||||||
|
private MqttProperties properties;
|
||||||
|
private byte reasonCode;
|
||||||
|
|
||||||
|
AuthBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthBuilder properties(MqttProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthBuilder reasonCode(byte reasonCode) {
|
||||||
|
this.reasonCode = reasonCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttMessage build() {
|
||||||
|
MqttFixedHeader mqttFixedHeader =
|
||||||
|
new MqttFixedHeader(MqttMessageType.AUTH, false, MqttQoS.AT_MOST_ONCE, false, 0);
|
||||||
|
MqttReasonCodeAndPropertiesVariableHeader mqttAuthVariableHeader =
|
||||||
|
new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, properties);
|
||||||
|
|
||||||
|
return new MqttMessage(mqttFixedHeader, mqttAuthVariableHeader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static ConnectBuilder connect() {
|
public static ConnectBuilder connect() {
|
||||||
return new ConnectBuilder();
|
return new ConnectBuilder();
|
||||||
}
|
}
|
||||||
@@ -278,6 +737,26 @@ public final class MqttMessageBuilders {
|
|||||||
return new UnsubscribeBuilder();
|
return new UnsubscribeBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PubAckBuilder pubAck() {
|
||||||
|
return new PubAckBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SubAckBuilder subAck() {
|
||||||
|
return new SubAckBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UnsubAckBuilder unsubAck() {
|
||||||
|
return new UnsubAckBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DisconnectBuilder disconnect() {
|
||||||
|
return new DisconnectBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthBuilder auth() {
|
||||||
|
return new AuthBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
private MqttMessageBuilders() {
|
private MqttMessageBuilders() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+16
-3
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -50,7 +50,8 @@ public final class MqttMessageFactory {
|
|||||||
case UNSUBACK:
|
case UNSUBACK:
|
||||||
return new MqttUnsubAckMessage(
|
return new MqttUnsubAckMessage(
|
||||||
mqttFixedHeader,
|
mqttFixedHeader,
|
||||||
(MqttMessageIdVariableHeader) variableHeader);
|
(MqttMessageIdVariableHeader) variableHeader,
|
||||||
|
(MqttUnsubAckPayload) payload);
|
||||||
|
|
||||||
case UNSUBSCRIBE:
|
case UNSUBSCRIBE:
|
||||||
return new MqttUnsubscribeMessage(
|
return new MqttUnsubscribeMessage(
|
||||||
@@ -65,17 +66,24 @@ public final class MqttMessageFactory {
|
|||||||
(ByteBuf) payload);
|
(ByteBuf) payload);
|
||||||
|
|
||||||
case PUBACK:
|
case PUBACK:
|
||||||
|
//Having MqttPubReplyMessageVariableHeader or MqttMessageIdVariableHeader
|
||||||
return new MqttPubAckMessage(mqttFixedHeader, (MqttMessageIdVariableHeader) variableHeader);
|
return new MqttPubAckMessage(mqttFixedHeader, (MqttMessageIdVariableHeader) variableHeader);
|
||||||
case PUBREC:
|
case PUBREC:
|
||||||
case PUBREL:
|
case PUBREL:
|
||||||
case PUBCOMP:
|
case PUBCOMP:
|
||||||
|
//Having MqttPubReplyMessageVariableHeader or MqttMessageIdVariableHeader
|
||||||
return new MqttMessage(mqttFixedHeader, variableHeader);
|
return new MqttMessage(mqttFixedHeader, variableHeader);
|
||||||
|
|
||||||
case PINGREQ:
|
case PINGREQ:
|
||||||
case PINGRESP:
|
case PINGRESP:
|
||||||
case DISCONNECT:
|
|
||||||
return new MqttMessage(mqttFixedHeader);
|
return new MqttMessage(mqttFixedHeader);
|
||||||
|
|
||||||
|
case DISCONNECT:
|
||||||
|
case AUTH:
|
||||||
|
//Having MqttReasonCodeAndPropertiesVariableHeader
|
||||||
|
return new MqttMessage(mqttFixedHeader,
|
||||||
|
variableHeader);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("unknown message type: " + mqttFixedHeader.messageType());
|
throw new IllegalArgumentException("unknown message type: " + mqttFixedHeader.messageType());
|
||||||
}
|
}
|
||||||
@@ -85,5 +93,10 @@ public final class MqttMessageFactory {
|
|||||||
return new MqttMessage(null, null, null, DecoderResult.failure(cause));
|
return new MqttMessage(null, null, null, DecoderResult.failure(cause));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MqttMessage newInvalidMessage(MqttFixedHeader mqttFixedHeader, Object variableHeader,
|
||||||
|
Throwable cause) {
|
||||||
|
return new MqttMessage(mqttFixedHeader, variableHeader, null, DecoderResult.failure(cause));
|
||||||
|
}
|
||||||
|
|
||||||
private MqttMessageFactory() { }
|
private MqttMessageFactory() { }
|
||||||
}
|
}
|
||||||
|
|||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable Header containing, Packet Id and Properties as in MQTT v5 spec.
|
||||||
|
*/
|
||||||
|
public final class MqttMessageIdAndPropertiesVariableHeader extends MqttMessageIdVariableHeader {
|
||||||
|
|
||||||
|
private final MqttProperties properties;
|
||||||
|
|
||||||
|
public MqttMessageIdAndPropertiesVariableHeader(int messageId, MqttProperties properties) {
|
||||||
|
super(messageId);
|
||||||
|
if (messageId < 1 || messageId > 0xffff) {
|
||||||
|
throw new IllegalArgumentException("messageId: " + messageId + " (expected: 1 ~ 65535)");
|
||||||
|
}
|
||||||
|
this.properties = MqttProperties.withEmptyDefaults(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttProperties properties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return StringUtil.simpleClassName(this) + "[" +
|
||||||
|
"messageId=" + messageId() +
|
||||||
|
", properties=" + properties +
|
||||||
|
']';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
MqttMessageIdAndPropertiesVariableHeader withDefaultEmptyProperties() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable → Regular
+12
-4
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -20,9 +20,9 @@ import io.netty.util.internal.StringUtil;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable Header containing only Message Id
|
* Variable Header containing only Message Id
|
||||||
* See <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#msg-id">MQTTV3.1/msg-id</a>
|
* See <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#msg-id">MQTTV3.1/msg-id</a>
|
||||||
*/
|
*/
|
||||||
public final class MqttMessageIdVariableHeader {
|
public class MqttMessageIdVariableHeader {
|
||||||
|
|
||||||
private final int messageId;
|
private final int messageId;
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ public final class MqttMessageIdVariableHeader {
|
|||||||
return new MqttMessageIdVariableHeader(messageId);
|
return new MqttMessageIdVariableHeader(messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MqttMessageIdVariableHeader(int messageId) {
|
protected MqttMessageIdVariableHeader(int messageId) {
|
||||||
this.messageId = messageId;
|
this.messageId = messageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,4 +49,12 @@ public final class MqttMessageIdVariableHeader {
|
|||||||
.append(']')
|
.append(']')
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttMessageIdAndPropertiesVariableHeader withEmptyProperties() {
|
||||||
|
return new MqttMessageIdAndPropertiesVariableHeader(messageId, MqttProperties.NO_PROPERTIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttMessageIdAndPropertiesVariableHeader withDefaultEmptyProperties() {
|
||||||
|
return withEmptyProperties();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+22
-7
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -33,7 +33,24 @@ public enum MqttMessageType {
|
|||||||
UNSUBACK(11),
|
UNSUBACK(11),
|
||||||
PINGREQ(12),
|
PINGREQ(12),
|
||||||
PINGRESP(13),
|
PINGRESP(13),
|
||||||
DISCONNECT(14);
|
DISCONNECT(14),
|
||||||
|
AUTH(15);
|
||||||
|
|
||||||
|
private static final MqttMessageType[] VALUES;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// this prevent values to be assigned with the wrong order
|
||||||
|
// and ensure valueOf to work fine
|
||||||
|
final MqttMessageType[] values = values();
|
||||||
|
VALUES = new MqttMessageType[values.length + 1];
|
||||||
|
for (MqttMessageType mqttMessageType : values) {
|
||||||
|
final int value = mqttMessageType.value;
|
||||||
|
if (VALUES[value] != null) {
|
||||||
|
throw new AssertionError("value already in use: " + value);
|
||||||
|
}
|
||||||
|
VALUES[value] = mqttMessageType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final int value;
|
private final int value;
|
||||||
|
|
||||||
@@ -46,12 +63,10 @@ public enum MqttMessageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static MqttMessageType valueOf(int type) {
|
public static MqttMessageType valueOf(int type) {
|
||||||
for (MqttMessageType t : values()) {
|
if (type <= 0 || type >= VALUES.length) {
|
||||||
if (t.value == type) {
|
throw new IllegalArgumentException("unknown message type: " + type);
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("unknown message type: " + type);
|
return VALUES[type];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,414 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
import io.netty.util.collection.IntObjectHashMap;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MQTT Properties container
|
||||||
|
* */
|
||||||
|
public final class MqttProperties {
|
||||||
|
|
||||||
|
public enum MqttPropertyType {
|
||||||
|
// single byte properties
|
||||||
|
PAYLOAD_FORMAT_INDICATOR(0x01),
|
||||||
|
REQUEST_PROBLEM_INFORMATION(0x17),
|
||||||
|
REQUEST_RESPONSE_INFORMATION(0x19),
|
||||||
|
MAXIMUM_QOS(0x24),
|
||||||
|
RETAIN_AVAILABLE(0x25),
|
||||||
|
WILDCARD_SUBSCRIPTION_AVAILABLE(0x28),
|
||||||
|
SUBSCRIPTION_IDENTIFIER_AVAILABLE(0x29),
|
||||||
|
SHARED_SUBSCRIPTION_AVAILABLE(0x2A),
|
||||||
|
|
||||||
|
// two bytes properties
|
||||||
|
SERVER_KEEP_ALIVE(0x13),
|
||||||
|
RECEIVE_MAXIMUM(0x21),
|
||||||
|
TOPIC_ALIAS_MAXIMUM(0x22),
|
||||||
|
TOPIC_ALIAS(0x23),
|
||||||
|
|
||||||
|
// four bytes properties
|
||||||
|
PUBLICATION_EXPIRY_INTERVAL(0x02),
|
||||||
|
SESSION_EXPIRY_INTERVAL(0x11),
|
||||||
|
WILL_DELAY_INTERVAL(0x18),
|
||||||
|
MAXIMUM_PACKET_SIZE(0x27),
|
||||||
|
|
||||||
|
// Variable Byte Integer
|
||||||
|
SUBSCRIPTION_IDENTIFIER(0x0B),
|
||||||
|
|
||||||
|
// UTF-8 Encoded String properties
|
||||||
|
CONTENT_TYPE(0x03),
|
||||||
|
RESPONSE_TOPIC(0x08),
|
||||||
|
ASSIGNED_CLIENT_IDENTIFIER(0x12),
|
||||||
|
AUTHENTICATION_METHOD(0x15),
|
||||||
|
RESPONSE_INFORMATION(0x1A),
|
||||||
|
SERVER_REFERENCE(0x1C),
|
||||||
|
REASON_STRING(0x1F),
|
||||||
|
USER_PROPERTY(0x26),
|
||||||
|
|
||||||
|
// Binary Data
|
||||||
|
CORRELATION_DATA(0x09),
|
||||||
|
AUTHENTICATION_DATA(0x16);
|
||||||
|
|
||||||
|
private static final MqttPropertyType[] VALUES;
|
||||||
|
|
||||||
|
static {
|
||||||
|
VALUES = new MqttPropertyType[43];
|
||||||
|
for (MqttPropertyType v : values()) {
|
||||||
|
VALUES[v.value] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
MqttPropertyType(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MqttPropertyType valueOf(int type) {
|
||||||
|
MqttPropertyType t = null;
|
||||||
|
try {
|
||||||
|
t = VALUES[type];
|
||||||
|
} catch (ArrayIndexOutOfBoundsException ignored) {
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
if (t == null) {
|
||||||
|
throw new IllegalArgumentException("unknown property type: " + type);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final MqttProperties NO_PROPERTIES = new MqttProperties(false);
|
||||||
|
|
||||||
|
static MqttProperties withEmptyDefaults(MqttProperties properties) {
|
||||||
|
if (properties == null) {
|
||||||
|
return MqttProperties.NO_PROPERTIES;
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MQTT property base class
|
||||||
|
*
|
||||||
|
* @param <T> property type
|
||||||
|
*/
|
||||||
|
public abstract static class MqttProperty<T> {
|
||||||
|
final T value;
|
||||||
|
final int propertyId;
|
||||||
|
|
||||||
|
protected MqttProperty(int propertyId, T value) {
|
||||||
|
this.propertyId = propertyId;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get MQTT property value
|
||||||
|
*
|
||||||
|
* @return property value
|
||||||
|
*/
|
||||||
|
public T value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get MQTT property ID
|
||||||
|
* @return property ID
|
||||||
|
*/
|
||||||
|
public int propertyId() {
|
||||||
|
return propertyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return propertyId + 31 * value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MqttProperty that = (MqttProperty) obj;
|
||||||
|
return this.propertyId == that.propertyId && this.value.equals(that.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class IntegerProperty extends MqttProperty<Integer> {
|
||||||
|
|
||||||
|
public IntegerProperty(int propertyId, Integer value) {
|
||||||
|
super(propertyId, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "IntegerProperty(" + propertyId + ", " + value + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class StringProperty extends MqttProperty<String> {
|
||||||
|
|
||||||
|
public StringProperty(int propertyId, String value) {
|
||||||
|
super(propertyId, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "StringProperty(" + propertyId + ", " + value + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class StringPair {
|
||||||
|
public final String key;
|
||||||
|
public final String value;
|
||||||
|
|
||||||
|
public StringPair(String key, String value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return key.hashCode() + 31 * value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
StringPair that = (StringPair) obj;
|
||||||
|
|
||||||
|
return that.key.equals(this.key) && that.value.equals(this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//User properties are the only properties that may be included multiple times and
|
||||||
|
//are the only properties where ordering is required. Therefore, they need a special handling
|
||||||
|
public static final class UserProperties extends MqttProperty<List<StringPair>> {
|
||||||
|
public UserProperties() {
|
||||||
|
super(MqttPropertyType.USER_PROPERTY.value, new ArrayList<StringPair>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create user properties from the collection of the String pair values
|
||||||
|
*
|
||||||
|
* @param values string pairs. Collection entries are copied, collection itself isn't shared
|
||||||
|
*/
|
||||||
|
public UserProperties(Collection<StringPair> values) {
|
||||||
|
this();
|
||||||
|
this.value.addAll(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UserProperties fromUserPropertyCollection(Collection<UserProperty> properties) {
|
||||||
|
UserProperties userProperties = new UserProperties();
|
||||||
|
for (UserProperty property: properties) {
|
||||||
|
userProperties.add(new StringPair(property.value.key, property.value.value));
|
||||||
|
}
|
||||||
|
return userProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(StringPair pair) {
|
||||||
|
this.value.add(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String key, String value) {
|
||||||
|
this.value.add(new StringPair(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder("UserProperties(");
|
||||||
|
boolean first = true;
|
||||||
|
for (StringPair pair: value) {
|
||||||
|
if (!first) {
|
||||||
|
builder.append(", ");
|
||||||
|
}
|
||||||
|
builder.append(pair.key + "->" + pair.value);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
builder.append(")");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class UserProperty extends MqttProperty<StringPair> {
|
||||||
|
public UserProperty(String key, String value) {
|
||||||
|
super(MqttPropertyType.USER_PROPERTY.value, new StringPair(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UserProperty(" + value.key + ", " + value.value + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class BinaryProperty extends MqttProperty<byte[]> {
|
||||||
|
|
||||||
|
public BinaryProperty(int propertyId, byte[] value) {
|
||||||
|
super(propertyId, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BinaryProperty(" + propertyId + ", " + value.length + " bytes)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttProperties() {
|
||||||
|
this(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MqttProperties(boolean canModify) {
|
||||||
|
this.canModify = canModify;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntObjectHashMap<MqttProperty> props;
|
||||||
|
private List<UserProperty> userProperties;
|
||||||
|
private List<IntegerProperty> subscriptionIds;
|
||||||
|
private final boolean canModify;
|
||||||
|
|
||||||
|
public void add(MqttProperty property) {
|
||||||
|
if (!canModify) {
|
||||||
|
throw new UnsupportedOperationException("adding property isn't allowed");
|
||||||
|
}
|
||||||
|
IntObjectHashMap<MqttProperty> props = this.props;
|
||||||
|
if (property.propertyId == MqttPropertyType.USER_PROPERTY.value) {
|
||||||
|
List<UserProperty> userProperties = this.userProperties;
|
||||||
|
if (userProperties == null) {
|
||||||
|
userProperties = new ArrayList<UserProperty>(1);
|
||||||
|
this.userProperties = userProperties;
|
||||||
|
}
|
||||||
|
if (property instanceof UserProperty) {
|
||||||
|
userProperties.add((UserProperty) property);
|
||||||
|
} else if (property instanceof UserProperties) {
|
||||||
|
for (StringPair pair: ((UserProperties) property).value) {
|
||||||
|
userProperties.add(new UserProperty(pair.key, pair.value));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("User property must be of UserProperty or UserProperties type");
|
||||||
|
}
|
||||||
|
} else if (property.propertyId == MqttPropertyType.SUBSCRIPTION_IDENTIFIER.value) {
|
||||||
|
List<IntegerProperty> subscriptionIds = this.subscriptionIds;
|
||||||
|
if (subscriptionIds == null) {
|
||||||
|
subscriptionIds = new ArrayList<IntegerProperty>(1);
|
||||||
|
this.subscriptionIds = subscriptionIds;
|
||||||
|
}
|
||||||
|
if (property instanceof IntegerProperty) {
|
||||||
|
subscriptionIds.add((IntegerProperty) property);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Subscription ID must be an integer property");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (props == null) {
|
||||||
|
props = new IntObjectHashMap<MqttProperty>();
|
||||||
|
this.props = props;
|
||||||
|
}
|
||||||
|
props.put(property.propertyId, property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<? extends MqttProperty> listAll() {
|
||||||
|
IntObjectHashMap<MqttProperty> props = this.props;
|
||||||
|
if (props == null && subscriptionIds == null && userProperties == null) {
|
||||||
|
return Collections.<MqttProperty>emptyList();
|
||||||
|
}
|
||||||
|
if (subscriptionIds == null && userProperties == null) {
|
||||||
|
return props.values();
|
||||||
|
}
|
||||||
|
if (props == null && userProperties == null) {
|
||||||
|
return subscriptionIds;
|
||||||
|
}
|
||||||
|
List<MqttProperty> propValues = new ArrayList<MqttProperty>(props != null ? props.size() : 1);
|
||||||
|
if (props != null) {
|
||||||
|
propValues.addAll(props.values());
|
||||||
|
}
|
||||||
|
if (subscriptionIds != null) {
|
||||||
|
propValues.addAll(subscriptionIds);
|
||||||
|
}
|
||||||
|
if (userProperties != null) {
|
||||||
|
propValues.add(UserProperties.fromUserPropertyCollection(userProperties));
|
||||||
|
}
|
||||||
|
return propValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
IntObjectHashMap<MqttProperty> props = this.props;
|
||||||
|
return props == null || props.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get property by ID. If there are multiple properties of this type (can be with Subscription ID)
|
||||||
|
* then return the first one.
|
||||||
|
*
|
||||||
|
* @param propertyId ID of the property
|
||||||
|
* @return a property if it is set, null otherwise
|
||||||
|
*/
|
||||||
|
public MqttProperty getProperty(int propertyId) {
|
||||||
|
if (propertyId == MqttPropertyType.USER_PROPERTY.value) {
|
||||||
|
//special handling to keep compatibility with earlier versions
|
||||||
|
List<UserProperty> userProperties = this.userProperties;
|
||||||
|
if (userProperties == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return UserProperties.fromUserPropertyCollection(userProperties);
|
||||||
|
}
|
||||||
|
if (propertyId == MqttPropertyType.SUBSCRIPTION_IDENTIFIER.value) {
|
||||||
|
List<IntegerProperty> subscriptionIds = this.subscriptionIds;
|
||||||
|
if (subscriptionIds == null || subscriptionIds.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return subscriptionIds.get(0);
|
||||||
|
}
|
||||||
|
IntObjectHashMap<MqttProperty> props = this.props;
|
||||||
|
return props == null ? null : props.get(propertyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get properties by ID.
|
||||||
|
* Some properties (Subscription ID and User Properties) may occur multiple times,
|
||||||
|
* this method returns all their values in order.
|
||||||
|
*
|
||||||
|
* @param propertyId ID of the property
|
||||||
|
* @return all properties having specified ID
|
||||||
|
*/
|
||||||
|
public List<? extends MqttProperty> getProperties(int propertyId) {
|
||||||
|
if (propertyId == MqttPropertyType.USER_PROPERTY.value) {
|
||||||
|
return userProperties == null ? Collections.<MqttProperty>emptyList() : userProperties;
|
||||||
|
}
|
||||||
|
if (propertyId == MqttPropertyType.SUBSCRIPTION_IDENTIFIER.value) {
|
||||||
|
return subscriptionIds == null ? Collections.<MqttProperty>emptyList() : subscriptionIds;
|
||||||
|
}
|
||||||
|
IntObjectHashMap<MqttProperty> props = this.props;
|
||||||
|
return (props == null || !props.containsKey(propertyId)) ?
|
||||||
|
Collections.<MqttProperty>emptyList() :
|
||||||
|
Collections.singletonList(props.get(propertyId));
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable → Regular
+2
-2
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#puback">MQTTV3.1/puback</a>
|
* See <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#puback">MQTTV3.1/puback</a>
|
||||||
*/
|
*/
|
||||||
public final class MqttPubAckMessage extends MqttMessage {
|
public final class MqttPubAckMessage extends MqttMessage {
|
||||||
|
|
||||||
|
|||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable Header containing Packet Id, reason code and Properties as in MQTT v5 spec.
|
||||||
|
*/
|
||||||
|
public final class MqttPubReplyMessageVariableHeader extends MqttMessageIdVariableHeader {
|
||||||
|
|
||||||
|
private final byte reasonCode;
|
||||||
|
private final MqttProperties properties;
|
||||||
|
|
||||||
|
public static final byte REASON_CODE_OK = 0;
|
||||||
|
|
||||||
|
public MqttPubReplyMessageVariableHeader(int messageId, byte reasonCode, MqttProperties properties) {
|
||||||
|
super(messageId);
|
||||||
|
if (messageId < 1 || messageId > 0xffff) {
|
||||||
|
throw new IllegalArgumentException("messageId: " + messageId + " (expected: 1 ~ 65535)");
|
||||||
|
}
|
||||||
|
this.reasonCode = reasonCode;
|
||||||
|
this.properties = MqttProperties.withEmptyDefaults(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte reasonCode() {
|
||||||
|
return reasonCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttProperties properties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return StringUtil.simpleClassName(this) + "[" +
|
||||||
|
"messageId=" + messageId() +
|
||||||
|
", reasonCode=" + reasonCode +
|
||||||
|
", properties=" + properties +
|
||||||
|
']';
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable → Regular
+4
-8
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -18,10 +18,10 @@ package io.netty.handler.codec.mqtt;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufHolder;
|
import io.netty.buffer.ByteBufHolder;
|
||||||
import io.netty.util.IllegalReferenceCountException;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#publish">MQTTV3.1/publish</a>
|
* See <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#publish">MQTTV3.1/publish</a>
|
||||||
*/
|
*/
|
||||||
public class MqttPublishMessage extends MqttMessage implements ByteBufHolder {
|
public class MqttPublishMessage extends MqttMessage implements ByteBufHolder {
|
||||||
|
|
||||||
@@ -44,11 +44,7 @@ public class MqttPublishMessage extends MqttMessage implements ByteBufHolder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf content() {
|
public ByteBuf content() {
|
||||||
final ByteBuf data = (ByteBuf) super.payload();
|
return ByteBufUtil.ensureAccessible((ByteBuf) super.payload());
|
||||||
if (data.refCnt() <= 0) {
|
|
||||||
throw new IllegalReferenceCountException(data.refCnt());
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Executable → Regular
+16
-2
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -24,11 +24,17 @@ import io.netty.util.internal.StringUtil;
|
|||||||
public final class MqttPublishVariableHeader {
|
public final class MqttPublishVariableHeader {
|
||||||
|
|
||||||
private final String topicName;
|
private final String topicName;
|
||||||
private final int packetId;
|
private int packetId;
|
||||||
|
private final MqttProperties properties;
|
||||||
|
|
||||||
public MqttPublishVariableHeader(String topicName, int packetId) {
|
public MqttPublishVariableHeader(String topicName, int packetId) {
|
||||||
|
this(topicName, packetId, MqttProperties.NO_PROPERTIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttPublishVariableHeader(String topicName, int packetId, MqttProperties properties) {
|
||||||
this.topicName = topicName;
|
this.topicName = topicName;
|
||||||
this.packetId = packetId;
|
this.packetId = packetId;
|
||||||
|
this.properties = MqttProperties.withEmptyDefaults(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String topicName() {
|
public String topicName() {
|
||||||
@@ -47,6 +53,14 @@ public final class MqttPublishVariableHeader {
|
|||||||
return packetId;
|
return packetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttProperties properties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPacketId(int packetId) {
|
||||||
|
this.packetId = packetId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder(StringUtil.simpleClassName(this))
|
return new StringBuilder(StringUtil.simpleClassName(this))
|
||||||
|
|||||||
Executable → Regular
+12
-6
@@ -5,7 +5,7 @@
|
|||||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||||
* copy of the License at:
|
* copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
@@ -32,11 +32,17 @@ public enum MqttQoS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static MqttQoS valueOf(int value) {
|
public static MqttQoS valueOf(int value) {
|
||||||
for (MqttQoS q: values()) {
|
switch (value) {
|
||||||
if (q.value == value) {
|
case 0:
|
||||||
return q;
|
return AT_MOST_ONCE;
|
||||||
}
|
case 1:
|
||||||
|
return AT_LEAST_ONCE;
|
||||||
|
case 2:
|
||||||
|
return EXACTLY_ONCE;
|
||||||
|
case 0x80:
|
||||||
|
return FAILURE;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("invalid QoS: " + value);
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("invalid QoS: " + value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable Header for AUTH and DISCONNECT messages represented by {@link MqttMessage}
|
||||||
|
*/
|
||||||
|
public final class MqttReasonCodeAndPropertiesVariableHeader {
|
||||||
|
|
||||||
|
private final byte reasonCode;
|
||||||
|
private final MqttProperties properties;
|
||||||
|
|
||||||
|
public static final byte REASON_CODE_OK = 0;
|
||||||
|
|
||||||
|
public MqttReasonCodeAndPropertiesVariableHeader(byte reasonCode,
|
||||||
|
MqttProperties properties) {
|
||||||
|
this.reasonCode = reasonCode;
|
||||||
|
this.properties = MqttProperties.withEmptyDefaults(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte reasonCode() {
|
||||||
|
return reasonCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttProperties properties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringBuilder(StringUtil.simpleClassName(this))
|
||||||
|
.append('[')
|
||||||
|
.append("reasonCode=").append(reasonCode)
|
||||||
|
.append(", properties=").append(properties)
|
||||||
|
.append(']')
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable → Regular
+14
-3
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -17,22 +17,33 @@
|
|||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#suback">MQTTV3.1/suback</a>
|
* See <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#suback">MQTTV3.1/suback</a>
|
||||||
*/
|
*/
|
||||||
public final class MqttSubAckMessage extends MqttMessage {
|
public final class MqttSubAckMessage extends MqttMessage {
|
||||||
|
|
||||||
public MqttSubAckMessage(
|
public MqttSubAckMessage(
|
||||||
MqttFixedHeader mqttFixedHeader,
|
MqttFixedHeader mqttFixedHeader,
|
||||||
MqttMessageIdVariableHeader variableHeader,
|
MqttMessageIdAndPropertiesVariableHeader variableHeader,
|
||||||
MqttSubAckPayload payload) {
|
MqttSubAckPayload payload) {
|
||||||
super(mqttFixedHeader, variableHeader, payload);
|
super(mqttFixedHeader, variableHeader, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttSubAckMessage(
|
||||||
|
MqttFixedHeader mqttFixedHeader,
|
||||||
|
MqttMessageIdVariableHeader variableHeader,
|
||||||
|
MqttSubAckPayload payload) {
|
||||||
|
this(mqttFixedHeader, variableHeader.withDefaultEmptyProperties(), payload);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MqttMessageIdVariableHeader variableHeader() {
|
public MqttMessageIdVariableHeader variableHeader() {
|
||||||
return (MqttMessageIdVariableHeader) super.variableHeader();
|
return (MqttMessageIdVariableHeader) super.variableHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttMessageIdAndPropertiesVariableHeader idAndPropertiesVariableHeader() {
|
||||||
|
return (MqttMessageIdAndPropertiesVariableHeader) super.variableHeader();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MqttSubAckPayload payload() {
|
public MqttSubAckPayload payload() {
|
||||||
return (MqttSubAckPayload) super.payload();
|
return (MqttSubAckPayload) super.payload();
|
||||||
|
|||||||
Executable → Regular
+26
-17
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
import io.netty.util.internal.ObjectUtil;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -27,43 +28,51 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class MqttSubAckPayload {
|
public class MqttSubAckPayload {
|
||||||
|
|
||||||
private final List<Integer> grantedQoSLevels;
|
private final List<Integer> reasonCodes;
|
||||||
|
|
||||||
public MqttSubAckPayload(int... grantedQoSLevels) {
|
public MqttSubAckPayload(int... reasonCodes) {
|
||||||
if (grantedQoSLevels == null) {
|
ObjectUtil.checkNotNull(reasonCodes, "reasonCodes");
|
||||||
throw new NullPointerException("grantedQoSLevels");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Integer> list = new ArrayList<Integer>(grantedQoSLevels.length);
|
List<Integer> list = new ArrayList<Integer>(reasonCodes.length);
|
||||||
for (int v: grantedQoSLevels) {
|
for (int v: reasonCodes) {
|
||||||
list.add(v);
|
list.add(v);
|
||||||
}
|
}
|
||||||
this.grantedQoSLevels = Collections.unmodifiableList(list);
|
this.reasonCodes = Collections.unmodifiableList(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttSubAckPayload(Iterable<Integer> grantedQoSLevels) {
|
public MqttSubAckPayload(Iterable<Integer> reasonCodes) {
|
||||||
if (grantedQoSLevels == null) {
|
ObjectUtil.checkNotNull(reasonCodes, "reasonCodes");
|
||||||
throw new NullPointerException("grantedQoSLevels");
|
|
||||||
}
|
|
||||||
List<Integer> list = new ArrayList<Integer>();
|
List<Integer> list = new ArrayList<Integer>();
|
||||||
for (Integer v: grantedQoSLevels) {
|
for (Integer v: reasonCodes) {
|
||||||
if (v == null) {
|
if (v == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
list.add(v);
|
list.add(v);
|
||||||
}
|
}
|
||||||
this.grantedQoSLevels = Collections.unmodifiableList(list);
|
this.reasonCodes = Collections.unmodifiableList(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Integer> grantedQoSLevels() {
|
public List<Integer> grantedQoSLevels() {
|
||||||
return grantedQoSLevels;
|
List<Integer> qosLevels = new ArrayList<Integer>(reasonCodes.size());
|
||||||
|
for (int code: reasonCodes) {
|
||||||
|
if (code > MqttQoS.EXACTLY_ONCE.value()) {
|
||||||
|
qosLevels.add(MqttQoS.FAILURE.value());
|
||||||
|
} else {
|
||||||
|
qosLevels.add(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return qosLevels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> reasonCodes() {
|
||||||
|
return reasonCodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder(StringUtil.simpleClassName(this))
|
return new StringBuilder(StringUtil.simpleClassName(this))
|
||||||
.append('[')
|
.append('[')
|
||||||
.append("grantedQoSLevels=").append(grantedQoSLevels)
|
.append("reasonCodes=").append(reasonCodes)
|
||||||
.append(']')
|
.append(']')
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+14
-3
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -17,23 +17,34 @@
|
|||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#subscribe">
|
* See <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#subscribe">
|
||||||
* MQTTV3.1/subscribe</a>
|
* MQTTV3.1/subscribe</a>
|
||||||
*/
|
*/
|
||||||
public final class MqttSubscribeMessage extends MqttMessage {
|
public final class MqttSubscribeMessage extends MqttMessage {
|
||||||
|
|
||||||
public MqttSubscribeMessage(
|
public MqttSubscribeMessage(
|
||||||
MqttFixedHeader mqttFixedHeader,
|
MqttFixedHeader mqttFixedHeader,
|
||||||
MqttMessageIdVariableHeader variableHeader,
|
MqttMessageIdAndPropertiesVariableHeader variableHeader,
|
||||||
MqttSubscribePayload payload) {
|
MqttSubscribePayload payload) {
|
||||||
super(mqttFixedHeader, variableHeader, payload);
|
super(mqttFixedHeader, variableHeader, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttSubscribeMessage(
|
||||||
|
MqttFixedHeader mqttFixedHeader,
|
||||||
|
MqttMessageIdVariableHeader variableHeader,
|
||||||
|
MqttSubscribePayload payload) {
|
||||||
|
this(mqttFixedHeader, variableHeader.withDefaultEmptyProperties(), payload);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MqttMessageIdVariableHeader variableHeader() {
|
public MqttMessageIdVariableHeader variableHeader() {
|
||||||
return (MqttMessageIdVariableHeader) super.variableHeader();
|
return (MqttMessageIdVariableHeader) super.variableHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttMessageIdAndPropertiesVariableHeader idAndPropertiesVariableHeader() {
|
||||||
|
return (MqttMessageIdAndPropertiesVariableHeader) super.variableHeader();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MqttSubscribePayload payload() {
|
public MqttSubscribePayload payload() {
|
||||||
return (MqttSubscribePayload) super.payload();
|
return (MqttSubscribePayload) super.payload();
|
||||||
|
|||||||
Executable → Regular
+6
-5
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -39,11 +39,12 @@ public final class MqttSubscribePayload {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder builder = new StringBuilder(StringUtil.simpleClassName(this)).append('[');
|
StringBuilder builder = new StringBuilder(StringUtil.simpleClassName(this)).append('[');
|
||||||
for (int i = 0; i < topicSubscriptions.size() - 1; i++) {
|
for (int i = 0; i < topicSubscriptions.size(); i++) {
|
||||||
builder.append(topicSubscriptions.get(i)).append(", ");
|
builder.append(topicSubscriptions.get(i)).append(", ");
|
||||||
}
|
}
|
||||||
builder.append(topicSubscriptions.get(topicSubscriptions.size() - 1));
|
if (!topicSubscriptions.isEmpty()) {
|
||||||
builder.append(']');
|
builder.setLength(builder.length() - 2);
|
||||||
return builder.toString();
|
}
|
||||||
|
return builder.append(']').toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model the SubscriptionOption used in Subscribe MQTT v5 packet
|
||||||
|
*/
|
||||||
|
public final class MqttSubscriptionOption {
|
||||||
|
|
||||||
|
public enum RetainedHandlingPolicy {
|
||||||
|
SEND_AT_SUBSCRIBE(0),
|
||||||
|
SEND_AT_SUBSCRIBE_IF_NOT_YET_EXISTS(1),
|
||||||
|
DONT_SEND_AT_SUBSCRIBE(2);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
RetainedHandlingPolicy(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RetainedHandlingPolicy valueOf(int value) {
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
return SEND_AT_SUBSCRIBE;
|
||||||
|
case 1:
|
||||||
|
return SEND_AT_SUBSCRIBE_IF_NOT_YET_EXISTS;
|
||||||
|
case 2:
|
||||||
|
return DONT_SEND_AT_SUBSCRIBE;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("invalid RetainedHandlingPolicy: " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final MqttQoS qos;
|
||||||
|
private final boolean noLocal;
|
||||||
|
private final boolean retainAsPublished;
|
||||||
|
private final RetainedHandlingPolicy retainHandling;
|
||||||
|
|
||||||
|
public static MqttSubscriptionOption onlyFromQos(MqttQoS qos) {
|
||||||
|
return new MqttSubscriptionOption(qos, false, false, RetainedHandlingPolicy.SEND_AT_SUBSCRIBE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttSubscriptionOption(MqttQoS qos,
|
||||||
|
boolean noLocal,
|
||||||
|
boolean retainAsPublished,
|
||||||
|
RetainedHandlingPolicy retainHandling) {
|
||||||
|
this.qos = qos;
|
||||||
|
this.noLocal = noLocal;
|
||||||
|
this.retainAsPublished = retainAsPublished;
|
||||||
|
this.retainHandling = retainHandling;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttQoS qos() {
|
||||||
|
return qos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNoLocal() {
|
||||||
|
return noLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRetainAsPublished() {
|
||||||
|
return retainAsPublished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RetainedHandlingPolicy retainHandling() {
|
||||||
|
return retainHandling;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttSubscriptionOption that = (MqttSubscriptionOption) o;
|
||||||
|
|
||||||
|
if (noLocal != that.noLocal) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (retainAsPublished != that.retainAsPublished) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (qos != that.qos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return retainHandling == that.retainHandling;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = qos.hashCode();
|
||||||
|
result = 31 * result + (noLocal ? 1 : 0);
|
||||||
|
result = 31 * result + (retainAsPublished ? 1 : 0);
|
||||||
|
result = 31 * result + retainHandling.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SubscriptionOption[" +
|
||||||
|
"qos=" + qos +
|
||||||
|
", noLocal=" + noLocal +
|
||||||
|
", retainAsPublished=" + retainAsPublished +
|
||||||
|
", retainHandling=" + retainHandling +
|
||||||
|
']';
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable → Regular
+14
-5
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -25,11 +25,16 @@ import io.netty.util.internal.StringUtil;
|
|||||||
public final class MqttTopicSubscription {
|
public final class MqttTopicSubscription {
|
||||||
|
|
||||||
private final String topicFilter;
|
private final String topicFilter;
|
||||||
private final MqttQoS qualityOfService;
|
private final MqttSubscriptionOption option;
|
||||||
|
|
||||||
public MqttTopicSubscription(String topicFilter, MqttQoS qualityOfService) {
|
public MqttTopicSubscription(String topicFilter, MqttQoS qualityOfService) {
|
||||||
this.topicFilter = topicFilter;
|
this.topicFilter = topicFilter;
|
||||||
this.qualityOfService = qualityOfService;
|
this.option = MqttSubscriptionOption.onlyFromQos(qualityOfService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttTopicSubscription(String topicFilter, MqttSubscriptionOption option) {
|
||||||
|
this.topicFilter = topicFilter;
|
||||||
|
this.option = option;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String topicName() {
|
public String topicName() {
|
||||||
@@ -37,7 +42,11 @@ public final class MqttTopicSubscription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MqttQoS qualityOfService() {
|
public MqttQoS qualityOfService() {
|
||||||
return qualityOfService;
|
return option.qos();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttSubscriptionOption option() {
|
||||||
|
return option;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -45,7 +54,7 @@ public final class MqttTopicSubscription {
|
|||||||
return new StringBuilder(StringUtil.simpleClassName(this))
|
return new StringBuilder(StringUtil.simpleClassName(this))
|
||||||
.append('[')
|
.append('[')
|
||||||
.append("topicFilter=").append(topicFilter)
|
.append("topicFilter=").append(topicFilter)
|
||||||
.append(", qualityOfService=").append(qualityOfService)
|
.append(", option=").append(this.option)
|
||||||
.append(']')
|
.append(']')
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
|||||||
Executable → Regular
+34
-4
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -17,16 +17,46 @@
|
|||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#unsuback">MQTTV3.1/unsuback</a>
|
* See <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#unsuback">
|
||||||
|
* MQTTV3.1/unsuback</a>
|
||||||
*/
|
*/
|
||||||
public final class MqttUnsubAckMessage extends MqttMessage {
|
public final class MqttUnsubAckMessage extends MqttMessage {
|
||||||
|
|
||||||
public MqttUnsubAckMessage(MqttFixedHeader mqttFixedHeader, MqttMessageIdVariableHeader variableHeader) {
|
public MqttUnsubAckMessage(MqttFixedHeader mqttFixedHeader,
|
||||||
super(mqttFixedHeader, variableHeader, null);
|
MqttMessageIdAndPropertiesVariableHeader variableHeader,
|
||||||
|
MqttUnsubAckPayload payload) {
|
||||||
|
super(mqttFixedHeader, variableHeader, MqttUnsubAckPayload.withEmptyDefaults(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttUnsubAckMessage(MqttFixedHeader mqttFixedHeader,
|
||||||
|
MqttMessageIdVariableHeader variableHeader,
|
||||||
|
MqttUnsubAckPayload payload) {
|
||||||
|
this(mqttFixedHeader, fallbackVariableHeader(variableHeader), payload);
|
||||||
|
}
|
||||||
|
public MqttUnsubAckMessage(MqttFixedHeader mqttFixedHeader,
|
||||||
|
MqttMessageIdVariableHeader variableHeader) {
|
||||||
|
this(mqttFixedHeader, variableHeader, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MqttMessageIdAndPropertiesVariableHeader fallbackVariableHeader(
|
||||||
|
MqttMessageIdVariableHeader variableHeader) {
|
||||||
|
if (variableHeader instanceof MqttMessageIdAndPropertiesVariableHeader) {
|
||||||
|
return (MqttMessageIdAndPropertiesVariableHeader) variableHeader;
|
||||||
|
}
|
||||||
|
return new MqttMessageIdAndPropertiesVariableHeader(variableHeader.messageId(), MqttProperties.NO_PROPERTIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MqttMessageIdVariableHeader variableHeader() {
|
public MqttMessageIdVariableHeader variableHeader() {
|
||||||
return (MqttMessageIdVariableHeader) super.variableHeader();
|
return (MqttMessageIdVariableHeader) super.variableHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttMessageIdAndPropertiesVariableHeader idAndPropertiesVariableHeader() {
|
||||||
|
return (MqttMessageIdAndPropertiesVariableHeader) super.variableHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MqttUnsubAckPayload payload() {
|
||||||
|
return (MqttUnsubAckPayload) super.payload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
|
import io.netty.util.internal.ObjectUtil;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Payload for MQTT unsuback message as in V5.
|
||||||
|
*/
|
||||||
|
public final class MqttUnsubAckPayload {
|
||||||
|
|
||||||
|
private final List<Short> unsubscribeReasonCodes;
|
||||||
|
|
||||||
|
private static final MqttUnsubAckPayload EMPTY = new MqttUnsubAckPayload();
|
||||||
|
|
||||||
|
public static MqttUnsubAckPayload withEmptyDefaults(MqttUnsubAckPayload payload) {
|
||||||
|
if (payload == null) {
|
||||||
|
return EMPTY;
|
||||||
|
} else {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttUnsubAckPayload(short... unsubscribeReasonCodes) {
|
||||||
|
ObjectUtil.checkNotNull(unsubscribeReasonCodes, "unsubscribeReasonCodes");
|
||||||
|
|
||||||
|
List<Short> list = new ArrayList<Short>(unsubscribeReasonCodes.length);
|
||||||
|
for (Short v: unsubscribeReasonCodes) {
|
||||||
|
list.add(v);
|
||||||
|
}
|
||||||
|
this.unsubscribeReasonCodes = Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttUnsubAckPayload(Iterable<Short> unsubscribeReasonCodes) {
|
||||||
|
ObjectUtil.checkNotNull(unsubscribeReasonCodes, "unsubscribeReasonCodes");
|
||||||
|
|
||||||
|
List<Short> list = new ArrayList<Short>();
|
||||||
|
for (Short v: unsubscribeReasonCodes) {
|
||||||
|
ObjectUtil.checkNotNull(v, "unsubscribeReasonCode");
|
||||||
|
list.add(v);
|
||||||
|
}
|
||||||
|
this.unsubscribeReasonCodes = Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Short> unsubscribeReasonCodes() {
|
||||||
|
return unsubscribeReasonCodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringBuilder(StringUtil.simpleClassName(this))
|
||||||
|
.append('[')
|
||||||
|
.append("unsubscribeReasonCodes=").append(unsubscribeReasonCodes)
|
||||||
|
.append(']')
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable → Regular
+14
-3
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -17,23 +17,34 @@
|
|||||||
package io.netty.handler.codec.mqtt;
|
package io.netty.handler.codec.mqtt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See <a href="http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#unsubscribe">
|
* See <a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#unsubscribe">
|
||||||
* MQTTV3.1/unsubscribe</a>
|
* MQTTV3.1/unsubscribe</a>
|
||||||
*/
|
*/
|
||||||
public final class MqttUnsubscribeMessage extends MqttMessage {
|
public final class MqttUnsubscribeMessage extends MqttMessage {
|
||||||
|
|
||||||
public MqttUnsubscribeMessage(
|
public MqttUnsubscribeMessage(
|
||||||
MqttFixedHeader mqttFixedHeader,
|
MqttFixedHeader mqttFixedHeader,
|
||||||
MqttMessageIdVariableHeader variableHeader,
|
MqttMessageIdAndPropertiesVariableHeader variableHeader,
|
||||||
MqttUnsubscribePayload payload) {
|
MqttUnsubscribePayload payload) {
|
||||||
super(mqttFixedHeader, variableHeader, payload);
|
super(mqttFixedHeader, variableHeader, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttUnsubscribeMessage(
|
||||||
|
MqttFixedHeader mqttFixedHeader,
|
||||||
|
MqttMessageIdVariableHeader variableHeader,
|
||||||
|
MqttUnsubscribePayload payload) {
|
||||||
|
this(mqttFixedHeader, variableHeader.withDefaultEmptyProperties(), payload);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MqttMessageIdVariableHeader variableHeader() {
|
public MqttMessageIdVariableHeader variableHeader() {
|
||||||
return (MqttMessageIdVariableHeader) super.variableHeader();
|
return (MqttMessageIdVariableHeader) super.variableHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttMessageIdAndPropertiesVariableHeader idAndPropertiesVariableHeader() {
|
||||||
|
return (MqttMessageIdAndPropertiesVariableHeader) super.variableHeader();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MqttUnsubscribePayload payload() {
|
public MqttUnsubscribePayload payload() {
|
||||||
return (MqttUnsubscribePayload) super.payload();
|
return (MqttUnsubscribePayload) super.payload();
|
||||||
|
|||||||
Executable → Regular
+6
-5
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -39,11 +39,12 @@ public final class MqttUnsubscribePayload {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder builder = new StringBuilder(StringUtil.simpleClassName(this)).append('[');
|
StringBuilder builder = new StringBuilder(StringUtil.simpleClassName(this)).append('[');
|
||||||
for (int i = 0; i < topics.size() - 1; i++) {
|
for (int i = 0; i < topics.size(); i++) {
|
||||||
builder.append("topicName = ").append(topics.get(i)).append(", ");
|
builder.append("topicName = ").append(topics.get(i)).append(", ");
|
||||||
}
|
}
|
||||||
builder.append("topicName = ").append(topics.get(topics.size() - 1))
|
if (!topics.isEmpty()) {
|
||||||
.append(']');
|
builder.setLength(builder.length() - 2);
|
||||||
return builder.toString();
|
}
|
||||||
|
return builder.append("]").toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+25
-15
@@ -5,7 +5,7 @@
|
|||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at:
|
* with the License. You may obtain a copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
@@ -25,7 +25,7 @@ import io.netty.util.internal.ObjectUtil;
|
|||||||
public enum MqttVersion {
|
public enum MqttVersion {
|
||||||
MQTT_3_1("MQIsdp", (byte) 3),
|
MQTT_3_1("MQIsdp", (byte) 3),
|
||||||
MQTT_3_1_1("MQTT", (byte) 4),
|
MQTT_3_1_1("MQTT", (byte) 4),
|
||||||
Wildfire_1("MQTT", (byte) 5),
|
MQTT_5("MQTT", (byte) 5),
|
||||||
Wildfire_2("MQTT", (byte) 6),
|
Wildfire_2("MQTT", (byte) 6),
|
||||||
Wildfire_Future("MQTT", (byte) 7);
|
Wildfire_Future("MQTT", (byte) 7);
|
||||||
|
|
||||||
@@ -50,21 +50,31 @@ public enum MqttVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static MqttVersion fromProtocolNameAndLevel(String protocolName, byte protocolLevel) {
|
public static MqttVersion fromProtocolNameAndLevel(String protocolName, byte protocolLevel) {
|
||||||
for (MqttVersion mv : values()) {
|
MqttVersion mv = null;
|
||||||
if (mv.name.equals(protocolName)) {
|
switch (protocolLevel) {
|
||||||
if (mv.level == protocolLevel) {
|
case 3:
|
||||||
return mv;
|
mv = MQTT_3_1;
|
||||||
} else {
|
break;
|
||||||
continue;
|
case 4:
|
||||||
}
|
mv = MQTT_3_1_1;
|
||||||
}
|
break;
|
||||||
|
case 5:
|
||||||
|
mv = MQTT_5;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
mv = Wildfire_2;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
mv = Wildfire_Future;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mv = Wildfire_Future;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if(protocolLevel > Wildfire_Future.level) {
|
|
||||||
return Wildfire_Future;
|
|
||||||
}
|
|
||||||
throw new MqttUnacceptableProtocolVersionException(protocolName + "is unknown protocol name");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return mv;
|
||||||
|
}
|
||||||
|
|
||||||
public static MqttVersion fromProtocolLevel(int protocolLevel) {
|
public static MqttVersion fromProtocolLevel(int protocolLevel) {
|
||||||
for (MqttVersion mv : values()) {
|
for (MqttVersion mv : values()) {
|
||||||
if (mv.level == (byte )protocolLevel) {
|
if (mv.level == (byte )protocolLevel) {
|
||||||
|
|||||||
Executable → Regular
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||||
* copy of the License at:
|
* copy of the License at:
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class DBUtil {
|
|||||||
if (embedDB != null && embedDB.equals("1")) {
|
if (embedDB != null && embedDB.equals("1")) {
|
||||||
IsEmbedDB = true;
|
IsEmbedDB = true;
|
||||||
LOG.info("Use h2 database");
|
LOG.info("Use h2 database");
|
||||||
String warning = "您正在使用内置h2数据库,建议仅在开发验证或者用户数小于100人时才使用此数据库,其它情况建议使用MySQL。";
|
String warning = "您正在使用h2数据库,建议仅在开发验证或者用户数小于100人时才使用此数据库。正式上线时建议切换到MySQL数据库。";
|
||||||
System.out.println(warning);
|
System.out.println(warning);
|
||||||
LOG.warn(warning);
|
LOG.warn(warning);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user