Compare commits

...
17 Commits
29 changed files with 253 additions and 73 deletions
+22 -2
View File
@@ -111,7 +111,7 @@ friend.disable_search false
##禁止按照昵称搜索用户。
friend.disable_nick_name_search false
##禁止发送好友邀请,通过server api添加好友不受此限制
##禁止发送好友邀请,通过server api添加好友不受此限制(force参数需要为true)
friend.disable_friend_request false
##好友请求重复发送的间隔,单位是毫秒,默认是7天,0为不限制
@@ -154,6 +154,20 @@ group.visible_quit_or_kickoff_notification 0
# 因此需要分段多次同步,此开关定义了每次同步的数据的条数。此需要客户端支持,客户端SDK在2021.5.25日后支持此功能。如果确认所有客户端都在这个日志之后才可以打开此设置。
#sync.data_part_size 1000
#*********************************************************************
# 限频配置
#*********************************************************************
##API接口限频设置,时间是10秒内允许的请求次数。
##管理api频率限制,默认是10秒10000次。
http.admin.rate_limit 10000
##机器人api频率限制,默认是10秒1000次,按照机器人ID区分。
http.robot.rate_limit 1000
##频道api频率限制,默认是10秒1000次,按照频道ID区分。
http.channel.rate_limit 1000
##客户端请求频率限制,默认是5秒100次。通常情况下不要修改这个值。
client.request_rate_limit 100
#*********************************************************************
# hazelcase configuration
#*********************************************************************
@@ -163,7 +177,8 @@ hazelcast.configuration config/hazelcast.xml
# Media server configuration
#*********************************************************************
##是否使用七牛云存储。1使用七牛;0使用内存文件服务器。默认的七牛账户信息不可用,请在七牛官网申请账户并配置
##配置使用七牛请参考:https://docs.wildfirechat.cn/server/media_server.html
##专业版另外支持阿里云存储和野火私有存储,关于野火私有存储请参考:https://github.com/wildfirechat/WF-minio
##关于对象存储的详细信息请参考:https://docs.wildfirechat.cn/server/oss.html
media.server.use_qiniu 0
##媒体类型分类
@@ -264,6 +279,11 @@ push.ios.server.address http://localhost:8085/ios/push
##需要转发的消息类型,当有多个时以英文逗号分割。如果转发所有消息,请注释掉配置或者设置为空
#message.forward.types 1,2,3
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。此转发不受${message.forward.types}限制。
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.mentionmsg.forward.url http://localhost:8888/im_event/message
##设备信息转发地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#things_message.forward.url http://localhost:8888/im_event/things/message
+1 -1
View File
@@ -5,7 +5,7 @@
<relativePath>../</relativePath>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.71</version>
<version>0.74</version>
</parent>
<artifactId>moquette-broker</artifactId>
@@ -13,6 +13,7 @@ import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.RequireAuthentication;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.server.config.IConfig;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.impl.Utils;
@@ -21,10 +22,12 @@ import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import cn.wildfirechat.common.ErrorCode;
import win.liyufan.im.RateLimiter;
import java.nio.charset.StandardCharsets;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
import static io.moquette.BrokerConstants.*;
/**
* 请求处理接口<br>
@@ -38,6 +41,36 @@ abstract public class Action {
public static ISessionsStore sessionsStore = null;
public ChannelHandlerContext ctx;
protected static RateLimiter adminLimiter = null;
protected static RateLimiter robotLimiter = null;
protected static RateLimiter channelLimiter = null;
public static void init(IConfig config) {
int adminRate = 10000;
int robotRate = 1000;
int channelRate = 1000;
try {
adminRate = Integer.parseInt(config.getProperty(HTTP_ADMIN_RATE_LIMIT, "10000"));
} catch (NumberFormatException e) {
}
try {
robotRate = Integer.parseInt(config.getProperty(HTTP_ROBOT_RATE_LIMIT, "1000"));
} catch (NumberFormatException e) {
}
try {
channelRate = Integer.parseInt(config.getProperty(HTTP_CHANNEL_RATE_LIMIT, "1000"));
} catch (NumberFormatException e) {
}
adminLimiter = new RateLimiter(10, adminRate);
robotLimiter = new RateLimiter(10, robotRate);
channelLimiter = new RateLimiter(10, channelRate);
}
protected class Result {
Object data;
ErrorCode errorCode;
@@ -29,7 +29,6 @@ abstract public class AdminAction extends Action {
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AdminAction.class);
private static String SECRET_KEY = "123456";
private static boolean NO_CHECK_TIME = false;
private final RateLimiter mLimitCounter = new RateLimiter(10, 500);
public static void setSecretKey(String secretKey) {
SECRET_KEY = secretKey;
}
@@ -44,7 +43,7 @@ abstract public class AdminAction extends Action {
@Override
public ErrorCode preAction(Request request, Response response) {
if (!mLimitCounter.isGranted("admin")) {
if (!adminLimiter.isGranted("admin")) {
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
}
@@ -88,14 +87,16 @@ abstract public class AdminAction extends Action {
}
protected void sendResponse(Response response, ErrorCode errorCode, Object data) {
response.setStatus(HttpResponseStatus.OK);
if (errorCode == null) {
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
}
if(response != null) {
response.setStatus(HttpResponseStatus.OK);
if (errorCode == null) {
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
}
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
response.setContent(new Gson().toJson(result));
response.send();
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
response.setContent(new Gson().toJson(result));
response.send();
}
}
protected void sendApiMessage(Response response, String fromUser, String topic, byte[] message, ApiCallback callback) {
sendApiMessage(response, fromUser, null, topic, message, callback, false);
@@ -12,16 +12,22 @@ import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputGetFriendList;
import cn.wildfirechat.pojos.InputUpdateAlias;
import cn.wildfirechat.pojos.OutputCreateChannel;
import cn.wildfirechat.pojos.OutputStringList;
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 com.xiaoleilu.loServer.model.FriendData;
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 win.liyufan.im.IMTopic;
import java.util.ArrayList;
import java.util.List;
@@ -39,8 +45,26 @@ public class AliasPutAction extends AdminAction {
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputUpdateAlias input = getRequestBody(request.getNettyRequest(), InputUpdateAlias.class);
ErrorCode errorCode = messagesStore.setFriendAliasRequest(input.getOperator(), input.getTargetId(), input.getAlias(), new long[1]);
setResponseContent(RestResult.resultOf(errorCode), response);
if(!StringUtil.isNullOrEmpty(input.getOperator()) && !StringUtil.isNullOrEmpty(input.getTargetId())) {
WFCMessage.AddFriendRequest addFriendRequest = WFCMessage.AddFriendRequest.newBuilder().setTargetUid(input.getTargetId()).setReason(input.getAlias()==null?"":input.getAlias()).build();
sendApiMessage(response, input.getOperator(), IMTopic.SetFriendAliasTopic, addFriendRequest.toByteArray(), result -> {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(result);
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
byte[] data = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(data);
String channelId = new String(data);
return new Result(ErrorCode.ERROR_CODE_SUCCESS, new OutputCreateChannel(channelId));
} else {
return new Result(errorCode);
}
});
return false;
} else {
ErrorCode errorCode = messagesStore.setFriendAliasRequest(input.getOperator(), input.getTargetId(), input.getAlias(), new long[1]);
setResponseContent(RestResult.resultOf(errorCode), response);
}
}
return true;
}
@@ -45,7 +45,7 @@ public class BlockUserAction extends AdminAction {
response.setContent(new Gson().toJson(result));
if (inputUserBlock.getStatus() == 2) {
sendApiMessage(response, null, ServerAPIHelper.KICKOFF_USER_REQUEST, inputUserBlock.getUserId().getBytes(), null);
sendApiMessage(null, null, ServerAPIHelper.KICKOFF_USER_REQUEST, inputUserBlock.getUserId().getBytes(), null);
}
sendResponse(response, ErrorCode.ERROR_CODE_SUCCESS, null);
@@ -18,6 +18,8 @@ import cn.wildfirechat.pojos.OutputGetIMTokenData;
import com.xiaoleilu.loServer.handler.Response;
import io.netty.handler.codec.http.FullHttpRequest;
import cn.wildfirechat.common.ErrorCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import win.liyufan.im.IMTopic;
import java.util.Base64;
@@ -25,7 +27,7 @@ import java.util.Base64;
@Route(APIPath.User_Get_Token)
@HttpMethod("POST")
public class GetIMTokenAction extends AdminAction {
private static final Logger LOG = LoggerFactory.getLogger(GetIMTokenAction.class);
@Override
public boolean isTransactionAction() {
return false;
@@ -49,6 +51,8 @@ public class GetIMTokenAction extends AdminAction {
}
String token = Base64.getEncoder().encodeToString(data);
LOG.info("get im token success {},{},{}", userId, input.getClientId(), token.substring(0, Math.min(10, token.length())));
return new Result(errorCode1, new OutputGetIMTokenData(userId, token));
} else {
return new Result(errorCode1);
@@ -31,7 +31,6 @@ import java.util.concurrent.Executor;
abstract public class ChannelAction extends Action {
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ChannelAction.class);
private final RateLimiter mLimitCounter = new RateLimiter(10, 200);
protected WFCMessage.ChannelInfo channelInfo;
protected interface Callback {
@@ -61,7 +60,7 @@ abstract public class ChannelAction extends Action {
return ErrorCode.ERROR_CODE_API_NOT_SIGNED;
}
if (!mLimitCounter.isGranted(cid)) {
if (!channelLimiter.isGranted(cid)) {
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
}
@@ -33,7 +33,6 @@ import java.util.concurrent.Executor;
abstract public class RobotAction extends Action {
protected static final Logger LOG = LoggerFactory.getLogger(RobotAction.class);
private final RateLimiter mLimitCounter = new RateLimiter(10, 1000);
protected WFCMessage.Robot robot;
@@ -64,7 +63,7 @@ abstract public class RobotAction extends Action {
return ErrorCode.ERROR_CODE_API_NOT_SIGNED;
}
if (!mLimitCounter.isGranted(rid)) {
if (!robotLimiter.isGranted(rid)) {
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
}
@@ -128,6 +128,8 @@ public final class BrokerConstants {
public static final String MESSAGE_Forward_Url = "message.forward.url";
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 SERVER_MULTI_ENDPOINT = "server.multi_endpoint";
public static final String SERVER_MULTI_PLATFROM_NOTIFICATION = "server.multi_platform_notification";
@@ -176,6 +178,10 @@ public final class BrokerConstants {
public static final String MESSAGES_RECALL_TIME_LIMIT = "message.recall_time_limit";
public static final String MESSAGES_DISABLE_GROUP_MANAGER_RECALL = "message.disable_group_manager_recall";
public static final String HTTP_ADMIN_RATE_LIMIT = "http.admin.rate_limit";
public static final String HTTP_ROBOT_RATE_LIMIT = "http.robot.rate_limit";
public static final String HTTP_CHANNEL_RATE_LIMIT = "http.channel.rate_limit";
public static final String CLIENT_REQUEST_RATE_LIMIT = "client.request_rate_limit";
private BrokerConstants() {
}
@@ -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.WFCMessage;
import com.hazelcast.util.StringUtil;
import com.xiaoleilu.loServer.model.FriendData;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import win.liyufan.im.IMTopic;
import java.util.List;
@Handler(value = IMTopic.ClearSessionTopic)
public class DisconnectHandler extends IMHandler<Byte> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, Byte request, Qos1PublishHandler.IMCallback callback) {
if (request == 8) {
m_sessionsStore.cleanSession(fromUser, clientID);
} else if(request == 1) {
m_sessionsStore.disableSession(fromUser, clientID);
}
return ErrorCode.ERROR_CODE_SUCCESS;
}
}
@@ -19,6 +19,7 @@ import win.liyufan.im.I18n;
import win.liyufan.im.IMTopic;
import win.liyufan.im.MessageShardingUtil;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_ALREADY_FRIENDS;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
@Handler(IMTopic.HandleFriendRequestTopic)
@@ -86,6 +87,10 @@ public class HandleFriendRequestHandler extends IMHandler<WFCMessage.HandleFrien
publisher.publishNotification(IMTopic.NotifyFriendTopic, fromUser, heads[1]);
}
}
if(errorCode == ERROR_CODE_ALREADY_FRIENDS) {
errorCode = ERROR_CODE_SUCCESS;
}
return errorCode;
}
}
@@ -38,6 +38,7 @@ import java.util.*;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_OVER_FREQUENCY;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
import static io.moquette.BrokerConstants.CLIENT_REQUEST_RATE_LIMIT;
/**
* 请求处理接口<br>
@@ -52,7 +53,7 @@ abstract public class IMHandler<T> {
protected static Server mServer = null;
protected static MessagesPublisher publisher;
private static ThreadPoolExecutorWrapper m_imBusinessExecutor;
private static final RateLimiter mLimitCounter = new RateLimiter(5, 100);
private static RateLimiter mLimitCounter;
private Method parseDataMethod;
private Class dataCls;
@@ -147,12 +148,24 @@ abstract public class IMHandler<T> {
publisher = p;
m_imBusinessExecutor = businessExecutor;
mServer = server;
int clientRateLimit = 100;
try {
clientRateLimit = Integer.parseInt(server.getConfig().getProperty(CLIENT_REQUEST_RATE_LIMIT, "100"));
} catch (Exception e) {
}
if(clientRateLimit == 0) {
clientRateLimit = 100;
}
mLimitCounter = new RateLimiter(5, clientRateLimit);
}
public ErrorCode preAction(String clientID, String fromUser, String topic, Qos1PublishHandler.IMCallback callback) {
public ErrorCode preAction(String clientID, String fromUser, String topic, Qos1PublishHandler.IMCallback callback, boolean isAdmin, boolean isRobotOrChannel) {
LOG.info("imHandler fromUser={}, clientId={}, topic={}", fromUser, clientID, topic);
if(!mLimitCounter.isGranted(clientID + fromUser + topic)) {
if(!isAdmin && !isRobotOrChannel && !mLimitCounter.isGranted(clientID + fromUser + topic)) {
ByteBuf ackPayload = Unpooled.buffer();
ackPayload.ensureWritable(1).writeByte(ERROR_CODE_OVER_FREQUENCY.getCode());
try {
@@ -166,7 +179,7 @@ abstract public class IMHandler<T> {
return ErrorCode.ERROR_CODE_SUCCESS;
}
public void doHandler(String clientID, String fromUser, String topic, byte[] payloadContent, Qos1PublishHandler.IMCallback callback, boolean isAdmin) {
public void doHandler(String clientID, String fromUser, String topic, byte[] payloadContent, Qos1PublishHandler.IMCallback callback, boolean isAdmin, boolean isRobotOrChannel) {
m_imBusinessExecutor.execute(() -> {
Qos1PublishHandler.IMCallback callbackWrapper = new Qos1PublishHandler.IMCallback() {
@Override
@@ -177,7 +190,7 @@ abstract public class IMHandler<T> {
}
};
ErrorCode preActionCode = preAction(clientID, fromUser, topic, callbackWrapper);
ErrorCode preActionCode = preAction(clientID, fromUser, topic, callbackWrapper, isAdmin, isRobotOrChannel);
if (preActionCode == ErrorCode.ERROR_CODE_SUCCESS) {
ByteBuf ackPayload = Unpooled.buffer(1);
@@ -33,6 +33,7 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
private int mSensitiveType = 0; //命中敏感词时,0 失败,1 吞掉, 2 敏感词替换成*。
private String mForwardUrl = null;
private Set<Integer> mForwardMessageTypes = new HashSet<>();
private String mMentionForwardUrl = null;
private int mBlacklistStrategy = 0; //黑名单中时,0失败,1吞掉。
private boolean mNoForwardAdminMessage = false;
@@ -43,10 +44,8 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
public SendMessageHandler() {
super();
String forwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Url);
if (!StringUtil.isNullOrEmpty(forwardUrl)) {
mForwardUrl = forwardUrl;
mForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Url);
if (!StringUtil.isNullOrEmpty(mForwardUrl)) {
String forwardTypes = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Types);
if (!StringUtil.isNullOrEmpty(forwardTypes)) {
String[] tss = forwardTypes.split(",");
@@ -56,6 +55,8 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
}
}
mMentionForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_MentionMsg_Forward_Url);
try {
mSensitiveType = Integer.parseInt(mServer.getConfig().getProperty(BrokerConstants.SENSITIVE_Filter_Type));
} catch (Exception e) {
@@ -169,10 +170,14 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
}
message = message.toBuilder().setFromUser(fromUser).setMessageId(messageId).setServerTimestamp(timestamp).build();
if (mForwardUrl != null && (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);
}
if(!StringUtil.isNullOrEmpty(mMentionForwardUrl) && message.hasContent() && message.getContent().getMentionedType() != 0 && !(isAdmin && mNoForwardAdminMessage)) {
publisher.forwardMessage(message, mMentionForwardUrl);
}
if (!isAdmin) {
if(StringUtil.isNullOrEmpty(mRemoteSensitiveServerUrl)) {
Set<String> matched = m_messagesStore.handleSensitiveWord(message.getContent().getSearchableContent());
@@ -640,6 +640,17 @@ public class MemoryMessagesStore implements IMessagesStore {
}
}
if(fromMessageId == 1) { //lite mode
if(maps == null || maps.isEmpty()) {
head = 0;
} else {
head = maps.lastKey();
}
builder.setCurrent(head);
builder.setHead(head);
return builder.build();
}
mReadLock.lock();
int size = 0;
try {
@@ -2808,6 +2819,10 @@ public class MemoryMessagesStore implements IMessagesStore {
return ErrorCode.ERROR_CODE_IN_BLACK_LIST;
}
if (friendData1 != null && friendData1.getState() == 0) {
return ErrorCode.ERROR_CODE_ALREADY_FRIENDS;
}
WFCMessage.FriendRequest newRequest = WFCMessage.FriendRequest
.newBuilder()
.setFromUid(userId)
@@ -2845,7 +2860,7 @@ public class MemoryMessagesStore implements IMessagesStore {
public ErrorCode handleFriendRequest(String userId, WFCMessage.HandleFriendRequest request, WFCMessage.Message.Builder msgBuilder, long[] heads, boolean isAdmin) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
boolean alreadyFriend = false;
if(!isAdmin && request.getStatus() == ProtoConstants.FriendRequestStatus.RequestStatus_Accepted) {
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
@@ -2857,10 +2872,9 @@ public class MemoryMessagesStore implements IMessagesStore {
for (FriendData fd : friends) {
if (fd.getFriendUid().equals(request.getTargetUid())) {
if (fd.getState() == 0) {
return ErrorCode.ERROR_CODE_ALREADY_FRIENDS;
} else {
break;
alreadyFriend = true;
}
break;
}
}
}
@@ -2939,7 +2953,7 @@ public class MemoryMessagesStore implements IMessagesStore {
}
if (existRequest != null) {
if (mFriendRequestExpiration > 0 && System.currentTimeMillis() - existRequest.getUpdateDt() > mFriendRequestExpiration) {
if (!alreadyFriend && mFriendRequestExpiration > 0 && System.currentTimeMillis() - existRequest.getUpdateDt() > mFriendRequestExpiration) {
return ErrorCode.ERROR_CODE_FRIEND_REQUEST_EXPIRED;
} else {
existRequest = existRequest.toBuilder().setStatus(request.getStatus()).setUpdateDt(System.currentTimeMillis()).build();
@@ -2965,7 +2979,7 @@ public class MemoryMessagesStore implements IMessagesStore {
}
}
if(request.getStatus() == ProtoConstants.FriendRequestStatus.RequestStatus_Accepted){
if(!alreadyFriend && request.getStatus() == ProtoConstants.FriendRequestStatus.RequestStatus_Accepted){
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
FriendData friendData1 = new FriendData(userId, request.getTargetUid(), "", request.getExtra(), 0, 0, System.currentTimeMillis());
databaseStore.persistOrUpdateFriendData(friendData1);
@@ -2986,6 +3000,9 @@ public class MemoryMessagesStore implements IMessagesStore {
callbackRelationEvent(userId, request.getTargetUid(), 0, "1");
}
if(alreadyFriend) {
return ErrorCode.ERROR_CODE_ALREADY_FRIENDS;
}
return ErrorCode.ERROR_CODE_SUCCESS;
}
} else {
@@ -531,7 +531,7 @@ public class MemorySessionStore implements ISessionsStore {
if (session.getUsername().equals(username)) {
return session;
} else {
cleanSession(clientID);
cleanSession(null, clientID);
}
}
return null;
@@ -739,9 +739,9 @@ public class MemorySessionStore implements ISessionsStore {
@Override
public void disableSession(String clientId) {
public void disableSession(String userId, String clientId) {
Session session = sessions.get(clientId);
if (session != null && session.getDeleted() == 0) {
if (session != null && session.getDeleted() == 0 && (userId == null || session.getUsername().equals(userId))) {
databaseStore.updateSessionDeleted(session.getUsername(), clientId, 1);
ConcurrentSkipListSet<String> sessionSet = getUserSessionSet(session.username);
sessionSet.remove(clientId);
@@ -750,7 +750,7 @@ public class MemorySessionStore implements ISessionsStore {
}
@Override
public void cleanSession(String clientID) {
public void cleanSession(String userId, String clientID) {
LOG.info("Fooooooooo <{}>", clientID);
Session session = sessions.get(clientID);
@@ -758,6 +758,11 @@ public class MemorySessionStore implements ISessionsStore {
LOG.error("Can't find the session for client <{}>", clientID);
return;
}
if(userId != null && !session.getUsername().equals(userId)) {
return;
}
ConcurrentSkipListSet<String> sessionSet = getUserSessionSet(session.username);
sessionSet.remove(clientID);
@@ -24,6 +24,7 @@ import com.hazelcast.config.FileSystemXmlConfig;
import com.hazelcast.core.*;
import com.xiaoleilu.loServer.LoServer;
import com.xiaoleilu.loServer.ServerSetting;
import com.xiaoleilu.loServer.action.Action;
import com.xiaoleilu.loServer.action.admin.AdminAction;
import io.moquette.BrokerConstants;
import io.moquette.persistence.*;
@@ -257,6 +258,7 @@ public class Server {
initMediaServerConfig(config);
Action.init(config);
String monitorEventAddress = config.getProperty(MONITOR_Exception_Event_Address);
Utility.setMonitorEventAddress(monitorEventAddress);
@@ -109,19 +109,19 @@ public class ClientSession {
}
public void disconnect(boolean cleanSession, boolean disableSession) {
public void disconnect(String userId, boolean cleanSession, boolean disableSession) {
if (cleanSession) {
LOG.info("Client disconnected. Removing its subscriptions. ClientId={}", clientID);
// cleanup topic subscriptions
cleanSession();
cleanSession(userId);
} else if(disableSession) {
LOG.info("Client disconnected. disable session. ClientId={}", clientID);
m_sessionsStore.disableSession(this.clientID);
m_sessionsStore.disableSession(userId, this.clientID);
}
}
public void cleanSession() {
m_sessionsStore.cleanSession(this.clientID);
public void cleanSession(String userId) {
m_sessionsStore.cleanSession(userId, this.clientID);
}
@@ -150,8 +150,8 @@ public interface ISessionsStore {
*/
int getSecondPhaseAckPendingMessages(String clientID);
void disableSession(String clientId);
void cleanSession(String clientID);
void disableSession(String userId, String clientId);
void cleanSession(String userId, String clientID);
boolean isMultiEndpointSupported();
@@ -44,12 +44,14 @@ import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import win.liyufan.im.HttpUtils;
import win.liyufan.im.IMTopic;
import win.liyufan.im.Utility;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
import static io.moquette.spi.impl.InternalRepublisher.createPublishForQos;
import static cn.wildfirechat.common.IMExceptionEvent.EventType.EVENT_CALLBACK_Exception;
import static io.moquette.spi.impl.Utils.readBytesAndRewind;
@@ -582,7 +584,7 @@ public class ProtocolProcessor {
//disconnect the session
m_sessionsStore.sessionForClient(clientID).disconnect(isDup, isRetain);
m_sessionsStore.sessionForClient(clientID).disconnect(session.getUsername(), isDup, isRetain);
}
@@ -69,7 +69,6 @@ public class Qos1PublishHandler extends QosPublishHandler {
private final MessagesPublisher publisher;
private final ISessionsStore m_sessionStore;
private final ThreadPoolExecutorWrapper m_imBusinessExecutor;
private final RateLimiter mLimitCounter = new RateLimiter(5, 100);
private HashMap<String, IMHandler> m_imHandlers = new HashMap<>();
@@ -148,19 +147,6 @@ public class Qos1PublishHandler extends QosPublishHandler {
void imHandler(String clientID, String fromUser, String topic, byte[] payloadContent, IMCallback callback, boolean isAdmin, boolean isRobotOrChannel) {
LOG.info("imHandler fromUser={}, topic={}", fromUser, topic);
if(!isAdmin && !isRobotOrChannel && !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);
}
LOG.warn("user {} request over frequency", fromUser);
return;
}
IMCallback wrapper = (errorcode, ackPayload) -> {
ackPayload.resetReaderIndex();
byte code = ackPayload.readByte();
@@ -215,7 +201,7 @@ public class Qos1PublishHandler extends QosPublishHandler {
IMHandler handler = m_imHandlers.get(topic);
if (handler != null) {
handler.doHandler(clientID, fromUser, topic, payloadContent, wrapper, isAdmin);
handler.doHandler(clientID, fromUser, topic, payloadContent, wrapper, isAdmin, isRobotOrChannel);
} else {
LOG.error("imHandler unknown topic={}", topic);
ByteBuf ackPayload = Unpooled.buffer();
@@ -107,8 +107,12 @@ public class DBUtil {
}
Flyway flyway = Flyway.configure().dataSource(comboPooledDataSource).locations(migrateLocation).baselineOnMigrate(true).load();
if(!IsEmbedDB && (flyway.info().current() == null || flyway.info().current().getVersion().getMajor().intValue() < 43)) {
System.out.println("数据库执行初始化或升级需要较长时间,可能长达数分钟或更长,请耐心等待,不要中断。。。");
if(!IsEmbedDB) {
if(flyway.info().current() == null) {
System.out.println("数据库执行初始化需要较长时间,可能长达数分钟或更长,请耐心等待,不要中断。。。");
} else if(flyway.info().current().getVersion().getMajor().intValue() < 43) {
System.out.println("数据库执行升级需要较长时间,可能长达数分钟或更长,请耐心等待,不要中断。。。");
}
}
flyway.migrate();
@@ -80,4 +80,6 @@ public interface IMTopic {
String LoadRemoteMessagesTopic = "LRM";
String KickoffPCClientTopic = "KPCC";
String ClearSessionTopic = "CST";
}
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.71</version>
<version>0.74</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -6,7 +6,7 @@
<!--<relativePath>../pom.xml</relativePath>-->
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.71</version>
<version>0.74</version>
</parent>
<artifactId>distribution</artifactId>
@@ -106,7 +106,7 @@ friend.disable_search false
##禁止按照昵称搜索用户。
friend.disable_nick_name_search false
##禁止发送好友邀请,通过server api添加好友不受此限制
##禁止发送好友邀请,通过server api添加好友不受此限制(force参数需要为true)
friend.disable_friend_request false
##好友请求重复发送的间隔,单位是毫秒,默认是7天,0为不限制
@@ -150,6 +150,20 @@ group.visible_quit_or_kickoff_notification 0
# 因此需要分段多次同步,此开关定义了每次同步的数据的条数。此需要客户端支持,客户端SDK在2021.5.25日后支持此功能。如果确认所有客户端都在这个日志之后才可以打开此设置。
#sync.data_part_size 1000
#*********************************************************************
# 限频配置
#*********************************************************************
##API接口限频设置,时间是10秒内允许的请求次数。
##管理api频率限制,默认是10秒10000次。
http.admin.rate_limit 10000
##机器人api频率限制,默认是10秒1000次,按照机器人ID区分。
http.robot.rate_limit 1000
##频道api频率限制,默认是10秒1000次,按照频道ID区分。
http.channel.rate_limit 1000
##客户端请求频率限制,默认是5秒100次。通常情况下不要修改这个值。
client.request_rate_limit 100
#*********************************************************************
# hazelcase configuration
#*********************************************************************
@@ -159,7 +173,8 @@ hazelcast.configuration config/hazelcast.xml
# Media server configuration
#*********************************************************************
##是否使用七牛云存储。1使用七牛;0使用内存文件服务器。默认的七牛账户信息不可用,请在七牛官网申请账户并配置
##配置使用七牛请参考:https://docs.wildfirechat.cn/server/media_server.html
##专业版另外支持阿里云存储和野火私有存储,关于野火私有存储请参考:https://github.com/wildfirechat/WF-minio
##关于对象存储的详细信息请参考:https://docs.wildfirechat.cn/server/oss.html
media.server.use_qiniu 0
##媒体类型分类
@@ -262,6 +277,11 @@ push.ios.server.address http://localhost:8085/ios/push
##需要转发的消息类型,当有多个时以英文逗号分割。如果转发所有消息,请注释掉配置或者设置为空
#message.forward.types 1,2,3
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。此转发不受${message.forward.types}限制。
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.mentionmsg.forward.url http://localhost:8888/im_event/message
##设备信息转发地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#things_message.forward.url http://localhost:8087/im_event/things/message
+1 -1
View File
@@ -12,7 +12,7 @@
<artifactId>wildfirechat-parent</artifactId>
<packaging>pom</packaging>
<version>0.71</version>
<version>0.74</version>
<name>Wildfire Chat</name>
<description>Wildfire Chat Server</description>
<inceptionYear>2019</inceptionYear>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.71</version>
<version>0.74</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -292,7 +292,7 @@ public class Main {
}
IMResult<Void> result = RelationAdmin.sendFriendRequest("ff1", "ff2", "hello", true);
if (result != null && result.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
if (result != null && (result.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS || result.getErrorCode() == ErrorCode.ERROR_CODE_ALREADY_FRIENDS)) {
System.out.println("send friend request success");
} else {
System.out.println("failure");
@@ -308,7 +308,7 @@ public class Main {
}
result = RelationAdmin.sendFriendRequest("ff1", "ff2", "hello3", true);
if (result != null && result.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
if (result != null && (result.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS || result.getErrorCode() == ErrorCode.ERROR_CODE_ALREADY_FRIENDS)) {
System.out.println("success");
} else {
System.out.println("send friend request success");