From 94f1b6dc7d75cc1a9f6a0c475d88ff7bc47a6388 Mon Sep 17 00:00:00 2001 From: CaiHQ Date: Mon, 24 Apr 2023 18:57:40 +0800 Subject: [PATCH] support doip-cluster --- build.gradle | 14 ++-- .../org/bdware/server/action/FileActions.java | 72 ++++++++++++++----- .../org/bdware/server/doip/BCOManager.java | 2 +- .../doip/ContractRepositoryHandler.java | 2 +- .../server/doip/ContractRepositoryMain.java | 37 ++++++---- .../server/doip/LocalDoipFrowarder.java | 58 +++++++++++++++ .../server/http/DOIPOverHttpHandler.java | 55 +++++++++++++- 7 files changed, 197 insertions(+), 43 deletions(-) create mode 100644 src/main/java/org/bdware/server/doip/LocalDoipFrowarder.java diff --git a/build.gradle b/build.gradle index 02c539d..2857756 100644 --- a/build.gradle +++ b/build.gradle @@ -40,17 +40,17 @@ dependencies { implementation 'io.grpc:grpc-all:1.43.1' implementation 'org.apache.velocity:velocity-engine-core:2.3' implementation 'com.nimbusds:nimbus-jose-jwt:9.10' - implementation 'org.bdware.doip:doip-sdk:1.3.9' - implementation 'org.bdware.doip:doip-audit-tool:1.2.2' - implementation 'org.bdware.doip:bdosclient:0.0.2' + implementation 'org.bdware.doip:doip-sdk:1.4.2' + implementation 'org.bdware.doip:doip-audit-tool:1.2.6' + implementation 'org.bdware.doip:bdosclient:0.0.3' implementation fileTree(dir: 'lib', include: '*.jar') testImplementation 'junit:junit:4.13.2' implementation 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final' implementation 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final-linux-aarch_64-fedora' - // implementation 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final' - // implementation 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final' - // implementation 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final' - // implementation 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final' + // implementation 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final' + // implementation 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final' + // implementation 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final' + // implementation 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final' } compileJava { diff --git a/src/main/java/org/bdware/server/action/FileActions.java b/src/main/java/org/bdware/server/action/FileActions.java index 84e2282..d5f4d7a 100644 --- a/src/main/java/org/bdware/server/action/FileActions.java +++ b/src/main/java/org/bdware/server/action/FileActions.java @@ -15,6 +15,7 @@ import io.netty.util.TimerTask; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.bdware.sc.ContractManager; +import org.bdware.sc.ContractMeta; import org.bdware.sc.YJSPacker; import org.bdware.sc.bean.Contract; import org.bdware.sc.bean.ContractExecType; @@ -135,10 +136,11 @@ public class FileActions { request.protocolVersion(), OK, Unpooled.wrappedBuffer( - "{\"status\":\"false\",\"data\":\"Missing argument!\"}" + "{\"status\":\"false\",\"data\":\"Missing argument, please check: path, fileName, isPrivate, order, count, pubKey, sign!\"}" .getBytes())); - ChannelFuture f = ctx.write(fullResponse); + + ChannelFuture f = addCrossOriginHeaderAndWrite(ctx, fullResponse); f.addListener(ChannelFutureListener.CLOSE); return; } @@ -236,7 +238,7 @@ public class FileActions { "{\"status\":\"false\",\"data\":\"Permission denied!\"}" .getBytes())); - ChannelFuture f = ctx.write(fullResponse); + ChannelFuture f = addCrossOriginHeaderAndWrite(ctx, fullResponse); f.addListener(ChannelFutureListener.CLOSE); return; } @@ -249,16 +251,47 @@ public class FileActions { new HttpPostRequestDecoder(new DefaultHttpDataFactory(true), request); httpDecoder.setDiscardThreshold(0); File dir; - boolean isPrivate = - transformedParam.containsKey("isPrivate") - && Boolean.parseBoolean(transformedParam.get("isPrivate")); - // TODO verify signature and - - if (isPrivate) { - String pub = "/" + transformedParam.get("pubKey"); - dir = new File(GlobalConf.instance.projectDir + "/private" + pub, dirName); + if (transformedParam.containsKey("contractID")) { + String contractID = transformedParam.get("contractID"); + ContractMeta meta = CMActions.manager.statusRecorder.getContractMeta(contractID); + if (meta == null) { + DefaultFullHttpResponse fullResponse = + new DefaultFullHttpResponse( + request.protocolVersion(), + OK, + Unpooled.wrappedBuffer( + "{\"status\":\"false\",\"data\":\"no such contract!\"}" + .getBytes())); + ChannelFuture f = addCrossOriginHeaderAndWrite(ctx, fullResponse); + f.addListener(ChannelFutureListener.CLOSE); + return; + } + if (!meta.contract.getOwner().equals(pubkey)) { + DefaultFullHttpResponse fullResponse = + new DefaultFullHttpResponse( + request.protocolVersion(), + OK, + Unpooled.wrappedBuffer( + "{\"status\":\"false\",\"data\":\"not owner!\"}" + .getBytes())); + ChannelFuture f = addCrossOriginHeaderAndWrite(ctx, fullResponse); + f.addListener(ChannelFutureListener.CLOSE); + return; + } + dir = new File(GlobalConf.instance.getDBPath(), meta.getName()); + dir = new File(dir, dirName); + if (!dir.exists()) dir.mkdirs(); } else { - dir = new File(GlobalConf.instance.publicDir, dirName); + boolean isPrivate = + transformedParam.containsKey("isPrivate") + && Boolean.parseBoolean(transformedParam.get("isPrivate")); + // TODO verify signature and + if (isPrivate) { + String pub = "/" + transformedParam.get("pubKey"); + dir = new File(GlobalConf.instance.projectDir + "/private" + pub, dirName); + } else { + dir = new File(GlobalConf.instance.publicDir, dirName); + } } if (!dir.isDirectory()) { dir = dir.getParentFile(); @@ -273,7 +306,7 @@ public class FileActions { "{\"status\":\"false\",\"data\":\"FileName illegal!\"}" .getBytes())); - ChannelFuture f = ctx.write(fullResponse); + ChannelFuture f = addCrossOriginHeaderAndWrite(ctx, fullResponse); f.addListener(ChannelFutureListener.CLOSE); return; } @@ -309,7 +342,9 @@ public class FileActions { boolean isDebug = transformedParam.containsKey("isDebug") && Boolean.parseBoolean(transformedParam.get("isDebug")); - String doi = unzipIfYpk(target, dir, isDebug); + String doi = null; + if (target.getParentFile().getParentFile().equals(new File(GlobalConf.instance.privateDir))) + doi = unzipIfYpk(target, dir, isDebug); if (null != doi) { retStr = retStr.replaceFirst("null", doi); } @@ -317,11 +352,16 @@ public class FileActions { DefaultFullHttpResponse fullResponse = new DefaultFullHttpResponse( request.protocolVersion(), OK, Unpooled.wrappedBuffer(retStr.getBytes())); - fullResponse.headers().add("Access-Control-Allow-Origin", "*"); - ChannelFuture f = ctx.write(fullResponse); + ChannelFuture f = addCrossOriginHeaderAndWrite(ctx, fullResponse); f.addListener(ChannelFutureListener.CLOSE); } + private static ChannelFuture addCrossOriginHeaderAndWrite(ChannelHandlerContext ctx, DefaultFullHttpResponse fullResponse) { + fullResponse.headers().add("Access-Control-Allow-Origin", "*"); + fullResponse.headers().add("Access-Control-Allow-Methods", "*"); + return ctx.write(fullResponse); + } + public static String geneRandomID() { String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; Random random = new Random(); diff --git a/src/main/java/org/bdware/server/doip/BCOManager.java b/src/main/java/org/bdware/server/doip/BCOManager.java index 924cf90..66895ae 100644 --- a/src/main/java/org/bdware/server/doip/BCOManager.java +++ b/src/main/java/org/bdware/server/doip/BCOManager.java @@ -126,7 +126,7 @@ public class BCOManager { } private File getYpkFile(YPKInfo ypkInfo) { - File f = new File(bcodir, ypkInfo.ypkName); + File f = new File(bcodir, ypkInfo.pkgName); if (ypkInfo.version != null) f = new File(f, ypkInfo.version); f = new File(f, ypkInfo.md5 + ".ypk"); diff --git a/src/main/java/org/bdware/server/doip/ContractRepositoryHandler.java b/src/main/java/org/bdware/server/doip/ContractRepositoryHandler.java index 0a041f2..42df4da 100644 --- a/src/main/java/org/bdware/server/doip/ContractRepositoryHandler.java +++ b/src/main/java/org/bdware/server/doip/ContractRepositoryHandler.java @@ -104,7 +104,7 @@ public class ContractRepositoryHandler extends RepositoryHandlerBase implements c.setType(ContractExecType.valueOf(digitalObject.attributes.get("contractExecType").getAsString())); else c.setType(ContractExecType.Sole); if (digitalObject.attributes.has("shardingId")) - c.setType(ContractExecType.valueOf(digitalObject.attributes.get("shardingId").getAsString())); + c.setShardingId(Integer.valueOf(digitalObject.attributes.get("shardingId").getAsString())); else c.setShardingId(-1); c.setScript(path); c.setOwner(doipMessage.credential.getSigner()); diff --git a/src/main/java/org/bdware/server/doip/ContractRepositoryMain.java b/src/main/java/org/bdware/server/doip/ContractRepositoryMain.java index fbea2e8..4fccc85 100644 --- a/src/main/java/org/bdware/server/doip/ContractRepositoryMain.java +++ b/src/main/java/org/bdware/server/doip/ContractRepositoryMain.java @@ -19,19 +19,20 @@ import org.bdware.server.GlobalConf; import org.bdware.server.action.CMActions; import org.zz.gmhelper.SM2KeyPair; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; public class ContractRepositoryMain { private static final Logger LOGGER = LogManager.getLogger(ContractRepositoryMain.class); static AuditDoipServer currentServer; + static LocalDoipFrowarder forwarder; static final String repoType = "BDRepo"; public static void start() { try { LOGGER.info("DOIPServer, start" + new Gson().toJson(GlobalConf.instance.doaConf)); String url = GlobalConf.instance.doaConf.doipAddress; + forwarder = new LocalDoipFrowarder(); if (url == null || GlobalConf.instance.doaConf.repoDoid.isEmpty()) { LOGGER.warn("missing args, failed to start! url:" + url + " doid:" + GlobalConf.instance.doaConf.repoDoid); return; @@ -60,22 +61,22 @@ public class ContractRepositoryMain { String id = message.header.parameters.id; id = id.replaceAll(".*/", ""); ContractMeta meta = CMActions.manager.statusRecorder.getContractMeta(id); - builder.addAttributes("port", meta.contract.getDoipPort()); - DoipMessage ret; - if (message.credential != null && message.credential.getSigner() != null) { - if (signer.verifyMessage(message)) { - ret = builder.create(); - } else { - DoipMessageFactory.DoipMessageBuilder builder2 = new DoipMessageFactory.DoipMessageBuilder(); - DoipMessageFactory.DoipMessageBuilder resp = builder2.createResponse(DoipResponseCode.UnAuth_Client, message); - resp.setBody("verify failed".getBytes(StandardCharsets.UTF_8)); - ret = resp.create(); - } - signer.signMessage(ret); - } else { + if (enableDelegate(meta)) { + LOGGER.info("delegate:" + message.requestID); + + //if port is near cmhttp server port + builder.addAttributes("port", meta.contract.getDoipPort()); + DoipMessage ret; ret = builder.create(); + if (message.credential != null && message.credential.getSigner() != null) { + signer.signMessage(ret); + } + return ret; + } else { + LOGGER.info("forward:" + message.requestID); + return forwarder.forward(meta, message); } - return ret; + } @Override @@ -97,4 +98,10 @@ public class ContractRepositoryMain { LOGGER.warn("load doip config exception!"); } } + + private static boolean enableDelegate(ContractMeta meta) { + int port = meta.contract.getDoipPort(); + int delta = Integer.valueOf(GlobalConf.instance.ipPort.split(":")[1]) - port; + return Math.abs(delta) < 200; + } } diff --git a/src/main/java/org/bdware/server/doip/LocalDoipFrowarder.java b/src/main/java/org/bdware/server/doip/LocalDoipFrowarder.java new file mode 100644 index 0000000..5820f47 --- /dev/null +++ b/src/main/java/org/bdware/server/doip/LocalDoipFrowarder.java @@ -0,0 +1,58 @@ +package org.bdware.server.doip; + +import org.bdware.doip.codec.doipMessage.DoipMessage; +import org.bdware.doip.codec.doipMessage.DoipMessageFactory; +import org.bdware.doip.codec.doipMessage.DoipResponseCode; +import org.bdware.doip.endpoint.client.ClientConfig; +import org.bdware.doip.endpoint.client.DoipClientImpl; +import org.bdware.doip.endpoint.client.DoipMessageCallback; +import org.bdware.sc.ContractMeta; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Map; + +public class LocalDoipFrowarder { + Map connections; + + public LocalDoipFrowarder() { + connections = new HashMap<>(); + } + + public DoipMessage forward(ContractMeta meta, DoipMessage message) { + DoipClientImpl connection; + try { + if (connections.containsKey(meta.contract.getDoipPort())) { + connection = connections.get(meta.contract.getDoipPort()); + } else { + connection = new DoipClientImpl(); + connection.connect(ClientConfig.fromUrl("tcp://127.0.0.1:" + meta.contract.getDoipPort())); + connections.put(meta.contract.getDoipPort(), connection); + } + if (!connection.isConnected()) connection.reconnect(); + DoipMessage[] result = new DoipMessage[1]; + connection.sendRawMessage(message, new DoipMessageCallback() { + @Override + public void onResult(DoipMessage msg) { + synchronized (result) { + result[0] = msg; + result.notify(); + } + } + }); + synchronized (result) { + if (result[0] == null) + result.wait(30000L); + } + return result[0]; + } catch (Exception e) { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bo)); + DoipMessageFactory.DoipMessageBuilder builder = new DoipMessageFactory.DoipMessageBuilder(); + builder.createResponse(DoipResponseCode.UnKnownError, message); + builder.setBody(bo.toByteArray()); + return builder.create(); + } + } +} diff --git a/src/main/java/org/bdware/server/http/DOIPOverHttpHandler.java b/src/main/java/org/bdware/server/http/DOIPOverHttpHandler.java index d3396cc..4320c26 100644 --- a/src/main/java/org/bdware/server/http/DOIPOverHttpHandler.java +++ b/src/main/java/org/bdware/server/http/DOIPOverHttpHandler.java @@ -87,7 +87,11 @@ public class DOIPOverHttpHandler { sendListContractProcess(arg, ctx); return; case Retrieve: - sendAssets(arg, ctx); + if (arg.has("__public") && arg.get("__public").getAsBoolean()) { + sendPublicFiles(arg, ctx); + } else { + sendAssets(arg, ctx); + } return; case RetrieveYPK: sendYpk(arg, ctx, request); @@ -163,6 +167,45 @@ public class DOIPOverHttpHandler { return; } + private void sendPublicFiles(JsonObject arg, ChannelHandlerContext ctx) throws Exception { + ContractMeta meta = + CMActions.manager.statusRecorder.getContractMeta( + arg.get("contractID").getAsString()); + if (meta == null) { + HttpFileHandleAdapter.sendError(ctx, HttpResponseStatus.NOT_FOUND); + return; + } + String path = arg.get("argument").getAsString(); + File dir = new File("./ContractDB", meta.getName()); + File toSend = new File(dir, path); + FileInputStream fin; + try { + if (!toSend.exists() || toSend.isDirectory()) { + HttpFileHandleAdapter.sendError(ctx, HttpResponseStatus.NOT_FOUND); + return; + } else + fin = new FileInputStream(toSend); + } catch (Exception e) { + HttpFileHandleAdapter.sendError(ctx, HttpResponseStatus.NOT_FOUND); + return; + } + HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.OK); + HttpFileHandleAdapter.appendContentType(path, response.headers()); + ctx.write(response); + ChannelFuture future = ctx.writeAndFlush(new ChunkedStream(fin)); + future.addListener( + new GenericFutureListener>() { + @Override + public void operationComplete(Future arg0) throws Exception { + ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); + fin.close(); + } + }); + // 写入文件尾部 + future.addListener(ChannelFutureListener.CLOSE); + return; + } + private void sendAssets(JsonObject arg, ChannelHandlerContext ctx) throws Exception { ContractMeta meta = CMActions.manager.statusRecorder.getContractMeta( @@ -225,7 +268,6 @@ public class DOIPOverHttpHandler { } private void injectExtraArgs(JsonObject transformedParam, String fulluri) { - String[] data = fulluri.split("/"); String contractIDOrName = data[2]; String uris[] = URLDecoder.decode(fulluri).split("\\?"); @@ -253,7 +295,8 @@ public class DOIPOverHttpHandler { transformedParam.addProperty("contractID", data[2].replaceAll("\\?.*$", "")); if (data.length > 3) { String assetPath = data[3]; - if (assetPath.equals("assets")) { + transformedParam.remove("__public"); + if (assetPath.equals("assets") && !fulluri.contains("..")) { transformedParam.addProperty("operation", Operations.Retrieve.val); int index = data[0].length() + data[1].length() + data[2].length() + 2; String path = fulluri.substring(index); @@ -261,6 +304,12 @@ public class DOIPOverHttpHandler { if (path.equals("/assets/")) path += "index.html"; transformedParam.addProperty("argument", path); + } else if (assetPath.equals("public") && !fulluri.contains("..")) { + transformedParam.addProperty("operation", Operations.Retrieve.val); + int index = data[0].length() + data[1].length() + data[2].length() + 2; + String path = fulluri.substring(index); + transformedParam.addProperty("argument", path); + transformedParam.addProperty("__public", true); } if (assetPath.startsWith("ypk?")) { transformedParam.addProperty("operation", Operations.RetrieveYPK.val);