WindChat/windchat-connector/src/main/java/com/windchat/im/connector/http/handler/HttpServerHandler.java

251 lines
8.4 KiB
Java
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright 2018-2028 WindChat Group
*
* Licensed 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
*
* http://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 com.windchat.im.connector.http.handler;
import java.net.InetSocketAddress;
import java.util.Base64;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.windchat.common.command.Command;
import com.windchat.common.command.CommandResponse;
import com.windchat.common.constant.CharsetCoding;
import com.windchat.common.constant.HttpUriAction;
import com.windchat.common.crypto.AESCrypto;
import com.windchat.common.executor.AbstracteExecutor;
import com.windchat.common.logs.LogUtils;
import com.windchat.common.utils.StringHelper;
import com.akaxin.proto.core.PluginProto;
import com.windchat.im.connector.constant.AkxProject;
import com.windchat.im.connector.constant.HttpConst;
import com.windchat.im.connector.constant.PluginConst;
import com.windchat.im.connector.session.PluginSession;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent;
/**
* 处理Http请求
*
* @author Sam{@link an.guoyue254@gmail.com}
* @since 2017-11-28 18:49:38
*/
public class HttpServerHandler extends ChannelInboundHandlerAdapter {
private static Logger logger = LoggerFactory.getLogger(HttpServerHandler.class);
private HttpRequest request;
private String httpClientIp;
private AbstracteExecutor<Command, CommandResponse> executor;
public HttpServerHandler(AbstracteExecutor<Command, CommandResponse> executor) {
this.executor = executor;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.debug("{} client connect to http server... client={}", AkxProject.PLN, ctx.channel().toString());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
/**
* 一次Http请求分两次处理第一次处理Http消息头第二次请求http消息体
*/
/**
* http-request包含 <br>
* 1.请求行 ,Method Request-URI Http-Version CRLF<br>
* 2.消息头 <br>
* 3.请求正文 <br>
*/
if (msg instanceof HttpRequest) {
request = (HttpRequest) msg;
if (!checkLegalRequest()) {
logger.error("{} http request method error. please use post!", AkxProject.PLN);
ctx.close();
return;
}
String sitePluginId = request.headers().get(PluginConst.SITE_PLUGIN_ID);
if (StringUtils.isEmpty(sitePluginId)) {
logger.error("{} http request illegal with error pluginId={}.", AkxProject.PLN, sitePluginId);
ctx.close();
return;
}
// set socket ip
InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
httpClientIp = address.getAddress().getHostAddress();
if (!checkLegalClientIp(sitePluginId, httpClientIp)) {
logger.error("{} http request illegal IP={}.", AkxProject.PLN, httpClientIp);
ctx.close();
return;
}
// 检测URIhttp://127.0.0.1:8280/akaxin-plugin-api/hai/user/friends
if (checkRequestUri()) {
logger.error("akaxin plugin http request illegal uri={}.", AkxProject.PLN, request.uri());
ctx.close();
return;
}
logger.debug("{} request uri:{} clientIp={}", AkxProject.PLN, request.uri(), httpClientIp);
}
/**
* HttpContent:表示HTTP实体正文和内容标头的基类 <br>
* method.name=POST 传输消息体存在内容
*/
if (msg instanceof LastHttpContent) {
HttpContent content = (HttpContent) msg;
ByteBuf httpByteBuf = content.content();
if (httpByteBuf == null) {
return;
}
if (!checkLegalRequest()) {
ctx.close();
return;
}
// String clientIp = request.headers().get(HttpConst.HTTP_H_FORWARDED);
String sitePluginId = request.headers().get(PluginConst.SITE_PLUGIN_ID);
byte[] contentBytes = new byte[httpByteBuf.readableBytes()];
httpByteBuf.readBytes(contentBytes);
httpByteBuf.release();
logger.debug("{} http request IP={} pluginId={}", AkxProject.PLN, httpClientIp, sitePluginId);
if (StringUtils.isEmpty(sitePluginId)) {
logger.error("{} http request body illegal pluginId={}.", AkxProject.PLN, sitePluginId);
ctx.close();
return;
}
// 查询扩展的auth——key
String authKey = PluginSession.getInstance().getPluginAuthKey(sitePluginId);
logger.debug("http request ip={} pluginId={} authKey={}", httpClientIp, sitePluginId, authKey);
if (StringUtils.isNotEmpty(authKey)) {
// byte[] tsk = AESCrypto.generateTSKey(authKey);
byte[] tsk = authKey.getBytes(CharsetCoding.ISO_8859_1);
byte[] decContent = AESCrypto.decrypt(tsk, contentBytes);
contentBytes = decContent;
}
PluginProto.ProxyPluginPackage pluginPackage = PluginProto.ProxyPluginPackage.parseFrom(contentBytes);
Map<Integer, String> proxyHeader = pluginPackage.getPluginHeaderMap();
String requestTime = proxyHeader.get(PluginProto.PluginHeaderKey.PLUGIN_TIMESTAMP_VALUE);
long currentTime = System.currentTimeMillis();
boolean timeOut = true;
if (StringUtils.isNotEmpty(requestTime)) {
long timeMills = Long.valueOf(requestTime);
if (currentTime - timeMills < 10 * 1000l) {
timeOut = false;
}
}
logger.debug("{} client={} http request timeOut={} currTime={} reqTime={}", AkxProject.PLN,
httpClientIp, timeOut, currentTime, requestTime);
if (!timeOut) {
Command command = new Command();
command.setField(PluginConst.PLUGIN_AUTH_KEY, authKey);
if (proxyHeader != null) {
command.setSiteUserId(proxyHeader.get(PluginProto.PluginHeaderKey.CLIENT_SITE_USER_ID_VALUE));
}
command.setChannelContext(ctx);
command.setUri(request.uri());
command.setParams(Base64.getDecoder().decode(pluginPackage.getData()));
command.setClientIp(httpClientIp);
command.setStartTime(System.currentTimeMillis());
command.setPluginId(sitePluginId);
command.setPluginAuthKey(authKey);
logger.info("{} client={} uri={} http server handler command={}", AkxProject.PLN, httpClientIp,
request.uri(), command.toString());
CommandResponse response = this.executor.execute(HttpUriAction.HTTP_ACTION.getRety(), command);
LogUtils.requestResultLog(logger, command, response);
} else {
// 超时10s认为此请求失效直接断开连接
ctx.close();
logger.error("{} client={} http request error.timeOut={} currTime={} reqTime={}", AkxProject.PLN,
httpClientIp, timeOut, currentTime, requestTime);
}
}
} catch (Exception e) {
ctx.close();
logger.error(StringHelper.format("{} http request error.", AkxProject.PLN), e);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
logger.error(StringHelper.format("{} channel exception caught", AkxProject.PLN), cause);
}
/**
* 所有扩展请求站点的http服务请求全部使用post请求
*
* @return
*/
private boolean checkLegalRequest() {
String methodName = request.method().name();
if (HttpConst.HTTP_M_POST.equals(methodName)) {
return true;
}
return false;
}
// 预留处理请求ip过滤
private boolean checkLegalClientIp(String pluginId, String ip) {
// #TODO 使用缓存
String allowIps = PluginSession.getInstance().getPluginAllowIp(pluginId);
if (StringUtils.isNotEmpty(allowIps)) {
if (!allowIps.contains(ip)) {
return false;
}
}
return true;
}
private boolean checkRequestUri() {
String uri = request.uri();
if (StringUtils.isNotEmpty(uri)) {
return uri.startsWith("/akaxin-plugin-api/hai/") || uri.startsWith("//akaxin-plugin-api/hai/")
|| uri.startsWith("akaxin-plugin-api/hai/");
}
return false;
}
}