diff --git a/boot/build.gradle.kts b/boot/build.gradle.kts index 0ec08078..f7d1f537 100644 --- a/boot/build.gradle.kts +++ b/boot/build.gradle.kts @@ -1,6 +1,6 @@ plugins { id("java") - id("org.springframework.boot") version "3.5.7" + id("org.springframework.boot") version "3.5.8" id("io.spring.dependency-management") version "1.1.7" } @@ -21,6 +21,8 @@ configurations { } } +extra["byte-buddy.version"] = libs.versions.byte.buddy.get() + dependencies { implementation(project(":generator")) { exclude(group = "commons-logging", module = "commons-logging") diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index a431f70e..e96e0a4d 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -7,5 +7,5 @@ repositories { } dependencies { - implementation("com.vanniktech:gradle-maven-publish-plugin:0.34.0") + implementation("com.vanniktech:gradle-maven-publish-plugin:0.35.0") } \ No newline at end of file diff --git a/build-logic/src/main/kotlin/maven-publish-convention.gradle.kts b/build-logic/src/main/kotlin/maven-publish-convention.gradle.kts index 211a68ed..9a9ffa52 100644 --- a/build-logic/src/main/kotlin/maven-publish-convention.gradle.kts +++ b/build-logic/src/main/kotlin/maven-publish-convention.gradle.kts @@ -3,7 +3,7 @@ plugins { } mavenPublishing { - publishToMavenCentral(true) + publishToMavenCentral() signAllPublications() coordinates( "io.github.reajason", diff --git a/build.gradle.kts b/build.gradle.kts index 36c01c87..a407da35 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ idea { } } -version = "2.4.0" +version = "2.4.1-SNAPSHOT" tasks.register("publishAllToMavenCentral") { dependsOn(":memshell-party-common:publishToMavenCentral") diff --git a/generator/build.gradle.kts b/generator/build.gradle.kts index 3543233c..5b948521 100644 --- a/generator/build.gradle.kts +++ b/generator/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("java") + id("java-library") alias(libs.plugins.lombok) id("maven-publish-convention") } @@ -29,17 +29,16 @@ tasks.test { dependencies { implementation(project(":memshell-party-common")) implementation(project(":packer")) - implementation(libs.byte.buddy) + api(libs.byte.buddy) implementation(libs.asm.commons) implementation(libs.javax.websocket.api) implementation(libs.javax.servlet.api) implementation(libs.spring.webmvc) implementation(libs.spring.webflux) implementation(libs.reactor.netty.core) - + implementation(libs.jackson.annotations) implementation(libs.bundles.jna) - implementation(libs.bcel) - implementation(libs.jackson.databind) + testImplementation(libs.junit.jupiter) testImplementation(libs.hamcrest) testRuntimeOnly(libs.junit.platform.launcher) diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java b/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java index 9018164b..bbcbda2d 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java @@ -35,7 +35,7 @@ public class CommandConfig extends ShellToolConfig { private ImplementationClass implementationClass = ImplementationClass.RuntimeExec; /** - * 命令执行模板,例如 sh -c "{command}" 2>&1,使用 {command} 作为占位符 + * 命令执行模板,使用 {command} 作为占位符 */ private String template; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java index 4bbb07b1..8f278e69 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java @@ -22,8 +22,8 @@ public final class ProcessorRegistry { ); private static final List> BYTE_PROCESSORS = Arrays.asList( - new ShrinkPostProcessor(), - new JettyHandlerPostProcessor() + new JettyHandlerPostProcessor(), + new ShrinkPostProcessor() ); private ProcessorRegistry() { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java index 416c63ec..0dc74a7e 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java @@ -80,7 +80,7 @@ private String getContextRoot(Object context) { /** * context: com.apusic.web.container.WebContainer - * context -> webapp: com.apusic.deploy.runtime.WebModule + * context - webapp: com.apusic.deploy.runtime.WebModule * /usr/local/ass/lib/apusic.jar */ public Set getContext() throws Exception { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java index ee259036..5bec4d50 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java @@ -84,12 +84,24 @@ public Set getContext() throws Exception { Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor")) { + String threadName = thread.getName(); + if (threadName.contains("ContainerBackgroundProcessor")) { Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); for (Object value : childrenMap.values()) { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } + } else if (threadName.contains("Poller") && !threadName.contains("ajp")) { + try { + Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto"); + Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine"); + Map childrenMap = (Map) getFieldValue(engine, "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } catch (Exception ignored) { + } } else if (thread.getContextClassLoader() != null) { String name = thread.getContextClassLoader().getClass().getSimpleName(); if (name.matches(".+WebappClassLoader")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatListenerInjector.java index 1f879c17..dec479b8 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatListenerInjector.java @@ -71,15 +71,27 @@ private String getContextRoot(Object context) { } public Set getContext() throws Exception { - Set contexts = new HashSet<>(); + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor")) { + String threadName = thread.getName(); + if (threadName.contains("ContainerBackgroundProcessor")) { Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); for (Object value : childrenMap.values()) { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } + } else if (threadName.contains("Poller") && !threadName.contains("ajp")) { + try { + Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto"); + Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine"); + Map childrenMap = (Map) getFieldValue(engine, "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } catch (Exception ignored) { + } } else if (thread.getContextClassLoader() != null) { String name = thread.getContextClassLoader().getClass().getSimpleName(); if (name.matches(".+WebappClassLoader")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java index 982d9032..2162582b 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java @@ -100,12 +100,24 @@ public Set getContext() throws Exception { Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor")) { + String threadName = thread.getName(); + if (threadName.contains("ContainerBackgroundProcessor")) { Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); for (Object value : childrenMap.values()) { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } + } else if (threadName.contains("Poller") && !threadName.contains("ajp")) { + try { + Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto"); + Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine"); + Map childrenMap = (Map) getFieldValue(engine, "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } catch (Exception ignored) { + } } else if (thread.getContextClassLoader() != null) { String name = thread.getContextClassLoader().getClass().getSimpleName(); if (name.matches(".+WebappClassLoader")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatServletInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatServletInjector.java index 9f630953..8a4f9a08 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatServletInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatServletInjector.java @@ -81,12 +81,24 @@ public Set getContext() throws Exception { Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor")) { + String threadName = thread.getName(); + if (threadName.contains("ContainerBackgroundProcessor")) { Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); for (Object value : childrenMap.values()) { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } + } else if (threadName.contains("Poller") && !threadName.contains("ajp")) { + try { + Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto"); + Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine"); + Map childrenMap = (Map) getFieldValue(engine, "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } catch (Exception ignored) { + } } else if (thread.getContextClassLoader() != null) { String name = thread.getContextClassLoader().getClass().getSimpleName(); if (name.matches(".+WebappClassLoader")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatUpgradeInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatUpgradeInjector.java index 6e1820d9..3109fdeb 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatUpgradeInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatUpgradeInjector.java @@ -80,12 +80,24 @@ public Set getContext() throws Exception { Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor")) { + String threadName = thread.getName(); + if (threadName.contains("ContainerBackgroundProcessor")) { Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); for (Object value : childrenMap.values()) { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } + } else if (threadName.contains("Poller") && !threadName.contains("ajp")) { + try { + Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto"); + Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine"); + Map childrenMap = (Map) getFieldValue(engine, "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } catch (Exception ignored) { + } } else if (thread.getContextClassLoader() != null) { String name = thread.getContextClassLoader().getClass().getSimpleName(); if (name.matches(".+WebappClassLoader")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatValveInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatValveInjector.java index 00616d89..bd4abbed 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatValveInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatValveInjector.java @@ -74,12 +74,24 @@ public Set getContext() throws Exception { Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor")) { + String threadName = thread.getName(); + if (threadName.contains("ContainerBackgroundProcessor")) { Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); for (Object value : childrenMap.values()) { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } + } else if (threadName.contains("Poller") && !threadName.contains("ajp")) { + try { + Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto"); + Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine"); + Map childrenMap = (Map) getFieldValue(engine, "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } catch (Exception ignored) { + } } else if (thread.getContextClassLoader() != null) { String name = thread.getContextClassLoader().getClass().getSimpleName(); if (name.matches(".+WebappClassLoader")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatWebSocketInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatWebSocketInjector.java index c9d7f24f..f01f812a 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatWebSocketInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatWebSocketInjector.java @@ -80,12 +80,24 @@ public Set getContext() throws Exception { Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor")) { - HashMap childrenMap = (HashMap) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); + String threadName = thread.getName(); + if (threadName.contains("ContainerBackgroundProcessor")) { + Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); for (Object value : childrenMap.values()) { - HashMap children = (HashMap) getFieldValue(value, "children"); + Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } + } else if (threadName.contains("Poller") && !threadName.contains("ajp")) { + try { + Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto"); + Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine"); + Map childrenMap = (Map) getFieldValue(engine, "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } catch (Exception ignored) { + } } else if (thread.getContextClassLoader() != null) { String name = thread.getContextClassLoader().getClass().getSimpleName(); if (name.matches(".+WebappClassLoader")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/Command.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/Command.java index 5ae8626e..ee1e0e36 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/Command.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/Command.java @@ -27,6 +27,8 @@ public boolean equals(Object obj) { InputStream inputStream = getInputStream(param); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); + outputStream.flush(); + outputStream.close(); return true; } } catch (Throwable e) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandControllerHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandControllerHandler.java index 83bb6263..0fd14c53 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandControllerHandler.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandControllerHandler.java @@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; +import java.util.Scanner; /** * @author ReaJason @@ -24,12 +25,9 @@ public ModelAndView handleRequest(HttpServletRequest request, HttpServletRespons if (p != null) { String param = getParam(p); InputStream inputStream = getInputStream(param); - ServletOutputStream outputStream = response.getOutputStream(); - byte[] buf = new byte[8192]; - int length; - while ((length = inputStream.read(buf)) != -1) { - outputStream.write(buf, 0, length); - } + response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next()); + response.getWriter().flush(); + response.getWriter().close(); } } catch (Throwable e) { e.printStackTrace(); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java index 4a1cc167..030c64ce 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java @@ -5,6 +5,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; +import java.util.Scanner; /** * @author ReaJason @@ -25,12 +26,9 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha if (p != null) { String param = getParam(p); InputStream inputStream = getInputStream(param); - ServletOutputStream outputStream = servletResponse.getOutputStream(); - byte[] buf = new byte[8192]; - int length; - while ((length = inputStream.read(buf)) != -1) { - outputStream.write(buf, 0, length); - } + response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next()); + response.getWriter().flush(); + response.getWriter().close(); return; } } catch (Throwable e) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandInterceptor.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandInterceptor.java index 6f00a08b..943cbafe 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandInterceptor.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandInterceptor.java @@ -26,6 +26,8 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons String param = getParam(p); InputStream inputStream = getInputStream(param); response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next()); + response.getWriter().flush(); + response.getWriter().close(); return false; } } catch (Throwable e) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyAgentHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyAgentHandler.java index 5037f4e8..2a65eaf9 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyAgentHandler.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyAgentHandler.java @@ -43,6 +43,8 @@ public boolean equals(Object obj) { InputStream inputStream = getInputStream(param); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); + outputStream.flush(); + outputStream.close(); if (baseRequest != null) { baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyCustomizer.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyCustomizer.java index 8595f9c1..a37877b5 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyCustomizer.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyCustomizer.java @@ -32,6 +32,8 @@ public void customize(Connector connector, HttpConfiguration channelConfig, Requ InputStream inputStream = getInputStream(param); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); + outputStream.flush(); + outputStream.close(); invokeMethod(request, "setHandled", new Class[]{boolean.class}, new Object[]{true}); } } catch (Throwable e) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java index 78beb225..42a9b50f 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java @@ -40,6 +40,8 @@ public boolean handle(Object request, Object response) { InputStream inputStream = getInputStream(param); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); + outputStream.flush(); + outputStream.close(); return true; } } catch (Throwable e) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandListener.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandListener.java index bd6fde1a..0ab2430f 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandListener.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandListener.java @@ -6,6 +6,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; +import java.util.Scanner; /** * @author ReaJason @@ -23,14 +24,11 @@ public void requestInitialized(ServletRequestEvent servletRequestEvent) { } if (p != null) { String param = getParam(p); - HttpServletResponse servletResponse = (HttpServletResponse) getResponseFromRequest(request); + HttpServletResponse response = (HttpServletResponse) getResponseFromRequest(request); InputStream inputStream = getInputStream(param); - ServletOutputStream outputStream = servletResponse.getOutputStream(); - byte[] buf = new byte[8192]; - int length; - while ((length = inputStream.read(buf)) != -1) { - outputStream.write(buf, 0, length); - } + response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next()); + response.getWriter().flush(); + response.getWriter().close(); } } catch (Throwable e) { e.printStackTrace(); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java index 55be5b40..af225cdf 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java @@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; +import java.util.Scanner; /** * @author ReaJason @@ -30,12 +31,9 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) if (p != null) { String param = getParam(p); InputStream inputStream = getInputStream(param); - ServletOutputStream outputStream = response.getOutputStream(); - byte[] buf = new byte[8192]; - int length; - while ((length = inputStream.read(buf)) != -1) { - outputStream.write(buf, 0, length); - } + response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next()); + response.getWriter().flush(); + response.getWriter().close(); } } catch (Throwable e) { e.printStackTrace(); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandStruct2Action.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandStruct2Action.java index 8505e2e8..9928c236 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandStruct2Action.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandStruct2Action.java @@ -29,6 +29,7 @@ public String execute() throws Exception { InputStream inputStream = getInputStream(param); response.getWriter().println(new Scanner(inputStream).useDelimiter("\\A").next()); response.getWriter().flush(); + response.getWriter().close(); } } catch (Throwable ignored) { } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUndertowServletHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUndertowServletHandler.java index 2bc459d9..30784641 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUndertowServletHandler.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUndertowServletHandler.java @@ -32,6 +32,8 @@ public boolean equals(Object obj) { InputStream inputStream = getInputStream(param); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); + outputStream.flush(); + outputStream.close(); return true; } } catch (Throwable e) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUpgrade.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUpgrade.java index 5632405c..f036150f 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUpgrade.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUpgrade.java @@ -35,7 +35,7 @@ public boolean accept(Request req) { OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); outputStream.flush(); - inputStream.close(); + outputStream.close(); return true; } } catch (Throwable e) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java index ca73dc8f..b77fbf15 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java @@ -26,6 +26,8 @@ public void invoke(Request request, Response response) throws IOException, Servl String param = getParam(p); InputStream inputStream = getInputStream(param); response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next()); + response.getWriter().flush(); + response.getWriter().close(); return; } } catch (Throwable e) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandWebSocket.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandWebSocket.java index 2e2008a8..2b94fd58 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandWebSocket.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandWebSocket.java @@ -6,6 +6,7 @@ import javax.websocket.Session; import java.io.ByteArrayOutputStream; import java.io.InputStream; +import java.util.Scanner; /** * wsMemShell @@ -21,13 +22,7 @@ public class CommandWebSocket extends Endpoint implements MessageHandler.Whole&1,使用 {command} 作为占位符 + * 命令执行模板,使用 {command} 作为占位符 */ private String commandTemplate; diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/ServerProbe.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/ServerProbe.java index 47731f38..2c94aa3a 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/ServerProbe.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/ServerProbe.java @@ -9,7 +9,6 @@ /** * HTTP 服务类型识别,主要识别 Servlet 容器实现,例如 WildFly 识别为 Undertow,Payara 识别为 GlassFish * 很多国产中间件都是基于 GlassFish 改的,都会识别为 GlassFish - *
* 额外需要注意: * 1. 不会识别 SpringWebMVC Struct2 这种框架,只识别其提供 HTTP 服务的 Servlet 容器类型 * 2. 识别的顺序很重要,部分类型的识别单独拿出来是不准确的,没有测试的情况下,不要以下的 if 判断顺序 diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ApusicWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ApusicWriter.java index 0b172bbb..3f93fcbc 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ApusicWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ApusicWriter.java @@ -1,5 +1,8 @@ package com.reajason.javaweb.probe.payload.response; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Array; import java.lang.reflect.Field; @@ -32,15 +35,25 @@ public ApusicWriter() { Object response = getFieldValue(servletInvocation, "response"); String data = getDataFromReq(request); if (data != null && !data.isEmpty()) { - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + String result = ""; try { - writer.write(run(data)); + result = run(data); } catch (Throwable e) { - e.printStackTrace(); - e.printStackTrace(writer); + result = getErrorMessage(e); + } + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + writer.write(result); + writer.flush(); + writer.close(); + } } - writer.flush(); - writer.close(); return; } } @@ -96,4 +109,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/GlassFishWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/GlassFishWriter.java index fdefa862..42a999ee 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/GlassFishWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/GlassFishWriter.java @@ -1,5 +1,8 @@ package com.reajason.javaweb.probe.payload.response; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -22,10 +25,8 @@ public GlassFishWriter() { // GlassFish3 Thread thread = Thread.currentThread(); Object request = invokeMethod(getFieldValue(getFieldValue(thread, "processorTask"), "request"), "getNote", new Class[]{Integer.TYPE}, new Object[]{1}); - Object response = invokeMethod(request, "getResponse", null, null); - String data = getDataFromReq(request); - if (data != null && !data.isEmpty()) { - execute(response, data); + if (tryWriteRes(request)) { + return; } } catch (Exception x) { // GlassFish4+ @@ -48,10 +49,7 @@ public GlassFishWriter() { } Object notesHolder = getFieldValue(getFieldValue(coyoteRequest, "request"), "notesHolder"); Object request = invokeMethod(notesHolder, "getAttribute", new Class[]{String.class}, new Object[]{"org.apache.catalina.connector.Request"}); - Object response = invokeMethod(request, "getResponse", null, null); - String data = getDataFromReq(request); - if (data != null && !data.isEmpty()) { - execute(response, data); + if (tryWriteRes(request)) { return; } } @@ -64,15 +62,32 @@ public GlassFishWriter() { } } - private void execute(Object response, String data) throws Exception { - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); - try { - writer.write(run(data)); - } catch (Throwable e) { - e.printStackTrace(writer); + private boolean tryWriteRes(Object request) throws Exception { + Object response = invokeMethod(request, "getResponse", null, null); + String data = getDataFromReq(request); + if (data != null && !data.isEmpty()) { + String result = ""; + try { + result = run(data); + } catch (Throwable e) { + result = getErrorMessage(e); + } + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + writer.write(result); + writer.flush(); + writer.close(); + } + } + return true; } - writer.flush(); - writer.close(); + return false; } private String getDataFromReq(Object request) throws Exception { @@ -119,4 +134,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java index ac4b480d..a569507d 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java @@ -2,8 +2,10 @@ import org.eclipse.jetty.util.Callback; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; -import java.io.StringWriter; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -49,23 +51,30 @@ public JettyWriter() { } String data = getDataFromReq(request); if (data != null && !data.isEmpty()) { - StringWriter sw = new StringWriter(); - PrintWriter writer = new PrintWriter(sw); + String result = ""; try { - writer.write(run(data)); + result = run(data); } catch (Throwable e) { - e.printStackTrace(); - e.printStackTrace(writer); + result = getErrorMessage(e); } - String result = sw.toString(); - System.out.println("result: " + result); - try { - PrintWriter resWriter = (PrintWriter) invokeMethod(response, "getWriter", null, null); - resWriter.write(result); - } catch (Exception e) { - invokeMethod(response, "setStatus", new Class[]{int.class}, new Object[]{200}); - ByteBuffer content = UTF_8.encode(result); - invokeMethod(response, "write", new Class[]{boolean.class, ByteBuffer.class, Callback.class}, new Object[]{true, content, null}); + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + try { + PrintWriter resWriter = (PrintWriter) invokeMethod(response, "getWriter", null, null); + resWriter.write(result); + resWriter.flush(); + resWriter.close(); + } catch (Exception x) { + invokeMethod(response, "setStatus", new Class[]{int.class}, new Object[]{200}); + ByteBuffer content = UTF_8.encode(result); + invokeMethod(response, "write", new Class[]{boolean.class, ByteBuffer.class, Callback.class}, new Object[]{true, content, null}); + } + } } return; } @@ -122,4 +131,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ResinWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ResinWriter.java index 6e74836a..335bf42c 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ResinWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ResinWriter.java @@ -1,5 +1,8 @@ package com.reajason.javaweb.probe.payload.response; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -22,17 +25,30 @@ public ResinWriter() { Object request = invokeMethod(invocationClazz, "getContextRequest", null, null); Object response = getFieldValue(request, "_response"); String data = getDataFromReq(request); - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); - try { - writer.write(run(data)); - } catch (Throwable e) { - e.printStackTrace(writer); + if (data != null && !data.isEmpty()) { + String result = ""; + try { + result = run(data); + } catch (Throwable e) { + result = getErrorMessage(e); + } + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + writer.write(result); + writer.flush(); + writer.close(); + } + } + // com.caucho.server.connection.AbstractHttpResponse.close + // 不关闭 response 的话会遇到重复写响应体的情况 +// invokeMethod(response, "close", null, null); } - writer.flush(); - writer.close(); - // com.caucho.server.connection.AbstractHttpResponse.close - // 不关闭 response 的话会遇到重复写响应体的情况 - invokeMethod(response, "close", null, null); } catch (Throwable e) { e.printStackTrace(); } finally { @@ -84,4 +100,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/SpringWebMvcWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/SpringWebMvcWriter.java index 3af3297f..842995fe 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/SpringWebMvcWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/SpringWebMvcWriter.java @@ -1,5 +1,8 @@ package com.reajason.javaweb.probe.payload.response; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -23,17 +26,26 @@ public SpringWebMvcWriter() { Object response = invokeMethod(requestAttributes, "getResponse", null, null); String data = getDataFromReq(request); if (data != null && !data.isEmpty()) { - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + String result = ""; try { - writer.write(run(data)); + result = run(data); } catch (Throwable e) { - e.printStackTrace(writer); + result = getErrorMessage(e); + } + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + writer.write(result); + writer.flush(); + writer.close(); + } } - writer.flush(); - writer.close(); - return; } - } catch (Throwable e) { e.printStackTrace(); } finally { @@ -85,4 +97,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/Struct2Writer.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/Struct2Writer.java index 66a866d8..bed217b6 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/Struct2Writer.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/Struct2Writer.java @@ -1,5 +1,8 @@ package com.reajason.javaweb.probe.payload.response; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -23,15 +26,25 @@ public Struct2Writer() { Object response = getMethod.invoke(context, "com.opensymphony.xwork2.dispatcher.HttpServletResponse"); String data = getDataFromReq(request); if (data != null && !data.isEmpty()) { - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + String result = ""; try { - writer.write(run(data)); + result = run(data); } catch (Throwable e) { - e.printStackTrace(); - e.printStackTrace(writer); + result = getErrorMessage(e); + } + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + writer.write(result); + writer.flush(); + writer.close(); + } } - writer.flush(); - writer.close(); } } catch (Throwable e) { e.printStackTrace(); @@ -84,4 +97,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TomcatWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TomcatWriter.java index aa430359..81db8a5f 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TomcatWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TomcatWriter.java @@ -1,5 +1,8 @@ package com.reajason.javaweb.probe.payload.response; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -71,14 +74,25 @@ public TomcatWriter() { Object response = invokeMethod(request, "getResponse", null, null); String data = getDataFromReq(request); if (data != null && !data.isEmpty()) { - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + String result = ""; try { - writer.write(run(data)); + result = run(data); } catch (Throwable e) { - e.printStackTrace(writer); + result = getErrorMessage(e); + } + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + writer.write(result); + writer.flush(); + writer.close(); + } } - writer.flush(); - writer.close(); return; } } @@ -134,4 +148,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TongWebWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TongWebWriter.java index 8323910d..663ca463 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TongWebWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TongWebWriter.java @@ -1,5 +1,8 @@ package com.reajason.javaweb.probe.payload.response; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -70,14 +73,25 @@ private boolean tryWriteRes(Object coyoteRequest) throws Exception { Object response = invokeMethod(request, "getResponse", null, null); String data = getDataFromReq(request); if (data != null && !data.isEmpty()) { - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + String result = ""; try { - writer.write(run(data)); + result = run(data); } catch (Throwable e) { - e.printStackTrace(writer); + result = getErrorMessage(e); + } + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + writer.write(result); + writer.flush(); + writer.close(); + } } - writer.flush(); - writer.close(); return true; } return false; @@ -127,4 +141,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/UndertowWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/UndertowWriter.java index 0a5b12ac..bb382f91 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/UndertowWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/UndertowWriter.java @@ -1,5 +1,8 @@ package com.reajason.javaweb.probe.payload.response; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Array; import java.lang.reflect.Field; @@ -31,14 +34,27 @@ public UndertowWriter() { Object request = getFieldValue(value, "servletRequest"); Object response = getFieldValue(value, "servletResponse"); String data = getDataFromReq(request); - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); - try { - writer.write(run(data)); - } catch (Throwable e) { - e.printStackTrace(writer); + if (data != null && !data.isEmpty()) { + String result = ""; + try { + result = run(data); + } catch (Throwable e) { + result = getErrorMessage(e); + } + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + writer.write(result); + writer.flush(); + writer.close(); + } + } } - writer.flush(); - writer.close(); return; } } @@ -93,4 +109,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebLogicWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebLogicWriter.java index 7139202b..0fae745e 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebLogicWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebLogicWriter.java @@ -1,5 +1,8 @@ package com.reajason.javaweb.probe.payload.response; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -38,16 +41,27 @@ public WebLogicWriter() { } String data = getDataFromReq(request); if (data != null && !data.isEmpty()) { - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + String result = ""; try { - writer.write(run(data)); + result = run(data); } catch (Throwable e) { - e.printStackTrace(writer); + result = getErrorMessage(e); + } + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + writer.write(result); + writer.flush(); + writer.close(); + } } - writer.flush(); - writer.close(); // 防止重复写响应,提前触发 send 操作 - invokeMethod(response, "send", null, null); +// invokeMethod(response, "send", null, null); } } catch (Throwable e) { e.printStackTrace(); @@ -101,4 +115,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebSphereWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebSphereWriter.java index 796284e3..f9966702 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebSphereWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebSphereWriter.java @@ -1,5 +1,8 @@ package com.reajason.javaweb.probe.payload.response; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -32,17 +35,27 @@ public WebSphereWriter() { Object response = getFieldValue(wsThreadLocal, "currentThreadsIExtendedResponse"); String data = getDataFromReq(request); if (data != null && !data.isEmpty()) { - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + String result = ""; try { - writer.write(run(data)); + result = run(data); } catch (Throwable e) { - e.printStackTrace(writer); + result = getErrorMessage(e); + } + if (result != null) { + try { + OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null); + outputStream.write(result.getBytes()); + outputStream.flush(); + outputStream.close(); + } catch (Throwable e) { + PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + writer.write(result); + writer.flush(); + writer.close(); + } } - writer.flush(); - writer.close(); return; } - break; } } } catch (Throwable e) { @@ -96,4 +109,19 @@ public static Object getFieldValue(Object obj, String name) throws Exception { } throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8443232d..6d5dacd5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,12 +10,12 @@ reactor-netty = "1.1.25" jackson = "2.19.0" jetbrains-annotations = "26.0.2" -byte-buddy = "1.18.1" +byte-buddy = "1.18.2" commons-io = "2.21.0" -commons-lang3 = "3.19.0" +commons-lang3 = "3.20.0" commons-codec = "1.20.0" -logback = "1.5.21" -okhttp3 = "5.3.0" +logback = "1.5.22" +okhttp3 = "5.3.2" fastjson2 = "2.0.60" java-websocket = "1.6.0" @@ -45,6 +45,7 @@ java-websocket = { module = "org.java-websocket:Java-WebSocket", version.ref = " okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp3" } fastjson2 = { module = "com.alibaba.fastjson2:fastjson2", version.ref = "fastjson2" } jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" } +jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" } jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" } mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" } diff --git a/memshell-party-common/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java b/memshell-party-common/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java index 3b06622c..b5f18bd0 100644 --- a/memshell-party-common/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java +++ b/memshell-party-common/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java @@ -18,7 +18,6 @@ /** * Debug 信息打印移除器 * 目前仅支持移除以下几种 - *
* 1. System.out.println() - (printf 还不支持) * 2. e.printStackTrace() * 3. Logger.info (java.util) @@ -47,7 +46,8 @@ public MethodVisitor wrap(@NotNull TypeDescription instrumentedType, @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { if ((opcode == INVOKEVIRTUAL && owner.equals("java/io/PrintStream") && name.equals("println")) - || (opcode == INVOKEVIRTUAL && owner.endsWith("Exception") && name.equals("printStackTrace")) + || (opcode == INVOKEVIRTUAL && owner.endsWith("Exception") && name.equals("printStackTrace") && descriptor.equals("()V")) + || (opcode == INVOKEVIRTUAL && owner.equals("java/lang/Throwable") && name.equals("printStackTrace") && descriptor.equals("()V")) || (opcode == INVOKEVIRTUAL && owner.equals("java/util/logging/Logger") && (name.equals("info") || name.equals("warning"))) ) { String[] args = descriptor.substring(1, descriptor.indexOf(')')).split(";"); diff --git a/packer/src/main/java/com/reajason/javaweb/packer/AggregatePacker.java b/packer/src/main/java/com/reajason/javaweb/packer/AggregatePacker.java index 0c3be7d7..0bba0a07 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/AggregatePacker.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/AggregatePacker.java @@ -16,7 +16,7 @@ public interface AggregatePacker extends Packer { * 聚合打包当前所有分类下的 payload * * @param config 生成结果 - * @return key -> 打包名称,value -> 打包 payload + * @return key - 打包名称,value - 打包 payload */ default Map packAll(ClassPackerConfig config) { return Packers.getPackersWithParent(this.getClass()).stream().collect(Collectors.toMap( diff --git a/packer/src/main/java/com/reajason/javaweb/packer/spel/SpELSpringGzipPacker.java b/packer/src/main/java/com/reajason/javaweb/packer/spel/SpELSpringGzipPacker.java index 18015c1b..3f5257ce 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/spel/SpELSpringGzipPacker.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/spel/SpELSpringGzipPacker.java @@ -8,7 +8,7 @@ /** * * @since 5.2.24,SpEL 限制了长度为 10000,常规的 Base64 编码已经不适用,需要进一步使用 GZIP 压缩 - * InternalSpelExpressionParser.java + * https://github.com/spring-projects/spring-framework/blob/2ed1b6e6dda48ff0c74b67b39cba65676b5397b6/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java#L100 * * @author ReaJason * @since 2024/12/13 diff --git a/web/app/components/image-zoom.css b/web/app/components/image-zoom.css new file mode 100644 index 00000000..85ab6957 --- /dev/null +++ b/web/app/components/image-zoom.css @@ -0,0 +1,77 @@ +[data-rmiz] { + display: block; + position: relative; +} + +[data-rmiz-ghost] { + pointer-events: none; + position: absolute; +} + +[data-rmiz-btn-zoom], +[data-rmiz-btn-unzoom] { + display: none; +} + +[data-rmiz-content="found"] img { + cursor: zoom-in; +} + +[data-rmiz-modal][open] { + width: 100vw /* fallback */; + width: 100dvw; + + height: 100vh /* fallback */; + height: 100dvh; + + background-color: transparent; + max-width: none; + max-height: none; + margin: 0; + padding: 0; + position: fixed; + overflow: hidden; +} + +[data-rmiz-modal]:focus-visible { + outline: none; +} + +[data-rmiz-modal-overlay] { + transition: background-color 0.3s; + position: absolute; + inset: 0; +} + +[data-rmiz-modal-overlay="visible"] { + background-color: var(--color-fd-background); +} + +[data-rmiz-modal-overlay="hidden"] { + background-color: transparent; +} + +[data-rmiz-modal-content] { + width: 100%; + height: 100%; + position: relative; +} + +[data-rmiz-modal]::backdrop { + display: none; +} + +[data-rmiz-modal-img] { + cursor: zoom-out; + image-rendering: high-quality; + transform-origin: 0 0; + transition: transform 0.3s; + position: absolute; +} + +@media (prefers-reduced-motion: reduce) { + [data-rmiz-modal-overlay], + [data-rmiz-modal-img] { + transition-duration: 0.01ms !important; + } +} diff --git a/web/app/components/image-zoom.tsx b/web/app/components/image-zoom.tsx new file mode 100644 index 00000000..6f15872e --- /dev/null +++ b/web/app/components/image-zoom.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { Image, type ImageProps } from "fumadocs-core/framework"; +import type { ComponentProps } from "react"; +import Zoom, { type UncontrolledProps } from "react-medium-image-zoom"; +import "@/components/image-zoom.css"; + +export type ImageZoomProps = ImageProps & { + /** + * Image props when zoom in + */ + zoomInProps?: ComponentProps<"img">; + + /** + * Props for `react-medium-image-zoom` + */ + rmiz?: UncontrolledProps; +}; + +function getImageSrc(src: ImageProps["src"]): string { + if (typeof src === "string") return src; + + if (typeof src === "object") { + // Next.js + if ("default" in src) + return (src as { default: { src: string } }).default.src; + return src.src; + } + + return ""; +} + +export function ImageZoom({ + zoomInProps, + children, + rmiz, + ...props +}: ImageZoomProps) { + return ( + + {children ?? ( + + )} + + ); +} diff --git a/web/app/components/magicui/rainbow-button.tsx b/web/app/components/magicui/rainbow-button.tsx deleted file mode 100644 index 57bb689e..00000000 --- a/web/app/components/magicui/rainbow-button.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { Slot } from "@radix-ui/react-slot"; -import { cva, type VariantProps } from "class-variance-authority"; -import React from "react"; -import { cn } from "@/lib/utils"; - -const rainbowButtonVariants = cva( - cn( - "relative cursor-pointer group transition-all animate-rainbow", - "inline-flex items-center justify-center gap-2 shrink-0", - "rounded-sm outline-none focus-visible:ring-[3px] aria-invalid:border-destructive", - "text-sm font-medium whitespace-nowrap", - "disabled:pointer-events-none disabled:opacity-50", - "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0", - ), - { - variants: { - variant: { - default: - "border-0 bg-[linear-gradient(#121213,#121213),linear-gradient(#121213_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-primary-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] [border:calc(0.125rem)_solid_transparent] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#fff,#fff),linear-gradient(#fff_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]", - outline: - "border border-input border-b-transparent bg-[linear-gradient(#ffffff,#ffffff),linear-gradient(#ffffff_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-accent-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#0a0a0a,#0a0a0a),linear-gradient(#0a0a0a_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]", - }, - size: { - default: "h-9 px-4 py-2", - sm: "h-8 rounded-xl px-3 text-xs", - lg: "h-11 rounded-xl px-8", - icon: "size-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - }, -); - -interface RainbowButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean; -} - -const RainbowButton = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button"; - return ( - - ); - }, -); - -RainbowButton.displayName = "RainbowButton"; - -export { RainbowButton, rainbowButtonVariants, type RainbowButtonProps }; diff --git a/web/app/components/memshell/main-config-card.tsx b/web/app/components/memshell/main-config-card.tsx index 977a0ee7..fbeda2d0 100644 --- a/web/app/components/memshell/main-config-card.tsx +++ b/web/app/components/memshell/main-config-card.tsx @@ -2,6 +2,7 @@ import { ArrowUpRightIcon, AxeIcon, CommandIcon, + InfoIcon, NetworkIcon, ServerIcon, ShieldOffIcon, @@ -9,8 +10,8 @@ import { WaypointsIcon, ZapIcon, } from "lucide-react"; -import { type JSX, useCallback, useEffect, useState } from "react"; -import { FormProvider, type UseFormReturn } from "react-hook-form"; +import { type JSX, useCallback, useRef, useState } from "react"; +import { Controller, type UseFormReturn, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { AntSwordTabContent } from "@/components/memshell/tabs/antsword-tab"; import { BehinderTabContent } from "@/components/memshell/tabs/behinder-tab"; @@ -21,15 +22,12 @@ import { NeoRegTabContent } from "@/components/memshell/tabs/neoreg-tab"; import { Suo5TabContent } from "@/components/memshell/tabs/suo5-tab"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { - FormControl, - FormDescription, - FormField, - FormFieldItem, - FormFieldLabel, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; + Field, + FieldContent, + FieldDescription, + FieldError, + FieldLabel, +} from "@/components/ui/field"; import { Label } from "@/components/ui/label"; import { Select, @@ -40,12 +38,18 @@ import { } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Tabs } from "@/components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { type MainConfig, type ServerConfig, ShellToolType, } from "@/types/memshell"; -import type { MemShellFormSchema } from "@/types/schema.ts"; +import type { MemShellFormSchema } from "@/types/schema"; +import { Spinner } from "../ui/spinner"; const shellToolIcons: Record = { [ShellToolType.Behinder]: , @@ -61,7 +65,7 @@ const shellToolIcons: Record = { const defaultServerVersionOptions = [ { name: "Unknown", - value: "unknown", + value: "Unknown", }, ]; @@ -74,32 +78,21 @@ export default function MainConfigCard({ form: UseFormReturn; servers?: ServerConfig; }>) { + const { t } = useTranslation(["common", "memshell"]); + const [shellToolMap, setShellToolMap] = useState<{ [toolName: string]: string[]; }>(); - const [shellTools, setShellTools] = useState([ - ShellToolType.Godzilla, - ShellToolType.Behinder, - ShellToolType.AntSword, - ShellToolType.Command, - ShellToolType.Suo5, - ShellToolType.NeoreGeorg, - ShellToolType.Custom, - ]); + const [shellTools, setShellTools] = useState([]); const [shellTypes, setShellTypes] = useState([]); - const shellTool = form.watch("shellTool"); - const { t } = useTranslation(["common", "memshell"]); - const [serverVersionOptions, setServerVersionOptions] = useState( defaultServerVersionOptions, ); - // 处理一下 shellTypes 由于 server 或 shellTool 变更时无法正常为 form.shellType 赋值的问题 - useEffect(() => { - if (shellTypes.length > 0) { - form.setValue("shellType", shellTypes[0]); - } - }, [shellTypes, form]); + const shellTool = useWatch({ + control: form.control, + name: "shellTool", + }); const handleServerChange = useCallback( (value: string) => { @@ -126,7 +119,10 @@ export default function MainConfigCard({ } setShellTypes(currentShellTypes); - // 特殊环境的 JDK 版本 + if (currentShellTypes && currentShellTypes.length > 0) { + form.setValue("shellType", currentShellTypes[0]); + } + if ( (value === "SpringWebFlux" || value === "XXLJOB") && Number.parseInt(form.getValues("targetJdkVersion") as string, 10) < 52 @@ -136,7 +132,6 @@ export default function MainConfigCard({ form.setValue("targetJdkVersion", "50"); } - // 特殊的服务需要指定版本 if (value === "TongWeb") { setServerVersionOptions([ ...defaultServerVersionOptions, @@ -163,16 +158,6 @@ export default function MainConfigCard({ [form, mainConfig], ); - // 处理一下默认值 server 不刷新 shellType 的问题 - useEffect(() => { - if (mainConfig) { - const initialServer = form.getValues("server"); - if (initialServer && mainConfig[initialServer]) { - handleServerChange(initialServer); - } - } - }, [mainConfig, form, handleServerChange]); - const handleShellToolChange = useCallback( (value: string) => { const resetCommand = () => { @@ -223,6 +208,11 @@ export default function MainConfigCard({ } setShellTypes(currentShellTypes); + // 直接设置 shellType 而不是依赖 useEffect + if (currentShellTypes && currentShellTypes.length > 0) { + form.setValue("shellType", currentShellTypes[0]); + } + form.resetField("urlPattern"); form.resetField("shellClassName"); form.resetField("injectorClassName"); @@ -247,8 +237,17 @@ export default function MainConfigCard({ [form, servers, shellToolMap], ); + const initializedRef = useRef(false); + if (!initializedRef.current && mainConfig) { + const initialServer = form.getValues("server"); + if (initialServer && mainConfig[initialServer]) { + handleServerChange(initialServer); + initializedRef.current = true; + } + } + return ( - + <> @@ -257,222 +256,300 @@ export default function MainConfigCard({ - + ) : ( +
+ -
- ( - - {t("common:shellTool")} - - - )} - /> -
-
- ( - - - - - {t("common:debug")} - - )} - /> - ( - - - - - - - )} - /> - ( - - - - - - - )} - /> - ( - - - - - - - )} - /> - ( - - - - - - - )} - /> - ( - - - - - - - )} - /> -
+ + + {t("common:serverVersion")} + + + {fieldState.error && ( + + )} + + + )} + /> +
+
+ ( + + + + {t("common:shellTool")} + + + + + )} + /> +
+
+ ( +
+ + + + + + + +

{t("common:debug.description")}

+
+
+
+ )} + /> + ( +
+ + + + + + + +

{t("common:probe.description")}

+
+
+
+ )} + /> + ( +
+ + + + + + + +

{t("common:byPassJavaModule.description")}

+
+
+
+ )} + /> + ( +
+ + + + + + + +

{t("common:lambdaSuffix.description")}

+
+
+
+ )} + /> + ( +
+ + + + + + + +

{t("common:shrink.description")}

+
+
+
+ )} + /> + ( +
+ + + + + + + +

{t("common:staticInitialize.description")}

+
+
+
+ )} + /> +
+ + )} - - - - - - - - - - - + {mainConfig && ( + + + + + + + + + + + )} + ); } diff --git a/web/app/components/memshell/package-config-card.tsx b/web/app/components/memshell/package-config-card.tsx index 8df7f32f..43b0ee90 100644 --- a/web/app/components/memshell/package-config-card.tsx +++ b/web/app/components/memshell/package-config-card.tsx @@ -1,22 +1,13 @@ import { PackageIcon } from "lucide-react"; -import { useEffect, useState } from "react"; -import { FormProvider, type UseFormReturn } from "react-hook-form"; +import { useMemo } from "react"; +import { Controller, type UseFormReturn, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { - FormControl, - FormField, - FormItem, - FormLabel, -} from "@/components/ui/form"; +import { FieldLabel, FieldSet } from "@/components/ui/field"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { Spinner } from "@/components/ui/spinner"; import type { PackerConfig } from "@/types/memshell"; -import type { MemShellFormSchema } from "@/types/schema.ts"; - -type Option = { - name: string; - value: string; -}; +import type { MemShellFormSchema } from "@/types/schema"; export default function PackageConfigCard({ packerConfig, @@ -25,13 +16,19 @@ export default function PackageConfigCard({ packerConfig: PackerConfig | undefined; form: UseFormReturn; }>) { - const [options, setOptions] = useState>([]); - - const shellType = form.watch("shellType"); - const server = form.watch("server"); const { t } = useTranslation("common"); - useEffect(() => { + const shellType = useWatch({ + control: form.control, + name: "shellType", + }); + + const server = useWatch({ + control: form.control, + name: "server", + }); + + const options = useMemo(() => { const filteredOptions = (packerConfig ?? []).filter((name) => { if (!shellType || shellType === " ") { return true; @@ -44,23 +41,12 @@ export default function PackageConfigCard({ } return !name.startsWith("Agent") && !name.toLowerCase().startsWith("xxl"); }); - - const mappedOptions = filteredOptions.map((name) => { - return { - name: t(name), - value: name, - }; - }); - - setOptions(mappedOptions); - const currentValue = form.getValues("packingMethod"); - if ( - filteredOptions.length > 0 && - (!currentValue || !filteredOptions.includes(currentValue)) - ) { - form.setValue("packingMethod", filteredOptions[0]); - } - }, [form, packerConfig, server, shellType, t]); + form.setValue("packingMethod", filteredOptions[0]); + return filteredOptions.map((name) => ({ + name: t(name), + value: name, + })); + }, [packerConfig, shellType, server, t, form]); return ( @@ -72,41 +58,34 @@ export default function PackageConfigCard({ {options.length > 0 ? ( - - ( - - {t("packerMethod")} - - - {options.map(({ name, value }) => ( - - - - - - {name} - - - ))} - - - - )} - /> - + ( +
+ {t("packerMethod")} + + {options.map(({ name, value }) => ( +
+ + + {name} + +
+ ))} +
+
+ )} + /> ) : ( -
-
+
+ {t("loading")} diff --git a/web/app/components/memshell/quick-usage.tsx b/web/app/components/memshell/quick-usage.tsx index 8d050e75..72b1cd00 100644 --- a/web/app/components/memshell/quick-usage.tsx +++ b/web/app/components/memshell/quick-usage.tsx @@ -7,13 +7,13 @@ export function QuickUsage() { return ( - + {t("common:quickUsage.title")} -
    +
    1. {t("memshell:quickUsage.step1")}
    2. {t("memshell:quickUsage.step2")}
    3. {t("memshell:quickUsage.step3")}
    4. diff --git a/web/app/components/memshell/results/multi-packer.tsx b/web/app/components/memshell/results/multi-packer.tsx index fdcc2bfb..8dbb2091 100644 --- a/web/app/components/memshell/results/multi-packer.tsx +++ b/web/app/components/memshell/results/multi-packer.tsx @@ -79,7 +79,7 @@ export function MultiPackResult({
      + + )} + /> +
      + ( - - - {t("shellToolConfig.antSword.pass")} {t("common:optional")} - + + {t("common:headerName")} - + )} /> -
      - ( - - {t("common:headerName")} - - - - - )} - /> - ( - - - {t("common:headerValue")} {t("common:optional")} - - - - )} - /> -
      - - - - - + ( + + + {t("common:headerValue")} {t("common:optional")} + + + + )} + /> +
      + + + + ); } diff --git a/web/app/components/memshell/tabs/behinder-tab.tsx b/web/app/components/memshell/tabs/behinder-tab.tsx index cad5a954..bb662ad1 100644 --- a/web/app/components/memshell/tabs/behinder-tab.tsx +++ b/web/app/components/memshell/tabs/behinder-tab.tsx @@ -1,7 +1,7 @@ -import { FormProvider, type UseFormReturn } from "react-hook-form"; +import { Controller, type UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Card, CardContent } from "@/components/ui/card"; -import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form"; +import { Field, FieldLabel } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { TabsContent } from "@/components/ui/tabs"; import type { MemShellFormSchema } from "@/types/schema"; @@ -18,63 +18,61 @@ export function BehinderTabContent({ }>) { const { t } = useTranslation(["memshell", "common"]); return ( - - - - -
      - - -
      - + + +
      + + +
      + ( + + + {t("shellToolConfig.behinder.pass")} {t("common:optional")} + + + + )} + /> +
      + ( - - - {t("shellToolConfig.behinder.pass")} {t("common:optional")} - + + {t("common:headerName")} - + )} /> -
      - ( - - {t("common:headerName")} - - - )} - /> - ( - - - {t("common:headerValue")} {t("common:optional")} - - - - )} - /> -
      - - - - - + ( + + + {t("common:headerValue")} {t("common:optional")} + + + + )} + /> +
      + +
      +
      +
      ); } diff --git a/web/app/components/memshell/tabs/classname-field.tsx b/web/app/components/memshell/tabs/classname-field.tsx index 0be5c3bc..834cc06e 100644 --- a/web/app/components/memshell/tabs/classname-field.tsx +++ b/web/app/components/memshell/tabs/classname-field.tsx @@ -1,11 +1,11 @@ import { Shuffle } from "lucide-react"; import { Fragment, useEffect, useState } from "react"; -import { FormProvider, type UseFormReturn } from "react-hook-form"; +import { Controller, type UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form"; +import { Field, FieldLabel } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; -import type { MemShellFormSchema } from "@/types/schema.ts"; +import type { MemShellFormSchema } from "@/types/schema"; export function OptionalClassFormField({ form, @@ -72,44 +72,43 @@ export function OptionalClassFormField({ onCheckedChange={handleToggleRandomClass} />
      - - {!useRandomClassName && ( - ( - - - {t("mainConfig.shellClassName")} {t("common:optional")} - - - - )} - /> - )} - {!useRandomClassName && ( - ( - - - {t("mainConfig.injectorClassName")} {t("common:optional")} - - - - )} - /> - )} - + + {!useRandomClassName && ( + ( + + + {t("mainConfig.shellClassName")} {t("common:optional")} + + + + )} + /> + )} + {!useRandomClassName && ( + ( + + + {t("mainConfig.injectorClassName")} {t("common:optional")} + + + + )} + /> + )} ); } diff --git a/web/app/components/memshell/tabs/command-tab.tsx b/web/app/components/memshell/tabs/command-tab.tsx index 59b98aba..bff17206 100644 --- a/web/app/components/memshell/tabs/command-tab.tsx +++ b/web/app/components/memshell/tabs/command-tab.tsx @@ -1,7 +1,7 @@ import { useQuery } from "@tanstack/react-query"; -import { ChevronDown, ChevronRight } from "lucide-react"; +import { ChevronDown, ChevronRight, InfoIcon } from "lucide-react"; import { useState } from "react"; -import { FormProvider, type UseFormReturn } from "react-hook-form"; +import { Controller, type UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Card, CardContent } from "@/components/ui/card"; import { @@ -9,12 +9,7 @@ import { CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; -import { - FormControl, - FormField, - FormFieldItem, - FormFieldLabel, -} from "@/components/ui/form"; +import { Field, FieldLabel } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { Select, @@ -24,6 +19,11 @@ import { SelectValue, } from "@/components/ui/select"; import { TabsContent } from "@/components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { env } from "@/config"; import type { MemShellFormSchema } from "@/types/schema"; import { OptionalClassFormField } from "./classname-field"; @@ -51,131 +51,129 @@ export function CommandTabContent({ }); return ( - - - - -
      - - -
      - ( - - + + + +
      + + +
      + ( + +
      + {t("common:paramName")} {t("common:optional")} - - - - - - )} - /> - - - - {isAdvancedOpen ? ( - - ) : ( - - )} - {t("common:advancedConfig")} - - -
      - ( - - {t("common:encryptor")} - - - )} - /> - ( - - - {t("common:implementationClass")} - - - - )} - /> + + + + + + +

      {t("common:paramName.description")}

      +
      +
      - + + )} + /> + + + + {isAdvancedOpen ? ( + + ) : ( + + )} + {t("common:advancedConfig")} + + +
      + ( + + {t("common:encryptor")} + + + )} + /> + ( - - - {t("common:commandTemplate")} {t("common:optional")} - - - - -

      - {t("common:commandTemplate.description")} -

      -
      + + {t("common:implementationClass")} + + )} /> - - +
      + ( + + + {t("common:commandTemplate")} {t("common:optional")} + + +

      + {t("common:commandTemplate.description")} +

      +
      + )} + /> +
      +
      - - - - - + + + + ); } diff --git a/web/app/components/memshell/tabs/custom-tab.tsx b/web/app/components/memshell/tabs/custom-tab.tsx index 20d33dff..21fe7f14 100644 --- a/web/app/components/memshell/tabs/custom-tab.tsx +++ b/web/app/components/memshell/tabs/custom-tab.tsx @@ -1,21 +1,15 @@ import { useEffect, useRef, useState } from "react"; -import { FormProvider, type UseFormReturn } from "react-hook-form"; +import { Controller, type UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { Card, CardContent } from "@/components/ui/card"; -import { - FormControl, - FormField, - FormFieldItem, - FormFieldLabel, - FormMessage, -} from "@/components/ui/form"; +import { Field, FieldError, FieldLabel } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { TabsContent } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; -import { env } from "@/config.ts"; +import { env } from "@/config"; import type { MemShellFormSchema } from "@/types/schema"; import { OptionalClassFormField } from "./classname-field"; import { ShellTypeFormField } from "./shelltype-field"; @@ -90,74 +84,71 @@ export default function CustomTabContent({ }, [classNameEndpoint, form, shellClassBase64, t]); return ( - - - - -
      - - -
      - ( - - {t("shellClass")} - { - field.onChange(""); - setIsFile(value === "file"); - }} - className="flex items-center space-x-2" - > -
      - - -
      -
      - - -
      -
      - - {isFile ? ( - { - const file = e.target.files?.[0]; - if (file) { - const reader = new FileReader(); - reader.onload = (event) => { - const base64String = - (event.target?.result as string)?.split( - ",", - )[1] || ""; - field.onChange(base64String); - }; - reader.readAsDataURL(file); - } - }} - accept=".class" - placeholder={t("common:placeholders.input")} - type="file" - /> - ) : ( -